mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-27 15:48:12 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39544e323c | ||
|
|
24932b7e1b | ||
|
|
2aaf34937d | ||
|
|
bce3426d26 | ||
|
|
51ca73cb10 | ||
|
|
cd3221abdc | ||
|
|
19dd23c288 | ||
|
|
20f1cef9b2 | ||
|
|
2f400491d5 | ||
|
|
f6351e3fff | ||
|
|
f15ddcf9c4 | ||
|
|
88d904acde | ||
|
|
fbcf57bef5 | ||
|
|
8bfb670e65 | ||
|
|
e00bece65a | ||
|
|
2a03f7b1d4 | ||
|
|
6c7b7d6fc4 | ||
|
|
c43db7d5f6 | ||
|
|
cc92af12a4 | ||
|
|
df21f6019e | ||
|
|
232dc2653e | ||
|
|
98b4ff331c | ||
|
|
846b5b6e8a | ||
|
|
fc0528876f | ||
|
|
299b4cfe1d | ||
|
|
d640f50203 | ||
|
|
95ac0a7a51 | ||
|
|
0dc506317c | ||
|
|
3e3b7d22e6 | ||
|
|
e02463d779 | ||
|
|
cb37aea614 | ||
|
|
b40265e029 | ||
|
|
10c9e46fbe | ||
|
|
01f037ae83 | ||
|
|
bf618dc0b3 | ||
|
|
8b36e9fb39 | ||
|
|
0cf29113c0 | ||
|
|
e75d162dbd | ||
|
|
038f8352e0 | ||
|
|
2f48a5007a | ||
|
|
45db10220e | ||
|
|
ebe623bc07 | ||
|
|
4bcfae5905 | ||
|
|
eb6a7b9fea | ||
|
|
df322e6d57 | ||
|
|
d46a6bfed5 | ||
|
|
0e810e1e96 | ||
|
|
5da6c490b3 | ||
|
|
2ec9dda408 | ||
|
|
5b03721db6 | ||
|
|
3c644a712d | ||
|
|
19013d360a | ||
|
|
9bcb744a6a | ||
|
|
06ea0c32d3 | ||
|
|
feb3d9d31f | ||
|
|
198ee01437 | ||
|
|
58e3b5489b | ||
|
|
73b2eb6aeb | ||
|
|
6202526666 | ||
|
|
25bb6e8af7 | ||
|
|
191819488e | ||
|
|
85eb4761e8 | ||
|
|
90bf2ece82 | ||
|
|
772233d003 | ||
|
|
33955c1cb0 |
+20
-34
@@ -1,6 +1,6 @@
|
||||
# Contribution to Ryujinx
|
||||
# Contribution to Kenji-NX
|
||||
|
||||
You can contribute to Ryujinx with PRs, testing of PRs and issues. Contributing code and other implementations is greatly appreciated alongside simply filing issues for problems you encounter.
|
||||
You can contribute to Kenji-NX with PRs, testing of PRs and issues. Contributing code and other implementations is greatly appreciated alongside simply filing issues for problems you encounter.
|
||||
Please read the entire document before continuing as it can potentially save everyone involved a significant amount of time.
|
||||
|
||||
# Quick Links
|
||||
@@ -14,17 +14,17 @@ We always welcome bug reports, feature proposals and overall feedback. Here are
|
||||
|
||||
### Finding Existing Issues
|
||||
|
||||
Before filing a new issue, please search our [open issues](https://github.com/KeatonTheBot/Ryujinx/issues) to check if it already exists.
|
||||
Before filing a new issue, please search our [open issues](https://git.ryujinx.app/kenji-nx/ryujinx/-/issues) to check if it already exists.
|
||||
|
||||
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
|
||||
|
||||
### Writing a Good Feature Request
|
||||
|
||||
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
|
||||
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please go [here](https://git.ryujinx.app/kenji-nx/ryujinx/-/issues).
|
||||
|
||||
### Writing a Good Bug Report
|
||||
|
||||
Good bug reports make it easier for maintainers to verify and root cause the underlying problem. The better a bug report, the faster the problem will be resolved.
|
||||
Good bug reports make it easier for maintainers to verify and root cause the underlying problem. The better a bug report, the faster the problem will be resolved.
|
||||
Ideally, a bug report should contain the following information:
|
||||
|
||||
* A high-level description of the problem.
|
||||
@@ -34,13 +34,13 @@ Ideally, a bug report should contain the following information:
|
||||
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
|
||||
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
|
||||
|
||||
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/KeatonTheBot/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
|
||||
When ready to submit a bug report, please go [here](https://git.ryujinx.app/kenji-nx/ryujinx/-/issues.
|
||||
|
||||
## Contributing Changes
|
||||
|
||||
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
|
||||
|
||||
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/KeatonTheBot/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
|
||||
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](LICENSE.txt) docs define additional guidance.
|
||||
|
||||
### DOs and DON'Ts
|
||||
|
||||
@@ -67,30 +67,25 @@ Please do not:
|
||||
We use and recommend the following workflow:
|
||||
|
||||
1. Create or find an issue for your work.
|
||||
- You can skip this step for trivial changes.
|
||||
- Get agreement from the team and the community that your proposed change is a good one if it is of significant size or changes core functionality.
|
||||
- Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
|
||||
- You can skip this step for trivial changes.
|
||||
- Get agreement from the team and the community that your proposed change is a good one if it is of significant size or changes core functionality.
|
||||
- Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
|
||||
2. Create a personal fork of the repository on GitHub (if you don't already have one).
|
||||
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
|
||||
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
||||
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
||||
4. Make and commit your changes to your branch.
|
||||
- [Build Instructions](https://github.com/KeatonTheBot/Ryujinx#building) explains how to build and test.
|
||||
- Commit messages should be clear statements of action and intent.
|
||||
- [Build Instructions](https://git.ryujinx.app/kenji-nx/ryujinx#building) explains how to build and test.
|
||||
- Commit messages should be clear statements of action and intent.
|
||||
6. Build the repository with your changes.
|
||||
- Make sure that the builds are clean.
|
||||
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
||||
- Make sure that the builds are clean.
|
||||
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
||||
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
|
||||
- State in the description what issue or improvement your change is addressing.
|
||||
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/KeatonTheBot/Ryujinx/actions) to check for outstanding errors.
|
||||
- State in the description what issue or improvement your change is addressing.
|
||||
8. Wait for feedback or approval of your changes from the core development team
|
||||
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
||||
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
||||
9. When the team members have signed off, and all checks are green, your PR will be merged.
|
||||
- The next official build will automatically include your change.
|
||||
- You can delete the branch you used for making the change.
|
||||
|
||||
### Good First Issues
|
||||
|
||||
The team marks the most straightforward issues as [good first issues](https://github.com/KeatonTheBot/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
|
||||
- The next official build will automatically include your change.
|
||||
- You can delete the branch you used for making the change.
|
||||
|
||||
### Commit Messages
|
||||
|
||||
@@ -111,15 +106,6 @@ Fix #42
|
||||
|
||||
Also do your best to factor commits appropriately, not too large with unrelated things in the same commit, and not too small with the same small change applied N times in N different commits.
|
||||
|
||||
### PR - CI Process
|
||||
|
||||
The [Ryujinx continuous integration](https://github.com/KeatonTheBot/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
|
||||
|
||||
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
|
||||
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
|
||||
* There is an error within the PR that fails a test or errors the compiler.
|
||||
* Random failure of the workflow can occasionally result in a CI failure. In this scenario a maintainer will manually restart the job.
|
||||
|
||||
### PR Feedback
|
||||
|
||||
Ryujinx team and community members will provide feedback on your change. Community feedback is highly valued. You may see the absence of team feedback if the community has already provided good review feedback.
|
||||
@@ -134,5 +120,5 @@ Ryujinx uses some implementations and frameworks from other projects. The follow
|
||||
|
||||
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
|
||||
- The license of the file is left in-tact.
|
||||
- The contribution is correctly attributed in the [3rd party notices](https://github.com/KeatonTheBot/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
||||
- The contribution is correctly attributed in the [3rd party notices](distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
||||
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||
<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.6.1" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
@@ -35,10 +32,12 @@
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
||||
<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.AllArch" Version="6.1.2-build3" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.2-build4" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.macOS" Version="5.0.3-build14" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.2-build4" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0-alpha.103" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS-Redux" Version="2.32.6" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
@@ -48,8 +47,8 @@
|
||||
<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.3" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.3" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.5" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.5" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<img src="distribution/misc/Logo.png" alt="Kenji-NX">
|
||||
<img src="https://git.ryujinx.app/kenji-nx/ryujinx/-/raw/master/distribution/misc/Logo.png" alt="Kenji-NX">
|
||||
<br>
|
||||
<b>Kenji-NX</b>
|
||||
<br>
|
||||
<a href="https://github.com/KeatonTheBot/Kenji-NX/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/KeatonTheBot/Kenji-NX"
|
||||
<a href="https://github.com/Kenji-NX/Releases/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/Kenji-NX/Releases"
|
||||
alt="Latest Release">
|
||||
</a>
|
||||
</h1>
|
||||
@@ -14,7 +14,7 @@
|
||||
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://github.com/KeatonTheBot/Kenji-NX/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
||||
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>
|
||||
@@ -59,11 +59,11 @@ 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).
|
||||
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
||||
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
|
||||
|
||||
### Step 2
|
||||
|
||||
Either use `git clone https://github.com/KeatonTheBot/Kenji-NX` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||
Either use `git clone https://git.ryujinx.app/kenji-nx/ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||
|
||||
### Step 3
|
||||
|
||||
|
||||
@@ -63,8 +63,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
@@ -201,10 +199,6 @@ Global
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
# Documents Index
|
||||
|
||||
This repo includes several documents that explain both high-level and low-level concepts about Ryujinx and its functions. These are very useful for contributors, to get context that can be very difficult to acquire from just reading code.
|
||||
This repo includes several documents that explain both high-level and low-level concepts about Kenji-NX and its functions. These are very useful for contributors, to get context that can be very difficult to acquire from just reading code.
|
||||
|
||||
Intro to Ryujinx
|
||||
Intro to Kenji-NX
|
||||
==================
|
||||
|
||||
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
||||
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.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Contributing Rules
|
||||
|
||||
All contributions to KeatonTheBot/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
|
||||
All contributions to Kenji-NX repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
|
||||
|
||||
To merge pull requests, you must have write permissions in the repository.
|
||||
|
||||
@@ -18,17 +18,13 @@ To merge pull requests, you must have write permissions in the repository.
|
||||
|
||||
## Pull Request Ownership
|
||||
|
||||
Every pull request will have automatically have labels and reviewers assigned. The label not only indicates the code segment which the change touches but also the area reviewers to be assigned.
|
||||
Every pull request will automatically have labels and reviewers assigned. The label not only indicates the code segment which the change touches but also the area reviewers to be assigned.
|
||||
|
||||
If during the code review process a merge conflict occurs, the PR author is responsible for its resolution. Help will be provided if necessary although GitHub makes this easier by allowing simple conflict resolution using the [conflict-editor](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github).
|
||||
|
||||
## Pull Request Builds
|
||||
|
||||
When submitting a PR to the `KeatonTheBot/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/KeatonTheBot/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
|
||||
|
||||
## Review Turnaround Times
|
||||
|
||||
Ryujinx is a project that is maintained by volunteers on a completely free-time basis. As such we cannot guarantee any particular timeframe for pull request review and approval. Weeks to months are common for larger (>500 line) PRs but there are some additional best practises to avoid review purgatory.
|
||||
Kenji-NX is a project that is maintained by volunteers on a completely free-time basis. As such we cannot guarantee any particular timeframe for pull request review and approval. Weeks to months are common for larger (>500 line) PRs but there are some additional best practises to avoid review purgatory.
|
||||
|
||||
* Make the reviewers life easier wherever possible. Make use of descriptive commit names, code comments and XML docs where applicable.
|
||||
* If there is disagreement on feedback then always lean on the side of the development team and community over any personal opinion.
|
||||
@@ -41,8 +37,7 @@ To re-iterate, make the review as easy for us as possible, respond promptly and
|
||||
Anyone with write access can merge a pull request manually when the following conditions have been met:
|
||||
|
||||
* The PR has been approved by two reviewers and any other objections are addressed.
|
||||
* You can request follow up reviews from the original reviewers if they requested changes.
|
||||
* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/KeatonTheBot/Ryujinx/actions) tab of your PR.
|
||||
* You can request follow-up reviews from the original reviewers if they requested changes.
|
||||
|
||||
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
|
||||
|
||||
|
||||
+18
-4
@@ -1,7 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
<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" />
|
||||
</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 -->
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="LibHacAlpha">
|
||||
<package pattern="Ryujinx.LibHac" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RuntimeIdentifiers>osx-arm64</RuntimeIdentifiers>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -283,8 +283,6 @@ namespace ARMeilleure.Instructions
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsr:
|
||||
shift = 32;
|
||||
break;
|
||||
case ShiftType.Asr:
|
||||
shift = 32;
|
||||
break;
|
||||
@@ -332,8 +330,6 @@ namespace ARMeilleure.Instructions
|
||||
switch (shiftType)
|
||||
{
|
||||
case ShiftType.Lsr:
|
||||
shift = 32;
|
||||
break;
|
||||
case ShiftType.Asr:
|
||||
shift = 32;
|
||||
break;
|
||||
|
||||
@@ -305,8 +305,6 @@ namespace ARMeilleure.Instructions
|
||||
context.Store16(physAddr, value);
|
||||
break;
|
||||
case 2:
|
||||
context.Store(physAddr, value);
|
||||
break;
|
||||
case 3:
|
||||
context.Store(physAddr, value);
|
||||
break;
|
||||
@@ -591,8 +589,6 @@ namespace ARMeilleure.Instructions
|
||||
value = context.VectorInsert16(vector, value, elem);
|
||||
break;
|
||||
case 2:
|
||||
value = context.VectorInsert(vector, value, elem);
|
||||
break;
|
||||
case 3:
|
||||
value = context.VectorInsert(vector, value, elem);
|
||||
break;
|
||||
@@ -733,8 +729,6 @@ namespace ARMeilleure.Instructions
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsr:
|
||||
shift = 32;
|
||||
break;
|
||||
case ShiftType.Asr:
|
||||
shift = 32;
|
||||
break;
|
||||
|
||||
@@ -2082,8 +2082,6 @@ namespace ARMeilleure.Instructions
|
||||
vector = context.VectorInsert16(vector, value, index);
|
||||
break;
|
||||
case 2:
|
||||
vector = context.VectorInsert(vector, value, index);
|
||||
break;
|
||||
case 3:
|
||||
vector = context.VectorInsert(vector, value, index);
|
||||
break;
|
||||
|
||||
@@ -1,39 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class MathHelper
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Abs(double value)
|
||||
{
|
||||
return Math.Abs(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Ceiling(double value)
|
||||
{
|
||||
return Math.Ceiling(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Floor(double value)
|
||||
{
|
||||
return Math.Floor(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Round(double value, int mode)
|
||||
{
|
||||
return Math.Round(value, (MidpointRounding)mode);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Truncate(double value)
|
||||
{
|
||||
return Math.Truncate(value);
|
||||
@@ -42,31 +62,51 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class MathHelperF
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Abs(float value)
|
||||
{
|
||||
return MathF.Abs(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Ceiling(float value)
|
||||
{
|
||||
return MathF.Ceiling(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Floor(float value)
|
||||
{
|
||||
return MathF.Floor(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Round(float value, int mode)
|
||||
{
|
||||
return MathF.Round(value, (MidpointRounding)mode);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Truncate(float value)
|
||||
{
|
||||
return MathF.Truncate(value);
|
||||
|
||||
@@ -2,7 +2,11 @@ using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
@@ -35,7 +39,11 @@ namespace ARMeilleure.Instructions
|
||||
Context = null;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void Break(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -45,7 +53,11 @@ namespace ARMeilleure.Instructions
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void SupervisorCall(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -55,7 +67,11 @@ namespace ARMeilleure.Instructions
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void Undefined(ulong address, int opCode)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -66,31 +82,51 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
#region "System registers"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCtrEl0()
|
||||
{
|
||||
return GetContext().CtrEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetDczidEl0()
|
||||
{
|
||||
return GetContext().DczidEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCntfrqEl0()
|
||||
{
|
||||
return GetContext().CntfrqEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCntpctEl0()
|
||||
{
|
||||
return GetContext().CntpctEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCntvctEl0()
|
||||
{
|
||||
return GetContext().CntvctEl0;
|
||||
@@ -98,31 +134,51 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Read"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static byte ReadByte(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<byte>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ushort ReadUInt16(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<ushort>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint ReadUInt32(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<uint>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong ReadUInt64(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<ulong>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 ReadVector128(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<V128>(address);
|
||||
@@ -130,56 +186,92 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Write"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteByte(ulong address, byte value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteUInt16(ulong address, ushort value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteUInt32(ulong address, uint value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteUInt64(ulong address, ulong value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteVector128(ulong address, V128 value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void EnqueueForRejit(ulong address)
|
||||
{
|
||||
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void SignalMemoryTracking(ulong address, ulong size, byte write)
|
||||
{
|
||||
GetMemoryManager().SignalMemoryTracking(address, size, write == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void ThrowInvalidMemoryAccess(ulong address)
|
||||
{
|
||||
throw new InvalidAccessException(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetFunctionAddress(ulong address)
|
||||
{
|
||||
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
|
||||
@@ -187,13 +279,21 @@ namespace ARMeilleure.Instructions
|
||||
return (ulong)function.FuncPointer.ToInt64();
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void InvalidateCacheLine(ulong address)
|
||||
{
|
||||
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static byte CheckSynchronization()
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class SoftFallback
|
||||
{
|
||||
#region "ShrImm64"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static long SignedShrImm64(long value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
@@ -50,7 +58,11 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
@@ -95,7 +107,11 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Saturation"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static int SatF32ToS32(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -107,7 +123,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static long SatF32ToS64(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -119,7 +139,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint SatF32ToU32(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -131,7 +155,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong SatF32ToU64(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -143,7 +171,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static int SatF64ToS32(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -155,7 +187,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static long SatF64ToS64(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -167,7 +203,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint SatF64ToU32(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -179,7 +219,11 @@ namespace ARMeilleure.Instructions
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong SatF64ToU64(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -193,7 +237,11 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Count"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
value ^= value >> 1;
|
||||
@@ -213,7 +261,11 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
if (value == 0ul)
|
||||
@@ -237,49 +289,81 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Table"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
|
||||
@@ -321,22 +405,54 @@ namespace ARMeilleure.Instructions
|
||||
private const uint Crc32RevPoly = 0xedb88320;
|
||||
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
|
||||
|
||||
private static uint Crc32h(uint crc, uint poly, ushort val)
|
||||
@@ -387,25 +503,41 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Aes"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Decrypt(V128 value, V128 roundKey)
|
||||
{
|
||||
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Encrypt(V128 value, V128 roundKey)
|
||||
{
|
||||
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 InverseMixColumns(V128 value)
|
||||
{
|
||||
return CryptoHelper.AesInvMixColumns(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 MixColumns(V128 value)
|
||||
{
|
||||
return CryptoHelper.AesMixColumns(value);
|
||||
@@ -413,7 +545,11 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Sha1"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
@@ -434,13 +570,21 @@ namespace ARMeilleure.Instructions
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint FixedRotate(uint hash_e)
|
||||
{
|
||||
return hash_e.Rol(30);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
@@ -461,7 +605,11 @@ namespace ARMeilleure.Instructions
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
@@ -482,7 +630,11 @@ namespace ARMeilleure.Instructions
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
|
||||
{
|
||||
ulong t2 = w4_7.Extract<ulong>(0);
|
||||
@@ -493,7 +645,11 @@ namespace ARMeilleure.Instructions
|
||||
return result ^ (w0_3 ^ w8_11);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
|
||||
{
|
||||
V128 t = tw0_3 ^ (w12_15 >> 32);
|
||||
@@ -538,19 +694,31 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Sha256"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||
{
|
||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||
{
|
||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
|
||||
{
|
||||
V128 result = new();
|
||||
@@ -569,7 +737,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
|
||||
{
|
||||
V128 result = new();
|
||||
@@ -671,7 +843,11 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
|
||||
{
|
||||
V128 result = V128.Zero;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
@@ -313,7 +317,11 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat16_32
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPConvert(ushort valueBits)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -489,7 +497,11 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat16_64
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPConvert(ushort valueBits)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -665,7 +677,11 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat32_16
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ushort FPConvert(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -785,13 +801,21 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat32
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPAdd(float value1, float value2)
|
||||
{
|
||||
return FPAddFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPAddFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -848,7 +872,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static int FPCompare(float value1, float value2, byte signalNaNs)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -887,7 +915,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareEQ(float value1, float value2)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, false);
|
||||
@@ -920,19 +952,31 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareGE(float value1, float value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareGEFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -962,13 +1006,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareGT(float value1, float value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareGTFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -998,31 +1050,51 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareLE(float value1, float value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareLT(float value1, float value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value2, value1, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPDiv(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1075,13 +1147,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMax(float value1, float value2)
|
||||
{
|
||||
return FPMaxFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMaxFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -1148,7 +1228,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMaxNum(float value1, float value2)
|
||||
{
|
||||
return FPMaxNumFpscrImpl(value1, value2, false);
|
||||
@@ -1174,19 +1258,31 @@ namespace ARMeilleure.Instructions
|
||||
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMaxNumFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMin(float value1, float value2)
|
||||
{
|
||||
return FPMinFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMinFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -1253,13 +1349,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMinNum(float value1, float value2)
|
||||
{
|
||||
return FPMinNumFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMinNumFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -1285,13 +1389,21 @@ namespace ARMeilleure.Instructions
|
||||
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMul(float value1, float value2)
|
||||
{
|
||||
return FPMulFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMulFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -1344,13 +1456,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMulAdd(float valueA, float value1, float value2)
|
||||
{
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMulAddFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||
@@ -1422,7 +1542,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMulSub(float valueA, float value1, float value2)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
@@ -1430,7 +1554,11 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
@@ -1438,7 +1566,11 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPMulX(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1484,7 +1616,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPNegMulAdd(float valueA, float value1, float value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
@@ -1493,7 +1629,11 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPNegMulSub(float valueA, float value1, float value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
@@ -1501,13 +1641,21 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRecipEstimate(float value)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRecipEstimateFpscr(float value, byte standardFpscr)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
@@ -1600,7 +1748,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRecipStep(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1635,7 +1787,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRecipStepFused(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1679,7 +1835,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRecpX(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1705,13 +1865,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRSqrtEstimate(float value)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
@@ -1831,7 +1999,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRSqrtStep(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1866,7 +2038,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPRSqrtStepFused(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1910,7 +2086,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPSqrt(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1953,7 +2133,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float FPSub(float value1, float value2)
|
||||
{
|
||||
return FPSubFpscrImpl(value1, value2, false);
|
||||
@@ -2200,7 +2384,11 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat64_16
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ushort FPConvert(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -2320,13 +2508,21 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat64
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPAdd(double value1, double value2)
|
||||
{
|
||||
return FPAddFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPAddFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2383,7 +2579,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static int FPCompare(double value1, double value2, byte signalNaNs)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -2422,13 +2622,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareEQ(double value1, double value2)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareEQFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2461,13 +2669,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareGE(double value1, double value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareGEFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2497,13 +2713,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareGT(double value1, double value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareGTFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2533,31 +2757,51 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareLE(double value1, double value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareLT(double value1, double value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value2, value1, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPDiv(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -2610,13 +2854,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMax(double value1, double value2)
|
||||
{
|
||||
return FPMaxFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMaxFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2683,13 +2935,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMaxNum(double value1, double value2)
|
||||
{
|
||||
return FPMaxNumFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMaxNumFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2715,13 +2975,21 @@ namespace ARMeilleure.Instructions
|
||||
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMin(double value1, double value2)
|
||||
{
|
||||
return FPMinFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMinFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2788,13 +3056,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMinNum(double value1, double value2)
|
||||
{
|
||||
return FPMinNumFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMinNumFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2820,13 +3096,21 @@ namespace ARMeilleure.Instructions
|
||||
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMul(double value1, double value2)
|
||||
{
|
||||
return FPMulFpscrImpl(value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMulFpscr(double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
@@ -2879,13 +3163,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMulAdd(double valueA, double value1, double value2)
|
||||
{
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMulAddFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||
@@ -2957,7 +3249,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMulSub(double valueA, double value1, double value2)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
@@ -2965,7 +3261,11 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
@@ -2973,7 +3273,11 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPMulX(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3019,7 +3323,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPNegMulAdd(double valueA, double value1, double value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
@@ -3028,7 +3336,11 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPNegMulSub(double valueA, double value1, double value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
@@ -3036,13 +3348,21 @@ namespace ARMeilleure.Instructions
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRecipEstimate(double value)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRecipEstimateFpscr(double value, byte standardFpscr)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
@@ -3135,7 +3455,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRecipStep(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3170,7 +3494,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRecipStepFused(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3214,7 +3542,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRecpX(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3240,13 +3572,21 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRSqrtEstimate(double value)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
@@ -3366,7 +3706,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRSqrtStep(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3401,7 +3745,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPRSqrtStepFused(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3445,7 +3793,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPSqrt(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3488,7 +3840,11 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double FPSub(double value1, double value2)
|
||||
{
|
||||
return FPSubFpscr(value1, value2, false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ARMeilleure
|
||||
{
|
||||
using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities;
|
||||
using X86HardwareCapabilities = ARMeilleure.CodeGen.X86.HardwareCapabilities;
|
||||
using Arm64HardwareCapabilities = CodeGen.Arm64.HardwareCapabilities;
|
||||
using X86HardwareCapabilities = CodeGen.X86.HardwareCapabilities;
|
||||
|
||||
public static class Optimizations
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
class DelegateInfo
|
||||
public class DelegateInfo
|
||||
{
|
||||
public IntPtr FuncPtr { get; }
|
||||
|
||||
|
||||
@@ -2,11 +2,358 @@ using ARMeilleure.Instructions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
#if ANDROID
|
||||
using ARMeilleure.State;
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class Delegates
|
||||
{
|
||||
#if ANDROID
|
||||
// MathHelper delegates
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double MathHelperAbsDelegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double MathHelperCeilingDelegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double MathHelperFloorDelegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double MathHelperRoundDelegate(double value, int mode);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double MathHelperTruncateDelegate(double value);
|
||||
|
||||
// MathHelperF delegates
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float MathHelperFAbsDelegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float MathHelperFCeilingDelegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float MathHelperFFloorDelegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float MathHelperFRoundDelegate(float value, int mode);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float MathHelperFTruncateDelegate(float value);
|
||||
|
||||
// NativeInterface delegates
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceBreakDelegate(ulong address, int imm);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate byte NativeInterfaceCheckSynchronizationDelegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceEnqueueForRejitDelegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceGetCntfrqEl0Delegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceGetCntpctEl0Delegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceGetCntvctEl0Delegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceGetCtrEl0Delegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceGetDczidEl0Delegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceGetFunctionAddressDelegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceInvalidateCacheLineDelegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate byte NativeInterfaceReadByteDelegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ushort NativeInterfaceReadUInt16Delegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint NativeInterfaceReadUInt32Delegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong NativeInterfaceReadUInt64Delegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 NativeInterfaceReadVector128Delegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceSignalMemoryTrackingDelegate(ulong address, ulong size, byte write);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceSupervisorCallDelegate(ulong address, int imm);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceThrowInvalidMemoryAccessDelegate(ulong address);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceUndefinedDelegate(ulong address, int opCode);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceWriteByteDelegate(ulong address, byte value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceWriteUInt16Delegate(ulong address, ushort value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceWriteUInt32Delegate(ulong address, uint value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceWriteUInt64Delegate(ulong address, ulong value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void NativeInterfaceWriteVector128Delegate(ulong address, V128 value);
|
||||
|
||||
// SoftFallback delegates
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong SoftFallbackCountLeadingSignsDelegate(ulong value, int size);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong SoftFallbackCountLeadingZerosDelegate(ulong value, int size);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32bDelegate(uint crc, byte val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32cbDelegate(uint crc, byte val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32chDelegate(uint crc, ushort val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32cwDelegate(uint crc, uint val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32cxDelegate(uint crc, ulong val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32hDelegate(uint crc, ushort val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32wDelegate(uint crc, uint val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackCrc32xDelegate(uint crc, ulong val);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackDecryptDelegate(V128 value, V128 roundKey);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackEncryptDelegate(V128 value, V128 roundKey);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackFixedRotateDelegate(uint hash_e);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackHashChooseDelegate(V128 hash_abcd, uint hash_e, V128 wk);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackHashLowerDelegate(V128 hash_abcd, V128 hash_efgh, V128 wk);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackHashMajorityDelegate(V128 hash_abcd, uint hash_e, V128 wk);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackHashParityDelegate(V128 hash_abcd, uint hash_e, V128 wk);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackHashUpperDelegate(V128 hash_abcd, V128 hash_efgh, V128 wk);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackInverseMixColumnsDelegate(V128 value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackMixColumnsDelegate(V128 value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackPolynomialMult64_128Delegate(ulong op1, ulong op2);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int SoftFallbackSatF32ToS32Delegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate long SoftFallbackSatF32ToS64Delegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackSatF32ToU32Delegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong SoftFallbackSatF32ToU64Delegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int SoftFallbackSatF64ToS32Delegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate long SoftFallbackSatF64ToS64Delegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate uint SoftFallbackSatF64ToU32Delegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong SoftFallbackSatF64ToU64Delegate(double value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackSha1SchedulePart1Delegate(V128 w0_3, V128 w4_7, V128 w8_11);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackSha1SchedulePart2Delegate(V128 tw0_3, V128 w12_15);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackSha256SchedulePart1Delegate(V128 w0_3, V128 w4_7);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackSha256SchedulePart2Delegate(V128 w0_3, V128 w8_11, V128 w12_15);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate long SoftFallbackSignedShrImm64Delegate(long value, long roundConst, int shift);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbl1Delegate(V128 vector, int bytes, V128 table);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbl2Delegate(V128 vector, int bytes, V128 table0, V128 table1);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbl3Delegate(V128 vector, int bytes, V128 table0, V128 table1, V128 table2);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbl4Delegate(V128 vector, int bytes, V128 table0, V128 table1, V128 table2, V128 table3);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbx1Delegate(V128 dest, V128 vector, int bytes, V128 table);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbx2Delegate(V128 dest, V128 vector, int bytes, V128 table0, V128 table1);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbx3Delegate(V128 dest, V128 vector, int bytes, V128 table0, V128 table1, V128 table2);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate V128 SoftFallbackTbx4Delegate(V128 dest, V128 vector, int bytes, V128 table0, V128 table1, V128 table2, V128 table3);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong SoftFallbackUnsignedShrImm64Delegate(ulong value, long roundConst, int shift);
|
||||
|
||||
// SoftFloat delegates
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat16_32FPConvertDelegate(ushort value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat16_64FPConvertDelegate(ushort value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPAddDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPAddFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int SoftFloat32FPCompareDelegate(float a, float b, byte signalNaNs);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareEQDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareEQFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareGEDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareGEFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareGTDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareGTFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareLEDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareLEFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareLTDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPCompareLTFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPDivDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMaxDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMaxFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMaxNumDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMaxNumFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMinDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMinFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMinNumDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMinNumFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulFpscrDelegate(float a, float b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulAddDelegate(float a, float b, float c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulAddFpscrDelegate(float a, float b, float c, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulSubDelegate(float a, float b, float c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulSubFpscrDelegate(float a, float b, float c, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPMulXDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPNegMulAddDelegate(float a, float b, float c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPNegMulSubDelegate(float a, float b, float c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRecipEstimateDelegate(float a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRecipEstimateFpscrDelegate(float a, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRecipStepDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRecipStepFusedDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRecpXDelegate(float a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRSqrtEstimateDelegate(float a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRSqrtEstimateFpscrDelegate(float a, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRSqrtStepDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPRSqrtStepFusedDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPSqrtDelegate(float a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate float SoftFloat32FPSubDelegate(float a, float b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ushort SoftFloat32_16FPConvertDelegate(float value);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPAddDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPAddFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int SoftFloat64FPCompareDelegate(double a, double b, byte signalNaNs);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareEQDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareEQFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareGEDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareGEFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareGTDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareGTFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareLEDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareLEFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareLTDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPCompareLTFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPDivDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMaxDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMaxFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMaxNumDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMaxNumFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMinDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMinFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMinNumDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMinNumFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulFpscrDelegate(double a, double b, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulAddDelegate(double a, double b, double c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulAddFpscrDelegate(double a, double b, double c, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulSubDelegate(double a, double b, double c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulSubFpscrDelegate(double a, double b, double c, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPMulXDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPNegMulAddDelegate(double a, double b, double c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPNegMulSubDelegate(double a, double b, double c);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRecipEstimateDelegate(double a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRecipEstimateFpscrDelegate(double a, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRecipStepDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRecipStepFusedDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRecpXDelegate(double a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRSqrtEstimateDelegate(double a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRSqrtEstimateFpscrDelegate(double a, byte standardFpscr);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRSqrtStepDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPRSqrtStepFusedDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPSqrtDelegate(double a);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate double SoftFloat64FPSubDelegate(double a, double b);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ushort SoftFloat64_16FPConvertDelegate(double value);
|
||||
|
||||
private static readonly Dictionary<string, Delegate> _androidDelegates;
|
||||
#endif
|
||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
||||
{
|
||||
if (index >= 0 && index < _delegates.Count)
|
||||
@@ -53,12 +400,37 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
string key = GetKey(method);
|
||||
|
||||
_delegates.Add(key, new DelegateInfo(method.MethodHandle.GetFunctionPointer())); // ArgumentException (key).
|
||||
#if ANDROID
|
||||
if (_androidDelegates.TryGetValue(key, out Delegate del))
|
||||
{
|
||||
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del);
|
||||
_delegates.Add(key, new DelegateInfo(funcPtr));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
_delegates.Add(key, new DelegateInfo(method.MethodHandle.GetFunctionPointer()));
|
||||
}
|
||||
catch (NotSupportedException) when (IsRunningOnAndroid())
|
||||
{
|
||||
throw new PlatformNotSupportedException($"Cannot obtain function pointer {key}: it must be registered.");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRunningOnAndroid()
|
||||
{
|
||||
#if ANDROID
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string GetKey(MethodInfo info)
|
||||
{
|
||||
return $"{info.DeclaringType.Name}.{info.Name}";
|
||||
return $"{info.DeclaringType?.Name}.{info.Name}";
|
||||
}
|
||||
|
||||
private static readonly SortedList<string, DelegateInfo> _delegates;
|
||||
@@ -67,6 +439,190 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
_delegates = new SortedList<string, DelegateInfo>();
|
||||
|
||||
#if ANDROID
|
||||
_androidDelegates = new Dictionary<string, Delegate>
|
||||
{
|
||||
// MathHelper delegates
|
||||
{ "MathHelper.Abs", new MathHelperAbsDelegate(Math.Abs) },
|
||||
{ "MathHelper.Ceiling", new MathHelperCeilingDelegate(Math.Ceiling) },
|
||||
{ "MathHelper.Floor", new MathHelperFloorDelegate(Math.Floor) },
|
||||
{ "MathHelper.Round", new MathHelperRoundDelegate((value, mode) => Math.Round(value, (MidpointRounding)mode)) },
|
||||
{ "MathHelper.Truncate", new MathHelperTruncateDelegate(Math.Truncate) },
|
||||
|
||||
// MathHelperF delegates
|
||||
{ "MathHelperF.Abs", new MathHelperFAbsDelegate(MathF.Abs) },
|
||||
{ "MathHelperF.Ceiling", new MathHelperFCeilingDelegate(MathF.Ceiling) },
|
||||
{ "MathHelperF.Floor", new MathHelperFFloorDelegate(MathF.Floor) },
|
||||
{ "MathHelperF.Round", new MathHelperFRoundDelegate((value, mode) => MathF.Round(value, (MidpointRounding)mode)) },
|
||||
{ "MathHelperF.Truncate", new MathHelperFTruncateDelegate(MathF.Truncate) },
|
||||
|
||||
// NativeInterface delegates
|
||||
{ "NativeInterface.Break", new NativeInterfaceBreakDelegate(NativeInterface.Break) },
|
||||
{ "NativeInterface.CheckSynchronization", new NativeInterfaceCheckSynchronizationDelegate(NativeInterface.CheckSynchronization) },
|
||||
{ "NativeInterface.EnqueueForRejit", new NativeInterfaceEnqueueForRejitDelegate(NativeInterface.EnqueueForRejit) },
|
||||
{ "NativeInterface.GetCntfrqEl0", new NativeInterfaceGetCntfrqEl0Delegate(NativeInterface.GetCntfrqEl0) },
|
||||
{ "NativeInterface.GetCntpctEl0", new NativeInterfaceGetCntpctEl0Delegate(NativeInterface.GetCntpctEl0) },
|
||||
{ "NativeInterface.GetCntvctEl0", new NativeInterfaceGetCntvctEl0Delegate(NativeInterface.GetCntvctEl0) },
|
||||
{ "NativeInterface.GetCtrEl0", new NativeInterfaceGetCtrEl0Delegate(NativeInterface.GetCtrEl0) },
|
||||
{ "NativeInterface.GetDczidEl0", new NativeInterfaceGetDczidEl0Delegate(NativeInterface.GetDczidEl0) },
|
||||
{ "NativeInterface.GetFunctionAddress", new NativeInterfaceGetFunctionAddressDelegate(NativeInterface.GetFunctionAddress) },
|
||||
{ "NativeInterface.InvalidateCacheLine", new NativeInterfaceInvalidateCacheLineDelegate(NativeInterface.InvalidateCacheLine) },
|
||||
{ "NativeInterface.ReadByte", new NativeInterfaceReadByteDelegate(NativeInterface.ReadByte) },
|
||||
{ "NativeInterface.ReadUInt16", new NativeInterfaceReadUInt16Delegate(NativeInterface.ReadUInt16) },
|
||||
{ "NativeInterface.ReadUInt32", new NativeInterfaceReadUInt32Delegate(NativeInterface.ReadUInt32) },
|
||||
{ "NativeInterface.ReadUInt64", new NativeInterfaceReadUInt64Delegate(NativeInterface.ReadUInt64) },
|
||||
{ "NativeInterface.ReadVector128", new NativeInterfaceReadVector128Delegate(NativeInterface.ReadVector128) },
|
||||
{ "NativeInterface.SignalMemoryTracking", new NativeInterfaceSignalMemoryTrackingDelegate(NativeInterface.SignalMemoryTracking) },
|
||||
{ "NativeInterface.SupervisorCall", new NativeInterfaceSupervisorCallDelegate(NativeInterface.SupervisorCall) },
|
||||
{ "NativeInterface.ThrowInvalidMemoryAccess", new NativeInterfaceThrowInvalidMemoryAccessDelegate(NativeInterface.ThrowInvalidMemoryAccess) },
|
||||
{ "NativeInterface.Undefined", new NativeInterfaceUndefinedDelegate(NativeInterface.Undefined) },
|
||||
{ "NativeInterface.WriteByte", new NativeInterfaceWriteByteDelegate(NativeInterface.WriteByte) },
|
||||
{ "NativeInterface.WriteUInt16", new NativeInterfaceWriteUInt16Delegate(NativeInterface.WriteUInt16) },
|
||||
{ "NativeInterface.WriteUInt32", new NativeInterfaceWriteUInt32Delegate(NativeInterface.WriteUInt32) },
|
||||
{ "NativeInterface.WriteUInt64", new NativeInterfaceWriteUInt64Delegate(NativeInterface.WriteUInt64) },
|
||||
{ "NativeInterface.WriteVector128", new NativeInterfaceWriteVector128Delegate(NativeInterface.WriteVector128) },
|
||||
|
||||
// SoftFallback delegates
|
||||
{ "SoftFallback.CountLeadingSigns", new SoftFallbackCountLeadingSignsDelegate(SoftFallback.CountLeadingSigns) },
|
||||
{ "SoftFallback.CountLeadingZeros", new SoftFallbackCountLeadingZerosDelegate(SoftFallback.CountLeadingZeros) },
|
||||
{ "SoftFallback.Crc32b", new SoftFallbackCrc32bDelegate(SoftFallback.Crc32b) },
|
||||
{ "SoftFallback.Crc32cb", new SoftFallbackCrc32cbDelegate(SoftFallback.Crc32cb) },
|
||||
{ "SoftFallback.Crc32ch", new SoftFallbackCrc32chDelegate(SoftFallback.Crc32ch) },
|
||||
{ "SoftFallback.Crc32cw", new SoftFallbackCrc32cwDelegate(SoftFallback.Crc32cw) },
|
||||
{ "SoftFallback.Crc32cx", new SoftFallbackCrc32cxDelegate(SoftFallback.Crc32cx) },
|
||||
{ "SoftFallback.Crc32h", new SoftFallbackCrc32hDelegate(SoftFallback.Crc32h) },
|
||||
{ "SoftFallback.Crc32w", new SoftFallbackCrc32wDelegate(SoftFallback.Crc32w) },
|
||||
{ "SoftFallback.Crc32x", new SoftFallbackCrc32xDelegate(SoftFallback.Crc32x) },
|
||||
{ "SoftFallback.Decrypt", new SoftFallbackDecryptDelegate(SoftFallback.Decrypt) },
|
||||
{ "SoftFallback.Encrypt", new SoftFallbackEncryptDelegate(SoftFallback.Encrypt) },
|
||||
{ "SoftFallback.FixedRotate", new SoftFallbackFixedRotateDelegate(SoftFallback.FixedRotate) },
|
||||
{ "SoftFallback.HashChoose", new SoftFallbackHashChooseDelegate(SoftFallback.HashChoose) },
|
||||
{ "SoftFallback.HashLower", new SoftFallbackHashLowerDelegate(SoftFallback.HashLower) },
|
||||
{ "SoftFallback.HashMajority", new SoftFallbackHashMajorityDelegate(SoftFallback.HashMajority) },
|
||||
{ "SoftFallback.HashParity", new SoftFallbackHashParityDelegate(SoftFallback.HashParity) },
|
||||
{ "SoftFallback.HashUpper", new SoftFallbackHashUpperDelegate(SoftFallback.HashUpper) },
|
||||
{ "SoftFallback.InverseMixColumns", new SoftFallbackInverseMixColumnsDelegate(SoftFallback.InverseMixColumns) },
|
||||
{ "SoftFallback.MixColumns", new SoftFallbackMixColumnsDelegate(SoftFallback.MixColumns) },
|
||||
{ "SoftFallback.PolynomialMult64_128", new SoftFallbackPolynomialMult64_128Delegate(SoftFallback.PolynomialMult64_128) },
|
||||
{ "SoftFallback.SatF32ToS32", new SoftFallbackSatF32ToS32Delegate(SoftFallback.SatF32ToS32) },
|
||||
{ "SoftFallback.SatF32ToS64", new SoftFallbackSatF32ToS64Delegate(SoftFallback.SatF32ToS64) },
|
||||
{ "SoftFallback.SatF32ToU32", new SoftFallbackSatF32ToU32Delegate(SoftFallback.SatF32ToU32) },
|
||||
{ "SoftFallback.SatF32ToU64", new SoftFallbackSatF32ToU64Delegate(SoftFallback.SatF32ToU64) },
|
||||
{ "SoftFallback.SatF64ToS32", new SoftFallbackSatF64ToS32Delegate(SoftFallback.SatF64ToS32) },
|
||||
{ "SoftFallback.SatF64ToS64", new SoftFallbackSatF64ToS64Delegate(SoftFallback.SatF64ToS64) },
|
||||
{ "SoftFallback.SatF64ToU32", new SoftFallbackSatF64ToU32Delegate(SoftFallback.SatF64ToU32) },
|
||||
{ "SoftFallback.SatF64ToU64", new SoftFallbackSatF64ToU64Delegate(SoftFallback.SatF64ToU64) },
|
||||
{ "SoftFallback.Sha1SchedulePart1", new SoftFallbackSha1SchedulePart1Delegate(SoftFallback.Sha1SchedulePart1) },
|
||||
{ "SoftFallback.Sha1SchedulePart2", new SoftFallbackSha1SchedulePart2Delegate(SoftFallback.Sha1SchedulePart2) },
|
||||
{ "SoftFallback.Sha256SchedulePart1", new SoftFallbackSha256SchedulePart1Delegate(SoftFallback.Sha256SchedulePart1) },
|
||||
{ "SoftFallback.Sha256SchedulePart2", new SoftFallbackSha256SchedulePart2Delegate(SoftFallback.Sha256SchedulePart2) },
|
||||
{ "SoftFallback.SignedShrImm64", new SoftFallbackSignedShrImm64Delegate(SoftFallback.SignedShrImm64) },
|
||||
{ "SoftFallback.Tbl1", new SoftFallbackTbl1Delegate(SoftFallback.Tbl1) },
|
||||
{ "SoftFallback.Tbl2", new SoftFallbackTbl2Delegate(SoftFallback.Tbl2) },
|
||||
{ "SoftFallback.Tbl3", new SoftFallbackTbl3Delegate(SoftFallback.Tbl3) },
|
||||
{ "SoftFallback.Tbl4", new SoftFallbackTbl4Delegate(SoftFallback.Tbl4) },
|
||||
{ "SoftFallback.Tbx1", new SoftFallbackTbx1Delegate(SoftFallback.Tbx1) },
|
||||
{ "SoftFallback.Tbx2", new SoftFallbackTbx2Delegate(SoftFallback.Tbx2) },
|
||||
{ "SoftFallback.Tbx3", new SoftFallbackTbx3Delegate(SoftFallback.Tbx3) },
|
||||
{ "SoftFallback.Tbx4", new SoftFallbackTbx4Delegate(SoftFallback.Tbx4) },
|
||||
{ "SoftFallback.UnsignedShrImm64", new SoftFallbackUnsignedShrImm64Delegate(SoftFallback.UnsignedShrImm64) },
|
||||
|
||||
// SoftFloat delegates
|
||||
{ "SoftFloat16_32.FPConvert", new SoftFloat16_32FPConvertDelegate(SoftFloat16_32.FPConvert) },
|
||||
{ "SoftFloat16_64.FPConvert", new SoftFloat16_64FPConvertDelegate(SoftFloat16_64.FPConvert) },
|
||||
|
||||
{ "SoftFloat32.FPAdd", new SoftFloat32FPAddDelegate(SoftFloat32.FPAdd) },
|
||||
{ "SoftFloat32.FPAddFpscr", new SoftFloat32FPAddFpscrDelegate(SoftFloat32.FPAddFpscr) },
|
||||
{ "SoftFloat32.FPCompare", new SoftFloat32FPCompareDelegate(SoftFloat32.FPCompare) },
|
||||
{ "SoftFloat32.FPCompareEQ", new SoftFloat32FPCompareEQDelegate(SoftFloat32.FPCompareEQ) },
|
||||
{ "SoftFloat32.FPCompareEQFpscr", new SoftFloat32FPCompareEQFpscrDelegate(SoftFloat32.FPCompareEQFpscr) },
|
||||
{ "SoftFloat32.FPCompareGE", new SoftFloat32FPCompareGEDelegate(SoftFloat32.FPCompareGE) },
|
||||
{ "SoftFloat32.FPCompareGEFpscr", new SoftFloat32FPCompareGEFpscrDelegate(SoftFloat32.FPCompareGEFpscr) },
|
||||
{ "SoftFloat32.FPCompareGT", new SoftFloat32FPCompareGTDelegate(SoftFloat32.FPCompareGT) },
|
||||
{ "SoftFloat32.FPCompareGTFpscr", new SoftFloat32FPCompareGTFpscrDelegate(SoftFloat32.FPCompareGTFpscr) },
|
||||
{ "SoftFloat32.FPCompareLE", new SoftFloat32FPCompareLEDelegate(SoftFloat32.FPCompareLE) },
|
||||
{ "SoftFloat32.FPCompareLEFpscr", new SoftFloat32FPCompareLEFpscrDelegate(SoftFloat32.FPCompareLEFpscr) },
|
||||
{ "SoftFloat32.FPCompareLT", new SoftFloat32FPCompareLTDelegate(SoftFloat32.FPCompareLT) },
|
||||
{ "SoftFloat32.FPCompareLTFpscr", new SoftFloat32FPCompareLTFpscrDelegate(SoftFloat32.FPCompareLTFpscr) },
|
||||
{ "SoftFloat32.FPDiv", new SoftFloat32FPDivDelegate(SoftFloat32.FPDiv) },
|
||||
{ "SoftFloat32.FPMax", new SoftFloat32FPMaxDelegate(SoftFloat32.FPMax) },
|
||||
{ "SoftFloat32.FPMaxFpscr", new SoftFloat32FPMaxFpscrDelegate(SoftFloat32.FPMaxFpscr) },
|
||||
{ "SoftFloat32.FPMaxNum", new SoftFloat32FPMaxNumDelegate(SoftFloat32.FPMaxNum) },
|
||||
{ "SoftFloat32.FPMaxNumFpscr", new SoftFloat32FPMaxNumFpscrDelegate(SoftFloat32.FPMaxNumFpscr) },
|
||||
{ "SoftFloat32.FPMin", new SoftFloat32FPMinDelegate(SoftFloat32.FPMin) },
|
||||
{ "SoftFloat32.FPMinFpscr", new SoftFloat32FPMinFpscrDelegate(SoftFloat32.FPMinFpscr) },
|
||||
{ "SoftFloat32.FPMinNum", new SoftFloat32FPMinNumDelegate(SoftFloat32.FPMinNum) },
|
||||
{ "SoftFloat32.FPMinNumFpscr", new SoftFloat32FPMinNumFpscrDelegate(SoftFloat32.FPMinNumFpscr) },
|
||||
{ "SoftFloat32.FPMul", new SoftFloat32FPMulDelegate(SoftFloat32.FPMul) },
|
||||
{ "SoftFloat32.FPMulFpscr", new SoftFloat32FPMulFpscrDelegate(SoftFloat32.FPMulFpscr) },
|
||||
{ "SoftFloat32.FPMulAdd", new SoftFloat32FPMulAddDelegate(SoftFloat32.FPMulAdd) },
|
||||
{ "SoftFloat32.FPMulAddFpscr", new SoftFloat32FPMulAddFpscrDelegate(SoftFloat32.FPMulAddFpscr) },
|
||||
{ "SoftFloat32.FPMulSub", new SoftFloat32FPMulSubDelegate(SoftFloat32.FPMulSub) },
|
||||
{ "SoftFloat32.FPMulSubFpscr", new SoftFloat32FPMulSubFpscrDelegate(SoftFloat32.FPMulSubFpscr) },
|
||||
{ "SoftFloat32.FPMulX", new SoftFloat32FPMulXDelegate(SoftFloat32.FPMulX) },
|
||||
{ "SoftFloat32.FPNegMulAdd", new SoftFloat32FPNegMulAddDelegate(SoftFloat32.FPNegMulAdd) },
|
||||
{ "SoftFloat32.FPNegMulSub", new SoftFloat32FPNegMulSubDelegate(SoftFloat32.FPNegMulSub) },
|
||||
{ "SoftFloat32.FPRecipEstimate", new SoftFloat32FPRecipEstimateDelegate(SoftFloat32.FPRecipEstimate) },
|
||||
{ "SoftFloat32.FPRecipEstimateFpscr", new SoftFloat32FPRecipEstimateFpscrDelegate(SoftFloat32.FPRecipEstimateFpscr) },
|
||||
{ "SoftFloat32.FPRecipStep", new SoftFloat32FPRecipStepDelegate(SoftFloat32.FPRecipStep) },
|
||||
{ "SoftFloat32.FPRecipStepFused", new SoftFloat32FPRecipStepFusedDelegate(SoftFloat32.FPRecipStepFused) },
|
||||
{ "SoftFloat32.FPRecpX", new SoftFloat32FPRecpXDelegate(SoftFloat32.FPRecpX) },
|
||||
{ "SoftFloat32.FPRSqrtEstimate", new SoftFloat32FPRSqrtEstimateDelegate(SoftFloat32.FPRSqrtEstimate) },
|
||||
{ "SoftFloat32.FPRSqrtEstimateFpscr", new SoftFloat32FPRSqrtEstimateFpscrDelegate(SoftFloat32.FPRSqrtEstimateFpscr) },
|
||||
{ "SoftFloat32.FPRSqrtStep", new SoftFloat32FPRSqrtStepDelegate(SoftFloat32.FPRSqrtStep) },
|
||||
{ "SoftFloat32.FPRSqrtStepFused", new SoftFloat32FPRSqrtStepFusedDelegate(SoftFloat32.FPRSqrtStepFused) },
|
||||
{ "SoftFloat32.FPSqrt", new SoftFloat32FPSqrtDelegate(SoftFloat32.FPSqrt) },
|
||||
{ "SoftFloat32.FPSub", new SoftFloat32FPSubDelegate(SoftFloat32.FPSub) },
|
||||
|
||||
{ "SoftFloat32_16.FPConvert", new SoftFloat32_16FPConvertDelegate(SoftFloat32_16.FPConvert) },
|
||||
|
||||
{ "SoftFloat64.FPAdd", new SoftFloat64FPAddDelegate(SoftFloat64.FPAdd) },
|
||||
{ "SoftFloat64.FPAddFpscr", new SoftFloat64FPAddFpscrDelegate(SoftFloat64.FPAddFpscr) },
|
||||
{ "SoftFloat64.FPCompare", new SoftFloat64FPCompareDelegate(SoftFloat64.FPCompare) },
|
||||
{ "SoftFloat64.FPCompareEQ", new SoftFloat64FPCompareEQDelegate(SoftFloat64.FPCompareEQ) },
|
||||
{ "SoftFloat64.FPCompareEQFpscr", new SoftFloat64FPCompareEQFpscrDelegate(SoftFloat64.FPCompareEQFpscr) },
|
||||
{ "SoftFloat64.FPCompareGE", new SoftFloat64FPCompareGEDelegate(SoftFloat64.FPCompareGE) },
|
||||
{ "SoftFloat64.FPCompareGEFpscr", new SoftFloat64FPCompareGEFpscrDelegate(SoftFloat64.FPCompareGEFpscr) },
|
||||
{ "SoftFloat64.FPCompareGT", new SoftFloat64FPCompareGTDelegate(SoftFloat64.FPCompareGT) },
|
||||
{ "SoftFloat64.FPCompareGTFpscr", new SoftFloat64FPCompareGTFpscrDelegate(SoftFloat64.FPCompareGTFpscr) },
|
||||
{ "SoftFloat64.FPCompareLE", new SoftFloat64FPCompareLEDelegate(SoftFloat64.FPCompareLE) },
|
||||
{ "SoftFloat64.FPCompareLEFpscr", new SoftFloat64FPCompareLEFpscrDelegate(SoftFloat64.FPCompareLEFpscr) },
|
||||
{ "SoftFloat64.FPCompareLT", new SoftFloat64FPCompareLTDelegate(SoftFloat64.FPCompareLT) },
|
||||
{ "SoftFloat64.FPCompareLTFpscr", new SoftFloat64FPCompareLTFpscrDelegate(SoftFloat64.FPCompareLTFpscr) },
|
||||
{ "SoftFloat64.FPDiv", new SoftFloat64FPDivDelegate(SoftFloat64.FPDiv) },
|
||||
{ "SoftFloat64.FPMax", new SoftFloat64FPMaxDelegate(SoftFloat64.FPMax) },
|
||||
{ "SoftFloat64.FPMaxFpscr", new SoftFloat64FPMaxFpscrDelegate(SoftFloat64.FPMaxFpscr) },
|
||||
{ "SoftFloat64.FPMaxNum", new SoftFloat64FPMaxNumDelegate(SoftFloat64.FPMaxNum) },
|
||||
{ "SoftFloat64.FPMaxNumFpscr", new SoftFloat64FPMaxNumFpscrDelegate(SoftFloat64.FPMaxNumFpscr) },
|
||||
{ "SoftFloat64.FPMin", new SoftFloat64FPMinDelegate(SoftFloat64.FPMin) },
|
||||
{ "SoftFloat64.FPMinFpscr", new SoftFloat64FPMinFpscrDelegate(SoftFloat64.FPMinFpscr) },
|
||||
{ "SoftFloat64.FPMinNum", new SoftFloat64FPMinNumDelegate(SoftFloat64.FPMinNum) },
|
||||
{ "SoftFloat64.FPMinNumFpscr", new SoftFloat64FPMinNumFpscrDelegate(SoftFloat64.FPMinNumFpscr) },
|
||||
{ "SoftFloat64.FPMul", new SoftFloat64FPMulDelegate(SoftFloat64.FPMul) },
|
||||
{ "SoftFloat64.FPMulFpscr", new SoftFloat64FPMulFpscrDelegate(SoftFloat64.FPMulFpscr) },
|
||||
{ "SoftFloat64.FPMulAdd", new SoftFloat64FPMulAddDelegate(SoftFloat64.FPMulAdd) },
|
||||
{ "SoftFloat64.FPMulAddFpscr", new SoftFloat64FPMulAddFpscrDelegate(SoftFloat64.FPMulAddFpscr) },
|
||||
{ "SoftFloat64.FPMulSub", new SoftFloat64FPMulSubDelegate(SoftFloat64.FPMulSub) },
|
||||
{ "SoftFloat64.FPMulSubFpscr", new SoftFloat64FPMulSubFpscrDelegate(SoftFloat64.FPMulSubFpscr) },
|
||||
{ "SoftFloat64.FPMulX", new SoftFloat64FPMulXDelegate(SoftFloat64.FPMulX) },
|
||||
{ "SoftFloat64.FPNegMulAdd", new SoftFloat64FPNegMulAddDelegate(SoftFloat64.FPNegMulAdd) },
|
||||
{ "SoftFloat64.FPNegMulSub", new SoftFloat64FPNegMulSubDelegate(SoftFloat64.FPNegMulSub) },
|
||||
{ "SoftFloat64.FPRecipEstimate", new SoftFloat64FPRecipEstimateDelegate(SoftFloat64.FPRecipEstimate) },
|
||||
{ "SoftFloat64.FPRecipEstimateFpscr", new SoftFloat64FPRecipEstimateFpscrDelegate(SoftFloat64.FPRecipEstimateFpscr) },
|
||||
{ "SoftFloat64.FPRecipStep", new SoftFloat64FPRecipStepDelegate(SoftFloat64.FPRecipStep) },
|
||||
{ "SoftFloat64.FPRecipStepFused", new SoftFloat64FPRecipStepFusedDelegate(SoftFloat64.FPRecipStepFused) },
|
||||
{ "SoftFloat64.FPRecpX", new SoftFloat64FPRecpXDelegate(SoftFloat64.FPRecpX) },
|
||||
{ "SoftFloat64.FPRSqrtEstimate", new SoftFloat64FPRSqrtEstimateDelegate(SoftFloat64.FPRSqrtEstimate) },
|
||||
{ "SoftFloat64.FPRSqrtEstimateFpscr", new SoftFloat64FPRSqrtEstimateFpscrDelegate(SoftFloat64.FPRSqrtEstimateFpscr) },
|
||||
{ "SoftFloat64.FPRSqrtStep", new SoftFloat64FPRSqrtStepDelegate(SoftFloat64.FPRSqrtStep) },
|
||||
{ "SoftFloat64.FPRSqrtStepFused", new SoftFloat64FPRSqrtStepFusedDelegate(SoftFloat64.FPRSqrtStepFused) },
|
||||
{ "SoftFloat64.FPSqrt", new SoftFloat64FPSqrtDelegate(SoftFloat64.FPSqrt) },
|
||||
{ "SoftFloat64.FPSub", new SoftFloat64FPSubDelegate(SoftFloat64.FPSub) },
|
||||
|
||||
{ "SoftFloat64_16.FPConvert", new SoftFloat64_16FPConvertDelegate(SoftFloat64_16.FPConvert) }
|
||||
};
|
||||
#endif
|
||||
|
||||
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Abs)));
|
||||
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Ceiling)));
|
||||
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Floor)));
|
||||
@@ -152,44 +708,44 @@ namespace ARMeilleure.Translation
|
||||
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
||||
@@ -197,44 +753,44 @@ namespace ARMeilleure.Translation
|
||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
||||
|
||||
@@ -97,7 +97,14 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
|
||||
{
|
||||
#if ANDROID
|
||||
// For Android, use the Delegates class to get the function pointer
|
||||
int index = Delegates.GetDelegateIndex(info);
|
||||
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||
#else
|
||||
// For other platforms, use direct method handle approach
|
||||
IntPtr funcPtr = info.MethodHandle.GetFunctionPointer();
|
||||
#endif
|
||||
|
||||
OperandType returnType = GetOperandType(info.ReturnType);
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 6998; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const uint InternalVersion = 7008; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
@@ -563,6 +563,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
if (AreCarriersEmpty() || ContainsBlacklistedFunctions())
|
||||
{
|
||||
ResetCarriersIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -869,7 +870,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
|
||||
|
||||
TranslatedFunction func = translator.Translate(address, executionMode, highCq);
|
||||
TranslatedFunction func = translator.Translate(address, executionMode, highCq, pptcTranslation: true);
|
||||
|
||||
if (func == null)
|
||||
{
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
}
|
||||
|
||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false)
|
||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false, bool pptcTranslation = false)
|
||||
{
|
||||
var context = new ArmEmitterContext(
|
||||
Memory,
|
||||
@@ -246,7 +246,7 @@ namespace ARMeilleure.Translation
|
||||
context.Branch(context.GetLabel(address));
|
||||
}
|
||||
|
||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
|
||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter, pptcTranslation);
|
||||
|
||||
if (cfg == null)
|
||||
{
|
||||
@@ -326,7 +326,8 @@ namespace ARMeilleure.Translation
|
||||
ArmEmitterContext context,
|
||||
Block[] blocks,
|
||||
out Range range,
|
||||
out Counter<uint> counter)
|
||||
out Counter<uint> counter,
|
||||
bool pptcTranslation)
|
||||
{
|
||||
counter = null;
|
||||
|
||||
@@ -411,7 +412,10 @@ namespace ARMeilleure.Translation
|
||||
if (opCode.Instruction.Emitter != null)
|
||||
{
|
||||
opCode.Instruction.Emitter(context);
|
||||
if (opCode.Instruction.Name == InstName.Und && blkIndex == 0)
|
||||
// if we're pre-compiling PPTC functions, and we hit an Undefined instruction as the first
|
||||
// instruction in the block, mark the function as blacklisted
|
||||
// this way, we don't pre-compile Exlaunch hooks, which allows ExeFS mods to run with PPTC
|
||||
if (pptcTranslation && opCode.Instruction.Name == InstName.Und && blkIndex == 0)
|
||||
{
|
||||
range = new Range(rangeStart, rangeEnd);
|
||||
return null;
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' == 'win-x64' OR '$(RuntimeIdentifier)' == 'win-arm64'">
|
||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>libsoundio.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' == 'osx-x64' OR '$(RuntimeIdentifier)' == 'osx-arm64'">
|
||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>libsoundio.dylib</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
|
||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>libsoundio.so</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
|
||||
@@ -81,14 +81,14 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
||||
{
|
||||
if ((uint)index >= (uint)coefficients.Length)
|
||||
if ((uint)index < (uint)coefficients.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||
|
||||
return 0;
|
||||
return coefficients[index];
|
||||
}
|
||||
|
||||
return coefficients[index];
|
||||
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
/// Map decibel to linear.
|
||||
/// </summary>
|
||||
/// <param name="db">The decibel value to convert</param>
|
||||
/// <returns>Converted linear value/returns>
|
||||
/// <returns>Converted linear value</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float DecibelToLinear(float db)
|
||||
{
|
||||
|
||||
@@ -496,12 +496,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
return (10090.9f, 3490.9f);
|
||||
case SampleRateConversionQuality.High:
|
||||
if (sampleCount == 160)
|
||||
{
|
||||
return (9446.36f, 2308.91f);
|
||||
}
|
||||
|
||||
return (12520.85f, 3480.61f);
|
||||
case SampleRateConversionQuality.Low:
|
||||
if (sampleCount == 160)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.GraphicsDriver
|
||||
{
|
||||
public static class DriverUtilities
|
||||
{
|
||||
private static void AddMesaFlags(string envVar, string newFlags)
|
||||
{
|
||||
string existingFlags = Environment.GetEnvironmentVariable(envVar);
|
||||
|
||||
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
|
||||
|
||||
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
|
||||
}
|
||||
|
||||
public static void InitDriverConfig(bool oglThreading)
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
AddMesaFlags("RADV_DEBUG", "nodcc");
|
||||
}
|
||||
|
||||
ToggleOGLThreading(oglThreading);
|
||||
}
|
||||
|
||||
public static void ToggleOGLThreading(bool enabled)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
|
||||
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
|
||||
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Text;
|
||||
|
||||
namespace Ryujinx.Common.Logging.Formatters
|
||||
{
|
||||
internal class DefaultLogFormatter : ILogFormatter
|
||||
public class DefaultLogFormatter : ILogFormatter
|
||||
{
|
||||
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||
|
||||
|
||||
@@ -135,11 +135,14 @@ namespace Ryujinx.Common.Logging
|
||||
|
||||
_time = Stopwatch.StartNew();
|
||||
|
||||
// Logger should log to console by default
|
||||
AddTarget(new AsyncLogTargetWrapper(
|
||||
new ConsoleLogTarget("console"),
|
||||
1000,
|
||||
AsyncLogTargetOverflowAction.Discard));
|
||||
if (!PlatformInfo.IsBionic)
|
||||
{
|
||||
// Logger should log to console by default
|
||||
AddTarget(new AsyncLogTargetWrapper(
|
||||
new ConsoleLogTarget("console"),
|
||||
1000,
|
||||
AsyncLogTargetOverflowAction.Discard));
|
||||
}
|
||||
|
||||
Notice = new Log(LogLevel.Notice);
|
||||
|
||||
|
||||
@@ -27,11 +27,7 @@ namespace Ryujinx.Common.Logging.Targets
|
||||
|
||||
private readonly int _overflowTimeout;
|
||||
|
||||
string ILogTarget.Name { get => _target.Name; }
|
||||
|
||||
public AsyncLogTargetWrapper(ILogTarget target)
|
||||
: this(target, -1)
|
||||
{ }
|
||||
string ILogTarget.Name => _target.Name;
|
||||
|
||||
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
|
||||
{
|
||||
|
||||
@@ -46,9 +46,9 @@ namespace Ryujinx.Common.Logging.Targets
|
||||
return null;
|
||||
}
|
||||
|
||||
// Clean up old logs, should only keep 3
|
||||
// Clean up old logs, should only keep 4
|
||||
FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray();
|
||||
for (int i = 0; i < files.Length - 2; i++)
|
||||
for (int i = 0; i < files.Length - 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
public static class PlatformInfo
|
||||
{
|
||||
public static bool IsBionic { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
@@ -26,6 +27,6 @@ namespace Ryujinx.Common
|
||||
|
||||
public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannelOwner);
|
||||
|
||||
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||
public static string Version => (IsValid || !RuntimeFeature.IsDynamicCodeCompiled) ? (PlatformInfo.IsBionic ? "Bionic_2.0.3" : BuildVersion) : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public partial class OsUtils
|
||||
{
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
|
||||
|
||||
public static void SetEnvironmentVariableNoCaching(string key, string value)
|
||||
{
|
||||
// Set the value in the cached environment variables, too.
|
||||
Environment.SetEnvironmentVariable(key, value);
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
int res = setenv(key, value, 1);
|
||||
Debug.Assert(res != -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ namespace Ryujinx.Common.Utilities
|
||||
|
||||
if (timedSw.Elapsed.TotalSeconds > 0)
|
||||
{
|
||||
Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
|
||||
Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
|
||||
}
|
||||
|
||||
if (freeSpaceValid)
|
||||
@@ -219,7 +219,7 @@ namespace Ryujinx.Common.Utilities
|
||||
|
||||
private bool CheckPadding(long readSizeB, CancellationToken? cancelToken = null)
|
||||
{
|
||||
long maxReads = readSizeB / XCIFileTrimmer.BufferSize;
|
||||
long maxReads = readSizeB / BufferSize;
|
||||
long read = 0;
|
||||
var buffer = new byte[BufferSize];
|
||||
|
||||
@@ -230,12 +230,12 @@ namespace Ryujinx.Common.Utilities
|
||||
return false;
|
||||
}
|
||||
|
||||
int bytes = _fileStream.Read(buffer, 0, XCIFileTrimmer.BufferSize);
|
||||
int bytes = _fileStream.Read(buffer, 0, BufferSize);
|
||||
if (bytes == 0)
|
||||
break;
|
||||
|
||||
Log?.Progress(read, maxReads, "Verifying file can be trimmed", false);
|
||||
if (buffer.Take(bytes).AsParallel().Any(b => b != XCIFileTrimmer.PaddingByte))
|
||||
if (buffer.Take(bytes).AsParallel().Any(b => b != PaddingByte))
|
||||
{
|
||||
Log?.Write(LogType.Warn, "Free space is NOT valid");
|
||||
return false;
|
||||
@@ -380,7 +380,7 @@ namespace Ryujinx.Common.Utilities
|
||||
|
||||
if (timedSw.Elapsed.TotalSeconds > 0)
|
||||
{
|
||||
Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
|
||||
Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
|
||||
}
|
||||
|
||||
if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
|
||||
@@ -408,13 +408,13 @@ namespace Ryujinx.Common.Utilities
|
||||
private void WritePadding(FileStream outfileStream, long bytesToWriteB, CancellationToken? cancelToken = null)
|
||||
{
|
||||
long bytesLeftToWriteB = bytesToWriteB;
|
||||
long writes = bytesLeftToWriteB / XCIFileTrimmer.BufferSize;
|
||||
long writes = bytesLeftToWriteB / BufferSize;
|
||||
int write = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var buffer = new byte[BufferSize];
|
||||
Array.Fill<byte>(buffer, XCIFileTrimmer.PaddingByte);
|
||||
Array.Fill<byte>(buffer, PaddingByte);
|
||||
|
||||
while (bytesLeftToWriteB > 0)
|
||||
{
|
||||
@@ -423,7 +423,7 @@ namespace Ryujinx.Common.Utilities
|
||||
return;
|
||||
}
|
||||
|
||||
long bytesToWrite = Math.Min(XCIFileTrimmer.BufferSize, bytesLeftToWriteB);
|
||||
long bytesToWrite = Math.Min(BufferSize, bytesLeftToWriteB);
|
||||
|
||||
#if !XCI_TRIMMER_READ_ONLY_MODE
|
||||
outfileStream.Write(buffer, 0, (int)bytesToWrite);
|
||||
@@ -504,12 +504,12 @@ namespace Ryujinx.Common.Utilities
|
||||
}
|
||||
|
||||
// Setup offset
|
||||
_offsetB = (long)(assumeKeyArea ? XCIFileTrimmer.CartKeyAreaSize : 0);
|
||||
_offsetB = (long)(assumeKeyArea ? CartKeyAreaSize : 0);
|
||||
|
||||
// Check header
|
||||
Pos = _offsetB + XCIFileTrimmer.HeaderFilePos;
|
||||
Pos = _offsetB + HeaderFilePos;
|
||||
string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4));
|
||||
if (head != XCIFileTrimmer.HeaderMagicValue)
|
||||
if (head != HeaderMagicValue)
|
||||
{
|
||||
if (!assumeKeyArea)
|
||||
{
|
||||
@@ -524,17 +524,17 @@ namespace Ryujinx.Common.Utilities
|
||||
}
|
||||
|
||||
// Read Cart Size
|
||||
Pos = _offsetB + XCIFileTrimmer.CartSizeFilePos;
|
||||
Pos = _offsetB + CartSizeFilePos;
|
||||
byte cartSizeId = _binaryReader.ReadByte();
|
||||
if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB))
|
||||
{
|
||||
Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})");
|
||||
return false;
|
||||
}
|
||||
_cartSizeB = cartSizeNGB * XCIFileTrimmer.CartSizeMBinFormattedGB * XCIFileTrimmer.BytesInAMegabyte;
|
||||
_cartSizeB = cartSizeNGB * CartSizeMBinFormattedGB * BytesInAMegabyte;
|
||||
|
||||
// Read data size
|
||||
Pos = _offsetB + XCIFileTrimmer.DataSizeFilePos;
|
||||
Pos = _offsetB + DataSizeFilePos;
|
||||
long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0);
|
||||
_dataSizeB = RecordsToByte(records);
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Ryujinx.Cpu
|
||||
{
|
||||
public class AddressSpace : IDisposable
|
||||
{
|
||||
private const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
|
||||
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
|
||||
public MemoryBlock Base { get; }
|
||||
@@ -25,28 +27,42 @@ namespace Ryujinx.Cpu
|
||||
{
|
||||
addressSpace = null;
|
||||
|
||||
const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
|
||||
MemoryBlock baseMemory = null;
|
||||
MemoryBlock mirrorMemory = null;
|
||||
|
||||
try
|
||||
{
|
||||
baseMemory = new MemoryBlock(asSize, AsFlags);
|
||||
mirrorMemory = new MemoryBlock(asSize, AsFlags);
|
||||
addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, asSize);
|
||||
}
|
||||
catch (SystemException)
|
||||
{
|
||||
baseMemory?.Dispose();
|
||||
mirrorMemory?.Dispose();
|
||||
}
|
||||
|
||||
return addressSpace != null;
|
||||
}
|
||||
|
||||
public static bool TryCreateWithoutMirror(ulong asSize, out MemoryBlock addressSpace)
|
||||
{
|
||||
addressSpace = null;
|
||||
|
||||
ulong minAddressSpaceSize = Math.Min(asSize, 1UL << 36);
|
||||
|
||||
// Attempt to create the address space with expected size or try to reduce it until it succeed.
|
||||
for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize >>= 1)
|
||||
for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize -= 0x100000000UL)
|
||||
{
|
||||
MemoryBlock baseMemory = null;
|
||||
MemoryBlock mirrorMemory = null;
|
||||
|
||||
try
|
||||
{
|
||||
baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
mirrorMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize);
|
||||
MemoryBlock baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
addressSpace = baseMemory;
|
||||
|
||||
break;
|
||||
}
|
||||
catch (SystemException)
|
||||
{
|
||||
baseMemory?.Dispose();
|
||||
mirrorMemory?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace ARMeilleure.Common
|
||||
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type)
|
||||
{
|
||||
// Assume software memory means that we don't want to use any signal handlers.
|
||||
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable;
|
||||
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable && PlatformInfo.IsBionic != true;
|
||||
|
||||
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Range;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||
/// </summary>
|
||||
public sealed class MemoryManagerHostNoMirror : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
private readonly InvalidAccessHandler _invalidAccessHandler;
|
||||
private readonly bool _unsafeMode;
|
||||
|
||||
private readonly MemoryBlock _addressSpace;
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
private readonly PageTable<ulong> _pageTable;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
protected override ulong AddressSpaceSize { get; }
|
||||
|
||||
private readonly MemoryEhMeilleure _memoryEh;
|
||||
|
||||
private readonly ManagedPageFlags _pages;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public IntPtr PageTablePointer => _addressSpace.Pointer;
|
||||
|
||||
public MemoryManagerType Type => _unsafeMode ? MemoryManagerType.HostMappedUnsafe : MemoryManagerType.HostMapped;
|
||||
|
||||
public MemoryTracking Tracking { get; }
|
||||
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the host mapped memory manager.
|
||||
/// </summary>
|
||||
/// <param name="addressSpace">Address space instance to use</param>
|
||||
/// <param name="unsafeMode">True if unmanaged access should not be masked (unsafe), false otherwise.</param>
|
||||
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
||||
public MemoryManagerHostNoMirror(
|
||||
MemoryBlock addressSpace,
|
||||
MemoryBlock backingMemory,
|
||||
bool unsafeMode,
|
||||
InvalidAccessHandler invalidAccessHandler)
|
||||
{
|
||||
_addressSpace = addressSpace;
|
||||
_backingMemory = backingMemory;
|
||||
_pageTable = new PageTable<ulong>();
|
||||
_invalidAccessHandler = invalidAccessHandler;
|
||||
_unsafeMode = unsafeMode;
|
||||
AddressSpaceSize = addressSpace.Size;
|
||||
|
||||
ulong asSize = PageSize;
|
||||
int asBits = PageBits;
|
||||
|
||||
while (asSize < addressSpace.Size)
|
||||
{
|
||||
asSize <<= 1;
|
||||
asBits++;
|
||||
}
|
||||
|
||||
AddressSpaceBits = asBits;
|
||||
|
||||
_pages = new ManagedPageFlags(asBits);
|
||||
|
||||
Tracking = new MemoryTracking(this, (int)MemoryBlock.GetPageSize(), invalidAccessHandler);
|
||||
_memoryEh = new MemoryEhMeilleure(addressSpace, null, Tracking);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the combination of virtual address and size is part of the addressable space and fully mapped.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
private void AssertMapped(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddressAndSize(va, size) || !_pages.IsRangeMapped(va, size))
|
||||
{
|
||||
throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
_addressSpace.MapView(_backingMemory, pa, va, size);
|
||||
_pages.AddMapping(va, size);
|
||||
PtMap(va, pa, size);
|
||||
|
||||
Tracking.Map(va, size);
|
||||
}
|
||||
|
||||
private void PtMap(ulong va, ulong pa, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Map(va, pa);
|
||||
|
||||
va += PageSize;
|
||||
pa += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
UnmapEvent?.Invoke(va, size);
|
||||
Tracking.Unmap(va, size);
|
||||
|
||||
_pages.RemoveMapping(va, size);
|
||||
PtUnmap(va, size);
|
||||
_addressSpace.UnmapView(_backingMemory, va, size);
|
||||
}
|
||||
|
||||
private void PtUnmap(ulong va, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Unmap(va);
|
||||
|
||||
va += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission permission)
|
||||
{
|
||||
}
|
||||
|
||||
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
||||
{
|
||||
ThrowMemoryNotContiguous();
|
||||
}
|
||||
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
||||
|
||||
return ref _backingMemory.GetRef<T>(GetPhysicalAddressChecked(va));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsMapped(ulong va)
|
||||
{
|
||||
return ValidateAddress(va) && _pages.IsMapped(va);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRangeMapped(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
return _pages.IsRangeMapped(va, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<HostMemoryRange>();
|
||||
}
|
||||
|
||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||
if (guestRegions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var regions = new HostMemoryRange[guestRegions.Count];
|
||||
|
||||
for (int i = 0; i < regions.Length; i++)
|
||||
{
|
||||
var guestRegion = guestRegions[i];
|
||||
IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<MemoryRange>();
|
||||
}
|
||||
|
||||
return GetPhysicalRegionsImpl(va, size);
|
||||
}
|
||||
|
||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int pages = GetPagesCount(va, (uint)size, out va);
|
||||
|
||||
var regions = new List<MemoryRange>();
|
||||
|
||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||
ulong regionSize = PageSize;
|
||||
|
||||
for (int page = 0; page < pages - 1; page++)
|
||||
{
|
||||
if (!ValidateAddress(va + PageSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||
|
||||
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||
{
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
regionStart = newPa;
|
||||
regionSize = 0;
|
||||
}
|
||||
|
||||
va += PageSize;
|
||||
regionSize += PageSize;
|
||||
}
|
||||
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
private ulong GetPhysicalAddressChecked(ulong va)
|
||||
{
|
||||
if (!IsMapped(va))
|
||||
{
|
||||
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
|
||||
}
|
||||
|
||||
return GetPhysicalAddressInternal(va);
|
||||
}
|
||||
|
||||
private ulong GetPhysicalAddressInternal(ulong va)
|
||||
{
|
||||
return _pageTable.Read(va) + (va & PageMask);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
||||
/// </remarks>
|
||||
public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
if (precise)
|
||||
{
|
||||
Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
|
||||
return;
|
||||
}
|
||||
|
||||
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
|
||||
{
|
||||
if (guest)
|
||||
{
|
||||
_addressSpace.Reprotect(va, size, protection, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pages.TrackingReprotect(va, size, protection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags)
|
||||
{
|
||||
return Tracking.BeginTracking(address, size, id, flags);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags)
|
||||
{
|
||||
return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||
{
|
||||
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources used by the memory manager.
|
||||
/// </summary>
|
||||
protected override void Destroy()
|
||||
{
|
||||
_addressSpace.Dispose();
|
||||
_memoryEh.Dispose();
|
||||
}
|
||||
|
||||
protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
||||
=> _backingMemory.GetMemory(pa, size);
|
||||
|
||||
protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
|
||||
=> _backingMemory.GetSpan(pa, size);
|
||||
|
||||
protected override nuint TranslateVirtualAddressChecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressChecked(va);
|
||||
|
||||
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressInternal(va);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
|
||||
@@ -703,8 +703,6 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
switch ((ArmShiftType)sType)
|
||||
{
|
||||
case ArmShiftType.Lsr:
|
||||
shift = 32;
|
||||
break;
|
||||
case ArmShiftType.Asr:
|
||||
shift = 32;
|
||||
break;
|
||||
|
||||
@@ -45,6 +45,11 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
_translator.InvalidateJitCacheRegion(address, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,24 @@ namespace Ryujinx.Cpu
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryEhMeilleure(ulong asSize, MemoryTracking tracking)
|
||||
{
|
||||
_tracking = tracking;
|
||||
_baseAddress = 0UL;
|
||||
ulong endAddress = asSize;
|
||||
|
||||
_trackingEvent = VirtualMemoryEvent;
|
||||
|
||||
_pageSize = MemoryBlock.GetPageSize();
|
||||
|
||||
bool added = NativeSignalHandler.AddTrackedRegion((nuint)_baseAddress, (nuint)endAddress, Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||
|
||||
if (!added)
|
||||
{
|
||||
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
|
||||
}
|
||||
}
|
||||
|
||||
private ulong VirtualMemoryEvent(ulong address, ulong size, bool write)
|
||||
{
|
||||
ulong pageSize = _pageSize;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
enum ArmCondition
|
||||
{
|
||||
Eq = 0,
|
||||
Ne = 1,
|
||||
GeUn = 2,
|
||||
LtUn = 3,
|
||||
Mi = 4,
|
||||
Pl = 5,
|
||||
Vs = 6,
|
||||
Vc = 7,
|
||||
GtUn = 8,
|
||||
LeUn = 9,
|
||||
Ge = 10,
|
||||
Lt = 11,
|
||||
Gt = 12,
|
||||
Le = 13,
|
||||
Al = 14,
|
||||
Nv = 15,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
enum ArmExtensionType
|
||||
{
|
||||
Uxtb = 0,
|
||||
Uxth = 1,
|
||||
Uxtw = 2,
|
||||
Uxtx = 3,
|
||||
Sxtb = 4,
|
||||
Sxth = 5,
|
||||
Sxtw = 6,
|
||||
Sxtx = 7,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
enum ArmShiftType
|
||||
{
|
||||
Lsl = 0,
|
||||
Lsr = 1,
|
||||
Asr = 2,
|
||||
Ror = 3,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
static class CodeGenCommon
|
||||
{
|
||||
public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR)
|
||||
{
|
||||
return TryEncodeBitMask(operand.Type, operand.Value, out immN, out immS, out immR);
|
||||
}
|
||||
|
||||
public static bool TryEncodeBitMask(OperandType type, ulong value, out int immN, out int immS, out int immR)
|
||||
{
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
value |= value << 32;
|
||||
}
|
||||
|
||||
return TryEncodeBitMask(value, out immN, out immS, out immR);
|
||||
}
|
||||
|
||||
public static bool TryEncodeBitMask(ulong value, out int immN, out int immS, out int immR)
|
||||
{
|
||||
// Some special values also can't be encoded:
|
||||
// 0 can't be encoded because we need to subtract 1 from onesCount (which would became negative if 0).
|
||||
// A value with all bits set can't be encoded because it is reserved according to the spec, because:
|
||||
// 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)
|
||||
{
|
||||
immN = 0;
|
||||
immS = 0;
|
||||
immR = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Normalize value, rotating it such that the LSB is 1: Ensures we get a complete element that has not
|
||||
// been cut-in-half across the word boundary.
|
||||
int rotation = BitOperations.TrailingZeroCount(value & (value + 1));
|
||||
ulong rotatedValue = ulong.RotateRight(value, rotation);
|
||||
|
||||
// Now that we have a complete element in the LSB with the LSB = 1, determine size and number of ones
|
||||
// in element.
|
||||
int elementSize = BitOperations.TrailingZeroCount(rotatedValue & (rotatedValue + 1));
|
||||
int onesInElement = BitOperations.TrailingZeroCount(~rotatedValue);
|
||||
|
||||
// Check the value is repeating; also ensures element size is a power of two.
|
||||
if (ulong.RotateRight(value, elementSize) != value)
|
||||
{
|
||||
immN = 0;
|
||||
immS = 0;
|
||||
immR = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
immN = (elementSize >> 6) & 1;
|
||||
immS = (((~elementSize + 1) << 1) | (onesInElement - 1)) & 0x3f;
|
||||
immR = (elementSize - rotation) & (elementSize - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
struct Operand
|
||||
{
|
||||
public readonly OperandKind Kind { get; }
|
||||
public readonly OperandType Type { get; }
|
||||
public readonly ulong Value { get; }
|
||||
|
||||
public Operand(OperandKind kind, OperandType type, ulong value)
|
||||
{
|
||||
Kind = kind;
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public Operand(int index, RegisterType regType, OperandType type) : this(OperandKind.Register, type, (ulong)((int)regType << 24 | index))
|
||||
{
|
||||
}
|
||||
|
||||
public Operand(OperandType type, ulong value) : this(OperandKind.Constant, type, value)
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Register GetRegister()
|
||||
{
|
||||
Debug.Assert(Kind == OperandKind.Register);
|
||||
|
||||
return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
|
||||
}
|
||||
|
||||
public readonly int AsInt32()
|
||||
{
|
||||
return (int)Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
enum OperandKind
|
||||
{
|
||||
None,
|
||||
Constant,
|
||||
Label,
|
||||
Register,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
enum OperandType
|
||||
{
|
||||
None,
|
||||
I32,
|
||||
I64,
|
||||
FP32,
|
||||
FP64,
|
||||
V128,
|
||||
}
|
||||
|
||||
static class OperandTypeExtensions
|
||||
{
|
||||
public static bool IsInteger(this OperandType type)
|
||||
{
|
||||
return type == OperandType.I32 ||
|
||||
type == OperandType.I64;
|
||||
}
|
||||
|
||||
public static int GetSizeInBytes(this OperandType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
OperandType.FP32 => 4,
|
||||
OperandType.FP64 => 8,
|
||||
OperandType.I32 => 4,
|
||||
OperandType.I64 => 8,
|
||||
OperandType.V128 => 16,
|
||||
_ => throw new InvalidOperationException($"Invalid operand type \"{type}\"."),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
readonly struct Register : IEquatable<Register>
|
||||
{
|
||||
public int Index { get; }
|
||||
|
||||
public RegisterType Type { get; }
|
||||
|
||||
public Register(int index, RegisterType type)
|
||||
{
|
||||
Index = index;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (ushort)Index | ((int)Type << 16);
|
||||
}
|
||||
|
||||
public static bool operator ==(Register x, Register y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool operator !=(Register x, Register y)
|
||||
{
|
||||
return !x.Equals(y);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Register reg && Equals(reg);
|
||||
}
|
||||
|
||||
public bool Equals(Register other)
|
||||
{
|
||||
return other.Index == Index &&
|
||||
other.Type == Type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
readonly struct RegisterSaveRestore
|
||||
{
|
||||
private const int FpRegister = 29;
|
||||
private const int LrRegister = 30;
|
||||
|
||||
private const int Encodable9BitsOffsetLimit = 0x200;
|
||||
|
||||
private readonly int _intMask;
|
||||
private readonly int _vecMask;
|
||||
private readonly OperandType _vecType;
|
||||
private readonly bool _hasCall;
|
||||
|
||||
public RegisterSaveRestore(int intMask, int vecMask = 0, OperandType vecType = OperandType.FP64, bool hasCall = false)
|
||||
{
|
||||
_intMask = intMask;
|
||||
_vecMask = vecMask;
|
||||
_vecType = vecType;
|
||||
_hasCall = hasCall;
|
||||
}
|
||||
|
||||
public void WritePrologue(Assembler asm)
|
||||
{
|
||||
int intMask = _intMask;
|
||||
int vecMask = _vecMask;
|
||||
|
||||
int intCalleeSavedRegsCount = BitOperations.PopCount((uint)intMask);
|
||||
int vecCalleeSavedRegsCount = BitOperations.PopCount((uint)vecMask);
|
||||
|
||||
int calleeSaveRegionSize = Align16(intCalleeSavedRegsCount * 8 + vecCalleeSavedRegsCount * _vecType.GetSizeInBytes());
|
||||
|
||||
int offset = 0;
|
||||
|
||||
WritePrologueCalleeSavesPreIndexed(asm, ref intMask, ref offset, calleeSaveRegionSize, OperandType.I64);
|
||||
|
||||
if (_vecType == OperandType.V128 && (intCalleeSavedRegsCount & 1) != 0)
|
||||
{
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
WritePrologueCalleeSavesPreIndexed(asm, ref vecMask, ref offset, calleeSaveRegionSize, _vecType);
|
||||
|
||||
if (_hasCall)
|
||||
{
|
||||
Operand rsp = Register(Assembler.SpRegister);
|
||||
|
||||
asm.StpRiPre(Register(FpRegister), Register(LrRegister), rsp, -16);
|
||||
asm.MovSp(Register(FpRegister), rsp);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WritePrologueCalleeSavesPreIndexed(
|
||||
Assembler asm,
|
||||
ref int mask,
|
||||
ref int offset,
|
||||
int calleeSaveRegionSize,
|
||||
OperandType type)
|
||||
{
|
||||
if ((BitOperations.PopCount((uint)mask) & 1) != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(mask);
|
||||
|
||||
mask &= ~(1 << reg);
|
||||
|
||||
if (offset != 0)
|
||||
{
|
||||
asm.StrRiUn(Register(reg, type), Register(Assembler.SpRegister), offset);
|
||||
}
|
||||
else if (calleeSaveRegionSize < Encodable9BitsOffsetLimit)
|
||||
{
|
||||
asm.StrRiPre(Register(reg, type), Register(Assembler.SpRegister), -calleeSaveRegionSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Sub(Register(Assembler.SpRegister), Register(Assembler.SpRegister), new Operand(OperandType.I64, (ulong)calleeSaveRegionSize));
|
||||
asm.StrRiUn(Register(reg, type), Register(Assembler.SpRegister), 0);
|
||||
}
|
||||
|
||||
offset += type.GetSizeInBytes();
|
||||
}
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int reg = BitOperations.TrailingZeroCount(mask);
|
||||
|
||||
mask &= ~(1 << reg);
|
||||
|
||||
int reg2 = BitOperations.TrailingZeroCount(mask);
|
||||
|
||||
mask &= ~(1 << reg2);
|
||||
|
||||
if (offset != 0)
|
||||
{
|
||||
asm.StpRiUn(Register(reg, type), Register(reg2, type), Register(Assembler.SpRegister), offset);
|
||||
}
|
||||
else if (calleeSaveRegionSize < Encodable9BitsOffsetLimit)
|
||||
{
|
||||
asm.StpRiPre(Register(reg, type), Register(reg2, type), Register(Assembler.SpRegister), -calleeSaveRegionSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Sub(Register(Assembler.SpRegister), Register(Assembler.SpRegister), new Operand(OperandType.I64, (ulong)calleeSaveRegionSize));
|
||||
asm.StpRiUn(Register(reg, type), Register(reg2, type), Register(Assembler.SpRegister), 0);
|
||||
}
|
||||
|
||||
offset += type.GetSizeInBytes() * 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteEpilogue(Assembler asm)
|
||||
{
|
||||
int intMask = _intMask;
|
||||
int vecMask = _vecMask;
|
||||
|
||||
int intCalleeSavedRegsCount = BitOperations.PopCount((uint)intMask);
|
||||
int vecCalleeSavedRegsCount = BitOperations.PopCount((uint)vecMask);
|
||||
|
||||
bool misalignedVector = _vecType == OperandType.V128 && (intCalleeSavedRegsCount & 1) != 0;
|
||||
|
||||
int offset = intCalleeSavedRegsCount * 8 + vecCalleeSavedRegsCount * _vecType.GetSizeInBytes();
|
||||
|
||||
if (misalignedVector)
|
||||
{
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
int calleeSaveRegionSize = Align16(offset);
|
||||
|
||||
if (_hasCall)
|
||||
{
|
||||
Operand rsp = Register(Assembler.SpRegister);
|
||||
|
||||
asm.LdpRiPost(Register(FpRegister), Register(LrRegister), rsp, 16);
|
||||
}
|
||||
|
||||
WriteEpilogueCalleeSavesPostIndexed(asm, ref vecMask, ref offset, calleeSaveRegionSize, _vecType);
|
||||
|
||||
if (misalignedVector)
|
||||
{
|
||||
offset -= 8;
|
||||
}
|
||||
|
||||
WriteEpilogueCalleeSavesPostIndexed(asm, ref intMask, ref offset, calleeSaveRegionSize, OperandType.I64);
|
||||
}
|
||||
|
||||
private static void WriteEpilogueCalleeSavesPostIndexed(
|
||||
Assembler asm,
|
||||
ref int mask,
|
||||
ref int offset,
|
||||
int calleeSaveRegionSize,
|
||||
OperandType type)
|
||||
{
|
||||
while (mask != 0)
|
||||
{
|
||||
int reg = HighestBitSet(mask);
|
||||
|
||||
mask &= ~(1 << reg);
|
||||
|
||||
if (mask != 0)
|
||||
{
|
||||
int reg2 = HighestBitSet(mask);
|
||||
|
||||
mask &= ~(1 << reg2);
|
||||
|
||||
offset -= type.GetSizeInBytes() * 2;
|
||||
|
||||
if (offset != 0)
|
||||
{
|
||||
asm.LdpRiUn(Register(reg2, type), Register(reg, type), Register(Assembler.SpRegister), offset);
|
||||
}
|
||||
else if (calleeSaveRegionSize < Encodable9BitsOffsetLimit)
|
||||
{
|
||||
asm.LdpRiPost(Register(reg2, type), Register(reg, type), Register(Assembler.SpRegister), calleeSaveRegionSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.LdpRiUn(Register(reg2, type), Register(reg, type), Register(Assembler.SpRegister), 0);
|
||||
asm.Add(Register(Assembler.SpRegister), Register(Assembler.SpRegister), new Operand(OperandType.I64, (ulong)calleeSaveRegionSize));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offset -= type.GetSizeInBytes();
|
||||
|
||||
if (offset != 0)
|
||||
{
|
||||
asm.LdrRiUn(Register(reg, type), Register(Assembler.SpRegister), offset);
|
||||
}
|
||||
else if (calleeSaveRegionSize < Encodable9BitsOffsetLimit)
|
||||
{
|
||||
asm.LdrRiPost(Register(reg, type), Register(Assembler.SpRegister), calleeSaveRegionSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.LdrRiUn(Register(reg, type), Register(Assembler.SpRegister), 0);
|
||||
asm.Add(Register(Assembler.SpRegister), Register(Assembler.SpRegister), new Operand(OperandType.I64, (ulong)calleeSaveRegionSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int HighestBitSet(int value)
|
||||
{
|
||||
return 31 - BitOperations.LeadingZeroCount((uint)value);
|
||||
}
|
||||
|
||||
private static Operand Register(int register, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static int Align16(int value)
|
||||
{
|
||||
return (value + 0xf) & ~0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.Cpu.Nce.Arm64
|
||||
{
|
||||
enum RegisterType
|
||||
{
|
||||
Integer,
|
||||
Vector,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Range;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||
/// </summary>
|
||||
public sealed class MemoryManagerNative : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
private readonly MemoryBlock _addressSpace;
|
||||
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
private readonly PageTable<ulong> _pageTable;
|
||||
|
||||
private readonly MemoryEhMeilleure _memoryEh;
|
||||
|
||||
private readonly ManagedPageFlags _pages;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public IntPtr PageTablePointer => IntPtr.Zero;
|
||||
|
||||
public ulong ReservedSize => (ulong)_addressSpace.Pointer.ToInt64();
|
||||
|
||||
public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe;
|
||||
|
||||
public MemoryTracking Tracking { get; }
|
||||
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
protected override ulong AddressSpaceSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the host mapped memory manager.
|
||||
/// </summary>
|
||||
/// <param name="addressSpace">Address space memory block</param>
|
||||
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
|
||||
/// <param name="addressSpaceSize">Size of the address space</param>
|
||||
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
||||
public MemoryManagerNative(
|
||||
MemoryBlock addressSpace,
|
||||
MemoryBlock backingMemory,
|
||||
ulong addressSpaceSize,
|
||||
InvalidAccessHandler invalidAccessHandler = null)
|
||||
{
|
||||
_backingMemory = backingMemory;
|
||||
_pageTable = new PageTable<ulong>();
|
||||
AddressSpaceSize = addressSpaceSize;
|
||||
|
||||
ulong asSize = PageSize;
|
||||
int asBits = PageBits;
|
||||
|
||||
while (asSize < addressSpaceSize)
|
||||
{
|
||||
asSize <<= 1;
|
||||
asBits++;
|
||||
}
|
||||
|
||||
AddressSpaceBits = asBits;
|
||||
|
||||
_pages = new ManagedPageFlags(asBits);
|
||||
|
||||
_addressSpace = addressSpace;
|
||||
|
||||
Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
|
||||
_memoryEh = new MemoryEhMeilleure(addressSpaceSize, Tracking);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
_addressSpace.MapView(_backingMemory, pa, AddressToOffset(va), size);
|
||||
_pages.AddMapping(va, size);
|
||||
PtMap(va, pa, size);
|
||||
|
||||
Tracking.Map(va, size);
|
||||
}
|
||||
|
||||
private void PtMap(ulong va, ulong pa, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Map(va, pa);
|
||||
|
||||
va += PageSize;
|
||||
pa += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
UnmapEvent?.Invoke(va, size);
|
||||
Tracking.Unmap(va, size);
|
||||
|
||||
_pages.RemoveMapping(va, size);
|
||||
PtUnmap(va, size);
|
||||
_addressSpace.UnmapView(_backingMemory, AddressToOffset(va), size);
|
||||
}
|
||||
|
||||
private void PtUnmap(ulong va, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Unmap(va);
|
||||
|
||||
va += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
|
||||
{
|
||||
_addressSpace.Reprotect(AddressToOffset(va), size, protection);
|
||||
}
|
||||
|
||||
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
||||
{
|
||||
ThrowMemoryNotContiguous();
|
||||
}
|
||||
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
||||
|
||||
return ref _backingMemory.GetRef<T>(GetPhysicalAddressChecked(va));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsMapped(ulong va)
|
||||
{
|
||||
return ValidateAddress(va) && _pages.IsMapped(va);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRangeMapped(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
return _pages.IsRangeMapped(va, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<HostMemoryRange>();
|
||||
}
|
||||
|
||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||
if (guestRegions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var regions = new HostMemoryRange[guestRegions.Count];
|
||||
|
||||
for (int i = 0; i < regions.Length; i++)
|
||||
{
|
||||
var guestRegion = guestRegions[i];
|
||||
IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<MemoryRange>();
|
||||
}
|
||||
|
||||
return GetPhysicalRegionsImpl(va, size);
|
||||
}
|
||||
|
||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int pages = GetPagesCount(va, (uint)size, out va);
|
||||
|
||||
var regions = new List<MemoryRange>();
|
||||
|
||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||
ulong regionSize = PageSize;
|
||||
|
||||
for (int page = 0; page < pages - 1; page++)
|
||||
{
|
||||
if (!ValidateAddress(va + PageSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||
|
||||
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||
{
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
regionStart = newPa;
|
||||
regionSize = 0;
|
||||
}
|
||||
|
||||
va += PageSize;
|
||||
regionSize += PageSize;
|
||||
}
|
||||
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
private ulong GetPhysicalAddressChecked(ulong va)
|
||||
{
|
||||
if (!IsMapped(va))
|
||||
{
|
||||
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
|
||||
}
|
||||
|
||||
return GetPhysicalAddressInternal(va);
|
||||
}
|
||||
|
||||
private ulong GetPhysicalAddressInternal(ulong va)
|
||||
{
|
||||
return _pageTable.Read(va) + (va & PageMask);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
||||
/// </remarks>
|
||||
public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
if (precise)
|
||||
{
|
||||
Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
|
||||
return;
|
||||
}
|
||||
|
||||
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
|
||||
{
|
||||
if (guest)
|
||||
{
|
||||
_addressSpace.Reprotect(AddressToOffset(va), size, protection, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pages.TrackingReprotect(va, size, protection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags)
|
||||
{
|
||||
return Tracking.BeginTracking(address, size, id, flags);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags)
|
||||
{
|
||||
return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||
{
|
||||
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
|
||||
}
|
||||
|
||||
private ulong AddressToOffset(ulong address)
|
||||
{
|
||||
if (address < ReservedSize)
|
||||
{
|
||||
throw new ArgumentException($"Invalid address 0x{address:x16}");
|
||||
}
|
||||
|
||||
return address - ReservedSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources used by the memory manager.
|
||||
/// </summary>
|
||||
protected override void Destroy()
|
||||
{
|
||||
_addressSpace.Dispose();
|
||||
_memoryEh.Dispose();
|
||||
}
|
||||
|
||||
protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
||||
=> _backingMemory.GetMemory(pa, size);
|
||||
|
||||
protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
|
||||
=> _backingMemory.GetSpan(pa, size);
|
||||
|
||||
protected override nuint TranslateVirtualAddressChecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressChecked(va);
|
||||
|
||||
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressInternal(va);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
/// <summary>
|
||||
/// Native Code Execution CPU code patch.
|
||||
/// </summary>
|
||||
public class NceCpuCodePatch
|
||||
{
|
||||
private readonly List<uint> _code;
|
||||
|
||||
private readonly struct PatchTarget
|
||||
{
|
||||
public readonly int TextIndex;
|
||||
public readonly int PatchStartIndex;
|
||||
public readonly int PatchBranchIndex;
|
||||
|
||||
public PatchTarget(int textIndex, int patchStartIndex, int patchBranchIndex)
|
||||
{
|
||||
TextIndex = textIndex;
|
||||
PatchStartIndex = patchStartIndex;
|
||||
PatchBranchIndex = patchBranchIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<PatchTarget> _patchTargets;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ulong Size => BitUtils.AlignUp((ulong)_code.Count * sizeof(uint), 0x1000UL);
|
||||
|
||||
public NceCpuCodePatch()
|
||||
{
|
||||
_code = new();
|
||||
_patchTargets = new();
|
||||
}
|
||||
|
||||
internal void AddCode(int textIndex, IEnumerable<uint> code)
|
||||
{
|
||||
int patchStartIndex = _code.Count;
|
||||
_code.AddRange(code);
|
||||
_patchTargets.Add(new PatchTarget(textIndex, patchStartIndex, _code.Count - 1));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Write(IVirtualMemoryManager memoryManager, ulong patchAddress, ulong textAddress)
|
||||
{
|
||||
uint[] code = _code.ToArray();
|
||||
|
||||
foreach (var patchTarget in _patchTargets)
|
||||
{
|
||||
ulong instPatchStartAddress = patchAddress + (ulong)patchTarget.PatchStartIndex * sizeof(uint);
|
||||
ulong instPatchBranchAddress = patchAddress + (ulong)patchTarget.PatchBranchIndex * sizeof(uint);
|
||||
ulong instTextAddress = textAddress + (ulong)patchTarget.TextIndex * sizeof(uint);
|
||||
|
||||
uint prevInst = memoryManager.Read<uint>(instTextAddress);
|
||||
|
||||
code[patchTarget.PatchBranchIndex] |= EncodeSImm26_2(checked((int)((long)instTextAddress - (long)instPatchBranchAddress + sizeof(uint))));
|
||||
memoryManager.Write(instTextAddress, 0x14000000u | EncodeSImm26_2(checked((int)((long)instPatchStartAddress - (long)instTextAddress))));
|
||||
|
||||
uint newInst = memoryManager.Read<uint>(instTextAddress);
|
||||
}
|
||||
|
||||
if (Size != 0)
|
||||
{
|
||||
memoryManager.Write(patchAddress, MemoryMarshal.Cast<uint, byte>(code));
|
||||
memoryManager.Reprotect(patchAddress, Size, MemoryPermission.ReadAndExecute);
|
||||
}
|
||||
}
|
||||
|
||||
private static uint EncodeSImm26_2(int value)
|
||||
{
|
||||
uint imm = (uint)(value >> 2) & 0x3ffffff;
|
||||
Debug.Assert(((int)imm << 6) >> 4 == value, $"Failed to encode constant 0x{value:X}.");
|
||||
return imm;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceCpuContext : ICpuContext
|
||||
{
|
||||
private static uint[] _getTpidrEl0Code = new uint[]
|
||||
{
|
||||
GetMrsTpidrEl0(0), // mrs x0, tpidr_el0
|
||||
0xd65f03c0u, // ret
|
||||
};
|
||||
|
||||
private static uint GetMrsTpidrEl0(uint rd)
|
||||
{
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
return 0xd53bd060u | rd; // TPIDRRO
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xd53bd040u | rd; // TPIDR
|
||||
}
|
||||
}
|
||||
|
||||
readonly struct CodeWriter
|
||||
{
|
||||
private readonly List<uint> _fullCode;
|
||||
|
||||
public CodeWriter()
|
||||
{
|
||||
_fullCode = new List<uint>();
|
||||
}
|
||||
|
||||
public ulong Write(uint[] code)
|
||||
{
|
||||
ulong offset = (ulong)_fullCode.Count * sizeof(uint);
|
||||
_fullCode.AddRange(code);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public MemoryBlock CreateMemoryBlock()
|
||||
{
|
||||
ReadOnlySpan<byte> codeBytes = MemoryMarshal.Cast<uint, byte>(_fullCode.ToArray());
|
||||
|
||||
MemoryBlock codeBlock = new(BitUtils.AlignUp((ulong)codeBytes.Length, 0x1000UL));
|
||||
|
||||
codeBlock.Write(0, codeBytes);
|
||||
codeBlock.Reprotect(0, (ulong)codeBytes.Length, MemoryPermission.ReadAndExecute, true);
|
||||
|
||||
return codeBlock;
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void ThreadStart(IntPtr nativeContextPtr);
|
||||
private delegate IntPtr GetTpidrEl0();
|
||||
private static MemoryBlock _codeBlock;
|
||||
private static ThreadStart _threadStart;
|
||||
private static GetTpidrEl0 _getTpidrEl0;
|
||||
|
||||
private readonly ITickSource _tickSource;
|
||||
private readonly IMemoryManager _memoryManager;
|
||||
|
||||
static NceCpuContext()
|
||||
{
|
||||
CodeWriter codeWriter = new();
|
||||
|
||||
uint[] threadStartCode = NcePatcher.GenerateThreadStartCode();
|
||||
uint[] ehSuspendCode = NcePatcher.GenerateSuspendExceptionHandler();
|
||||
|
||||
ulong threadStartCodeOffset = codeWriter.Write(threadStartCode);
|
||||
ulong getTpidrEl0CodeOffset = codeWriter.Write(_getTpidrEl0Code);
|
||||
ulong ehSuspendCodeOffset = codeWriter.Write(ehSuspendCode);
|
||||
|
||||
MemoryBlock codeBlock = null;
|
||||
|
||||
NativeSignalHandler.InitializeSignalHandler((IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr) =>
|
||||
{
|
||||
uint[] ehWrapperCode = NcePatcher.GenerateWrapperExceptionHandler(oldSignalHandlerSegfaultPtr, signalHandlerPtr);
|
||||
ulong ehWrapperCodeOffset = codeWriter.Write(ehWrapperCode);
|
||||
codeBlock = codeWriter.CreateMemoryBlock();
|
||||
return codeBlock.GetPointer(ehWrapperCodeOffset, (ulong)ehWrapperCode.Length * sizeof(uint));
|
||||
});
|
||||
|
||||
NativeSignalHandler.InstallUnixSignalHandler(NceThreadPal.UnixSuspendSignal, codeBlock.GetPointer(ehSuspendCodeOffset, (ulong)ehSuspendCode.Length * sizeof(uint)));
|
||||
|
||||
_threadStart = Marshal.GetDelegateForFunctionPointer<ThreadStart>(codeBlock.GetPointer(threadStartCodeOffset, (ulong)threadStartCode.Length * sizeof(uint)));
|
||||
_getTpidrEl0 = Marshal.GetDelegateForFunctionPointer<GetTpidrEl0>(codeBlock.GetPointer(getTpidrEl0CodeOffset, (ulong)_getTpidrEl0Code.Length * sizeof(uint)));
|
||||
_codeBlock = codeBlock;
|
||||
}
|
||||
|
||||
public NceCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_memoryManager = memory;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||
{
|
||||
return new NceExecutionContext(exceptionCallbacks);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Execute(IExecutionContext context, ulong address)
|
||||
{
|
||||
NceExecutionContext nec = (NceExecutionContext)context;
|
||||
NceNativeInterface.RegisterThread(nec, _tickSource);
|
||||
int tableIndex = NceThreadTable.Register(_getTpidrEl0(), nec.NativeContextPtr);
|
||||
|
||||
nec.SetStartAddress(address);
|
||||
_threadStart(nec.NativeContextPtr);
|
||||
nec.Exit();
|
||||
|
||||
NceThreadTable.Unregister(tableIndex);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateCacheRegion(ulong address, ulong size)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
{
|
||||
return new DummyDiskCacheLoadState();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using ARMeilleure.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
public class NceEngine : ICpuEngine
|
||||
{
|
||||
public ITickSource TickSource{ get; }
|
||||
|
||||
public NceEngine(ITickSource tickSource)
|
||||
{
|
||||
TickSource = tickSource;
|
||||
}
|
||||
|
||||
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
|
||||
{
|
||||
return new NceCpuContext(TickSource, memoryManager, for64Bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceExecutionContext : IExecutionContext
|
||||
{
|
||||
private const ulong AlternateStackSize = 0x4000;
|
||||
|
||||
private readonly NceNativeContext _context;
|
||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||
|
||||
internal IntPtr NativeContextPtr => _context.BasePtr;
|
||||
|
||||
public ulong Pc => 0UL;
|
||||
|
||||
public long TpidrEl0
|
||||
{
|
||||
get => (long)_context.GetStorage().TpidrEl0;
|
||||
set => _context.GetStorage().TpidrEl0 = (ulong)value;
|
||||
}
|
||||
|
||||
public long TpidrroEl0
|
||||
{
|
||||
get => (long)_context.GetStorage().TpidrroEl0;
|
||||
set => _context.GetStorage().TpidrroEl0 = (ulong)value;
|
||||
}
|
||||
|
||||
public uint Pstate
|
||||
{
|
||||
get => _context.GetStorage().Pstate;
|
||||
set => _context.GetStorage().Pstate = value;
|
||||
}
|
||||
|
||||
public uint Fpcr
|
||||
{
|
||||
get => _context.GetStorage().Fpcr;
|
||||
set => _context.GetStorage().Fpcr = value;
|
||||
}
|
||||
|
||||
public uint Fpsr
|
||||
{
|
||||
get => _context.GetStorage().Fpsr;
|
||||
set => _context.GetStorage().Fpsr = value;
|
||||
}
|
||||
|
||||
public bool IsAarch32
|
||||
{
|
||||
get => false;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Running { get; private set; }
|
||||
|
||||
private delegate bool SupervisorCallHandler(int imm);
|
||||
private SupervisorCallHandler _svcHandler;
|
||||
|
||||
private MemoryBlock _alternateStackMemory;
|
||||
|
||||
public NceExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||
{
|
||||
_svcHandler = OnSupervisorCall;
|
||||
IntPtr svcHandlerPtr = Marshal.GetFunctionPointerForDelegate(_svcHandler);
|
||||
|
||||
_context = new NceNativeContext();
|
||||
|
||||
ref var storage = ref _context.GetStorage();
|
||||
storage.SvcCallHandler = svcHandlerPtr;
|
||||
storage.InManaged = 1u;
|
||||
storage.CtrEl0 = 0x8444c004; // TODO: Get value from host CPU instead of using guest one?
|
||||
|
||||
Running = true;
|
||||
_exceptionCallbacks = exceptionCallbacks;
|
||||
}
|
||||
|
||||
public ulong GetX(int index) => _context.GetStorage().X[index];
|
||||
public void SetX(int index, ulong value) => _context.GetStorage().X[index] = value;
|
||||
|
||||
public V128 GetV(int index) => _context.GetStorage().V[index];
|
||||
public void SetV(int index, V128 value) => _context.GetStorage().V[index] = value;
|
||||
|
||||
// TODO
|
||||
public bool GetPstateFlag(PState flag) => false;
|
||||
public void SetPstateFlag(PState flag, bool value) { }
|
||||
|
||||
// TODO
|
||||
public bool GetFPstateFlag(FPState flag) => false;
|
||||
public void SetFPstateFlag(FPState flag, bool value) { }
|
||||
|
||||
public void SetStartAddress(ulong address)
|
||||
{
|
||||
ref var storage = ref _context.GetStorage();
|
||||
storage.X[30] = address;
|
||||
storage.HostThreadHandle = NceThreadPal.GetCurrentThreadHandle();
|
||||
|
||||
RegisterAlternateStack();
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_context.GetStorage().HostThreadHandle = IntPtr.Zero;
|
||||
|
||||
UnregisterAlternateStack();
|
||||
}
|
||||
|
||||
private void RegisterAlternateStack()
|
||||
{
|
||||
// We need to use an alternate stack to handle the suspend signal,
|
||||
// as the guest stack may be in a state that is not suitable for the signal handlers.
|
||||
|
||||
_alternateStackMemory = new MemoryBlock(AlternateStackSize);
|
||||
NativeSignalHandler.InstallUnixAlternateStackForCurrentThread(_alternateStackMemory.GetPointer(0UL, AlternateStackSize), AlternateStackSize);
|
||||
}
|
||||
|
||||
private void UnregisterAlternateStack()
|
||||
{
|
||||
NativeSignalHandler.UninstallUnixAlternateStackForCurrentThread();
|
||||
_alternateStackMemory.Dispose();
|
||||
_alternateStackMemory = null;
|
||||
}
|
||||
|
||||
public bool OnSupervisorCall(int imm)
|
||||
{
|
||||
_exceptionCallbacks.SupervisorCallback?.Invoke(this, 0UL, imm);
|
||||
return Running;
|
||||
}
|
||||
|
||||
public bool OnInterrupt()
|
||||
{
|
||||
_exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||
return Running;
|
||||
}
|
||||
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
IntPtr threadHandle = _context.GetStorage().HostThreadHandle;
|
||||
if (threadHandle != IntPtr.Zero)
|
||||
{
|
||||
// Bit 0 set means that the thread is currently running managed code.
|
||||
// Bit 1 set means that an interrupt was requested for the thread.
|
||||
// This, we only need to send the suspend signal if the value was 0 (not running managed code,
|
||||
// and no interrupt was requested before).
|
||||
|
||||
ref uint inManaged = ref _context.GetStorage().InManaged;
|
||||
uint oldValue = Interlocked.Or(ref inManaged, 2);
|
||||
|
||||
if (oldValue == 0)
|
||||
{
|
||||
NceThreadPal.SuspendThread(threadHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StopRunning()
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceMemoryAllocator : IJitMemoryAllocator
|
||||
{
|
||||
public IJitMemoryBlock Allocate(ulong size) => new NceMemoryBlock(size, MemoryAllocationFlags.None);
|
||||
public IJitMemoryBlock Reserve(ulong size) => new NceMemoryBlock(size, MemoryAllocationFlags.Reserve);
|
||||
|
||||
public ulong GetPageSize() => MemoryBlock.GetPageSize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceMemoryBlock : IJitMemoryBlock
|
||||
{
|
||||
private readonly MemoryBlock _impl;
|
||||
|
||||
public IntPtr Pointer => _impl.Pointer;
|
||||
|
||||
public NceMemoryBlock(ulong size, MemoryAllocationFlags flags)
|
||||
{
|
||||
_impl = new MemoryBlock(size, flags);
|
||||
}
|
||||
|
||||
public void Commit(ulong offset, ulong size) => _impl.Commit(offset, size);
|
||||
public void MapAsRw(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndWrite);
|
||||
public void MapAsRx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndExecute);
|
||||
public void MapAsRwx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadWriteExecute);
|
||||
|
||||
public void Dispose() => _impl.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceNativeContext : IDisposable
|
||||
{
|
||||
public struct NativeCtxStorage
|
||||
{
|
||||
public Array32<ulong> X;
|
||||
public Array32<V128> V;
|
||||
public ulong TpidrEl0;
|
||||
public ulong TpidrroEl0;
|
||||
public ulong CtrEl0;
|
||||
public uint Pstate;
|
||||
public uint Fpcr;
|
||||
public uint Fpsr;
|
||||
public uint InManaged;
|
||||
public ulong HostSp;
|
||||
public IntPtr HostThreadHandle;
|
||||
public ulong TempStorage;
|
||||
public IntPtr SvcCallHandler;
|
||||
}
|
||||
|
||||
private static NativeCtxStorage _dummyStorage = new();
|
||||
|
||||
private readonly MemoryBlock _block;
|
||||
|
||||
public IntPtr BasePtr => _block.Pointer;
|
||||
|
||||
public NceNativeContext()
|
||||
{
|
||||
_block = new MemoryBlock((ulong)Unsafe.SizeOf<NativeCtxStorage>());
|
||||
}
|
||||
|
||||
public static int GetXOffset(int index)
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[index]);
|
||||
}
|
||||
|
||||
public static int GetGuestSPOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[31]);
|
||||
}
|
||||
|
||||
public static int GetVOffset(int index)
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[index]);
|
||||
}
|
||||
|
||||
public static int GetTpidrEl0Offset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
|
||||
}
|
||||
|
||||
public static int GetTpidrroEl0Offset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
|
||||
}
|
||||
|
||||
public static int GetInManagedOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.InManaged);
|
||||
}
|
||||
|
||||
public static int GetHostSPOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.HostSp);
|
||||
}
|
||||
|
||||
public static int GetCtrEl0Offset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CtrEl0);
|
||||
}
|
||||
|
||||
public static int GetTempStorageOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TempStorage);
|
||||
}
|
||||
|
||||
public static int GetSvcCallHandlerOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.SvcCallHandler);
|
||||
}
|
||||
|
||||
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
||||
{
|
||||
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
||||
}
|
||||
|
||||
public unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer);
|
||||
|
||||
public void Dispose() => _block.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceNativeInterface
|
||||
{
|
||||
private delegate ulong GetTickCounterDelegate();
|
||||
private delegate bool SuspendThreadHandlerDelegate();
|
||||
private static GetTickCounterDelegate _getTickCounter;
|
||||
private static SuspendThreadHandlerDelegate _suspendThreadHandler;
|
||||
private static IntPtr _getTickCounterPtr;
|
||||
private static IntPtr _suspendThreadHandlerPtr;
|
||||
|
||||
[ThreadStatic]
|
||||
private static NceExecutionContext _context;
|
||||
|
||||
[ThreadStatic]
|
||||
private static ITickSource _tickSource;
|
||||
|
||||
static NceNativeInterface()
|
||||
{
|
||||
_getTickCounter = GetTickCounter;
|
||||
_suspendThreadHandler = SuspendThreadHandler;
|
||||
_getTickCounterPtr = Marshal.GetFunctionPointerForDelegate(_getTickCounter);
|
||||
_suspendThreadHandlerPtr = Marshal.GetFunctionPointerForDelegate(_suspendThreadHandler);
|
||||
}
|
||||
|
||||
public static void RegisterThread(NceExecutionContext context, ITickSource tickSource)
|
||||
{
|
||||
_context = context;
|
||||
_tickSource = tickSource;
|
||||
}
|
||||
|
||||
public static ulong GetTickCounter()
|
||||
{
|
||||
return _tickSource.Counter;
|
||||
}
|
||||
|
||||
public static bool SuspendThreadHandler()
|
||||
{
|
||||
return _context.OnInterrupt();
|
||||
}
|
||||
|
||||
public static IntPtr GetTickCounterAccessFunctionPointer()
|
||||
{
|
||||
return _getTickCounterPtr;
|
||||
}
|
||||
|
||||
public static IntPtr GetSuspendThreadHandlerFunctionPointer()
|
||||
{
|
||||
return _suspendThreadHandlerPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,603 @@
|
||||
using Ryujinx.Cpu.Nce.Arm64;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
public static class NcePatcher
|
||||
{
|
||||
private const int ScratchBaseReg = 19;
|
||||
|
||||
private const uint IntCalleeSavedRegsMask = 0x1ff80000; // X19 to X28
|
||||
private const uint FpCalleeSavedRegsMask = 0xff00; // D8 to D15
|
||||
|
||||
public static NceCpuCodePatch CreatePatch(ReadOnlySpan<byte> textSection)
|
||||
{
|
||||
NceCpuCodePatch codePatch = new();
|
||||
|
||||
var textUint = MemoryMarshal.Cast<byte, uint>(textSection);
|
||||
|
||||
for (int i = 0; i < textUint.Length; i++)
|
||||
{
|
||||
uint inst = textUint[i];
|
||||
ulong address = (ulong)i * sizeof(uint);
|
||||
|
||||
if ((inst & ~(0xffffu << 5)) == 0xd4000001u) // svc #0
|
||||
{
|
||||
uint svcId = (ushort)(inst >> 5);
|
||||
codePatch.AddCode(i, WriteSvcPatch(svcId));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched SVC #{svcId} at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53bd060) // mrs x0, tpidrro_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
codePatch.AddCode(i, WriteMrsTpidrroEl0Patch(rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, tpidrro_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53bd040) // mrs x0, tpidr_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
codePatch.AddCode(i, WriteMrsTpidrEl0Patch(rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, tpidr_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53b0020 && OperatingSystem.IsMacOS()) // mrs x0, ctr_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
codePatch.AddCode(i, WriteMrsCtrEl0Patch(rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, ctr_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53be020) // mrs x0, cntpct_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
codePatch.AddCode(i, WriteMrsCntpctEl0Patch(rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, cntpct_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd51bd040) // msr tpidr_el0, x0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
codePatch.AddCode(i, WriteMsrTpidrEl0Patch(rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MSR tpidr_el0, x{rd} at 0x{address:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
return codePatch;
|
||||
}
|
||||
|
||||
private static uint[] WriteSvcPatch(uint svcId)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
WriteManagedCall(asm, (asm, ctx, tmp, tmp2) =>
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
asm.StrRiUn(Gpr(i), ctx, NceNativeContext.GetXOffset(i));
|
||||
}
|
||||
|
||||
WriteInManagedLockAcquire(asm, ctx, tmp, tmp2);
|
||||
|
||||
asm.Mov(Gpr(0, OperandType.I32), svcId);
|
||||
asm.LdrRiUn(tmp, ctx, NceNativeContext.GetSvcCallHandlerOffset());
|
||||
asm.Blr(tmp);
|
||||
|
||||
Operand lblContinue = asm.CreateLabel();
|
||||
Operand lblQuit = asm.CreateLabel();
|
||||
|
||||
asm.Cbnz(Gpr(0, OperandType.I32), lblContinue);
|
||||
|
||||
asm.MarkLabel(lblQuit);
|
||||
|
||||
CreateRegisterSaveRestoreForManaged().WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
asm.MarkLabel(lblContinue);
|
||||
|
||||
WriteInManagedLockRelease(asm, ctx, tmp, tmp2, ThreadExitMethod.Label, lblQuit);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
asm.LdrRiUn(Gpr(i), ctx, NceNativeContext.GetXOffset(i));
|
||||
}
|
||||
}, 0xff);
|
||||
|
||||
asm.B(0);
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
private static uint[] WriteMrsTpidrroEl0Patch(uint rd)
|
||||
{
|
||||
return WriteMrsContextRead(rd, NceNativeContext.GetTpidrroEl0Offset());
|
||||
}
|
||||
|
||||
private static uint[] WriteMrsTpidrEl0Patch(uint rd)
|
||||
{
|
||||
return WriteMrsContextRead(rd, NceNativeContext.GetTpidrEl0Offset());
|
||||
}
|
||||
|
||||
private static uint[] WriteMrsCtrEl0Patch(uint rd)
|
||||
{
|
||||
return WriteMrsContextRead(rd, NceNativeContext.GetCtrEl0Offset());
|
||||
}
|
||||
|
||||
private static uint[] WriteMrsCntpctEl0Patch(uint rd)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
WriteManagedCall(asm, (asm, ctx, tmp, tmp2) =>
|
||||
{
|
||||
WriteInManagedLockAcquire(asm, ctx, tmp, tmp2);
|
||||
|
||||
asm.Mov(tmp, (ulong)NceNativeInterface.GetTickCounterAccessFunctionPointer());
|
||||
asm.Blr(tmp);
|
||||
asm.StrRiUn(Gpr(0), ctx, NceNativeContext.GetTempStorageOffset());
|
||||
|
||||
WriteInManagedLockRelease(asm, ctx, tmp, tmp2, ThreadExitMethod.GenerateReturn);
|
||||
|
||||
asm.LdrRiUn(Gpr((int)rd), ctx, NceNativeContext.GetTempStorageOffset());
|
||||
}, 1u << (int)rd);
|
||||
|
||||
asm.B(0);
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
private static uint[] WriteMsrTpidrEl0Patch(uint rd)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[3];
|
||||
PickScratchRegs(scratchRegs, 1u << (int)rd);
|
||||
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]));
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
WriteLoadContext(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
asm.StrRiUn(Gpr((int)rd), Gpr(scratchRegs[0]),NceNativeContext.GetTpidrEl0Offset());
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.B(0);
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
private static uint[] WriteMrsContextRead(uint rd, int contextOffset)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[3];
|
||||
PickScratchRegs(scratchRegs, 1u << (int)rd);
|
||||
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]));
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
WriteLoadContext(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
asm.Add(Gpr((int)rd), Gpr(scratchRegs[0]), Const((ulong)contextOffset));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.LdrRiUn(Gpr((int)rd), Gpr((int)rd), 0);
|
||||
|
||||
asm.B(0);
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
private static void WriteLoadContext(Assembler asm, Operand tmp0, Operand tmp1, Operand tmp2)
|
||||
{
|
||||
asm.Mov(tmp0, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
asm.MrsTpidrroEl0(tmp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.MrsTpidrEl0(tmp1);
|
||||
}
|
||||
|
||||
Operand lblFound = asm.CreateLabel();
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.LdrRiPost(tmp2, tmp0, 16);
|
||||
asm.Cmp(tmp1, tmp2);
|
||||
asm.B(lblFound, ArmCondition.Eq);
|
||||
asm.B(lblLoop);
|
||||
|
||||
asm.MarkLabel(lblFound);
|
||||
|
||||
asm.Ldur(tmp0, tmp0, -8);
|
||||
}
|
||||
|
||||
private static void WriteLoadContextSafe(Assembler asm, Operand lblFail, Operand tmp0, Operand tmp1, Operand tmp2, Operand tmp3)
|
||||
{
|
||||
asm.Mov(tmp0, (ulong)NceThreadTable.EntriesPointer);
|
||||
asm.Ldur(tmp3, tmp0, -8);
|
||||
asm.Add(tmp3, tmp0, tmp3, ArmShiftType.Lsl, 4);
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
asm.MrsTpidrroEl0(tmp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.MrsTpidrEl0(tmp1);
|
||||
}
|
||||
|
||||
Operand lblFound = asm.CreateLabel();
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Cmp(tmp0, tmp3);
|
||||
asm.B(lblFail, ArmCondition.GeUn);
|
||||
asm.LdrRiPost(tmp2, tmp0, 16);
|
||||
asm.Cmp(tmp1, tmp2);
|
||||
asm.B(lblFound, ArmCondition.Eq);
|
||||
asm.B(lblLoop);
|
||||
|
||||
asm.MarkLabel(lblFound);
|
||||
|
||||
asm.Ldur(tmp0, tmp0, -8);
|
||||
}
|
||||
|
||||
private static void PickScratchRegs(Span<int> scratchRegs, uint blacklistedRegMask)
|
||||
{
|
||||
int scratchReg = ScratchBaseReg;
|
||||
|
||||
for (int i = 0; i < scratchRegs.Length; i++)
|
||||
{
|
||||
while ((blacklistedRegMask & (1u << scratchReg)) != 0)
|
||||
{
|
||||
scratchReg++;
|
||||
}
|
||||
|
||||
if (scratchReg >= 29)
|
||||
{
|
||||
throw new ArgumentException($"No enough register for {scratchRegs.Length} scratch register, started from {ScratchBaseReg}");
|
||||
}
|
||||
|
||||
scratchRegs[i] = scratchReg++;
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand Gpr(int register, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static Operand Vec(int register, OperandType type = OperandType.V128)
|
||||
{
|
||||
return new Operand(register, RegisterType.Vector, type);
|
||||
}
|
||||
|
||||
private static Operand Const(ulong value)
|
||||
{
|
||||
return new Operand(OperandType.I64, value);
|
||||
}
|
||||
|
||||
private static Operand Const(OperandType type, ulong value)
|
||||
{
|
||||
return new Operand(type, value);
|
||||
}
|
||||
|
||||
private static uint GetImm26(ulong sourceAddress, ulong targetAddress)
|
||||
{
|
||||
long offset = (long)(targetAddress - sourceAddress);
|
||||
long offsetTrunc = (offset >> 2) & 0x3FFFFFF;
|
||||
|
||||
if ((offsetTrunc << 38) >> 36 != offset)
|
||||
{
|
||||
throw new Exception($"Offset out of range: 0x{sourceAddress:X} -> 0x{targetAddress:X} (0x{offset:X})");
|
||||
}
|
||||
|
||||
return (uint)offsetTrunc;
|
||||
}
|
||||
|
||||
private static int GetOffset(ulong sourceAddress, ulong targetAddress)
|
||||
{
|
||||
long offset = (long)(targetAddress - sourceAddress);
|
||||
|
||||
return checked((int)offset);
|
||||
}
|
||||
|
||||
private static uint[] GetCopy(uint[] code)
|
||||
{
|
||||
uint[] codeCopy = new uint[code.Length];
|
||||
code.CopyTo(codeCopy, 0);
|
||||
|
||||
return codeCopy;
|
||||
}
|
||||
|
||||
private static void WriteManagedCall(Assembler asm, Action<Assembler, Operand, Operand, Operand> writeCall, uint blacklistedRegMask)
|
||||
{
|
||||
int intMask = 0x7fffffff & (int)~blacklistedRegMask;
|
||||
int vecMask = unchecked((int)0xffffffff);
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[3];
|
||||
PickScratchRegs(scratchRegs, blacklistedRegMask);
|
||||
|
||||
RegisterSaveRestore rsr = new(intMask, vecMask, OperandType.V128);
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
WriteLoadContext(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
|
||||
asm.MovSp(Gpr(scratchRegs[1]), Gpr(Assembler.SpRegister));
|
||||
asm.StrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetGuestSPOffset());
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetHostSPOffset());
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
writeCall(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetGuestSPOffset());
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
}
|
||||
|
||||
internal static uint[] GenerateThreadStartCode()
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
CreateRegisterSaveRestoreForManaged().WritePrologue(asm);
|
||||
|
||||
asm.MovSp(Gpr(1), Gpr(Assembler.SpRegister));
|
||||
asm.StrRiUn(Gpr(1), Gpr(0), NceNativeContext.GetHostSPOffset());
|
||||
|
||||
for (int i = 2; i < 30; i += 2)
|
||||
{
|
||||
asm.LdpRiUn(Gpr(i), Gpr(i + 1), Gpr(0), NceNativeContext.GetXOffset(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i += 2)
|
||||
{
|
||||
asm.LdpRiUn(Vec(i), Vec(i + 1), Gpr(0), NceNativeContext.GetVOffset(i));
|
||||
}
|
||||
|
||||
asm.LdpRiUn(Gpr(30), Gpr(1), Gpr(0), NceNativeContext.GetXOffset(30));
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(1));
|
||||
|
||||
asm.StrRiUn(Gpr(Assembler.ZrRegister, OperandType.I32), Gpr(0), NceNativeContext.GetInManagedOffset());
|
||||
|
||||
asm.LdpRiUn(Gpr(0), Gpr(1), Gpr(0), NceNativeContext.GetXOffset(0));
|
||||
asm.Br(Gpr(30));
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
internal static uint[] GenerateSuspendExceptionHandler()
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[4];
|
||||
PickScratchRegs(scratchRegs, 0u);
|
||||
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]) | (1 << scratchRegs[3]), hasCall: true);
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
Operand lblAgain = asm.CreateLabel();
|
||||
Operand lblFail = asm.CreateLabel();
|
||||
|
||||
WriteLoadContextSafe(asm, lblFail, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]), Gpr(scratchRegs[3]));
|
||||
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetHostSPOffset());
|
||||
asm.MovSp(Gpr(scratchRegs[2]), Gpr(Assembler.SpRegister));
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
asm.Cmp(Gpr(0, OperandType.I32), Const((ulong)NceThreadPal.UnixSuspendSignal));
|
||||
asm.B(lblFail, ArmCondition.Ne);
|
||||
|
||||
// SigUsr2
|
||||
|
||||
asm.Mov(Gpr(scratchRegs[1], OperandType.I32), Const(OperandType.I32, 1));
|
||||
asm.StrRiUn(Gpr(scratchRegs[1], OperandType.I32), Gpr(scratchRegs[0]), NceNativeContext.GetInManagedOffset());
|
||||
|
||||
asm.MarkLabel(lblAgain);
|
||||
|
||||
asm.Mov(Gpr(scratchRegs[3]), (ulong)NceNativeInterface.GetSuspendThreadHandlerFunctionPointer());
|
||||
asm.Blr(Gpr(scratchRegs[3]));
|
||||
|
||||
// TODO: Check return value, exit if we must.
|
||||
|
||||
WriteInManagedLockReleaseForSuspendHandler(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[3]), lblAgain);
|
||||
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[2]));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
asm.MarkLabel(lblFail);
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
internal static uint[] GenerateWrapperExceptionHandler(IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[4];
|
||||
PickScratchRegs(scratchRegs, 0u);
|
||||
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]) | (1 << scratchRegs[3]), hasCall: true);
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
Operand lblFail = asm.CreateLabel();
|
||||
|
||||
WriteLoadContextSafe(asm, lblFail, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]), Gpr(scratchRegs[3]));
|
||||
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetHostSPOffset());
|
||||
asm.MovSp(Gpr(scratchRegs[2]), Gpr(Assembler.SpRegister));
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
// SigSegv
|
||||
|
||||
WriteInManagedLockAcquire(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[3]));
|
||||
|
||||
asm.Mov(Gpr(scratchRegs[3]), (ulong)signalHandlerPtr);
|
||||
asm.Blr(Gpr(scratchRegs[3]));
|
||||
|
||||
WriteInManagedLockRelease(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[3]), ThreadExitMethod.None);
|
||||
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[2]));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
asm.MarkLabel(lblFail);
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Mov(Gpr(3), (ulong)oldSignalHandlerSegfaultPtr);
|
||||
asm.Br(Gpr(3));
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
private static void WriteInManagedLockAcquire(Assembler asm, Operand ctx, Operand tmp, Operand tmp2)
|
||||
{
|
||||
Operand tmpUint = new Operand(tmp.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
Operand tmp2Uint = new Operand(tmp2.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
|
||||
// Bit 0 set means that the thread is currently executing managed code (that case should be impossible here).
|
||||
// Bit 1 being set means there is a signal pending, we should wait for the signal, otherwise it could trigger
|
||||
// while running managed code.
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Add(tmp, ctx, Const((ulong)NceNativeContext.GetInManagedOffset()));
|
||||
asm.Ldaxr(tmp2Uint, tmp);
|
||||
asm.Cbnz(tmp2Uint, lblLoop);
|
||||
asm.Mov(tmp2Uint, Const(OperandType.I32, 1));
|
||||
asm.Stlxr(tmp2Uint, tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
}
|
||||
|
||||
private enum ThreadExitMethod
|
||||
{
|
||||
None,
|
||||
GenerateReturn,
|
||||
Label
|
||||
}
|
||||
|
||||
private static void WriteInManagedLockRelease(Assembler asm, Operand ctx, Operand tmp, Operand tmp2, ThreadExitMethod exitMethod, Operand lblQuit = default)
|
||||
{
|
||||
Operand tmpUint = new Operand(tmp.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
Operand tmp2Uint = new Operand(tmp2.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
Operand lblInterrupt = asm.CreateLabel();
|
||||
Operand lblDone = asm.CreateLabel();
|
||||
|
||||
// Bit 0 set means that the thread is currently executing managed code (it should be always set here, as we just returned from managed code).
|
||||
// Bit 1 being set means a interrupt was requested while it was in managed, we should service it.
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Add(tmp, ctx, Const((ulong)NceNativeContext.GetInManagedOffset()));
|
||||
asm.Ldaxr(tmp2Uint, tmp);
|
||||
asm.Cmp(tmp2Uint, Const(OperandType.I32, 3));
|
||||
asm.B(lblInterrupt, ArmCondition.Eq);
|
||||
asm.Stlxr(Gpr(Assembler.ZrRegister, OperandType.I32), tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.B(lblDone);
|
||||
|
||||
asm.MarkLabel(lblInterrupt);
|
||||
|
||||
// If we got here, a interrupt was requested while it was in managed code.
|
||||
// Let's service the interrupt and check what we should do next.
|
||||
|
||||
asm.Mov(tmp2Uint, Const(OperandType.I32, 1));
|
||||
asm.Stlxr(tmp2Uint, tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.Mov(tmp, (ulong)NceNativeInterface.GetSuspendThreadHandlerFunctionPointer());
|
||||
asm.Blr(tmp);
|
||||
|
||||
// The return value from the interrupt handler indicates if we should continue running.
|
||||
// From here, we either try to release the lock again. We might have received another interrupt
|
||||
// request in the meantime, in which case we should service it again.
|
||||
// If we were requested to exit, then we exit if we can.
|
||||
// TODO: We should also exit while on a signal handler. To do that we need to modify the PC value on the
|
||||
// context. It's a bit more tricky to do, so for now we ignore that case with "ThreadExitMethod.None".
|
||||
|
||||
if (exitMethod == ThreadExitMethod.None)
|
||||
{
|
||||
asm.B(lblLoop);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Cbnz(Gpr(0, OperandType.I32), lblLoop);
|
||||
|
||||
if (exitMethod == ThreadExitMethod.Label)
|
||||
{
|
||||
asm.B(lblQuit);
|
||||
}
|
||||
else if (exitMethod == ThreadExitMethod.GenerateReturn)
|
||||
{
|
||||
CreateRegisterSaveRestoreForManaged().WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
}
|
||||
}
|
||||
|
||||
asm.MarkLabel(lblDone);
|
||||
}
|
||||
|
||||
private static void WriteInManagedLockReleaseForSuspendHandler(Assembler asm, Operand ctx, Operand tmp, Operand tmp2, Operand lblAgain)
|
||||
{
|
||||
Operand tmpUint = new Operand(tmp.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
Operand tmp2Uint = new Operand(tmp2.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
Operand lblInterrupt = asm.CreateLabel();
|
||||
Operand lblDone = asm.CreateLabel();
|
||||
|
||||
// Bit 0 set means that the thread is currently executing managed code (it should be always set here, as we just returned from managed code).
|
||||
// Bit 1 being set means a interrupt was requested while it was in managed, we should service it.
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Add(tmp, ctx, Const((ulong)NceNativeContext.GetInManagedOffset()));
|
||||
asm.Ldaxr(tmp2Uint, tmp);
|
||||
asm.Cmp(tmp2Uint, Const(OperandType.I32, 3));
|
||||
asm.B(lblInterrupt, ArmCondition.Eq);
|
||||
asm.Stlxr(Gpr(Assembler.ZrRegister, OperandType.I32), tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.B(lblDone);
|
||||
|
||||
asm.MarkLabel(lblInterrupt);
|
||||
|
||||
// If we got here, a interrupt was requested while it was in managed code.
|
||||
// Let's service the interrupt and check what we should do next.
|
||||
|
||||
asm.Mov(tmp2Uint, Const(OperandType.I32, 1));
|
||||
asm.Stlxr(tmp2Uint, tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.B(lblAgain);
|
||||
|
||||
asm.MarkLabel(lblDone);
|
||||
}
|
||||
|
||||
private static RegisterSaveRestore CreateRegisterSaveRestoreForManaged()
|
||||
{
|
||||
return new RegisterSaveRestore((int)IntCalleeSavedRegsMask, unchecked((int)FpCalleeSavedRegsMask), OperandType.FP64, hasCall: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadPal
|
||||
{
|
||||
private const int SigUsr2Linux = 12;
|
||||
private const int SigUsr2MacOS = 31;
|
||||
|
||||
public static int UnixSuspendSignal => OperatingSystem.IsMacOS() ? SigUsr2MacOS : SigUsr2Linux;
|
||||
|
||||
public static IntPtr GetCurrentThreadHandle()
|
||||
{
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || PlatformInfo.IsBionic)
|
||||
{
|
||||
return NceThreadPalUnix.GetCurrentThreadHandle();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SuspendThread(IntPtr handle)
|
||||
{
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
NceThreadPalAndroid.SuspendThread(handle);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
NceThreadPalUnix.SuspendThread(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadPalAndroid
|
||||
{
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int pthread_kill(IntPtr thread, int sig);
|
||||
|
||||
public static void SuspendThread(IntPtr handle)
|
||||
{
|
||||
int result = pthread_kill(handle, NceThreadPal.UnixSuspendSignal);
|
||||
if (result != 0)
|
||||
{
|
||||
throw new Exception($"Thread kill returned error 0x{result:X}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadPalUnix
|
||||
{
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern IntPtr pthread_self();
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int pthread_threadid_np(IntPtr arg0, out ulong tid);
|
||||
|
||||
[DllImport("libpthread", SetLastError = true)]
|
||||
private static extern int pthread_kill(IntPtr thread, int sig);
|
||||
|
||||
public static IntPtr GetCurrentThreadHandle()
|
||||
{
|
||||
return pthread_self();
|
||||
}
|
||||
|
||||
public static ulong GetCurrentThreadId()
|
||||
{
|
||||
pthread_threadid_np(IntPtr.Zero, out ulong tid);
|
||||
return tid;
|
||||
}
|
||||
|
||||
public static void SuspendThread(IntPtr handle)
|
||||
{
|
||||
int result = pthread_kill(handle, NceThreadPal.UnixSuspendSignal);
|
||||
if (result != 0)
|
||||
{
|
||||
throw new Exception($"Thread kill returned error 0x{result:X}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadTable
|
||||
{
|
||||
private const int MaxThreads = 4096;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public IntPtr ThreadId;
|
||||
public IntPtr NativeContextPtr;
|
||||
|
||||
public Entry(IntPtr threadId, IntPtr nativeContextPtr)
|
||||
{
|
||||
ThreadId = threadId;
|
||||
NativeContextPtr = nativeContextPtr;
|
||||
}
|
||||
}
|
||||
|
||||
private static MemoryBlock _block;
|
||||
|
||||
public static IntPtr EntriesPointer => _block.Pointer + 8;
|
||||
|
||||
static NceThreadTable()
|
||||
{
|
||||
_block = new MemoryBlock((ulong)Unsafe.SizeOf<Entry>() * MaxThreads + 8UL);
|
||||
_block.Write(0UL, 0UL);
|
||||
}
|
||||
|
||||
public static int Register(IntPtr threadId, IntPtr nativeContextPtr)
|
||||
{
|
||||
Span<Entry> entries = GetStorage();
|
||||
|
||||
lock (_block)
|
||||
{
|
||||
ref ulong currentThreadCount = ref GetThreadsCount();
|
||||
|
||||
for (int i = 0; i < MaxThreads; i++)
|
||||
{
|
||||
if (entries[i].ThreadId == IntPtr.Zero)
|
||||
{
|
||||
entries[i] = new Entry(threadId, nativeContextPtr);
|
||||
|
||||
if (currentThreadCount < (ulong)i + 1)
|
||||
{
|
||||
currentThreadCount = (ulong)i + 1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"Number of active threads exceeds limit of {MaxThreads}.");
|
||||
}
|
||||
|
||||
public static void Unregister(int tableIndex)
|
||||
{
|
||||
Span<Entry> entries = GetStorage();
|
||||
|
||||
lock (_block)
|
||||
{
|
||||
if (entries[tableIndex].ThreadId != IntPtr.Zero)
|
||||
{
|
||||
entries[tableIndex] = default;
|
||||
|
||||
ulong currentThreadCount = GetThreadsCount();
|
||||
|
||||
for (int i = (int)currentThreadCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (entries[i].ThreadId != IntPtr.Zero)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentThreadCount = (ulong)i;
|
||||
}
|
||||
|
||||
GetThreadsCount() = currentThreadCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ref ulong GetThreadsCount()
|
||||
{
|
||||
return ref _block.GetRef<ulong>(0UL);
|
||||
}
|
||||
|
||||
private static unsafe Span<Entry> GetStorage()
|
||||
{
|
||||
return new Span<Entry>((void*)_block.GetPointer(8UL, (ulong)Unsafe.SizeOf<Entry>() * MaxThreads), MaxThreads);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,10 +89,16 @@ namespace Ryujinx.Cpu.Signal
|
||||
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || PlatformInfo.IsBionic)
|
||||
{
|
||||
_signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateUnixSignalHandler(_handlerConfig, rangeStructSize));
|
||||
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
config.StructAddressOffset = 16; // si_addr
|
||||
config.StructWriteOffset = 8; // si_code
|
||||
}
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||
@@ -122,6 +128,21 @@ namespace Ryujinx.Cpu.Signal
|
||||
}
|
||||
}
|
||||
|
||||
public static void InstallUnixAlternateStackForCurrentThread(IntPtr stackPtr, ulong stackSize)
|
||||
{
|
||||
UnixSignalHandlerRegistration.RegisterAlternateStack(stackPtr, stackSize);
|
||||
}
|
||||
|
||||
public static void UninstallUnixAlternateStackForCurrentThread()
|
||||
{
|
||||
UnixSignalHandlerRegistration.UnregisterAlternateStack();
|
||||
}
|
||||
|
||||
public static void InstallUnixSignalHandler(int sigNum, IntPtr action)
|
||||
{
|
||||
UnixSignalHandlerRegistration.RegisterExceptionHandler(sigNum, action);
|
||||
}
|
||||
|
||||
private static IntPtr MapCode(ReadOnlySpan<byte> code)
|
||||
{
|
||||
Debug.Assert(_codeBlock == null);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.Signal
|
||||
{
|
||||
@@ -20,26 +22,73 @@ namespace Ryujinx.Cpu.Signal
|
||||
public IntPtr sa_restorer;
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("android"), StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
public struct SigActionBionic
|
||||
{
|
||||
public int sa_flags;
|
||||
public IntPtr sa_handler;
|
||||
public SigSet sa_mask;
|
||||
public IntPtr sa_restorer;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
public struct Stack
|
||||
{
|
||||
public IntPtr ss_sp;
|
||||
public int ss_flags;
|
||||
public IntPtr ss_size;
|
||||
}
|
||||
|
||||
private const int SIGSEGV = 11;
|
||||
private const int SIGBUS = 10;
|
||||
private const int SA_SIGINFO = 0x00000004;
|
||||
private const int SA_ONSTACK = 0x08000000;
|
||||
private const int SS_DISABLE = 2;
|
||||
private const int SS_AUTODISARM = 1 << 31;
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
||||
|
||||
[SupportedOSPlatform("android"), LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, ref SigActionBionic sigAction, out SigActionBionic oldAction);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, IntPtr sigAction, out SigAction oldAction);
|
||||
|
||||
[SupportedOSPlatform("android"), LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, IntPtr sigAction, out SigActionBionic oldAction);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigemptyset(ref SigSet set);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaltstack(ref Stack ss, out Stack oldSs);
|
||||
|
||||
public static SigAction GetSegfaultExceptionHandler()
|
||||
{
|
||||
int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old);
|
||||
int result;
|
||||
SigAction old;
|
||||
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
result = sigaction(SIGSEGV, IntPtr.Zero, out SigActionBionic tmp);
|
||||
|
||||
old = new SigAction
|
||||
{
|
||||
sa_handler = tmp.sa_handler,
|
||||
sa_mask = tmp.sa_mask,
|
||||
sa_flags = tmp.sa_flags,
|
||||
sa_restorer = tmp.sa_restorer
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result = sigaction(SIGSEGV, IntPtr.Zero, out old);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not get SIGSEGV sigaction. Error: {result}");
|
||||
throw new SystemException($"Could not get SIGSEGV sigaction. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
|
||||
return old;
|
||||
@@ -47,37 +96,167 @@ namespace Ryujinx.Cpu.Signal
|
||||
|
||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
||||
{
|
||||
SigAction sig = new()
|
||||
int result;
|
||||
SigAction old;
|
||||
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO,
|
||||
};
|
||||
SigActionBionic sig = new()
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO | SA_ONSTACK,
|
||||
};
|
||||
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
|
||||
int result = sigaction(SIGSEGV, ref sig, out SigAction old);
|
||||
result = sigaction(SIGSEGV, ref sig, out SigActionBionic tmp);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
|
||||
old = new SigAction
|
||||
{
|
||||
sa_handler = tmp.sa_handler,
|
||||
sa_mask = tmp.sa_mask,
|
||||
sa_flags = tmp.sa_flags,
|
||||
sa_restorer = tmp.sa_restorer
|
||||
};
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
else
|
||||
{
|
||||
result = sigaction(SIGBUS, ref sig, out _);
|
||||
SigAction sig = new SigAction
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO,
|
||||
};
|
||||
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
|
||||
result = sigaction(SIGSEGV, ref sig, out old);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
|
||||
throw new SystemException($"Could not register SIGSEGV sigaction. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
{
|
||||
result = sigaction(SIGBUS, ref sig, out _);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new SystemException($"Could not register SIGBUS sigaction. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
public static void RegisterAlternateStack(IntPtr stackPtr, ulong stackSize)
|
||||
{
|
||||
Stack stack = new()
|
||||
{
|
||||
ss_sp = stackPtr,
|
||||
ss_flags = SS_AUTODISARM,
|
||||
ss_size = (IntPtr)stackSize
|
||||
};
|
||||
|
||||
int result = sigaltstack(ref stack, out _);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new SystemException($"Could not set alternate stack. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterAlternateStack()
|
||||
{
|
||||
Stack stack = new()
|
||||
{
|
||||
ss_flags = SS_DISABLE
|
||||
};
|
||||
|
||||
int result = sigaltstack(ref stack, out _);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new SystemException($"Could not remove alternate stack. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterExceptionHandler(int sigNum, IntPtr action)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
SigActionBionic sig = new()
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO | SA_ONSTACK
|
||||
};
|
||||
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
|
||||
result = sigaction(sigNum, ref sig, out SigActionBionic oldu);
|
||||
|
||||
if (oldu.sa_handler != IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"SIG{sigNum} is already in use.");
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new SystemException($"Could not register SIG{sigNum} sigaction. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SigAction sig = new()
|
||||
{
|
||||
sa_handler = action,
|
||||
sa_flags = SA_SIGINFO | SA_ONSTACK,
|
||||
};
|
||||
|
||||
sigemptyset(ref sig.sa_mask);
|
||||
|
||||
result = sigaction(sigNum, ref sig, out SigAction oldu);
|
||||
|
||||
if (oldu.sa_handler != IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"SIG{sigNum} is already in use.");
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new SystemException($"Could not register SIG{sigNum} sigaction. Error: {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RestoreExceptionHandler(SigAction oldAction)
|
||||
{
|
||||
return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0);
|
||||
if (PlatformInfo.IsBionic)
|
||||
{
|
||||
SigActionBionic tmp = new SigActionBionic
|
||||
{
|
||||
sa_handler = oldAction.sa_handler,
|
||||
sa_mask = oldAction.sa_mask,
|
||||
sa_flags = oldAction.sa_flags,
|
||||
sa_restorer = oldAction.sa_restorer
|
||||
};
|
||||
|
||||
return sigaction(SIGSEGV, ref tmp, out SigActionBionic _) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool success = sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0;
|
||||
|
||||
if (success && (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()))
|
||||
{
|
||||
success = sigaction(SIGBUS, ref oldAction, out SigAction _) == 0;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsViewportSwizzle;
|
||||
public readonly bool SupportsIndirectParameters;
|
||||
public readonly bool SupportsDepthClipControl;
|
||||
public readonly bool SupportsExtendedDynamicState;
|
||||
public readonly bool SupportsExtendedDynamicState2;
|
||||
public readonly bool SupportsLogicOpDynamicState;
|
||||
public readonly bool SupportsPatchControlPointsDynamicState;
|
||||
|
||||
public readonly int UniformBufferSetIndex;
|
||||
public readonly int StorageBufferSetIndex;
|
||||
@@ -122,10 +118,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsViewportSwizzle,
|
||||
bool supportsIndirectParameters,
|
||||
bool supportsDepthClipControl,
|
||||
bool supportsExtendedDynamicState,
|
||||
bool supportsExtendedDynamicState2,
|
||||
bool supportsLogicOpDynamicState,
|
||||
bool supportsPatchControlPointsDynamicState,
|
||||
int uniformBufferSetIndex,
|
||||
int storageBufferSetIndex,
|
||||
int textureSetIndex,
|
||||
@@ -188,10 +180,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||
SupportsIndirectParameters = supportsIndirectParameters;
|
||||
SupportsDepthClipControl = supportsDepthClipControl;
|
||||
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
||||
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
|
||||
SupportsLogicOpDynamicState = supportsLogicOpDynamicState;
|
||||
SupportsPatchControlPointsDynamicState = supportsPatchControlPointsDynamicState;
|
||||
UniformBufferSetIndex = uniformBufferSetIndex;
|
||||
StorageBufferSetIndex = storageBufferSetIndex;
|
||||
TextureSetIndex = textureSetIndex;
|
||||
|
||||
@@ -2,7 +2,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum Face
|
||||
{
|
||||
None = 0,
|
||||
Front = 0x404,
|
||||
Back = 0x405,
|
||||
FrontAndBack = 0x408,
|
||||
|
||||
@@ -275,8 +275,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D24UnormS8Uint:
|
||||
return 4;
|
||||
case Format.D32FloatS8Uint:
|
||||
return 8;
|
||||
|
||||
case Format.Bc1RgbaUnorm:
|
||||
case Format.Bc1RgbaSrgb:
|
||||
return 8;
|
||||
@@ -303,8 +301,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
case Format.Etc2RgbaUnorm:
|
||||
case Format.Etc2RgbaSrgb:
|
||||
return 16;
|
||||
|
||||
case Format.Astc4x4Unorm:
|
||||
case Format.Astc5x4Unorm:
|
||||
case Format.Astc5x5Unorm:
|
||||
|
||||
@@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.GAL
|
||||
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
|
||||
void SetDepthClamp(bool clamp);
|
||||
void SetDepthMode(DepthMode mode);
|
||||
void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true);
|
||||
void SetDepthTest(DepthTestDescriptor depthTest);
|
||||
|
||||
void SetFaceCulling(Face face);
|
||||
void SetFaceCulling(bool enable, Face face);
|
||||
|
||||
void SetFrontFace(FrontFace frontFace);
|
||||
|
||||
@@ -75,16 +75,16 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void SetPrimitiveRestart(bool enable, int index);
|
||||
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true);
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||
|
||||
void SetProgram(IProgram program, bool signalChange = true);
|
||||
void SetProgram(IProgram program);
|
||||
|
||||
void SetRasterizerDiscard(bool discard);
|
||||
|
||||
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true);
|
||||
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
|
||||
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
|
||||
|
||||
void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true);
|
||||
void SetScissors(ReadOnlySpan<Rectangle<int>> regions);
|
||||
|
||||
void SetStencilTest(StencilTestDescriptor stencilTest);
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
|
||||
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
|
||||
|
||||
void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true);
|
||||
void SetViewports(ReadOnlySpan<Viewport> viewports);
|
||||
|
||||
void TextureBarrier();
|
||||
void TextureBarrierTiled();
|
||||
|
||||
@@ -3,16 +3,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand>
|
||||
{
|
||||
public readonly CommandType CommandType => CommandType.SetFaceCulling;
|
||||
private bool _enable;
|
||||
private Face _face;
|
||||
|
||||
public void Set(Face face)
|
||||
public void Set(bool enable, Face face)
|
||||
{
|
||||
_enable = enable;
|
||||
_face = face;
|
||||
}
|
||||
|
||||
public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetFaceCulling(command._face);
|
||||
renderer.Pipeline.SetFaceCulling(command._enable, command._face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,15 +159,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
|
||||
public void SetDepthTest(DepthTestDescriptor depthTest)
|
||||
{
|
||||
_renderer.New<SetDepthTestCommand>().Set(depthTest);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetFaceCulling(Face face)
|
||||
public void SetFaceCulling(bool enable, Face face)
|
||||
{
|
||||
_renderer.New<SetFaceCullingCommand>().Set(face);
|
||||
_renderer.New<SetFaceCullingCommand>().Set(enable, face);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
@@ -243,13 +243,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
||||
{
|
||||
_renderer.New<SetPrimitiveTopologyCommand>().Set(topology);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetProgram(IProgram program, bool signalChange = true)
|
||||
public void SetProgram(IProgram program)
|
||||
{
|
||||
_renderer.New<SetProgramCommand>().Set(Ref(program));
|
||||
_renderer.QueueCommand();
|
||||
@@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true)
|
||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
||||
{
|
||||
_renderer.New<SetRenderTargetColorMasksCommand>().Set(_renderer.CopySpan(componentMask));
|
||||
_renderer.QueueCommand();
|
||||
@@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors, bool signalChange = true)
|
||||
public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors)
|
||||
{
|
||||
_renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors));
|
||||
_renderer.QueueCommand();
|
||||
@@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
|
||||
public void SetViewports(ReadOnlySpan<Viewport> viewports)
|
||||
{
|
||||
_renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports));
|
||||
_renderer.QueueCommand();
|
||||
|
||||
@@ -51,12 +51,11 @@ namespace Ryujinx.Graphics.GAL
|
||||
public StencilTestDescriptor StencilTest;
|
||||
public FrontFace FrontFace;
|
||||
public Face CullMode;
|
||||
public bool CullEnable;
|
||||
|
||||
public PolygonModeMask BiasEnable;
|
||||
|
||||
public bool AlphaToCoverageEnable;
|
||||
public bool AlphaToOneEnable;
|
||||
|
||||
public float LineWidth;
|
||||
// TODO: Polygon mode.
|
||||
public bool DepthClampEnable;
|
||||
public bool RasterizerDiscard;
|
||||
@@ -64,9 +63,6 @@ namespace Ryujinx.Graphics.GAL
|
||||
public bool PrimitiveRestartEnable;
|
||||
public uint PatchControlPoints;
|
||||
|
||||
public float DepthBiasUnits;
|
||||
public float DepthBiasFactor;
|
||||
|
||||
public DepthMode DepthMode;
|
||||
|
||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -506,7 +506,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
maxDrawCount,
|
||||
stride,
|
||||
indexCount,
|
||||
Threed.IndirectDrawType.DrawIndexedIndirectCount);
|
||||
IndirectDrawType.DrawIndexedIndirectCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
||||
|
||||
@@ -853,9 +853,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
|
||||
|
||||
_pipeline.BiasEnable = enables;
|
||||
_pipeline.DepthBiasUnits = units / 2f;
|
||||
_pipeline.DepthBiasFactor = factor;
|
||||
|
||||
_context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
|
||||
}
|
||||
|
||||
@@ -1028,6 +1025,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
float width = _state.State.LineWidthSmooth;
|
||||
bool smooth = _state.State.LineSmoothEnable;
|
||||
|
||||
_pipeline.LineWidth = width;
|
||||
_context.Renderer.Pipeline.SetLineParameters(width, smooth);
|
||||
}
|
||||
|
||||
@@ -1197,16 +1195,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
var yControl = _state.State.YControl;
|
||||
var face = _state.State.FaceState;
|
||||
|
||||
if (face.CullEnable)
|
||||
{
|
||||
_pipeline.CullMode = face.CullFace;
|
||||
_context.Renderer.Pipeline.SetFaceCulling(face.CullFace);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pipeline.CullMode = Face.None;
|
||||
_context.Renderer.Pipeline.SetFaceCulling(Face.None);
|
||||
}
|
||||
_pipeline.CullEnable = face.CullEnable;
|
||||
_pipeline.CullMode = face.CullFace;
|
||||
_context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
|
||||
|
||||
UpdateFrontFace(yControl, face.FrontFace);
|
||||
}
|
||||
@@ -1396,8 +1387,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0;
|
||||
bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0;
|
||||
|
||||
_pipeline.AlphaToCoverageEnable = alphaToCoverageEnable;
|
||||
_pipeline.AlphaToOneEnable = alphaToOneEnable;
|
||||
_context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor(
|
||||
alphaToCoverageEnable,
|
||||
_state.State.AlphaToCoverageDitherEnable,
|
||||
|
||||
@@ -56,9 +56,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
private const ulong TextureSizeCapacity4GiB = 2 * GiB;
|
||||
private const ulong TextureSizeCapacity6GiB = 4 * GiB;
|
||||
private const ulong TextureSizeCapacity8GiB = 6 * GiB;
|
||||
private const ulong TextureSizeCapacity10GiB = 10 * GiB;
|
||||
private const ulong TextureSizeCapacity12GiB = 12 * GiB;
|
||||
|
||||
|
||||
private const float MemoryScaleFactor = 0.50f;
|
||||
private ulong _maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
||||
|
||||
@@ -85,26 +85,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
var cpuMemorySizeGiB = cpuMemorySize / GiB;
|
||||
var MaximumGpuMemoryGiB = context.Capabilities.MaximumGpuMemory / GiB;
|
||||
|
||||
if (cpuMemorySizeGiB < 6 && MaximumGpuMemoryGiB < 6 || context.Capabilities.MaximumGpuMemory == 0)
|
||||
MaxTextureSizeCapacity = cpuMemorySizeGiB switch
|
||||
{
|
||||
_maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
||||
}
|
||||
else if (cpuMemorySizeGiB < 6 && MaximumGpuMemoryGiB >= 6)
|
||||
{
|
||||
MaxTextureSizeCapacity = TextureSizeCapacity4GiB;
|
||||
}
|
||||
else if (cpuMemorySizeGiB == 6)
|
||||
{
|
||||
MaxTextureSizeCapacity = TextureSizeCapacity6GiB;
|
||||
}
|
||||
else if (cpuMemorySizeGiB == 8)
|
||||
{
|
||||
MaxTextureSizeCapacity = TextureSizeCapacity8GiB;
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxTextureSizeCapacity = TextureSizeCapacity12GiB;
|
||||
}
|
||||
< 6 when MaximumGpuMemoryGiB < 6 || context.Capabilities.MaximumGpuMemory == 0 =>
|
||||
DefaultTextureSizeCapacity,
|
||||
< 6 => TextureSizeCapacity4GiB,
|
||||
6 => TextureSizeCapacity6GiB,
|
||||
8 => TextureSizeCapacity8GiB,
|
||||
10 => TextureSizeCapacity10GiB,
|
||||
_ => TextureSizeCapacity12GiB
|
||||
};
|
||||
|
||||
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return (AddressMode)(Word0 & 7);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// <summary>
|
||||
/// Unpacks the texture wrap mode along the Y axis.
|
||||
/// </summary>
|
||||
/// <returns>The texture wrap mode enum</returns>
|
||||
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return (AddressMode)((Word0 >> 3) & 7);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// <summary>
|
||||
/// Unpacks the texture wrap mode along the Z axis.
|
||||
/// </summary>
|
||||
/// <returns>The texture wrap mode enum</returns>
|
||||
|
||||
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="texturePool">The current texture pool</param>
|
||||
/// <param name="samplerPool">The current sampler pool</param>
|
||||
/// <param name="stage">The shader stage using the textures to be bound</param>
|
||||
/// <param name="stageIndex">The stage number of the specified shader stage</param
|
||||
/// <param name="stageIndex">The stage number of the specified shader stage</param>
|
||||
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
|
||||
/// <param name="specState">Specialization state for the bound shader</param>
|
||||
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
||||
|
||||
@@ -723,7 +723,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// Checks if aliasing of two formats that would normally be considered incompatible be allowed,
|
||||
/// using copy dependencies.
|
||||
/// </summary>
|
||||
/// <param name="lhsFormat">Format information of the first texture</param
|
||||
/// <param name="lhsFormat">Format information of the first texture</param>
|
||||
/// <param name="rhsFormat">Format information of the second texture</param>
|
||||
/// <returns>True if aliasing should be allowed, false otherwise</returns>
|
||||
private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat)
|
||||
@@ -747,7 +747,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// Check if the target of the first texture view information is compatible with the target of the second texture view information.
|
||||
/// This follows the host API target compatibility rules.
|
||||
/// </summary>
|
||||
/// <param name="lhs">Texture information of the texture view</param
|
||||
/// <param name="lhs">Texture information of the texture view</param>
|
||||
/// <param name="rhs">Texture information of the texture view</param>
|
||||
/// <param name="caps">Host GPU capabilities</param>
|
||||
/// <returns>True if the targets are compatible, false otherwise</returns>
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
case TextureTarget.Texture1D:
|
||||
return Target.Texture1D;
|
||||
case TextureTarget.Texture2D:
|
||||
return Target.Texture2D;
|
||||
case TextureTarget.Texture2DRect:
|
||||
return Target.Texture2D;
|
||||
case TextureTarget.Texture3D:
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// Sets whether the format of a given render target is a BGRA format.
|
||||
/// </summary>
|
||||
/// <param name="index">Render target index</param>
|
||||
/// <param name="isBgra">True if the format is BGRA< false otherwise</param>
|
||||
/// <param name="isBgra">True if the format is BGRA, false otherwise</param>
|
||||
public void SetRenderTargetIsBgra(int index, bool isBgra)
|
||||
{
|
||||
bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra;
|
||||
|
||||
@@ -2,7 +2,6 @@ using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly Action<ShaderCacheState, int, int> _stateChangeCallback;
|
||||
|
||||
private readonly HashSet<ProgramPipelineState> _pipelineStateSet = new();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the cache should be loaded.
|
||||
/// </summary>
|
||||
@@ -234,7 +232,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
|
||||
for (int index = 0; index < ThreadCount; index++)
|
||||
{
|
||||
workThreads[index] = new Thread(ProcessAsyncQueue) { Name = $"GPU.AsyncTranslationThread.{index}", };
|
||||
workThreads[index] = new Thread(ProcessAsyncQueue)
|
||||
{
|
||||
Name = $"GPU.AsyncTranslationThread.{index}",
|
||||
};
|
||||
}
|
||||
|
||||
int programCount = _hostStorage.GetProgramCount();
|
||||
@@ -304,8 +305,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
using var streams = _hostStorage.GetOutputStreams(_context);
|
||||
|
||||
int packagedShaders = 0;
|
||||
ProgramPipelineState currentPipelineState = default;
|
||||
|
||||
foreach (var kv in _programList)
|
||||
{
|
||||
if (!Active)
|
||||
@@ -315,53 +314,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
|
||||
(CachedShaderProgram program, byte[] binaryCode) = kv.Value;
|
||||
|
||||
if (program.SpecializationState.PipelineState.HasValue && _context.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
currentPipelineState = program.SpecializationState.PipelineState.Value;
|
||||
_hostStorage.AddShader(_context, program, binaryCode, streams);
|
||||
|
||||
if (_context.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
currentPipelineState.StencilTest = default;
|
||||
|
||||
currentPipelineState.CullMode = 0;
|
||||
currentPipelineState.FrontFace = 0;
|
||||
currentPipelineState.DepthTest = default;
|
||||
currentPipelineState.Topology = ConvertToClass(currentPipelineState.Topology);
|
||||
}
|
||||
|
||||
if (_context.Capabilities.SupportsExtendedDynamicState2)
|
||||
{
|
||||
currentPipelineState.PrimitiveRestartEnable = false;
|
||||
currentPipelineState.BiasEnable = 0;
|
||||
currentPipelineState.RasterizerDiscard = false;
|
||||
}
|
||||
|
||||
if (_context.Capabilities.SupportsLogicOpDynamicState)
|
||||
{
|
||||
currentPipelineState.LogicOp = 0;
|
||||
}
|
||||
|
||||
if (_context.Capabilities.SupportsPatchControlPointsDynamicState)
|
||||
{
|
||||
currentPipelineState.PatchControlPoints = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_pipelineStateSet.Contains(currentPipelineState) ||
|
||||
!_context.Capabilities.SupportsExtendedDynamicState ||
|
||||
!program.SpecializationState.PipelineState.HasValue)
|
||||
{
|
||||
_hostStorage.AddShader(_context, program, binaryCode, streams);
|
||||
|
||||
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
|
||||
|
||||
if (_context.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
_pipelineStateSet.Add(currentPipelineState);
|
||||
}
|
||||
}
|
||||
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
|
||||
}
|
||||
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {packagedShaders} shaders successfully.");
|
||||
|
||||
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -385,29 +343,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
_stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount);
|
||||
}
|
||||
|
||||
private PrimitiveTopology ConvertToClass(PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
PrimitiveTopology.Points => PrimitiveTopology.Points,
|
||||
PrimitiveTopology.Lines or
|
||||
PrimitiveTopology.LineStrip or
|
||||
PrimitiveTopology.LinesAdjacency or
|
||||
PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.Lines,
|
||||
PrimitiveTopology.Triangles or
|
||||
PrimitiveTopology.TriangleStrip or
|
||||
PrimitiveTopology.TriangleFan or
|
||||
PrimitiveTopology.TrianglesAdjacency or
|
||||
PrimitiveTopology.TriangleStripAdjacency or
|
||||
PrimitiveTopology.Polygon => PrimitiveTopology.TriangleStrip,
|
||||
PrimitiveTopology.Patches => PrimitiveTopology.Patches,
|
||||
PrimitiveTopology.Quads => PrimitiveTopology.Quads,
|
||||
PrimitiveTopology.QuadStrip => PrimitiveTopology.QuadStrip,
|
||||
PrimitiveTopology.LineLoop => PrimitiveTopology.LineLoop,
|
||||
_ => PrimitiveTopology.TriangleStrip,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a host program for compilation.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||
|
||||
private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new()
|
||||
{
|
||||
{ AvCodecLibraryName, (58, 61) },
|
||||
{ AvUtilLibraryName, (56, 59) },
|
||||
{ AvCodecLibraryName, (59, 60) },
|
||||
{ AvUtilLibraryName, (57, 58) },
|
||||
};
|
||||
|
||||
private static string FormatLibraryNameForCurrentOs(string libraryName, int version)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -136,8 +136,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
|
||||
|
||||
ulong frameSize = (ulong)(1 + (useHighbitdepth ? 1 : 0)) * (yplaneSize + (2 * uvplaneSize));
|
||||
|
||||
ArrayPtr<byte> buf = ArrayPtr<byte>.Null;
|
||||
|
||||
// frame_size is stored in buffer_alloc_sz, which is an int. If it won't
|
||||
// fit, fail early.
|
||||
if (frameSize > int.MaxValue)
|
||||
@@ -212,7 +210,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
|
||||
SubsamplingX = ssX;
|
||||
SubsamplingY = ssY;
|
||||
|
||||
buf = BufferAlloc;
|
||||
ArrayPtr<byte> buf = BufferAlloc;
|
||||
if (useHighbitdepth)
|
||||
{
|
||||
// Store uint16 addresses when using 16bit framebuffers
|
||||
@@ -237,4 +235,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
|
||||
Marshal.FreeHGlobal(_pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,11 +277,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
|
||||
public CodecErr Decode(MemoryAllocator allocator, ArrayPtr<byte> data)
|
||||
{
|
||||
ArrayPtr<byte> dataStart = data;
|
||||
CodecErr res;
|
||||
Array8<uint> frameSizes = new();
|
||||
int frameCount = 0;
|
||||
|
||||
res = Types.Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out frameCount);
|
||||
CodecErr res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out frameCount);
|
||||
if (res != CodecErr.Ok)
|
||||
{
|
||||
return res;
|
||||
@@ -322,7 +321,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
|
||||
// Account for suboptimal termination by the encoder.
|
||||
while (dataStart.Length != 0)
|
||||
{
|
||||
byte marker = Types.Decoder.ReadMarker(dataStart);
|
||||
byte marker = Decoder.ReadMarker(dataStart);
|
||||
if (marker != 0)
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -191,10 +191,6 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||
supportsDepthClipControl: true,
|
||||
supportsExtendedDynamicState: false,
|
||||
supportsExtendedDynamicState2: false,
|
||||
supportsLogicOpDynamicState: false,
|
||||
supportsPatchControlPointsDynamicState: false,
|
||||
uniformBufferSetIndex: 0,
|
||||
storageBufferSetIndex: 1,
|
||||
textureSetIndex: 2,
|
||||
|
||||
@@ -898,7 +898,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
|
||||
public void SetDepthTest(DepthTestDescriptor depthTest)
|
||||
{
|
||||
if (depthTest.TestEnable)
|
||||
{
|
||||
@@ -915,11 +915,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_depthTestEnable = depthTest.TestEnable;
|
||||
}
|
||||
|
||||
public void SetFaceCulling(Face face)
|
||||
public void SetFaceCulling(bool enable, Face face)
|
||||
{
|
||||
_cullEnable = face != Face.None;
|
||||
_cullEnable = enable;
|
||||
|
||||
if (!_cullEnable)
|
||||
if (!enable)
|
||||
{
|
||||
GL.Disable(EnableCap.CullFace);
|
||||
return;
|
||||
@@ -1107,12 +1107,12 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
GL.Enable(EnableCap.PrimitiveRestart);
|
||||
}
|
||||
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
||||
{
|
||||
_primitiveType = topology.Convert();
|
||||
}
|
||||
|
||||
public void SetProgram(IProgram program, bool signalChange = true)
|
||||
public void SetProgram(IProgram program)
|
||||
{
|
||||
Program prg = (Program)program;
|
||||
|
||||
@@ -1154,7 +1154,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_rasterizerDiscard = discard;
|
||||
}
|
||||
|
||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks, bool signalChange = true)
|
||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
|
||||
{
|
||||
_componentMasks = 0;
|
||||
|
||||
@@ -1195,7 +1195,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_framebuffer.SetDrawBuffers(colors.Length);
|
||||
}
|
||||
|
||||
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true)
|
||||
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
|
||||
{
|
||||
int count = Math.Min(regions.Length, Constants.MaxViewports);
|
||||
|
||||
@@ -1388,7 +1388,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_vertexArray.SetVertexBuffers(vertexBuffers);
|
||||
}
|
||||
|
||||
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
|
||||
public void SetViewports(ReadOnlySpan<Viewport> viewports)
|
||||
{
|
||||
Array.Resize(ref _viewportArray, viewports.Length * 4);
|
||||
Array.Resize(ref _depthRangeArray, viewports.Length * 2);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -9,6 +9,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
public static void RunPass(TransformContext context)
|
||||
{
|
||||
for (int blkIndex = 0; blkIndex < context.Blocks.Length; blkIndex++)
|
||||
{
|
||||
XmadOptimizer.RunPass(context.Blocks[blkIndex]);
|
||||
}
|
||||
|
||||
RunOptimizationPasses(context.Blocks, context.ResourceManager);
|
||||
|
||||
// TODO: Some of those are not optimizations and shouldn't be here.
|
||||
@@ -355,7 +360,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
operation.TurnIntoCopy(attrMulLhs);
|
||||
}
|
||||
|
||||
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
|
||||
public static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
|
||||
{
|
||||
// Remove a node from the nodes list, and also remove itself
|
||||
// from all the use lists on the operands that this node uses.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user