Compare commits

...

441 Commits

Author SHA1 Message Date
Ikko Eltociear Ashimine
f8ec878796 Fix typo in TextureBindingsManager.cs (#4798)
accomodate -> accommodate
2023-05-05 22:17:36 +02:00
Skyth (Asilkan)
9ff21f9ab6 Use ToLowerInvariant when detecting GPU vendor. (#4815) 2023-05-05 16:35:59 +00:00
gdkchan
aa021085cf Allow any shader SSBO constant buffer slot and offset (#2237)
* Allow any shader SSBO constant buffer slot and offset

* Fix slot value passed to SetUsedStorageBuffer on fallback case

* Shader cache version

* Ensure that the storage buffer source constant buffer offset is word aligned

* Fix FirstBinding on GetUniformBufferDescriptors
2023-05-05 14:20:20 +00:00
riperiperi
1f5d881860 GPU: Allow granular buffer updates from the constant buffer updater (#4749)
* GPU: Allow granular buffer updates from the constant buffer updater

Sometimes, constant buffer updates can't be avoided, either due to a cb0 access that cannot be eliminated, or the game updating a buffer between draws to the detriment of everyone.

To avoid uploading the full 4096 bytes each time, this PR remembers the offset and size containing all constant buffer updates since the last sync. It will then upload that range after sync.

* Allow clearing the dirty range

* Always use precise

Might want to not do this if distance between the existing range and new one is too high.

* Use old force dirty mechanism when distance between regions is too great

* Update src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix inheritance of _dirtyStart and _dirtyEnd

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-05-05 13:47:15 +00:00
TSRBerry
1f664100bd ModLoader: Fix case sensitivy issues without breaking cheats (#4783)
* Fix case sensitivity for mod subdirectories

* Small refactoring of ModLoader

* Don't share instruction list between all cheats

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-05-05 09:39:08 +02:00
Horizon
1f5e1ffa80 fix: linux launcher breaks when there are spaces in the directory path (#4795)
* fix: linux launcher breaks when there are spaces in the directory path

* Add quotes around $0 as well

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-05-05 09:06:15 +02:00
gdkchan
264438ff19 Revert "bcat ipc (#4446)" (#4801)
This reverts commit 4250732353.
2023-05-04 18:16:51 +02:00
Ac_K
3b8ac1641a UI: Move ApplicationContextMenu in a separated class (#4755)
* UI: Move ApplicationContextMenu in a separated class

This PR remove duplicated code related to the context menu on the Application list/grid by create a control for the menu which include related handler.

I've renamed "GameList/GameGrid" by "Application" for consistencies. And I've removed all uneeded field from the project file too.

While I cleaned up things, I've found an issue about purging Ptc/Shader cache, both methods list files even if the user say "No", shader cache is purged even if the user say "No". It's fixed.

* Adresses feedbacks
2023-05-04 14:41:06 +00:00
Emmanuel Hansen
4250732353 bcat ipc (#4446) 2023-05-04 16:26:10 +02:00
gdkchan
4d1579acbf Fix some invalid blits involving depth textures (#4723) 2023-05-03 21:20:12 -03:00
Erdem Keskin
6279f5e430 Update SettingsWindow.cs (#4785)
fix saving if directory path directly pasted in to the text field instead of using FileChooser.
2023-05-03 16:04:40 +02:00
Mary
b7d2bff6aa Revert "ModLoader: Fix case sensitivy issues (#4720)" (#4781)
This reverts commit cc1a933a2f.
2023-05-03 11:20:05 +02:00
riperiperi
7c327fecb3 Vulkan: Record modifications after changing the framebuffer (#4775)
Our Vulkan backend inserts image barriers when a texture is sampled after it is rendered. This is done via a "modification flag" which is set when a render target is unbound (presuming that a texture has finished drawing to it).

Imagine the following scenario:
- Game sets render target to texture A
- Game renders to texture A
- (render pass ends)
- Game binds texture A to a sampler
- Game sets render target to texture B
- Renders to texture B using texture A (barrier required)

Because of the previous behaviour, the check to add a barrier for sampling a texture actually happens before it is registered as modified, meaning no barrier was added at all. This isn't always the case, but it was definitely causing issues in Xenoblade 2.

This doesn't fix any more complicated issues where a texture is repeatedly sampled while it is currently being rendered.

Fixes visual glitches at lower resolutions in Xenoblade 2. May fix other cases.
2023-05-03 10:42:21 +02:00
TSRBerry
cc1a933a2f ModLoader: Fix case sensitivy issues (#4720)
* Fix case sensitivity for mod subdirectories

* Small refactoring of ModLoader
2023-05-03 02:07:16 +02:00
TSRBerry
dd574146fb Add hide-cursor command line argument & always hide cursor option (#4613)
* Add hide-cursor command line argument

* gtk: Adjust SettingsWindow for hide cursor options

* ava: Adjust SettingsWindow for hide cursor options

* ava: Add override check for HideCursor arg

* Remove copy&paste sins

* ava: Leave a little more room between the options

* gtk: Fix hide cursor issues

* ava: Only hide cursor if it's within the embedded window
2023-05-02 03:29:47 +02:00
riperiperi
2c94ac455e GPU: Keep rendered textures without any pool references alive (#4662)
* GPU: Keep sampled textures without any pool references alive

Occasionally games are very wasteful and clear/write to a texture without ever sampling it. As rendered textures in NVN games seem to all have overlapping memory ranges, the texture will eventually get overwritten.

Normally, this would trigger a removal from the auto delete cache, but a pool entry would keep the texture alive. However, with these textures that are never used, they will get deleted immediately and recreated on the next frame.

This change makes it so the ShortTextureCache can keep textures that have naver had a pool reference alive for a few frames, so they're not constantly being created and deleted.

This improves performance in Zelda BOTW a little.

* Cleanup
2023-05-01 16:27:51 -03:00
riperiperi
e18d258fa0 GPU: Pre-emptively flush textures that are flushed often (to imported memory when available) (#4711)
* WIP texture pre-flush

Improve performance of TextureView GetData to buffer

Fix copy/sync ordering

Fix minor bug

Make this actually work

WIP host mapping stuff

* Fix usage flags

* message

* Cleanup 1

* Fix rebase

* Fix

* Improve pre-flush rules

* Fix pre-flush

* A lot of cleanup

* Use the host memory bits

* Select the correct memory type

* Cleanup TextureGroupHandle

* Missing comment

* Remove debugging logs

* Revert BufferHandle _value access modifier

* One interrupt action at a time.

* Support D32S8 to D24S8 conversion, safeguards

* Interrupt cannot happen in sync handle's lock

Waitable needs to be checked twice now, but this should stop it from deadlocking.

* Remove unused using

* Address some feedback

* Address feedback

* Address more feedback

* Address more feedback

* Improve sync rules

Should allow for faster sync in some cases.
2023-05-01 16:05:12 -03:00
riperiperi
36f10df775 GPU: Fix errors handling texture remapping (#4745)
* GPU: Fix errors handling texture remapping

- Fixes an error where a pool entry and memory mapping changing at the same time could cause a texture to rebind its data from the wrong GPU VA (data swaps)
- Fixes an error where the texture pool could act on a mapping change before the mapping has actually been changed ("Unmapped" event happens before change, we need to signal it changed _after_ it completes)

TODO: remove textures from partially mapped list... if they aren't.

* Add Remap actions for handling post-mapping behaviours

* Remove unused code.

* Address feedback

* Nit
2023-05-01 15:32:32 -03:00
al81-ru
680e548022 Uneven frame pacing with vsync (#4744)
fixes issue #3906
2023-04-29 21:54:41 +01:00
MutantAura
21c4176157 Allow window to remember its size, position and state (GTK + Avalonia) (#4657)
* Update ConfigurationState.cs

* Update ConfigurationFileFormat.cs

* Update MainWindow.cs

* Update ConfigurationFileFormat.cs

* Update ConfigurationState.cs

* Update MainWindow.cs

* Update MainWindow.cs

* Update Ryujinx.Ui.Common/Configuration/ConfigurationState.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update MainWindow.cs

* Update Ryujinx/Ui/MainWindow.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Initial properties

* Viewmodel adjustments and additions

* abstract and monitor dimension changes

* Remove position from ViewModel and simplify methods

* Remove unused dep

* Update configuration and fix typo from AA

* review changes

* Review changes

* Screensize checks - Ava

* Review changes 2

* basic review changes

* Standardise GTK/Ava functions

* Actually call function

---------

Co-authored-by: HaizenTrist <123991082+HaizenTrist@users.noreply.github.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-04-28 22:59:53 +02:00
dependabot[bot]
3b4ff2d6d9 nuget: bump System.IdentityModel.Tokens.Jwt from 6.29.0 to 6.30.0 (#4736)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.29.0 to 6.30.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.29.0...6.30.0)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-28 11:54:19 +02:00
TSRBerry
12504f280c Fix paths and typos for macOS scripts (#4738)
* Fix paths and typos for macOS scripts

* Update outdated comments about rcodesign

---------

Co-authored-by: Mary <thog@protonmail.com>
2023-04-28 08:14:44 +00:00
TSR Berry
250fc51374 Adjust github workflows for new src directory 2023-04-27 23:51:14 +02:00
TSR Berry
206e0882c2 Adjust Ryujinx.Tests.Memory namespace 2023-04-27 23:51:14 +02:00
TSR Berry
609abc8b9b Rename Ryujinx.Memory.Tests to Ryujinx.Tests.Memory 2023-04-27 23:51:14 +02:00
TSR Berry
cee7121058 Move solution and projects to src 2023-04-27 23:51:14 +02:00
gdkchan
cd124bda58 Fix geometry shader layer passthrough regression (#4735)
* Fix geometry shader layer passthrough regression

* Shader cache version bump
2023-04-27 11:09:49 -03:00
gdkchan
9f12e50a54 Refactor attribute handling on the shader generator (#4565)
* Refactor attribute handling on the shader generator

* Implement gl_ViewportMask[]

* Add back the Intel FrontFacing bug workaround

* Fix GLSL transform feedback outputs mistmatch with fragment stage

* Shader cache version bump

* Fix geometry shader recognition

* PR feedback

* Delete GetOperandDef and GetOperandUse

* Remove replacements that are no longer needed on GLSL compilation on Vulkan

* Fix incorrect load for per-patch outputs

* Fix build
2023-04-25 19:51:07 -03:00
gdkchan
097562bc6c Add missing check for thread termination on ArbitrateLock (#4722)
* Add missing check for thread termination on ArbitrateLock

* Use TerminationRequested in all places where it can be used
2023-04-25 19:33:14 -03:00
gdkchan
db4242c5dc Implement DMA texture copy component shuffle (#4717)
* Implement DMA texture copy component shuffle

* Set UInt24 alignment to 1
2023-04-24 15:28:03 +02:00
gdkchan
4dd77316f7 Use vector transform feedback outputs with fragment shaders (#4708)
* Use vector transform feedback outputs with fragment shaders

* Shader cache version bump

* Fix missing outputs when vector transform feedback outputs are used
2023-04-24 08:34:38 +02:00
TSRBerry
3f98369a17 Set the console title for GTK again (#4706)
Fixes a regression from #3707 where I accidentally removed that line.
2023-04-24 08:15:19 +02:00
TSRBerry
c26aeefe03 Fix amiibo timeout issues & log errors/exceptions (#4712) 2023-04-24 02:08:31 +00:00
jhorv
666e05f5cb Reducing Memory Allocations 202303 (#4624)
* use ArrayPool, avoid 6000-7000 allocs/sec of runtime

* use ArrayPool, avoid ~7k allocs/second during game execution

* use ArrayPool, avoid ~3000 allocs/sec during game execution

* use MemoryPool, reduce 0.5 MB/sec of new allocations during game execution

* avoid over-allocation by setting List<> Capacity when known

* remove LINQ in KTimeManager.UnscheduleFutureInvocation

* KTimeManager - avoid spinning one more time when the time has arrived

* KTimeManager - let SpinWait decide when to Thread.Yield(), and don't SpinOnce() immediately after Thread.Yield()

* use MemoryPool, reduce ~175k bytes/sec allocation during game execution

* IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array

* Make ButtonMappingEntry a record struct to avoid allocations. Set the List<ButtonMappingEntry> capacity according to use.

* add MemoryBuffer type for working with MemoryPool<byte>

* update changes to use MemoryBuffer

* make parameter ReadOnlySpan instead of Span

* whitespace fix

* Revert "IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array"

This reverts commit f2c698bdf65f049e8481c9f2ec7138d9b9a8261d.

* tweak KTimeManager spin behavior

* replace MemoryBuffer with ByteMemoryPool modeled after System.Buffers.ArrayMemoryPool<T>

* make ByteMemoryPoolBuffer responsible for renting memory
2023-04-24 02:06:23 +00:00
riperiperi
8d9d508dc7 Shader: Bias textureGather instructions on AMD/Intel (#4703)
* Experimental (GLSL, forced)

* SPIR-V attempt

* Add capability

* Fix pCount == 1 on glsl

* Fix typo
2023-04-22 18:02:39 -03:00
SpicerXD
e27f5522e2 Removed MotionInput Calibration (#4705)
Don't know why this is here.
It just seems to set the filter to an identity. Which then quickly returns to where its supposed to be anyways.
2023-04-22 15:31:28 +02:00
gdkchan
add2a9d151 Avoid LM service crashes by not reading more than the buffer size (#4701) 2023-04-20 17:10:17 +02:00
dependabot[bot]
9e50dd99d7 nuget: bump System.IdentityModel.Tokens.Jwt from 6.28.1 to 6.29.0 (#4694)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.28.1 to 6.29.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.28.1...6.29.0)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-17 21:31:12 +02:00
dependabot[bot]
0dec91bb42 nuget: bump System.Management from 7.0.0 to 7.0.1 (#4695)
Bumps [System.Management](https://github.com/dotnet/runtime) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v7.0.0...v7.0.1)

---
updated-dependencies:
- dependency-name: System.Management
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-17 21:30:40 +02:00
gdkchan
d9b63353b0 Support copy between multisample and non-multisample depth textures (#4676)
* Support copy between multisample and non-multisample depth textures

* PR feedback
2023-04-17 08:13:53 +00:00
gdkchan
eabd0ec93f Revert "chore: Update Silk.NET to 2.17.1 (#4686)" (#4690)
This reverts commit 79d1c190db.
2023-04-16 20:56:27 -03:00
riperiperi
138d5dc64a Vulkan: HashTableSlim lookup optimization (#4688) 2023-04-16 14:57:01 -03:00
gdkchan
3e68a87d63 Change SMAA filter texture clear method (#4685)
* Change SMAA filter texture clear method

* Alpha should be 1

* Delete more unnecessary code
2023-04-16 14:26:22 -03:00
TSRBerry
69b6ef7a4a [GUI] Add network interface dropdown (#4597)
* Add network adapter dropdown from LDN build

* Ava: Add NetworkInterfaces to SettingsNetworkTab

* Add headless network interface option

* Add network interface dropdown to Avalonia

* Fix handling network interfaces without a gateway address

* gtk: Actually save selected network interface to config

* Increment config version
2023-04-16 15:25:20 +00:00
Mary
40e87c634e Fix a crash in Ryujinx.Headless.SDL2 when loading an app (#4687)
Caused by the recent application loader changes.
2023-04-16 16:50:30 +02:00
Mary
79d1c190db chore: Update Silk.NET to 2.17.1 (#4686) 2023-04-16 09:38:07 +00:00
Ac_K
2bc88467eb Update README.md 2023-04-16 09:37:31 +00:00
Vincenzo Nizza
baf8752e74 Ensure the updater doesn't delete hidden or system files (#4626)
* Copy desktop.ini to update directory if it exists in HomeDir

* EnumerateFilesToDelete() exclude files with "Hidden" and "System" attributes
2023-04-16 09:19:33 +00:00
dependabot[bot]
d5e4378aea nuget: bump DynamicData from 7.13.1 to 7.13.5 (#4654)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.1 to 7.13.5.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.1...7.13.5)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-16 09:02:06 +00:00
TSRBerry
6dbcdfea47 Ava: Fix nca extraction window never closing & minor cleanup (#4569)
* ava: Remove unused doWhileDeferred parameters

* ava: Minimally improve swkbd dialog

It's currently impossible to get the dialog to redirect focus to the InputBox.

* ava: Fix nca extraction dialog never closing

Also contains some minor cleanup
2023-04-16 07:09:02 +00:00
NitroTears
c5258cf082 Ability to hide file types in Game List (#4555)
* Added HiddenFileTypes to config state, and check to file enumeration

* Added hiddenfiletypes checkboxes to the UI

* Added Ava version of HiddenFileTypes

* Inverted Hide to Show with file types, minor formatting

* all variables with a reference to 'hidden' is now 'shown'

* one more variable name changed

* review feedback

* added FileTypes extension methof to get the correlating config value

* moved extension method to new folder and file in Ryujinx.Ui.Common

* added default case for ToggleFileType

* changed exception type to OutOfRangeException
2023-04-16 01:03:35 +00:00
Daniel Shala
5c89e22bb9 Added check for eventual symlink when displaying game files. (#4526)
* Added check for eventual symlink when displaying game files.

* Moved symlink check logic

* Moved symlink check logic

* Fixed prev commit

---------

Co-authored-by: Daniel Shala <danielshala00@gmail.com>
2023-04-15 16:11:24 +00:00
Alex Barney
11ecff2ff0 Rename Hipc to Cmif where appropriate (#3880) 2023-04-14 20:00:34 -03:00
MutantAura
4c3f09644a Move swkbd message null check into constructor (#4671) 2023-04-12 21:18:40 +02:00
TSRBerry
e187a8870a HLE: Deal with empty title names properly (#4643)
* hle: Deal with empty titleNames in some languages

* gui: Fix displaying the wrong title name

* Remove unnecessary bounds check

* Fix a NRE when getting the version string

* Restore empty string logic
2023-04-12 01:09:47 +00:00
riperiperi
a64fee29dc Vulkan: add situational "Fast Flush" mode (#4667)
* Flush in the middle of long command buffers.

* Vulkan: add situational "Fast Flush" mode

The AutoFlushCounter class was added to periodically flush Vulkan command buffers throughout a frame, which reduces latency to the GPU as commands are submitted and processed much sooner. This was done by allowing command buffers to flush when framebuffer attachments changed.

However, some games have incredibly long render passes with a large number of draws, and really aggressive data access that forces GPU sync.

The Vulkan backend could potentially end up building a single command buffer for 4-5ms if a pass has enough draws, such as in BOTW. In the scenario where sync is waited on immediately after submission, this would have to wait for the completion of a much longer command buffer than usual.

The solution is to force command buffer submission periodically in a "fast flush" mode. This will end up splitting render passes, but it will only enable if sync is aggressive enough.

This should improve performance in GPU limited scenarios, or in games that aggressively wait on synchronization. In some games, it may only kick in when res scaling. It won't trigger in games like SMO where sync is not an issue.

Improves performance in Pokemon Scarlet/Violet (res scaled) and BOTW (in general).

* Add conversions in milliseconds next to flush timers.
2023-04-11 09:23:41 +02:00
riperiperi
9ef94c8292 ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661)
* ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext

Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition.

Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile.

* Remove unreachable code.

* Add ulong conversion for offsets

* Nit
2023-04-11 08:55:04 +02:00
riperiperi
915d6d044c OpenGL: Fix OBS/Overlays again by binding FB before present (#4668)
This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled.

I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome.
2023-04-11 08:32:31 +02:00
MutantAura
a4780ab33b Force activate parent window before dialog is shown (#4663) 2023-04-11 00:04:31 +02:00
TSRBerry
a947a45d81 gtk: Fix a NRE when disposing OpenGL (#4648) 2023-04-10 17:00:23 +02:00
riperiperi
9db73f74cf ARMeilleure: Respect FZ/RM flags for all floating point operations (#4618)
* ARMeilleure: Respect Fz flag for all floating point operations.

This is a change in strategy for emulating the Fz FPCR flag. Before, it was set before instructions that "needed it" and reset after. However, this missed a few hot instructions like the multiplication instruction, and the entirety of A32.

The new strategy is to set the Fz flag only in the following circumstances:

- Set to match FPCR before translated functions/loop are executed.
- Reset when calling SoftFloat methods, set when returning.
- Reset when exiting execution.

This allows us to remove the code around the existing Fz aware instructions, and get the accuracy benefits on all floating point instructions executed while in translated code.

Single step executions now need to be called with a context wrapper - right now it just contains the Fz flag initialization, and won't actually do anything on ARM.

This fixes a bug in Breath of the Wild where some physics interactions could randomly crash the game due to subnormal values not flushing to zero.

This is draft right now because I need to answer the questions:
- Does dotnet avoid changing the value of Mxcsr?
- Is it a good idea to assume that? Or should the flag set/restore be done on every managed method call, not just softfloat?
- If we assume that, do we want a unit test to verify the behaviour?

I recommend testing a bunch of games, especially games affected when this was originally added, such as #1611.

* Remove unused method

* Use FMA for Fmadd, Fmsub, Fnmadd, Fnmsub, Fmla, Fmls

...when available.

Similar implementation to A32

* Use FMA for Frecps, Frsqrts

* Don't set DAZ.

* Add round mode to ARM FP mode

* Fix mistakes

* Add test for FP state when calling managed methods

* Add explanatory comment to test.

* Cleanup

* Add A64 FPCR flags

* Vrintx_S A32 fast path on A64 backend

* Address feedback 1, re-enable DAZ

* Fix FMA instructions By Elem

* Address feedback
2023-04-10 12:22:58 +02:00
gdkchan
a1efd87c45 Implement remaining Arm64 HINT instructions as NOP (#4658)
* Implement remaining HINT instructions as NOP

* Split HINT encodings more to account for CSDB
2023-04-09 13:21:16 -03:00
jhorv
49be977588 Eliminate boxing allocations caused by ISampledData structs (#4556)
* Redesign use of ISampledData for accessing the SamplingNumber value on input data structs.

* Always read SamplingNumber as little-endian

* Restored field order for SixAxisSensorState. Rework to allow possibility of non-zero offsets for the SamplingNumber field. Set StructLayout Pack=8 - the KeyboardState struct is 4 bytes shorter with any other value.

* fix spelling

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

* set Pack = 1 for ISampledDataStruct types, added Unknown field to KeyboardState

* extend size of KeyboardModifier

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-04-05 17:42:32 -03:00
Mary
c95be55091 vulkan: Cleanup PhysicalDevice and Instance querying (#4632)
* vulkan: Move most of the properties enumeration to VulkanPhysicalDevice

That clean up a bit of duplicate logic.
Also move to use an hashset for device extensions.

* vulkan: Move instance querying to VulkanInstance

Also cleanup code to use span when possible instead of unsafe pointers.

* Address gdkchan's comments
2023-04-05 14:48:38 -03:00
dependabot[bot]
63dedbda86 nuget: bump System.IdentityModel.Tokens.Jwt from 6.27.0 to 6.28.1 (#4639)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.27.0 to 6.28.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.27.0...6.28.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 07:55:57 +02:00
gdkchan
c532118d94 Use index fragment shader output when dual source blend is enabled (#4404)
* Use index fragment shader output when dual source blend is enabled

* Shader cache version bump

* Actually set DualSourceBlendEnabled to true

* Fix XML doc

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-04-05 05:25:19 +02:00
TSRBerry
52d6f2e656 hle: Set ProcessResult name from NACP (#4633)
* Extract titleName from nacp

* Address formatting feedback

* Check if the desired language is actually available
2023-04-05 03:34:21 +02:00
TSRBerry
c9bc4eaf58 Fix missing string enum converters for the config (#4634)
* Fix missing string enum converters for the config

* Revert changing KeyboardHotkeys to struct

This needs to be done because
Avalonia's TwoWay Binding breaks otherwise.
2023-04-03 15:37:27 +02:00
Andrey Sukharev
3249f8ff41 Source generated json serializers (#4582)
* Use source generated json serializers in order to improve code trimming

* Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing

* Use separate model for LogEventArgs serialization

* Make dynamic object formatter static. Fix string builder pooling.

* Do not inherit json version of LogEventArgs from EventArgs

* Fix extra space in object formatting

* Write log json directly to stream instead of using buffer writer

* Rebase fixes

* Rebase fixes

* Rebase fixes

* Enforce block-scoped namespaces in the solution. Convert style for existing code

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Rebase indent fix

* Fix indent

* Delete unnecessary json properties

* Rebase fix

* Remove overridden json property names as they are handled in the options

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Use default json options in github api calls

* Indentation and spacing fixes

* Fix json serialization

* Fix missing JsonConverter for config enums

* Add double \n\n after the whole string, not inside join

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-04-03 10:14:19 +00:00
dependabot[bot]
1b41b285ac nuget: bump DynamicData from 7.12.11 to 7.13.1 (#4490)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.12.11 to 7.13.1.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.12.11...7.13.1)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 08:23:09 +00:00
Mary
f5a6f45b27 vulkan: Separate debug utils logic from VulkanInitialization (#4609)
* vulkan: Separate debug utils logic from VulkanInitialization

Also checks for VK_EXT_debug_utils existence instead of force enabling it and allow possible error during messenger init

* Address gdkchan's comment

* Use CreateDebugUtilsMessenger Span variant
2023-04-01 08:05:02 +00:00
TSRBerry
210557951b nuget: bump Avalonia dependencies from 0.10.18 to 0.10.19 (#4602)
* infra: Update Avalonia to 0.10.19

* infra: Update XamlNameReferenceGenerator to 1.6.1
2023-04-01 07:27:34 +00:00
Ac_K
4c2d9ff3ff HLE: Refactoring of ApplicationLoader (#4480)
* HLE: Refactoring of ApplicationLoader

* Fix SDL2 Headless

* Addresses gdkchan feedback

* Fixes LoadUnpackedNca RomFS loading

* remove useless casting

* Cleanup and fixe empty application name

* Remove ProcessInfo

* Fixes typo

* ActiveProcess to ActiveApplication

* Update check

* Clean using.

* Use the correct filepath when loading Homebrew.npdm

* Fix NRE in ProcessResult if MetaLoader is null

* Add more checks for valid processId & return success

* Add missing logging statement for npdm error

* Return result for LoadKip()

* Move error logging out of PFS load extension method

This avoids logging "Could not find Main NCA"
followed by "Loading main..." when trying to start hbl.

* Fix GUIs not checking load results

* Fix style and formatting issues

* Fix formatting and wording

* gtk: Refactor LoadApplication()

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-03-31 21:16:46 +02:00
jhorv
8198b99935 Fix Linux hang on shutdown (#4617)
* Rework StdErr-to-log redirection to use built-in FileStream, and do reads asynchronously to avoid hanging the process shutdown.

* set _disposable to false ASAP
2023-03-30 22:07:07 +02:00
ACGNnsj
460f96967d Slight Code Refactoring (#4373)
* Simplify return statements by using ternary expressions

* Remove a redundant type conversion

* Reduce nesting by inverting "if" statements

* Try to improve code readability by using LINQ and inverting "if" statements

* Try to improve code readability by using LINQ, using ternary expressions, and inverting "if" statements

* Add line breaks to long LINQ

* Add line breaks to long LINQ
2023-03-28 14:59:43 +02:00
Mary
7ca779a26d audout: Fix a possible crash with SDL2 when the SDL2 audio backend is dummy (#4605)
This change makes audio device error not fatal.
In case of error, the SDL2 audio backend will behave like the dummy
backend.
2023-03-27 20:56:36 +02:00
Mary
b5032b3c91 vulkan: Fix access level of extensions fields and make them readonly (#4608) 2023-03-27 08:40:27 +02:00
Mary
f0a3dff136 vulkan: Remove CreateCommandBufferPool from VulkanInitialization (#4606)
It was only called in one place, that can be simplified.
2023-03-27 02:16:31 +00:00
Mary
f659dcb9d8 vulkan: fix broken "VK_EXT_subgroup_size_control" support check (#4607)
Not sure since when it was broken...
2023-03-26 19:01:30 +02:00
riperiperi
a34fb0e939 Vulkan: Insert barriers before clears (#4596)
* Vulkan: Insert barriers before clears

Newer NVIDIA GPUs seem to be able to start clearing render targets before the last rasterization task is completed, which can cause it to clear a texture while it is being sampled.

This change adds a barrier from read to write when doing a clear, assuming it has been sampled in the past. It could be possible for this to be needed for sample into draw by some GPU, but it's not right now afaik.

This should fix visual artifacts on newer NVIDIA GPUs and driver combos. Contrary to popular belief, Tetris® Effect: Connected is not affected. Testing welcome, hopefully should fix most cases of this and not cost too much performance.

* Visual Studio Moment

* Address feedback

* Address Feedback 2
2023-03-26 12:51:02 +02:00
Mary
21ce8a9b80 chore: Update Ryujinx.SDL2-CS to 2.26.3 (#4479) 2023-03-24 22:42:24 +01:00
gdkchan
9ecbee8032 Batch inline index buffer update (#4587) 2023-03-24 14:19:54 +01:00
gdkchan
80519af67d Update short cache textures if modified (#4586) 2023-03-24 12:54:58 +01:00
gdkchan
26e30faff3 Fix handle leak on IShopServiceAccessServerInterface.CreateServerInterface (#4591) 2023-03-24 11:56:54 +01:00
Wunk
0992310b76 ARMeilleure: Check for XSAVE cpuid flag for AVX{2,512} (#4584)
Protection for the `xgetbv` instruction for systems that do not support
`xcr0` such as nehalem processors.

The `XSAVE` cpuid indicates support for `XSAVE`, `XRESTOR`, `XSETBV`,
`XGETBV` while `OSXSAVE` indicates if the operating system itself has
`XSAVE` turned on. Both must be checked at the same time.
2023-03-22 14:51:21 -03:00
Andrew Glaze
009c1101d2 CI: add a version tag to correlate release versions with commits (#4572)
* add step to tag commit with release version

* add step to tag commit with release version

* Rename step to “Create Tag”

* Fix name
2023-03-22 13:17:28 +01:00
gdkchan
ba95ee54ab Revert "Use source generated json serializers in order to improve code trimming (#4094)" (#4576)
This reverts commit 4ce4299ca2.
2023-03-21 20:14:46 -03:00
Andrey Sukharev
4ce4299ca2 Use source generated json serializers in order to improve code trimming (#4094)
* Use source generated json serializers in order to improve code trimming

* Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing

* Use separate model for LogEventArgs serialization

* Make dynamic object formatter static. Fix string builder pooling.

* Do not inherit json version of LogEventArgs from EventArgs

* Fix extra space in object formatting

* Write log json directly to stream instead of using buffer writer

* Rebase fixes

* Rebase fixes

* Rebase fixes

* Enforce block-scoped namespaces in the solution. Convert style for existing code

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Rebase indent fix

* Fix indent

* Delete unnecessary json properties

* Rebase fix

* Remove overridden json property names as they are handled in the options

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Use default json options in github api calls

* Indentation and spacing fixes

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-03-21 19:41:19 -03:00
Wunk
17620d18db ARMeilleure: Add initial support for AVX512 (EVEX encoding) (cont) (#4147)
* ARMeilleure: Add AVX512{F,VL,DQ,BW} detection

Add `UseAvx512Ortho` and `UseAvx512OrthoFloat` optimization flags as
short-hands for `F+VL` and `F+VL+DQ`.

* ARMeilleure: Add initial support for EVEX instruction encoding

Does not implement rounding, or exception controls.

* ARMeilleure: Add `X86Vpternlogd`

Accelerates the vector-`Not` instruction.

* ARMeilleure: Add check for `OSXSAVE` for AVX{2,512}

* ARMeilleure: Add check for `XCR0` flags

Add XCR0 register checks for AVX and AVX512F, following the guidelines
from section 14.3 and 15.2 from the Intel Architecture Software
Developer's Manual.

* ARMeilleure: Remove redundant `ReProtect` and `Dispose`, formatting

* ARMeilleure: Move XCR0 procedure to GetXcr0Eax

* ARMeilleure: Add `XCR0` to `FeatureInfo` structure

* ARMeilleure: Utilize `ReadOnlySpan` for Xcr0 assembly

Avoids an additional allocation

* ARMeilleure: Formatting fixes

* ARMeilleure: Fix EVEX encoding src2 register index

> Just like in VEX prefix, vvvv is provided in inverted form.

* ARMeilleure: Add `X86Vpternlogd` acceleration to `Vmvn_I`

Passes unit tests, verified instruction utilization

* ARMeilleure: Fix EVEX register operand designations

Operand 2 was being sourced improperly.

EVEX encoded instructions source their operands like so:
Operand 1: ModRM:reg
Operand 2: EVEX.vvvvv
Operand 3: ModRM:r/m
Operand 4: Imm

This fixes the improper register designations when emitting vpternlog.
Now "dest", "src1", "src2" arguments emit in the proper order in EVEX instructions.

* ARMeilleure: Add `X86Vpternlogd` acceleration to `Orn_V`

* ARMeilleure: PTC version bump

* ARMeilleure: Update EVEX encoding Debug.Assert to Debug.Fail

* ARMeilleure: Update EVEX encoding comment capitalization
2023-03-20 16:09:24 -03:00
riperiperi
9f1cf6458c Vulkan: Migrate buffers between memory types to improve GPU performance (#4540)
* Initial implementation of migration between memory heaps

- Missing OOM handling
- Missing `_map` data safety when remapping
  - Copy may not have completed yet (needs some kind of fence)
  - Map may be unmapped before it is done being used. (needs scoped access)
- SSBO accesses are all "writes" - maybe pass info in another way.
- Missing keeping map type when resizing buffers (should this be done?)

* Ensure migrated data is in place before flushing.

* Fix issue where old waitable would be signalled.

- There is a real issue where existing Auto<> references need to be replaced.

* Swap bound Auto<> instances when swapping buffer backing

* Fix conversion buffers

* Don't try move buffers if the host has shared memory.

* Make GPU methods return PinnedSpan with scope

* Storage Hint

* Fix stupidity

* Fix rebase

* Tweak rules

Attempt to sidestep BOTW slowdown

* Remove line

* Migrate only when command buffers flush

* Change backing swap log to debug

* Address some feedback

* Disallow backing swap when the flush lock is held by the current thread

* Make PinnedSpan from ReadOnlySpan explicitly unsafe

* Fix some small issues

- Index buffer swap fixed
- Allocate DeviceLocal buffers using a separate block list to images.

* Remove alternative flags

* Address feedback
2023-03-19 17:56:48 -03:00
gdkchan
67b4e63cff Remove MultiRange Min/MaxAddress and rename GetSlice to Slice (#4566)
* Delete MinAddress and MaxAddress from MultiRange

* Rename MultiRange.GetSlice to MultiRange.Slice
2023-03-19 17:31:35 +01:00
TSRBerry
c05c688ee8 Avoid copying more handles than we have space for (#4564)
* Avoid copying more handles than we have space for

* Use locks instead

* Reduce nesting by combining the lock statements

* Add locks for other uses of _sessionHandles and _portHandles

* Use one object to lock instead of locking twice

* Release the lock as soon as possible
2023-03-19 11:30:04 +01:00
riperiperi
b2623dc27d OpenGL: Fix inverted conditional for counter flush from #4471 (#4560)
Fixes OpenGL.
2023-03-18 20:39:05 -03:00
jhorv
5131b71437 Reducing memory allocations (#4537)
* add RecyclableMemoryStream dependency and MemoryStreamManager

* organize BinaryReader/BinaryWriter extensions

* add StreamExtensions to reduce need for BinaryWriter

* simple replacments of MemoryStream with RecyclableMemoryStream

* add write ReadOnlySequence<byte> support to IVirtualMemoryManager

* avoid 0-length array creation

* rework IpcMessage and related types to greatly reduce memory allocation by using RecylableMemoryStream, keeping streams around longer, avoiding their creation when possible, and avoiding creation of BinaryReader and BinaryWriter when possible

* reduce LINQ-induced memory allocations with custom methods to query KPriorityQueue

* use RecyclableMemoryStream in StreamUtils, and use StreamUtils in EmbeddedResources

* add constants for nanosecond/millisecond conversions

* code formatting

* XML doc adjustments

* fix: StreamExtension.WriteByte not writing non-zero values for lengths <= 16

* XML Doc improvements. Implement StreamExtensions.WriteByte() block writes for large-enough count values.

* add copyless path for StreamExtension.Write(ReadOnlySpan<int>)

* add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence<byte>); remove previous explicit implementations

* code style fixes

* remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator
2023-03-17 13:14:50 +01:00
TSRBerry
7870423671 Update syscall capabilites to include SVCs from FW 15.0.0 (#4530)
* Add CapabilityType enum

* Add SupervisorCallCount

* kernel: Add CapabilityExtensions & Change type of capabilities to uint

* Remove private setter from Mask arrays

* Pass ReadOnlySpan directly & Remove redundant type casts
2023-03-17 12:55:19 +01:00
dependabot[bot]
b72916fbc1 nuget: bump UnicornEngine.Unicorn (#4543)
Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-f7c841d to 2.0.2-rc1-fb78016.
- [Release notes](https://github.com/unicorn-engine/unicorn/releases)
- [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog)
- [Commits](https://github.com/unicorn-engine/unicorn/commits)

---
updated-dependencies:
- dependency-name: UnicornEngine.Unicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 12:50:52 +01:00
riperiperi
da073fce61 GPU: Fast path for adding one texture view to a group (#4528)
* GPU: Fast path for adding one texture view to a group

Texture group handles must store a list of their overlapping views, so they can be properly notified when a write is detected, and a few other things relating to texture readback. This is generally created when the group is established, with each handle looping over all views to find its overlaps. This whole process was also done when only a single view was added (and no handles were changed), however...

Sonic Frontiers had a huge cubemap array with 7350 faces (175 cubemaps * 6 faces * 7 levels), so iterating over both handles and existing views added up very fast. Since we are only adding a single view, we only need to _add_ that view to the existing overlaps, rather than recalculate them all.

This greatly improves performance during loading screens and a few seconds into gameplay on the "open zone" sections of Sonic Frontiers. May improve loading times or stutters on some other games.

Note that the current texture cache rules will cause these views to fall out of the cache, as there are more than the hard cap, so the cost will be repaid when reloading the open zone.

I also added some code to properly remove overlaps when texture views are removed, since it seems that was missing.

This can be improved further by only iterating handles that overlap the view (filter by range), but so can a few places in TextureGroup, so better to do all at once. The full generation of overlaps could probably be improved in a similar way.

I recommend testing a few games to make sure nothing breaks.

* Address feedback
2023-03-14 17:33:44 -03:00
riperiperi
1fc90e57d2 Update range for remapped sparse textures instead of recreating them (#4442)
* Update sparsely mapped texture ranges without recreating

Important TODO in TexturePool. Smaller TODO: should I look into making textures with views also do this? It needs to be able to detect if the views can be instantly deleted without issue if they're now remapped.

* Actually do partial updates

* Signal group dirty after mappings changed

* Fix various issues (should work now)

* Further optimisation

Should load a lot less data (16x) when partial updating 3d textures.

* Improve stability

* Allow granular uploads on large textures, improve rules

* Actually avoid updating slices that aren't modified.

* Address some feedback, minor optimisation

* Small tweak

* Refactor DereferenceRequest

More specific initialization methods.

* Improve code for resetting handles

* Explain data loading a bit more

* Add some safety for setting null from different threads.

All texture sets come from the one thread, but null sets can come from multiple. Only decrement ref count if we succeeded the null set first.

* Address feedback 1

* Make a bit safer
2023-03-14 17:08:44 -03:00
Isaac Marovitz
eafcc314a9 Ava UI: DownloadableContentManager Refactor (#4300)
* Start refactor

* Move around functions

* It builds

* Menu opens

* Buttons

* Fix overlapping text

* SaveAndClose and Close buttons

* Remove button

* Layout

* It’s a little funky but it works

* Enable all/disable all buttons

* Fix UpdateCount desyncs

* Search bar

* Search by title id

* Fix fuck ups

* Fix selection mode

* Update Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Fix search bar

* Log corrupted DLC json

* Fix LibHac changes

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-03-14 17:04:38 +01:00
riperiperi
6e9bd4de13 GPU: Scale counter results before addition (#4471)
* GPU: Scale counter results before addition

Counter results were being scaled on ReportCounter, which meant that the _total_ value of the counter was being scaled. Not only could this result in very large numbers and weird overflows if the game doesn't clear the counter, but it also caused the result to change drastically.

This PR changes scaling to be done when the value is added to the counter on the backend. This should evaluate the scale at the same time as before, on report counter, but avoiding the issue with scaling the total.

Fixes scaling in Warioware, at least in the demo, where it seems to compare old/new counters and broke down when scaling was enabled.

* Fix issues when result is partially uploaded.

Drivers tend to write the low half first, then the high half. Retry if the high half is FFFFFFFF.
2023-03-12 18:01:15 +01:00
TimeZlicer
05a41b31bc Misc: Support space in path on macOS distribution (#4462)
* .

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* wildcard(*) needs to be outside of quotes(") for cp to work

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-03-12 17:21:21 +01:00
MutantAura
eed17f963e Increase access permissions for Ava timezones (#4538) 2023-03-12 17:20:09 +01:00
TSRBerry
c09c0c002d [Flatpak] Beautify multiline strings again & Add full git commit hash (#4535)
* Don't destroy multiline strings

* Use full git commit hash
2023-03-12 10:42:33 +01:00
Mary
d56d335c0b misc: Some dependencies cleanup (#4507)
* Remove dependencies on libraries provided by .NET standard library

* Use System.IO.Hashing instead of Crc32.NET
2023-03-12 03:24:11 +01:00
jhorv
23c844b2aa Misc performance tweaks (#4509)
* use Array.Empty() where instead of allocating new zero-length arrays

* structure for loops in a way that the JIT will elide array/Span bounds checking

* avoiding function calls in for loop condition tests

* avoid LINQ in a hot path

* conform with code style

* fix mistake in GetNextWaitingObject()

* fix GetNextWaitingObject() possibility of returning null if all list items have TimePoint == long.MaxValue

* make GetNextWaitingObject() behave FIFO behavior for multiple items with the same TimePoint
2023-03-11 17:05:48 -03:00
Mary
81691b9e37 gha(release): Attempt to fix flathub pusher 2023-03-11 20:11:09 +01:00
Mary
2dc422bc14 gha(release): Hopefully fixes it 2023-03-11 19:16:08 +01:00
Mary
a80fa5e33f gha(release): Makes environment variables global 2023-03-11 19:09:48 +01:00
Mary
954e995321 Attempt to fix syntax error of previous merge 2023-03-11 19:06:42 +01:00
TSRBerry
dad9ab6bb6 [Flatpak] Add release github workflow (#4529)
* Add flatpak release workflow

Co-authored-by: Mary <mary@mary.zone>

* infra: Update required SDK version to 7.0.200

---------

Co-authored-by: Mary <mary@mary.zone>
2023-03-11 19:04:13 +01:00
gdkchan
f0562b9c75 CPU: Avoid argument value copies on the JIT (#4484)
* Minor refactoring of the pre-allocator

* Avoid LoadArgument copies

* PPTC version bump
2023-03-08 23:25:35 +01:00
dependabot[bot]
b8556530f2 nuget: bump Microsoft.CodeAnalysis.CSharp from 4.4.0 to 4.5.0 (#4488)
Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-04 15:37:08 +01:00
gdkchan
4f3af839be Minor code formatting (#4498) 2023-03-04 14:43:08 +01:00
dependabot[bot]
155736c986 nuget: bump UnicornEngine.Unicorn (#4500)
Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-a913199 to 2.0.2-rc1-f7c841d.
- [Release notes](https://github.com/unicorn-engine/unicorn/releases)
- [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog)
- [Commits](https://github.com/unicorn-engine/unicorn/commits)

---
updated-dependencies:
- dependency-name: UnicornEngine.Unicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-04 11:32:30 +00:00
MetrosexualGarbodor
dba908dc78 Add post processing feature to the readme (#4499)
* Add post processing feature to the readme

Adds post processing information to the GPU section in the readme.

* correct "Anti-Aliasing"
2023-03-04 02:15:29 +00:00
Steveice10
ecee34a50c Update LibHac to 0.18.0 (#4414)
* Update LibHac to 0.18.0

* Change instance of AsBytes(CreateReadOnlySpan(...)) to AsReadOnlyByteSpan(...)
2023-03-01 23:42:27 -03:00
riperiperi
9b5a0c3889 Sockets: Properly convert error codes on MacOS (#4491)
* Sockets: Properly convert error codes on MacOS

The error codes for MacOS are very different to how they are on windows or linux. An alternate mapping is used when the host operating system is MacOS.

This PR also defaults IsDhcpEnabled to true when interfaceProperties.DhcpServerAddresses is not available.

This change was already in `macos1`.

* Address feedback
2023-02-28 03:41:44 +00:00
Emmanuel Hansen
80b4972139 Add Support for Post Processing Effects (#3616)
* Add Post Processing Effects

* fix events and shader issues

* fix gtk upscale slider value

* fix bgra games

* don't swap swizzle if already swapped

* restore opengl texture state after effects run

* addressed review

* use single pipeline for smaa and fsr

* call finish on all pipelines

* addressed review

* attempt fix file case

* attempt fixing file case

* fix filter level tick frequency

* adjust filter slider margins

* replace fxaa shaders with original shader

* addressed review
2023-02-27 18:11:55 -03:00
gdkchan
5d85468302 Vulkan: Support list topology primitive restart (#4483) 2023-02-26 19:19:00 -03:00
merry
9b1cc2cec6 Logging: Redirect StdErr into logging system (#4427)
* Logging: Redirect StdErr into logging system

* Remove Mono.Unix

* Apply suggestions from code review

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

* Address comments

---------

Co-authored-by: Mary <thog@protonmail.com>
Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Co-authored-by: Mary <mary@mary.zone>
2023-02-25 15:07:23 +00:00
Mary
e691622f0a misc: Add missing DefineConstants definition in Ryujinx.Common
Fix flathub and nixpkgs build hopefully now.
2023-02-25 13:32:20 +01:00
Mary
f663a5cd38 macos: Add updater support (#4464)
This is a very basic updater but should be enough for now.

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-02-25 12:30:48 +01:00
Mary
f7c2e867f4 chore: Update OpenTK to 4.7.7 (#4478) 2023-02-25 10:55:57 +00:00
gdkchan
cedd200745 Move gl_Layer to vertex shader if geometry is not supported (#4368)
* Set gl_Layer on vertex shader if it's set on the geometry shader and it does nothing else

* Shader cache version bump

* PR feedback

* Fix typo
2023-02-25 10:39:51 +00:00
jhorv
58207685c0 Perform bounds checking before list indexer to avoid frequent exceptions (#4438)
* Perform bounds checking before list indexer to avoid frequent ArgumentOutOfRangeExceptions

* do a single compare after casting id and .Count to uint
2023-02-25 10:26:39 +00:00
gdkchan
095ad923ad Account for multisample when calculating render target size hint (#4467) 2023-02-23 10:08:54 +01:00
Isaac Marovitz
f07ae7d53f Fix Title Update Manager not selecting right update (#4452) 2023-02-22 17:58:32 -03:00
dependabot[bot]
c308f09722 nuget: bump Microsoft.NET.Test.Sdk from 17.4.1 to 17.5.0 (#4458)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.1 to 17.5.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.1...v17.5.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-22 16:08:25 +01:00
dependabot[bot]
f1eef29409 nuget: bump UnicornEngine.Unicorn (#4459)
Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-9c9356d to 2.0.2-rc1-a913199.
- [Release notes](https://github.com/unicorn-engine/unicorn/releases)
- [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog)
- [Commits](https://github.com/unicorn-engine/unicorn/commits)

---
updated-dependencies:
- dependency-name: UnicornEngine.Unicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-22 13:30:53 +01:00
TSRBerry
1f8d66db7c Ava: Fix Updater crashing on Linux (#4457) 2023-02-22 09:13:50 +01:00
gdkchan
c3a5716a95 Add copy dependency for some incompatible texture formats (#4380)
* Add copy dependency for some incompatible texture formats

* Simplify compatibility check
2023-02-21 19:21:57 -03:00
Mary
1f1e2a7f03 misc: changes base application directory behaviour (#4460)
This allows changing base application directory behavior at build time via FORCE_EXTERNAL_BASE_DIR.

This is intended to be used by nixpkgs and flathub builds.

I also added the missing patch for macOS that we have on macos1 to avoid invalidating code signature.
2023-02-21 22:38:34 +01:00
Andrew Glaze
e54f9dc4b4 Move Ryujinx Folder from ~/.config to ~/Library/Application Support on macOS (#4296)
* Move Ryujinx folder to Application Support on macOS

* Create a symlink to preserve back compat

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Remove extra whitespace

* Don’t create a symlink

* Update Ryujinx.Common/Configuration/AppDataManager.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Revert "Don’t create a symlink"

This reverts commit 31752fe8ab.

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-02-21 12:14:31 +01:00
Logan Stromberg
edfd4d70c0 Use SIMD acceleration for audio upsampler (#4410)
* Use SIMD acceleration for audio upsampler filter kernel for a moderate speedup

* Address formatting. Implement AVX2 fast path for high quality resampling in ResamplerHelper

* now really, are we really getting the benefit of inlining 50+ line methods?

* adding unit tests for resampler + upsampler. The upsampler ones fail for some reason

* Fixing upsampler test. Apparently this algo only works at specific ratios

---------

Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2023-02-21 11:44:57 +01:00
riperiperi
fc43aecbbd Memory: Faster Split for NonOverlappingRangeList (#4451)
I noticed that in Xenoblade 2, the game can end up spending a lot of time adding and removing tracking handles. One of the main causes of this is actually splitting existing handles, which does the following:

- Remove existing handle from list
- Update existing handle to end at split address, create new handle starting at split address
- Add updated handle (left) to list
- Add new handle (right) to list

This costs 1 deletion and 2 insertions. When there are more handles, this gets a lot more expensive, as insertions are done by copying all values to the right, and deletions by copying values to the left.

This PR simply allows it to look up the handle being split, and replace its entry with the new end address without insertion or deletion. This makes a split only cost one insertion and a binary search lookup (very cheap). This isn't all of the cost on Xenoblade 2, but it does significantly reduce it.

There might be something else to this - we could find a way to reduce the handle count for the game (merging on deletion? buffer deletion?), we could use a different structure for virtual regions, as the current one is optimal for buffer lookups which nearly always read, memory tracking has more of a balance between read/write. That's for a later date though, this was an easy improvment.
2023-02-21 10:53:38 +01:00
gdkchan
58d7a1fe97 Mark texture as modified and sync on I2M fast path (#4449) 2023-02-21 10:40:23 +01:00
gdkchan
7aa430f1a5 Add support for advanced blend (part 1/2) (#2801)
* Add blend microcode registers

* Add advanced blend support using host extension

* Remove debug message

* Use pre-generated table for blend functions

* XML docs

* Rename AdvancedBlendMode to AdvancedBlendOp for consistency

* Remove redundant code

* Fix some advanced blend related issues on Vulkan

* Formatting
2023-02-19 22:37:37 -03:00
dependabot[bot]
6bf460e104 nuget: bump System.IdentityModel.Tokens.Jwt from 6.26.1 to 6.27.0 (#4441)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.26.1 to 6.27.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-18 02:35:02 +01:00
gdkchan
efb135b74c Clear CPU side data on GPU buffer clears (#4125)
* Clear CPU side data on GPU buffer clears

* Implement tracked fill operation that can signal other resource types except buffer

* Fix tests, add missing XML doc

* PR feedback
2023-02-16 18:28:49 -03:00
gdkchan
a707842e14 Validate dimensions before creating texture (#4430) 2023-02-16 11:16:31 -03:00
TSRBerry
a5a9b9bc8b GUI: Small Updater refactor & Set correct permissions on Linux when extracting files (#4315)
* ava: Refactor Updater.cs

Fix typos
Remove unused usings
Rename variables to follow naming scheme

* ava: Set file permissions when extracting update files

* gtk: Apply the same refactor to Updater.cs

* updater: Replace assert with if statement

* updater: Remove await usings again
2023-02-15 22:36:35 +00:00
Mary
17078ad929 vulkan: Respect VK_KHR_portability_subset vertex stride alignment (#4419)
* vulkan: Respect VK_KHR_portability_subset vertex stride alignment

We were hardcoding alignment to 4, but by specs it can be any values that
is a power of 2.

This also enable VK_KHR_portability_subset if present as per specs
requirements.

* address gdkchan's comment

* Make NeedsVertexBufferAlignment internal
2023-02-15 08:41:48 +00:00
Mary
32450d45de vulkan: Clean up MemoryAllocator (#4418)
This started as an attempt to remove vkGetPhysicalDeviceMemoryProperties
in FindSuitableMemoryTypeIndex (As this could have some overhead and
shouldn't change at runtime) and turned in a little bigger cleanup.
2023-02-15 07:50:26 +01:00
Ac_K
ed7a0474c6 Infra: Issues template cleanup (#4421)
* Infra: Issues template cleanup

* applied
2023-02-14 15:58:57 +01:00
Mary
fe9c49949a vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level (#4408)
* vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level

This ensure we don't end up trying to initialize with anything currently incompatible.

* Address riperiperi's comment
2023-02-13 22:04:55 +00:00
Mary
052b23c83c vulkan: Do not call vkCmdSetViewport when viewportCount is 0 (#4406)
This fix validation error "VUID-vkCmdSetViewport-viewportCount-arraylength".
2023-02-13 20:32:20 +00:00
riperiperi
e4f68592c3 Fix partial updates for textures. (#4401)
I was forcing some types of texture to partially update when investigating performance with games that stream in data, and noticed that partially loading texture data was really broken on both backends.

Fixes Vulkan texture set by getting the correct expected size for the texture. Fixes partial upload on both backends for both Texture 2D Array and Cubemap using the wrong offset and uploading to the first layer/level for a handle. 3D might also be affected.

This might fix textures randomly having incorrect data in games that render to it - jumbled in the case of OpenGL, and outdated/black in the case of Vulkan. This case typically happens in UE4 games.
2023-02-12 10:30:26 +01:00
Logan Stromberg
1dcd44b94f Treat NpadIdType < 0 as invalid. Filter invalid SupportedPlayers inside IHidServer.SetSupportedNpadIdType(). (#4377)
Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2023-02-10 12:37:20 -03:00
gdkchan
61b1ce252f Allow partially mapped textures with unmapped start (#4394) 2023-02-10 11:47:59 -03:00
gdkchan
5f38086f94 Fix SPIR-V when all inputs/outputs are indexed (#4389) 2023-02-09 04:48:25 +01:00
Isaac Marovitz
7bae440d3a ObjectiveC Helper Class (#4286)
* `NativeMacOS` Helper Class

* Corrections

* Make CFString IDisposable

* Fix `openURL:`

* `dealloc` metal layer

* Remove releases

* Use NSString

* Update Ryujinx.Ui.Common/Helper/NativeMacOS.cs

Co-authored-by: merry <git@mary.rs>

* Programatically select updates in Finder

* Address feedback

* Feedback

* Ptr

* Fix whoopsie

* Ack suggestions

* Update Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* GDK Suggestions

---------

Co-authored-by: merry <git@mary.rs>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-02-09 04:08:15 +01:00
riperiperi
f1943fd0b6 Log shader compile errors with Warning level (#2617)
* Log shader compile errors with Warning level

These are infrequent enough that I think it's worth dumping any errors into the log. They also keep causing graphical glitches, and the only indication that anything went wrong is a debug log that is never enabled.

* Add maximum length for shader log
2023-02-09 03:50:18 +01:00
TSRBerry
ec8d4f3af5 Replace unicorn bindings with Nuget package (#4378)
* Replace unicorn bindings with Nuget package

* Use nameof for ValueSource args

* Remove redundant code from test projects

* Fix wrong values for EmuStart()

Add notes to address this later again

* Improve formatting

* Fix formatting/alignment issues
2023-02-09 02:24:32 +01:00
riperiperi
b3f0978869 Vulkan: Flush command buffers for queries less aggressively (#4387)
The AutoFlushCounter would flush command buffers on any attachment change (write mask or bindings change) if there was a pending query. This is to get query results as soon as possible for draw skips, but it's assuming that a full occlusion query _pass_ happened, that we want to flush it's data before getting onto draws, rather than the queries being randomly interspersed throughout a pass that also draws.

Xenoblade 2 repeatedly switches between performing a samples passed query and outputting to a render target on each draw, and flips the write mask to do so. Flushing the command buffer every 2 draws isn't ideal, so it's best that we only do this if the pattern matches the large block style of occlusion query.

This change makes this flush only happen after a few consecutive query reports. "Consecutive" is interrupted by attachment changes or command buffer flush.

This doesn't really solve the issue where it resets more queries than it uses, it just stops the game doing it as often. I'm not sure of the best way to do that. The cost of resetting could probably be reduced by using query pools with more than one element and resetting in bulk.
2023-02-09 02:03:41 +01:00
Ac_K
f614d2c435 bug_report.yml hotfix 2023-02-09 02:02:00 +01:00
Ac_K
40c9416097 Misc: Update issues form (#4383) 2023-02-09 00:52:43 +00:00
dependabot[bot]
618c8edc79 nuget: bump System.IdentityModel.Tokens.Jwt from 6.26.0 to 6.26.1 (#4384)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.26.0 to 6.26.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.26.0...v6.26.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 22:51:07 +01:00
Berkan Diler
99fc4fa61b Replace BitConverter.ToString(bytes).Replace("-", "") with Convert.ToHexString(bytes) (#4382) 2023-02-08 14:54:58 +01:00
gdkchan
f6d5499a16 Fix some Vulkan validation errors (#4357) 2023-02-08 14:34:22 +01:00
gdkchan
26bf13a65d Limit texture cache based on total texture size (#4350)
* Limit texture cache based on total texture size

* Formatting
2023-02-08 14:19:43 +01:00
gdkchan
96cf242bcf Handle mismatching texture size with copy dependencies (#4364)
* Handle mismatching texture size with copy dependencies

* Create copy and render textures with the minimum possible size

* Only align width for comparisons, assume that height is always exact

* Fix IsExactMatch size check

* Allow sampler and copy textures to match textures with larger width

* Delete texture ChangeSize related code

* Move AdjustSize to TextureInfo and give it a better name, adjust usages

* Fix GetMinimumWidthInGob when minimumWidth > width

* Only update render targets that are actually cleared for clear

Avoids creating textures with incorrect sizes

* Delete UpdateRenderTargetState method that is not needed anymore

Clears now only ever sets the render targets that will be cleared rather than all of them
2023-02-08 08:48:09 +01:00
TSRBerry
59755818ef Add ChangeVSyncMode() call to Avalonia render loop (#4379) 2023-02-08 01:28:53 +01:00
gdkchan
f8beeeb7d3 Support safe blit on non-2D textures (#4374)
* Support safe blit on non-2D textures (except multisample)

* Change safe blit with different levels and layers to match CmdBlitImage path

* Remove now unused variables

* Multisample safe blit support
2023-02-07 13:55:59 -03:00
gdkchan
cb250162cb Accelerate NVDEC VIC surface read/write and colorspace conversion with Arm64 HW intrinsics (#4351)
* Accelerate NVDEC VIC surface read/write and colorspace conversion with Arm64 HW intrinsics

* Improve ReadNv12 x86 SSE path
2023-02-07 02:38:54 +00:00
gdkchan
7528f94536 Implement safe depth-stencil blit using stencil export extension (#4356)
* Implement safe depth-stencil blit using stencil export extension

* Delete depth-stencil blit with buffer path
2023-02-06 00:19:31 -03:00
gdkchan
43081c16c4 Insert bitcast for assignment of fragment integer outputs on GLSL (#4369)
* Insert bitcast for assignment of fragment integer outputs on GLSL

* Shader cache version bump
2023-02-05 18:52:57 -03:00
gdkchan
780627e7b0 Implement Account LoadOpenContext (#4359)
* Implement Account LoadOpenContext

* Formatting
2023-02-01 12:52:36 -03:00
dependabot[bot]
9044cb38d1 nuget: bump SharpZipLib from 1.4.1 to 1.4.2 (#4353)
Bumps [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/icsharpcode/SharpZipLib/releases)
- [Changelog](https://github.com/icsharpcode/SharpZipLib/blob/master/docs/Changes.txt)
- [Commits](https://github.com/icsharpcode/SharpZipLib/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: SharpZipLib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 01:58:25 +01:00
gdkchan
a53cfdab78 Initial Apple Hypervisor based CPU emulation (#4332)
* Initial Apple Hypervisor based CPU emulation implementation

* Add UseHypervisor Setting

* Add basic MacOS support to Avalonia

* Fix initialization

* Fix GTK build

* Fix/silence warnings

* Change exceptions to asserts on HvAddressSpaceRange

* Replace DllImport with LibraryImport

* Fix LibraryImport

* Remove unneeded usings

* Revert outdated change

* Set DiskCacheLoadState when using hypervisor too

* Fix HvExecutionContext PC value

* Address PR feedback

* Use existing entitlements.xml file on distribution folder

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-01-29 08:37:52 -03:00
Piplup
c7f9962dde converts the templates into forms (#4068)
* Create bug_report.yml

* Update bug_report.yml

* Update bug_report.yml

* Create feature_request.yml

* Update feature_request.yml

* Update feature_request.yml

* Update feature_request.yml

* Update feature_request.yml

* a

* Update missing_cpu_instruction.yml

* Update missing_cpu_instruction.yml

* Update missing_cpu_instruction.yml

* Update missing_cpu_instruction.yml

* b

* addressed some of the feedback

* forget the label

* added missing text inputs

* formatting changes

* dropdown menu

added dropdown menu for os, idk if we will keep this

* addressed feedback

addressed the long overdue feedback, sorry about that

* added markdowns

everything should be addressed now i hope

* game version optional

made game version optional after further feedback

* feature request checkbox
2023-01-27 18:23:55 +00:00
gdkchan
296c4a3d01 Relax Vulkan requirements (#4282)
* Relax Vulkan requirements

* Fix MaxColorAttachmentIndex

* Fix ColorBlendAttachmentStateCount value mismatch for background pipelines

* Change query capability check to check for pipeline statistics query rather than geometry shader support
2023-01-26 18:34:35 -03:00
riperiperi
e7cf4e6eaf Vulkan: Reset queries on same command buffer (#4329)
* Reset queries on same command buffer

Vulkan seems to complain when the queries are reset on another command buffer. No idea why, the spec really could be written better in this regard. This fixes complaints, and hopefully any implementations that care extensively about them.

This change _guesses_ how many queries need to be reset and resets as many as possible at the same time to avoid splitting render passes. If it resets too many queries, we didn't waste too much time - if it runs out of resets it will batch reset 10 more.

The number of queries reset is the maximum number of queries in the last 3 frames. This has been worked into the AutoFlushCounter so that it only resets up to 32 if it is yet to force a command buffer submission in this attachment.

This is only done for samples passed queries right now, as they have by far the most resets.

* Address Feedback
2023-01-24 13:32:56 -03:00
gdkchan
a1a4771ac1 Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer (#4337)
* Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer

* Rename FuncPtr to FuncPointer
2023-01-23 22:37:53 +00:00
gdkchan
2fd819613f SPIR-V: Change BitfieldExtract and BitfieldInsert for SPIRV-Cross (#4336)
* SPIR-V: Change BitfieldExtract and BitfieldInsert types to make Metal MSL compiler happy

* Shader cache version bump
2023-01-23 19:20:40 -03:00
TSRBerry
ad6ff6ce99 GUI: Add option to register file types (#4250)
* Add FileAssociationHelper.cs

* Add register file types option to gtk

* Add register file types option to avalonia

* Add Windows support to FileAssociationHelper.cs

* linux: Add uninstall support for file types

* Ignore .glade~ backup files

* Rename Register/Unregister methods

* gtk: Add manage file types submenu

* ava: Add manage file types submenu

* windows: Add uninstall support for file types

* Don't invert uninstall condition (formatting change)

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Add IsTypesRegisteredWindows & Fix Windows install function

* Add AreMimeTypesRegisteredLinux()

* Fix wrong indention

Co-authored-by: AcK77 <acoustik666@gmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-22 17:39:00 +00:00
gnisman
dc30d94852 Handle parsing of corrupt Config.json and prevent crash on launch (#4309)
* Handle parsing of corrupt Config.json and prevent crash on launch

* Implement a cleaner solution to handle empty json object
2023-01-22 17:04:33 +01:00
merry
4f293f8cbe Arm64: Simplify TryEncodeBitMask and use for constants (#4328)
* Arm64: Simplify TryEncodeBitMask

* CodeGenerator: Use TryEncodeBitMask in GenerateConstantCopy

* Ptc: Bump version
2023-01-22 14:15:49 +00:00
merry
32a1cd83fd AvaloniaKeyboardDriver: Swallow TextInput events to avoid bell (#4320) 2023-01-22 11:21:52 +01:00
riperiperi
e3d0ccf8d5 Allow setting texture data from 1x to fix some textures resetting randomly (#2860)
* Allow setting texture data from 1x to fix some textures resetting randomly

Expected targets:

- Deltarune 1+2
- Crash Team Racing
- Those new pokemon games idk

* Allow scaling of MSAA textures, propagate scale on copy.

* Fix Rebase

Oops

* Automatic disable

* A bit more aggressive

* Without the debug log

* Actually decrement the score when writing.
2023-01-22 02:03:30 +00:00
Ac_K
c14844d12c Ava UI: Various Fixes (#4326)
* Ava UI: Various Fixes

* use WriteAllBytes
2023-01-22 01:42:55 +01:00
gdkchan
7fea26e97e Remove use of reflection on GAL multithreading (#4287)
* Introduce new IGALCommand<T> interface and use it

* Remove use of reflection on GAL multithreading

* Unmanaged constraint
2023-01-22 01:07:43 +01:00
dependabot[bot]
7b7f62c776 nuget: bump Microsoft.CodeAnalysis.Analyzers from 3.3.3 to 3.3.4 (#4310)
* nuget: bump Microsoft.CodeAnalysis.Analyzers from 3.3.3 to 3.3.4

Bumps [Microsoft.CodeAnalysis.Analyzers](https://github.com/dotnet/roslyn-analyzers) from 3.3.3 to 3.3.4.
- [Release notes](https://github.com/dotnet/roslyn-analyzers/releases)
- [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/main/PostReleaseActivities.md)
- [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v3.3.3...v3.3.4)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.Analyzers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fixes warning

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-21 20:04:39 +00:00
riperiperi
423dbc8888 Use volatile read/writes for GAL threading (#4327) 2023-01-21 19:49:55 +00:00
gdkchan
6adf15e479 Implement CSET and CSETP shader instructions (#4318)
* Implement CSET and CSETP shader instructions

* Shader cache version bump

* Fix CC.HI
2023-01-21 12:18:05 -03:00
dependabot[bot]
2747f12591 nuget: bump System.IdentityModel.Tokens.Jwt from 6.25.1 to 6.26.0 (#4322)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.25.1 to 6.26.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-21 05:24:57 +01:00
Ac_K
a47824f961 Ava UI: Add Notifications and Cleanup (#4275)
* Ava UI: Add Notifications and Cleanup

* Revert notifications on ErrorDialog

* remove unused code from game list views

* Fix cast
2023-01-21 02:57:37 +01:00
Ac_K
8474d52778 Ava UI: Fix string.Format issues in Locale (#4305)
* Ava UI: Fix `string.Format` issues in Locale

* LoacLanguage everytime now

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* fix UpdateAndGetDynamicValue

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-01-21 02:06:19 +01:00
Phi
dd7a924596 Catch Profile.json parse to prevent crash on launch (#3393)
* Catch Profile.json parse to prevent crash on launch

* Update Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: PhiZero <wolkan.craanen@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-21 01:36:57 +01:00
gnisman
a76eaf9a9a Ava UI: Add Control+Cmd+F HotKey for Mac OS (#4317)
* Ava UI: Add Control+Cmd+F HotKey for Mac OS

* fix aligned

* Remove comment from code
2023-01-20 22:18:01 +01:00
merry
009e6bcd1b Audio: Implement PCM24 output (#4321) 2023-01-20 21:46:13 +01:00
Ac_K
eb2cc159fa Ava UI: Fixes and cleanup Updater (#4269)
* ava: Fixes and cleanup Updater

* _updateSuccessful
2023-01-20 21:30:21 +01:00
Fliperworld
bb89e36fd8 Vulkan: Destroy old swapchain on swapchain recreation (#3889)
* Destroy old swapchain on swapchain recreation

* vkDeviceWaitIdle before DestroySwapchain

* Update Ryujinx.Graphics.Vulkan/Window.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Avoid unsafe code on RecreateSwapchain()

* Destroying old Swapchain on a queue.

* Cleanup and fix on destroying old Swapchain.

* Update Ryujinx.Graphics.Vulkan/Window.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update Ryujinx.Graphics.Vulkan/Window.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update Ryujinx.Graphics.Vulkan/Window.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update Window.cs

Done.

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-19 21:31:25 -03:00
riperiperi
de3134adbe Vulkan: Explicitly enable precise occlusion queries (#4292)
The only guarantee of the occlusion query type in Vulkan is that it will be zero when no samples pass, and non-zero when any samples pass. Of course, most GPUs implement this by just placing the # of samples in the result and calling it a day. However, this lax restriction means that GPUs could just report a boolean (1/0) or report a value after one is recorded, but before all samples have been counted.

MoltenVK falls in the first category - by default it only reports 1/0 for occlusion queries. Thankfully, there is a feature and flag that you can use to force compatible drivers to provide a "precise" query result, that being the real # of samples passed.

Should fix ink collision in Splatoon 2/3 on MoltenVK.
2023-01-19 00:30:42 +00:00
merry
36d53819a4 NativeSignalHandler: Fix write flag (#4306)
* NativeSignalHandler: Fix write flag

* address comments
2023-01-19 00:13:17 +00:00
Andrey Sukharev
ae4324032a Optimize string memory usage. Use Spans and StringBuilders where possible (#3933)
* Optimize string memory usage. Use ReadOnlySpan<char> and StringBuilder where possible.

* Fix copypaste error

* Code generator review fixes

* Use if statement instead of switch

* Code style fixes

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Another code style fix

* Styling fix

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Styling fix

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-18 22:25:16 +00:00
Ac_K
f449895e6d HOS: Load RomFs by pid (#4301)
We currently loading only one RomFs at a time, which could be wrong if one day we want to load more than one guest at time.
This PR fixes that by loading romfs by pid.
2023-01-18 13:50:42 +00:00
TSRBerry
410be95ab6 Fix NRE when disposing AddressSpace with 4KB pages support (#4307) 2023-01-17 14:50:39 +00:00
merry
cff9046fc7 ConfigurationState: Default to Vulkan on macOS (#4299) 2023-01-17 05:32:08 +01:00
gdkchan
86fd0643c2 Implement support for page sizes > 4KB (#4252)
* Implement support for page sizes > 4KB

* Check and work around more alignment issues

* Was not meant to change this

* Use MemoryBlock.GetPageSize() value for signal handler code

* Do not take the path for private allocations if host supports 4KB pages

* Add Flags attribute on MemoryMapFlags

* Fix dirty region size with 16kb pages

Would accidentally report a size that was too high (generally 16k instead of 4k, uploading 4x as much data)

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-01-17 05:13:24 +01:00
Ac_K
43a83a401e Ava UI: Readd some infos to the GameList (#4302) 2023-01-17 04:57:21 +01:00
riperiperi
f0e27a23a5 Add short duration texture cache (#3754)
* Add short duration texture cache

This texture cache takes textures that lose their last pool reference and keeps them alive until the next frame, or until an incompatible overlap removes it. This is done since under certain circumstances, a texture's reference can be wiped from a pool despite it still being in use - though typically the reference will return when rendering the next frame.

While this may slightly increase texture memory usage when quickly going through a bunch of temporary textures, it's still bounded due to the overlap removal rule.

This greatly increases performance in Hyrule Warriors: Age of Calamity. It may positively affect some UE4 games which dip framerate severely under certain circumstances.

* Small optimization

* Don't forget this.

* Add short cache dictionary

* Address feedback

* Address some feedback
2023-01-17 04:39:46 +01:00
TSRBerry
e68650237d Ava: Fix Linux Vulkan renderer regression (#4303)
* ava: Fix Linux Vulkan renderer staying transparent

* ava: Minor Renderer cleanup

* Don't supress potential NRE warning

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-16 03:59:41 +01:00
Ac_K
1faff14e73 UI: Fixes GTK sorting regression of #4294 2023-01-16 03:09:52 +01:00
Ac_K
784cf9d594 Ava UI: Renderer refactoring (#4297)
* Ava UI: `Renderer` refactoring

* Fix Vulkan CreateSurface
2023-01-16 01:14:01 +01:00
Ac_K
64263c5218 UI: Fix applications times (#4294)
* Fix applications times

* Add spaces

* Fix TimeString formatting
2023-01-16 00:11:16 +01:00
gdkchan
065c4e520d Specify image view usage flags on Vulkan (#4283)
* Specify image view usage flags on Vulkan

* PR feedback
2023-01-15 23:12:52 +01:00
Isaac Marovitz
139a930407 Implement missing service calls in pm (#4210)
* Implement `GetTitleId`

Fixes #2516

* Null check + Proper result code

* Better comment

* Implement `GetApplicationProcessId`

* Add TODOs

* Update Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Remove new function from KernelStatic

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-15 22:16:24 +01:00
Isaac Marovitz
719dc97bbd Ava UI: TitleUpdateWindow Refactor (#4276)
* Start Refactor

* Dialogue opens

* Changes

* Switch to ListBox

* Fix bugs and stuff

* Fix spacing

* Implement OpenLocation

* Change icon

* Color

* Color

* Remove background

* Make no update the same height

* Fix height and smooth scroll

* Height

* Fix update selection

* Make window smaller

* Add back remove all button

* Make selection more obvious

* Hide selection bar on SaveManager

* Fix autoscroll

* Fix no update not staying selected

* Better file opener

* Fix

* Revert that

* Update Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Log warning

* Update Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-15 11:11:52 +00:00
merry
41bba5310a Audren: Implement polyphase upsampler (#4256)
* Audren: Implement polyphase upsampler

* prefer shifting to modulo

* prefer MathF

* fix nits

* rm ResampleForUpsampler

* oop

* Array20

* nits
2023-01-15 05:20:49 +01:00
Ac_K
8071c8c8c0 Ava UI: Fixes "Hide Cursor on Idle" for Windows (#4266)
* Ava: Fixes "Hide Cursor on Idle" for Windows

* Add check in MouseDriver and reduce the time of idling

* Fix linux error

* Change idle time everywhere for consistencies
2023-01-15 01:05:44 +01:00
gnisman
b402b4e7f6 Change GetPageSize to use Environment.SystemPageSize (#4291)
* Change GetPageSize to use Environment.SystemPageSize

* Fix PR comment
2023-01-14 15:37:04 -03:00
gdkchan
93df366b2c Fix texture flush from CPU WaitSync regression on OpenGL (#4289) 2023-01-14 11:23:57 -03:00
gdkchan
cd3a15aea5 Fix NRE when MemoryUnmappedHandler is called for a destroyed channel (#4285) 2023-01-14 00:16:06 -03:00
gdkchan
070136b3f7 Fix texture modified on CPU from GPU thread after being modified on GPU not being updated (#4284) 2023-01-13 23:46:45 -03:00
Ac_K
08ab47c6c0 Update Program.cs 2023-01-13 07:56:41 +01:00
Ac_K
85faa9d8fa Revert "Relax Vulkan requirements (#4228)" (#4279)
This reverts commit dca5b14493.
2023-01-13 06:04:59 +00:00
gdkchan
dca5b14493 Relax Vulkan requirements (#4228) 2023-01-13 06:09:48 +01:00
Ac_K
4d2c8e2a44 Prepo: Fix SaveSystemReport* IPC definitions (#4278)
* Prepo: Fix SaveSystemReport IPC definitions

* Follow original code

* Fix args index in HipcGenerator

* Addresses feedback

* oops
2023-01-13 01:50:14 +01:00
riperiperi
8fa248ceb4 Vulkan: Add workarounds for MoltenVK (#4202)
* Add MVK basics.

* Use appropriate output attribute types

* 4kb vertex alignment, bunch of fixes

* Add reduced shader precision mode for mvk.

* Disable ASTC on MVK for now

* Only request robustnes2 when it is available.

* It's just the one feature actually

* Add triangle fan conversion

* Allow NullDescriptor on MVK for some reason.

* Force safe blit on MoltenVK

* Use ASTC only when formats are all available.

* Disable multilevel 3d texture views

* Filter duplicate render targets (on backend)

* Add Automatic MoltenVK Configuration

* Do not create color attachment views with formats that are not RT compatible

* Make sure that the host format matches the vertex shader input types for invalid/unknown guest formats

* FIx rebase for Vertex Attrib State

* Fix 4b alignment for vertex

* Use asynchronous queue submits for MVK

* Ensure color clear shader has correct output type

* Update MoltenVK config

* Always use MoltenVK workarounds on MacOS

* Make MVK supersede all vendors

* Fix rebase

* Various fixes on rebase

* Get portability flags from extension

* Fix some minor rebasing issues

* Style change

* Use LibraryImport for MVKConfiguration

* Rename MoltenVK vendor to Apple

Intel and AMD GPUs on moltenvk report with the those vendors - only apple silicon reports with vendor 0x106B.

* Fix features2 rebase conflict

* Rename fragment output type

* Add missing check for fragment output types

Might have caused the crash in MK8

* Only do fragment output specialization on MoltenVK

* Avoid copy when passing capabilities

* Self feedback

* Address feedback

Co-authored-by: gdk <gab.dark.100@gmail.com>
Co-authored-by: nastys <nastys@users.noreply.github.com>
2023-01-13 01:31:21 +01:00
Ac_K
30862b5ffd ava: Reorder settings of Resolution Scaler (#4270) 2023-01-13 00:07:53 +01:00
Isaac Marovitz
9f57747c57 Ava UI: Various Fixes (#4268)
* Fix saves disappearing

* Better size formatter

* Move TextBox alignment fix to Styles

* Fix bug

* Left align

* Add border

* Update Ryujinx.Ava/UI/Models/SaveModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Models/SaveModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Models/SaveModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Whitespace

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-12 12:23:24 +00:00
Isaac Marovitz
fe29a2ff6e Ava UI: Settings Adjustments (#4273)
* Visual adjustments

* Match border to rest of app

* Fix overlapping controls

* Fix

* Fix
2023-01-12 12:09:32 +00:00
merry
e9a173e00c Ptc: Check process architecture (#4272) 2023-01-12 07:50:45 +00:00
merry
a11784fcbf Arm64: Cpu feature detection (#4264)
* Arm64: Cpu feature detection

* Ptc: Add Arm64 feature info

* nits

* simplify CheckSysctlName

* restore some macos flags

* feedback
2023-01-12 08:05:18 +01:00
Ac_K
fd36c8deca lm: Handle Tail flag in LogPacket (#4274)
* lm: Handle TailFlag in LogPacket

* Addresses feedback
2023-01-12 07:42:05 +01:00
Ac_K
70638340b3 Ava UI: Move Ava logging to Logger.Debug (#4255)
* Ava: Move Ava logging to Logger.Debug

Since #4231 we currently redirect Avalonia logs to our Logger, which is pretty nice. But since it uses our Logging level too, it now leads to a massive flood in our Log files.
To avoid that, I've included all `AvaLogLevel` to the log message, and make all Ava Logs using `Logger.Debug`.

* Logs errors to Error and other to Debug

* missing level

* keep var
2023-01-11 23:27:26 +01:00
Ac_K
4b495f3333 Ava UI: Fixes PerformanceCheck condition (#4271) 2023-01-11 20:47:15 +00:00
Isaac Marovitz
934b5a64e5 Ava GUI: User Profile Manager + Other Fixes (#4166)
* Fix redundancies

* Add back elses

* Loading Screen fixes

* Redesign User Profile Manager

- Backported long selection bar in Grid/List view not working
- Backported UserSelector is jank

* Fix SelectionIndicator

* Fix DataType

* Fix SaveManager bug

* Remove debug log

* Load saves on UIThread

* Reduce UI thread blocking

* Fix locale keys

* Use block namespaces

* Fix close button width

* Make UserProfile ordering consistent

* Alphabetical order

* Adjust layout, remove green circle for blue selector

* Fix some inconsistencies

* Fix no inital selected profile

* Adjust appearance of edit button

* Adjust SaveManager

* Remove redundant warning dialog

* Make firmware avatar selector clearer

* View redesign again :hero_depressed:

* Consistency adjustments

* Adjust margins

* Make `UserProfileImageSelector` consistent

* Make `UserFirmwareAvatarSelector` consistent

* Fix long grid view selector

* Switch case

* Remove long selection bar

Handled in #4178

* Consistency

* Started dialog titles

* Fixes

* Remaining titles

* Update Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Fix build

* Hide UserRecoverer if no LostProfiles are found

* UserEditor Avatar Placeholder

* Watermark + locale adjustment

* Border radius

* Remove unnecessary styles

* Fix firmware avatar image order

* Cleanup `ColorPickerButton`

* Make `UserId` copy/paste able

* Make `FirmwareAvatarSelector` 6 images wide

* Make selection bar better

* Unsaved changes dialogue

* Fix indentation

* Remove extra check

* Address suggestions

* Reorganise

- Remove unused views
- Rename views to match convention
- Fix weird namespacing

* Update Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* UserRecovererView empty placeholder

* Update Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Models/UserProfile.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Controls/NavigationDialogHost.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Remove AddModel

* Update Ryujinx.Ava/Assets/Locales/en_US.json

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Fix bug

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-11 06:20:19 +01:00
Ac_K
cee667b491 Ava: Fixes Update count in heading (#4265) 2023-01-11 05:39:25 +01:00
gdkchan
94a64f2aea Remove textures from cache on unmap if not mapped and modified (#4211) 2023-01-11 01:53:56 +00:00
Ac_K
2355c2af62 ava: Generate Locale menu automatically (#4243)
Currently in `MenuMainBarView.axaml` we list all available languages and hardcode the language name with the language key.
It's a bit bad beause if we want to add a new language, we have to edit the `csproj` and the `axaml` with the translated language name and the language code.
I've put all translations in their respective locale files, add code into `MainMenuBarView` constructor to generate the menu automatically. Now we just have to edit the `csproj` if we want to add a new language.
2023-01-11 01:29:22 +01:00
gdkchan
5e0f8e8738 Implement JIT Arm64 backend (#4114)
* Implement JIT Arm64 backend

* PPTC version bump

* Address some feedback from Arm64 JIT PR

* Address even more PR feedback

* Remove unused IsPageAligned function

* Sync Qc flag before calls

* Fix comment and remove unused enum

* Address riperiperi PR feedback

* Delete Breakpoint IR instruction that was only implemented for Arm64
2023-01-10 19:16:59 -03:00
UltraHDR
d16288a2a8 Set LSApplicationCategoryType to games (#4257)
https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype
Makes it auto-add to the macOS Launchpad games folder
2023-01-10 21:49:46 +00:00
Isaac Marovitz
600f86dc7b Fix context menu locales (#4242) 2023-01-10 19:15:15 +01:00
Mary-nyan
7210c17c5e misc: Enforce LF (#4253)
Because we are building everything on Windows for release at the moment,
git default line ending to CRLF causing issues when packing the
Ryujinx.sh script.

This addresses this by enforcing all files to use LF via .gitattributes.
2023-01-10 19:00:14 +01:00
Ac_K
a16854e55a ava: Cleanup AppHost (#4240)
* ava: Cleanup AppHost

This PR cleaned up the AppHost file a bit (adding the infamous extra spaces to improve readability), resorting private vars, remove useless vars, and improve the code here and there, like the AudioBackend check.

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* Remove 'renderer"

* Revert currentTime

* revert if condition

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
2023-01-10 18:45:55 +01:00
TSRBerry
3e455a90a1 Ava: Add missing null check to ContentDialogHelper.ShowAsync() (#4248)
* ava: Add missing null check to ContentDialogHelper.ShowAsync()

* Replace "is not" with != operator

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-10 09:22:25 +01:00
TSRBerry
e4413542b2 Add command line arguments to override docked mode (#4239)
* Add command line args for docked mode

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-10 00:59:23 +01:00
TSRBerry
8c720783f5 linux: Fix packaging step for CI & Add Ryujinx.Headless.SDL2 to Ryujinx.sh (#4249) 2023-01-09 23:45:46 +00:00
TSRBerry
8734ea9dd4 Linux: Add Avalonia detection to Ryujinx.sh (#4224)
* Revert "ava: Fix regression caused by #4013 (#4222)"

This reverts commit b9f2a96595.

* linux: Detect Ryujinx.Ava and don't rename the Ryujinx.Ava assembly
2023-01-09 22:58:51 +01:00
gdkchan
c586e6d2b7 Replace tabs with spaces across the project (#4244)
* Replace tabs with spaces across the project

* Include AXAML files too
2023-01-09 22:58:29 +01:00
TSRBerry
3a4eeb77fe headless: Change window icon size to 48x48 (#4247) 2023-01-09 18:02:41 +00:00
TSRBerry
51b3953cfc [Headless] Add missing arguments & Fix typos (#4193)
* headless: Fix typos in command line options

* Remove nullable from command line options

Add EnableMacroHLE option
Add HideCursorOnIdle option

* headless: Adjust enable-ptc help text

* headless: Use switch statement instead of if-else chain

* headless: Improve formatting for long constructors

* headless: Remove discards from SDL_ShowCursor()

* headless: Add window icon

* Fix hiding cursor on idle

At least on Wayland, SDL2 doesn't produce any mouse motion events.

* Add new command line args: BaseDataDir and UserProfile

* headless: Read icon from embedded resource

* headless: Skip SetWindowIcon() on Windows if dll isn't present

* headless: Fix division by zero

* headless: Fix command line options not working correctly

* headless: Fix crash when viewing command line options

* headless: Load window icon bmp from memory

* Add comment to the workaround for SDL_LoadBMP_RW

* headless: Enable logging to file by default

* headless: Add 3 options for --hide-cursor

Replaces --disable-hide-cursor-on-idle
2023-01-09 04:55:37 +01:00
Ac_K
610eecc1c1 ava: Fixes regressions from refactoring (#4237)
* ava: Fix regressions from #4178

* Remove duplicated code

* real fix for right click menu

Co-Authored-By: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com>

* Remove ContentDialogOverlay

Co-authored-by: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com>
2023-01-09 03:37:20 +00:00
merry
492056abf6 Ava: Make Avalonia use our logging system (#4231)
* Ava: Make Avalonia use our logging system

* LoggerAdapter: Address review comments

* Update Ryujinx.Common/Logging/LogClass.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-09 04:32:20 +01:00
Isaac Marovitz
ee6e682ab4 Fix selection bar (#4236) 2023-01-09 03:36:11 +01:00
gdkchan
6f60e102a2 HIPC: Fix reply possibly also receiving one request (#4232) 2023-01-08 15:34:49 -03:00
Isaac Marovitz
eeb2af9953 Ava GUI: MainWindow Refactor (#4178)
* Fix redundancies

* Add back elses

* `MainWindow` Refactor

* Switch commands to `ReflectionBinding`

Not required in Ava 11

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Resolve issues

* Remove Ava 11 Fix

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix whitespace + other suggestions

* Move Vsync colours to `Styles.xaml`

* Remove catch all

* Use `switch` instead of `if`

* Update locale keys

* Use block-scoped namespaces

* Fix improper Ava api usage then

* Static PTC

* Fix `GridItemSelectorSize` with `ShowNames`

* Update for new About Window

* Add back search fix

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-08 18:46:25 +01:00
Ac_K
550747eac6 Horizon: Impl Prepo, Fixes bugs, Clean things (#4220)
* Horizon: Impl Prepo, Fixes bugs, Clean things

* remove ToArray()

* resultCode > status

* Remove old services

* Addresses gdkchan's comments and more cleanup

* Addresses Gdkchan's feedback 2

* Reorganize services, make sure service are loaded before guest

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* Create interfaces for lm and sm

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
2023-01-08 12:13:39 +00:00
merry
3ffceab1fb MainWindow: Vertically center SearchBox TextPresenter (#4223) 2023-01-07 16:01:53 +00:00
Mary-nyan
b9f2a96595 ava: Fix regression caused by #4013 (#4222)
Avalonia seems to not like when the artifact doesns't match the root namespace...

Address that by moving the binary to "Ryujinx" like we do on macOS build.
2023-01-07 12:24:21 +01:00
RMED24
cbaa845f5d Include a start.sh file with correct launch options (#4013)
* Include reference to start.sh to be bundled

* Add start.sh

* Fix silly mistake I made on windows-x64

* ... I cannot read properly

* Make same changes for avalonia csproj

* Remove notice from start.sh

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx/Ryujinx.csproj

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Ava/Ryujinx.Ava.csproj

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update distribution/linux/start.sh

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update distribution/linux/start.sh

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/Ryujinx.Ava.csproj

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.csproj

* Update Ryujinx.Ava.csproj

* Rename start.sh to Ryujinx.sh

* Update Ryujinx.csproj

* Update Ryujinx.Ava.csproj

* Update Ryujinx.Ava.csproj

* Update Ryujinx.Ava/Ryujinx.Ava.csproj

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Add `GDK_BACKEND` variable

* Update Ryujinx.Ava.csproj

* Update Program.cs

* Update Program.cs

* Update Ryujinx.sh

* Update Program.cs

* linux: Register mime types on launch

* Add DOTNET_EnableAlternateStackCheck=1 to desktop file

* linux: Add exclusion for RegisterMimeTypes for flathub builds

* Update logo path

* Cleanup Ryujinx.sh

* Fix typo in ReleaseInformation

* gha: Fix permissions for linux release binaries

* ava: Rename output assembly to Ryujinx

* Update mime database after installing new types

Wait until logging is available before registering mime types

* Copy mime types to output directory

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-01-07 09:06:13 +01:00
Isaac Marovitz
9e2681f2d7 Ava GUI: AboutWindow Refactor (#4196)
* Start `AboutWindow` refactor

* Redesign

* Update logos + ViewModel

* Fix GTK

* Cleanup usings

* Fix mismatched font size

* Update LocaleKeys

* Block scoped namespace

* Fix appearence on German

* Update Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Move version number up

* Move see all contributors button left

* Add a couple `\n`

* Tooltips

* Layout fix

* Update Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-07 01:29:18 +01:00
TSRBerry
81fae0d1a6 [hipc] Fix 'Unexpected result code Success returned' in Reply() (#4215)
* horizon: Add AbortOnFailureUnless()

* hipc: Replace AbortUnless() with AbortOnFailureUnless() in Reply()
2023-01-07 00:57:21 +01:00
Isaac Marovitz
38519f3b9a Ava GUI: SettingsWindow Refactor (#4177)
* Fix redundancies

* Add back elses

* Settings Refactor

* Fix Disposal functions

* Use `ReflectionBinding` instead of redundant funcs

* Ac_K suggestions

* Update Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update locale keys

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Use block-scoped namespaces

* Fix typo

* Make `TimeZone` internal again

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-01-07 00:35:21 +01:00
Mary-nyan
7f27aabbd0 chore: Update Ryujinx.SDL2-CS to 2.26.1 (#4199) 2023-01-06 17:52:25 +01:00
Ac_K
e876c43ce9 Misc; Remove duplicated entries and clean locale csproj (#4209) 2023-01-05 04:30:55 +01:00
Mary
8639245533 hle: Add safety measure around overflow in ScheduleFutureInvocation
Fix crash on Linux since 08831eecf7.
2023-01-05 01:55:27 +01:00
Ac_K
d6b86a6629 Readd Ryujinx.Ui.LocaleGenerator removed in #4188 (again) 2023-01-05 00:23:17 +01:00
Ac_K
8f2b7b5b8e Readd Ryujinx.Ui.LocaleGenerator removed in #4188 2023-01-05 00:20:47 +01:00
gdkchan
fc4b7cba2c Make PPTC state non-static (#4157)
* Make PPTC state non-static

* DiskCacheLoadState can be null
2023-01-05 00:01:44 +01:00
gdkchan
08831eecf7 IPC refactor part 3+4: New server HIPC message processor (#4188)
* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization

* Make types match on calls to AlignUp/AlignDown

* Formatting

* Address some PR feedback

* Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations

* Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory

* Implement EventType

* Address more PR feedback

* Log request processing errors since they are not normal

* Rename waitable to multiwait and add missing lock

* PR feedback

* Ac_K PR feedback
2023-01-04 23:15:45 +01:00
Ac_K
c6a139a6e7 New Crowdin updates (#3985)
* New translations en_US.json (French)

* New translations en_US.json (Portuguese, Brazilian)

* New translations en_US.json (French)

* New translations en_US.json (Portuguese, Brazilian)

* Delete fr_FR.json

* Delete pt_BR.json

* New translations en_US.json (French)

* New translations en_US.json (Spanish)

* New translations en_US.json (German)

* New translations en_US.json (Greek)

* New translations en_US.json (Italian)

* New translations en_US.json (Japanese)

* New translations en_US.json (Korean)

* New translations en_US.json (Polish)

* New translations en_US.json (Russian)

* New translations en_US.json (Turkish)

* New translations en_US.json (Ukrainian)

* New translations en_US.json (Chinese Simplified)

* New translations en_US.json (Chinese Traditional)

* New translations en_US.json (Portuguese, Brazilian)

* New translations en_US.json (German)
2023-01-04 22:44:51 +01:00
Emmanuel Hansen
02714a1291 Avalonia - Add source generator for locale items (#3999)
* Add source generator for locale keys

* use locale keys in Ui subdir
2023-01-03 19:45:08 +01:00
Mary-nyan
09c9686498 misc: Use official names for NVDEC registers (#4192)
* misc: Uses official names for NVDEC registers

* Address gdkchan's comment

* Address comments
2023-01-02 14:48:46 +00:00
Mary-nyan
b6614c6ad5 chore: Update tests dependencies (#3978)
* chore: Update tests dependencies

* Apply TSR Berry suggestion to add a GC.SuppressFinalize in MemoryBlock.cs

* Ensure we wait for the test thread to be dead on PartialUnmap

* Use platform attribute for os specific tests

* Make P/Invoke methods private

* Downgrade NUnit3TestAdapter to 4.1.0

* test: Disable warning about platform compat for ThreadLocalMap()

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-01-01 17:35:29 +01:00
Emmanuel Hansen
b1d4b174a6 fix typo in left joycon sl binding (#4195) 2023-01-01 15:46:02 +01:00
Andrew Glaze
2b23463daa Filter hidden game files from the Game List (#4051)
* Filter “._” files from the game list

* Filter all hidden files from the game list

* Fix style

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* merge OR expression into a pattern

* migrate from GetFiles/Directories to Enumerate

* Remove GetFilesInDirectory()

* Update Ryujinx.Ui.Common/App/ApplicationLibrary.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* add error handeling

* code cleanup

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 16:52:30 +01:00
gdkchan
9dfe81770a Use vector outputs for texture operations (#3939)
* Change AggregateType to include vector type counts

* Replace VariableType uses with AggregateType and delete VariableType

* Support new local vector types on SPIR-V and GLSL

* Start using vector outputs for texture operations

* Use vectors on more texture operations

* Use vector output for ImageLoad operations

* Replace all uses of single destination texture constructors with multi destination ones

* Update textureGatherOffsets replacement to split vector operations

* Shader cache version bump

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 16:09:34 +01:00
Luminoso-256
52c115a1f8 HLE: Add basic stubs to get Labo VR booting to title screen. (#4007)
* HLE: Add basic stubs to get Labo VR booting to title screen.

* Address code review

* Apply suggestions from code review (pt. 2)

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Apply suggestions from code review (pt. 3)

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Formatting: final batch?

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Address last? bit of formatting

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 15:57:35 +01:00
riperiperi
e20abbf9cc Vulkan: Don't flush commands when creating most sync (#4087)
* Vulkan: Don't flush commands when creating most sync

When the WaitForIdle method is called, we create sync as some internal GPU method may read back written buffer data. Some games randomly intersperse compute dispatch into their render passes, which result in this happening an unbounded number of times depending on how many times they run compute.

Creating sync in Vulkan is expensive, as we need to flush the current command buffer so that it can be waited on. We have a limited number of active command buffers due to how we track resource usage, so submitting too many command buffers will force us to wait for them to return to the pool.

This PR allows less "important" sync (things which are less likely to be waited on) to wait on a command buffer's result without submitting it, instead relying on AutoFlush or another, more important sync to flush it later on.

Because of the possibility of us waiting for a command buffer that hasn't submitted yet, any thread needs to be able to force the active command buffer to submit. The ability to do this has been added to the backend multithreading via an "Interrupt", though it is not supported without multithreading.

OpenGL drivers should already be doing something similar so they don't blow up when creating lots of sync, which is why this hasn't been a problem for these games over there.

Improves Vulkan performance on Xenoblade DE, Pokemon Scarlet/Violet, and Zelda BOTW (still another large issue here)

* Add strict argument

This is technically a separate concern from whether the sync is a host syncpoint.

* Remove _interrupted variable

* Actually wait for the invoke

This is required by AMD GPUs, and also may have caused some issues on other GPUs.

* Remove unused using.

* I don't know why it added these ones.

* Address Feedback

* Fix typo
2022-12-29 15:39:04 +01:00
Isaac Marovitz
76671d63d4 Ava GUI: Restructure Ryujinx.Ava (#4165)
* Restructure `Ryujinx.Ava`

* Stylistic consistency

* Update Ryujinx.Ava/UI/Controls/UserEditor.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/UserEditor.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/UserSelector.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Fix redundancies

* Remove redunancies

* Add back elses

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-29 15:24:05 +01:00
TSRBerry
3d1a0bf374 [Headless] Fix for not receiving any SDL events on Linux (#4182)
* headless: Fix SDL events not working

* Add nuget.config
2022-12-29 15:09:18 +01:00
Mary-nyan
c20f3fbebd haydn: Add support for PCMFloat, PCM32 and PCM8 conversions (#4100)
* haydn: Add support for PCMFloat, PCM32 and PCM8 conversions

This adds support in the compatibility layer for other sample format
than PCM16.

This should help extends compatibility with soundio on devices that
doesn't expose PCM16.

I ommited PCM24 conversion for now as it's not simplest of all.

* Address TSRBerry's comment

* Address comments

* Fix conversion issue and clean up saturation usage

* Revert saturation changes

* Address gdkchan's comment
2022-12-28 19:34:30 -03:00
Berkan Diler
0d3b82477e Use new ArgumentNullException and ObjectDisposedException throw-helper API (#4163) 2022-12-27 20:27:11 +01:00
riperiperi
470be03c2f GPU: Add fallback when 16-bit formats are not supported (#4108)
* Add conversion for 16 bit RGBA formats (not supported in Rosetta)

* Rebase fix

Rebase fix

* Forgot to remove this

* Fix RGBA16 format conversion

* Add RGBA4 -> RGBA8 conversion

* Handle host stride alignment

* Address Feedback Part 1

* Can't count

* Don't zero out rgb when alpha is 0

* Separate RGBA4 and 5-bit component formats

Not sure of a better way to name them...

* Add A1B5G5R5 conversion

* Put this in the right place.

* Make format naming consistent for capabilities

* Change method names
2022-12-26 15:50:27 -03:00
Hunter
c963b3c804 Added Generic Math to BitUtils (#3929)
* Generic Math Update

Updated Several functions in Ryujinx.Common/Utilities/BitUtils to use generic math

* Updated BitUtil calls

* Removed Whitespace

* Switched decrement

* Fixed changed method calls.

The method calls were originally changed on accident due to me relying too much on intellisense doing stuff for me

* Update Ryujinx.Common/Utilities/BitUtils.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-26 14:11:05 +00:00
gnisman
a4fdfb5f94 bsd::RecvFrom: verify output buffer size before writing socket address (#4135)
* bsd::RecvFrom: Ryujinx does not verify output buffer size before writing socket address

* Calculate the size of BsdSockAddr

* use bsdSockAddr variable

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* set errno to ENOMEM in case we can't write the address to memory

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-26 13:49:26 +00:00
Berkan Diler
37d27c4c99 Some minor cleanups and optimizations (#4174)
* Replace Array.Clear(x, 0, x.Length) with Array.Clear(x)

* Use DateTime.UnixEpoch field

* Replace SHA256.ComputeHash calls with static SHA256.HashData call

More performant and avoids the need to initialize a SHA256 instance.
2022-12-24 14:30:39 -03:00
gdkchan
f906eb06c2 Implement a software ETC2 texture decoder (#4121)
* Implement a software ETC2 texture decoder

* Fix output size calculation for non-2D textures

* Address PR feedback
2022-12-21 20:39:58 -03:00
gdkchan
219f63ff4e Fix CPU FCVTN instruction implementation (slow path) (#4159)
* Fix CPU FCVTN instruction implementation (slow path)

* PPTC version bump
2022-12-21 23:05:58 +00:00
gdkchan
1cca3e99ab GPU: Force rebind when pool changes (#4129) 2022-12-21 17:35:28 -03:00
Ccl
55a23e5ec8 make UI display correct content in Chinese (#4155) 2022-12-21 17:20:37 -03:00
Mary-nyan
479d1fd8b0 hle: Handle GPU profiler and debugger device path correctly (#4138)
This fix a warning on "慟哭そして…" by handling correctly the debug mode
flag.

When debug mode isn't enabled, opening /dev/nvhost-dbg-gpu or /dev/nvhost-prof-gpu should fail with a not implemented error code.

This implement this behaviour and also define stubbed interfaces for
completness.
2022-12-21 18:23:11 +00:00
gdkchan
cb70e7bb30 Fix DrawArrays vertex buffer size (#4141) 2022-12-21 19:08:12 +01:00
riperiperi
c200a7b7c6 ARMeilleure: Hash _data pointer instead of value for Operand (#4156)
I noticed a weirdly high cost for dictionary accesses from MarkLabel etc. Turns out that the hash code was always the same for labels, so the whole point of having a dictionary was missed and it was putting everything in the same bucket. I made it always hash the _data pointer as that's a good source of identifiable and "random" data.
2022-12-21 02:28:18 +01:00
Emmanuel Hansen
6268170a10 fix sw kbd row collision (#4144) 2022-12-19 15:09:36 -03:00
gdkchan
ee0f9b03a4 Eliminate zero-extension moves in more cases on 32-bit games (#4140)
* Eliminate zero-extension moves in more cases on 32-bit games

* PPTC version bump

* Revert X86Optimizer changes
2022-12-19 14:45:58 -03:00
gdkchan
f93c5f006a Revert "ARMeilleure: Add initial support for AVX512(EVEX encoding) (#3663)" (#4145)
This reverts commit 295fbd0542.
2022-12-18 20:21:10 -03:00
Wunk
295fbd0542 ARMeilleure: Add initial support for AVX512(EVEX encoding) (#3663)
* ARMeilleure: Add AVX512{F,VL,DQ,BW} detection

Add `UseAvx512Ortho` and `UseAvx512OrthoFloat` optimization flags as
short-hands for `F+VL` and `F+VL+DQ`.

* ARMeilleure: Add initial support for EVEX instruction encoding

Does not implement rounding, or exception controls.

* ARMeilleure: Add `X86Vpternlogd`

Accelerates the vector-`Not` instruction.

* ARMeilleure: Add check for `OSXSAVE` for AVX{2,512}

* ARMeilleure: Add check for `XCR0` flags

Add XCR0 register checks for AVX and AVX512F, following the guidelines
from section 14.3 and 15.2 from the Intel Architecture Software
Developer's Manual.

* ARMeilleure: Increment InternalVersion

* ARMeilleure: Remove redundant `ReProtect` and `Dispose`, formatting

* ARMeilleure: Move XCR0 procedure to GetXcr0Eax

* ARMeilleure: Add `XCR0` to `FeatureInfo` structure

* ARMeilleure: Utilize `ReadOnlySpan` for Xcr0 assembly

Avoids an additional allocation

* ARMeilleure: Formatting fixes
2022-12-18 16:46:13 -03:00
Mary-nyan
d7310d7a1c hle: Fix wrong conversion in UserPresence.ToString (#4142)
This fixes an error from #3805 that caused a wrong conversion of ``AppKeyValueStorage`` to string.
As that information isn't really relevant without appropriate parsing, it was removed from ``ToString``.

This should get ride of "bell warning" in Mario Kart 8 when entering Time Trials.
2022-12-18 14:23:19 +00:00
dependabot[bot]
8c50943a2e nuget: bump Microsoft.NET.Test.Sdk from 17.4.0 to 17.4.1 (#4137)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0 to 17.4.1.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0...v17.4.1)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-17 01:19:04 +01:00
gdkchan
ec4cd57ccf Implement another non-indexed draw method on GPU (#4123) 2022-12-16 12:06:38 -03:00
riperiperi
5a085cba0f GPU: Fix layered attachment write (#4131)
Fixes a regression caused by #4003 where the code that writes `_vtgWritesRtLayer` was removed, breaking the crowd in mario strikers.
2022-12-16 09:40:01 -03:00
TSRBerry
1a1d33a018 ava: Fix invisible swkbd applet on Linux (#4130) 2022-12-16 02:40:55 +01:00
Isaac Marovitz
0fbcd630bc Replace DllImport usage with LibraryImport (#4084)
* Replace usage of `DllImport` with `LibraryImport`

* Mark methods as `partial`

* Marshalling

* More `partial` & marshalling

* More `partial` and marshalling

* More partial and marshalling

* Update GdiPlusHelper to LibraryImport

* Unicorn

* More Partial

* Marshal

* Specify EntryPoint

* Specify EntryPoint

* Change GlobalMemoryStatusEx to LibraryImport

* Change RegisterClassEx to LibraryImport

* Define EntryPoints

* Update Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Move return mashal

* Remove calling convention specification

* Remove calling conventions

* Update Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx/Modules/Updater/Updater.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Ava/Modules/Updater/Updater.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Mary-nyan <thog@protonmail.com>
2022-12-15 18:07:31 +01:00
gdkchan
f4d731ae20 Fix NRE when loading Vulkan shader cache with Vertex A shaders (#4124) 2022-12-15 17:52:12 +01:00
Isaac Marovitz
8ac53c66b4 Remove Half Conversion (#4106)
* Remove HalfConversion

* Update `CodeGenVersion`
2022-12-14 21:13:23 -03:00
Georg Lehmann
0f50de72be Vulkan: enable VK_EXT_custom_border_color features (#4116)
* Vulkan: enable VK_EXT_custom_border_color features

radv only create the border color bo if this feature is enabled, so it crashed when creating samplers with custom border colors
Fixes #4072
Fixes #3993

* Address gdkchan's comment

Co-authored-by: Mary <mary@mary.zone>
2022-12-14 20:53:33 -03:00
TSRBerry
df758eddd1 Bsd: Add support for dns_mitm (#4102)
* bsd: Add dns_mitm from Atmosphère

related AMS files:
- https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp
- https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp

* Remove debug logging and adjust redirect message

* Improve formatting

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Replace Initialize with instance property

* bsd: dns_mitm - Ignore empty lines

* bsd: Mark _mitmHostEntries as readonly

* bsd: Initialize Aliases when returning IpHostEntry

Fixes NullReferenceException

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-12 18:04:08 +01:00
Mary-nyan
5f32a8ed94 misc: Update to Ryujinx.Graphics.Nvdec.Dependencies 5.0.1-build13 (#4097)
Fix packaging issues on macOS related to an unsatisfied dependency on libX11
2022-12-12 16:19:46 +01:00
Andrey Sukharev
535fbec675 Use NuGet Central Package Management to manage package versions solution-wise (#4095) 2022-12-12 16:03:10 +01:00
Mary-nyan
6fe88115a3 misc: Some fixes to the updaters (#4092)
This was meant to be only an upgrade of how we set unix permission in
the updater to use .NET 7 new APIs, but I end up finding bugs along the
way.

Changelog:
- Remove direct usage of chmod to use File.SetUnixFileMode.
- Fix command line being broken when updating (#3744) but on
  Ryujinx.Ava.
- Makes Ryujinx.Ava updater fallback to Ryujinx executable if current
  name isn't found.
- Make permission setter function more generic.
2022-12-12 15:17:22 +01:00
&Olga
475fa4d390 Fix "UI" abbreviation being miscapitalized (#4093) 2022-12-12 15:11:55 +01:00
Andrey Sukharev
edf7e628ca Use method overloads that support trimming. Mark some types to be trimming friendly (#4083)
* Use method overloads that support trimming. Mark some types to be trimming friendly

* Use generic version of marshalling method
2022-12-12 15:10:05 +01:00
TSRBerry
ba5c0cf5d8 Bsd: Implement Select (#4017)
* bsd: Add gdkchan's Select implementation

Co-authored-by: TSRBerry <20988865+tsrberry@users.noreply.github.com>

* bsd: Fix Select() causing a crash with an ArgumentException

.NET Sockets have to be used for the Select() call

* bsd: Make Select more generic

* bsd: Adjust namespaces and remove unused imports

* bsd: Fix NullReferenceException in Select

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-12 14:59:31 +01:00
Mary-nyan
403e67d983 audio: Rewrite SoundIo bindings (#4088)
* audio: Rewrite SoundIo bindings

This rewrite SoundIo bindings to be safer and not a pedantic autogenerated mess.

* Address comments

* Switch DllImport to LibraryImport

* Address gdkchan's comment
2022-12-11 00:57:01 +01:00
Isaac Marovitz
c6f1908e0f Fix Lambda Explicit Type Specification Warning (#4090) 2022-12-10 22:12:51 +01:00
Isaac Marovitz
851d81d24a Fix Redundant Qualifer Warnings (#4091)
* Fix Redundant Qualifer Warnings

* Remove unnecessary using
2022-12-10 21:21:13 +01:00
gdkchan
459c4caeba Fix HasUnalignedStorageBuffers value when buffers are always unaligned (#4078) 2022-12-09 17:41:40 -03:00
Mary
539b22ef7b Add explicit dependency on System.Drawing.Common on Ryujinx.Ava to workaround triming bugs 2022-12-09 20:12:09 +01:00
Mary-nyan
872f036d64 misc: Remove dependency on System.Drawing.Common (#4082)
We only used it in one spot for DPI scaling factor.

This implements the same behaviour using gdiplus.

This remove 700KB of dependency to download and around 170KB unpacked.
2022-12-09 18:00:53 +01:00
TSRBerry
dca96122bf gha: Add concurrency restriction on release workflow (#4081) 2022-12-09 16:15:28 +00:00
Mary-nyan
e752959109 misc: Update Ryujinx.Graphics.Nvdec.Dependencies to 5.0.1-build12 (#4080)
This adds support for Linux x64, macOS x64 and macOS arm64.
2022-12-09 15:46:07 +01:00
Ac_K
cf01664698 ava: Restyle the Status Bar (#4048) 2022-12-09 15:21:54 +01:00
dependabot[bot]
b283a4adcd nuget: bump CommandLineParser from 2.8.0 to 2.9.1 (#4058)
Bumps [CommandLineParser](https://github.com/commandlineparser/commandline) from 2.8.0 to 2.9.1.
- [Release notes](https://github.com/commandlineparser/commandline/releases)
- [Changelog](https://github.com/commandlineparser/commandline/blob/master/CHANGELOG.md)
- [Commits](https://github.com/commandlineparser/commandline/compare/2.8.0...v2.9.1)

---
updated-dependencies:
- dependency-name: CommandLineParser
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-09 14:16:22 +01:00
gdkchan
8428bb6541 Fix shader FSWZADD instruction (#4069)
* Fix shader FSWZADD instruction

* Shader cache version bump
2022-12-08 14:08:07 -03:00
gdkchan
9a0330f7f8 Shader: Implement PrimitiveID (#4067)
* Shader: Implement PrimitiveID

* Shader cache version bump
2022-12-08 10:55:03 +01:00
IverCoder
57fc996337 Fix inconsistent capitalization (#4070) 2022-12-08 09:32:24 +00:00
Ac_K
1f3b860f06 acc: Stub CheckNetworkServiceAvailabilityAsync (#4052) 2022-12-07 23:19:22 +01:00
dependabot[bot]
abe3c02ab4 nuget: bump DynamicData from 7.12.8 to 7.12.11 (#4059)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.12.8 to 7.12.11.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.12.8...7.12.11)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-07 23:07:23 +01:00
dependabot[bot]
45b417b2b4 nuget: bump NUnit from 3.12.0 to 3.13.3 (#4060)
Bumps [NUnit](https://github.com/nunit/nunit) from 3.12.0 to 3.13.3.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/v3.13.3/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v3.12...v3.13.3)

---
updated-dependencies:
- dependency-name: NUnit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-07 22:02:31 +01:00
TSRBerry
d076339e3e Add Ryujinx license file to builds (#4057) 2022-12-07 18:20:18 +01:00
dependabot[bot]
837836431d nuget: bump System.Drawing.Common from 6.0.0 to 7.0.0 (#4024)
Bumps [System.Drawing.Common](https://github.com/dotnet/runtime) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.0...v7.0.0)

---
updated-dependencies:
- dependency-name: System.Drawing.Common
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-07 16:24:34 +00:00
Ac_K
9f555db5cd hle: Do not add disabled AoC item to the list (#4044)
* hle: Do not add disabled AoC item to the list

We currently add all AoC items to a list in `ContentManager` and the enable check is only done when FS service ask for the data. Which is wrong. It causes an issue in MK8D which doesn't boot even if you have disabled a not updated DLC.

I've fixed it by not adding the disabled AoC item to the list, I've removed some duplicate code too.
There is still an edge case because we currently don't check the AoC Item version, but that should be fixed later since now MK8D throw an error if the DLC isn't updated.

* remove useless "enabled"
2022-12-07 15:00:28 +01:00
Isaac Marovitz
bf7fa60dfc Fix struct layout packing (#4039) 2022-12-07 02:04:01 +00:00
Ac_K
752b93d3b7 gtk: Fixes warnings about obsolete components (#4049)
* gtk: Fixes warnings about obsolete components

* remove wrong using
2022-12-07 01:49:37 +01:00
riperiperi
f23b2878cc Shader: Add fallback for LDG from "ube" buffer ranges. (#4027)
We have a conversion from LDG on the compute shader to a special constant buffer binding that's used to exceed hardware limits on compute, but it was only running if the byte offset could be identified. The fallback that checks all of the bindings at runtime only checks the storage buffers.

This PR adds checking ube ranges to the LoadGlobal fallback. This extends the changes in #4011 to only check ube entries which are accessed by the shader.

Fixes particles affected by the wind in The Legend of Zelda: Breath of the Wild. May fix other weird issues with compute shaders in some games.

Try a bunch of games and drivers to make sure they don't blow up loading constants willynilly from searchable buffers.
2022-12-06 23:15:44 +00:00
riperiperi
e211c3f00a UI: Add Metal surface creation for MoltenVK (#3980)
* Initial implementation of metal surface across UIs

* Fix SDL2 on windows

* Update Ryujinx/Ryujinx.csproj

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Address Feedback

Co-authored-by: Mary-nyan <thog@protonmail.com>
2022-12-06 19:00:25 -03:00
dependabot[bot]
d3709a753f nuget: bump XamlNameReferenceGenerator from 1.4.2 to 1.5.1 (#4026)
Bumps [XamlNameReferenceGenerator](https://github.com/avaloniaui/Avalonia.NameGenerator) from 1.4.2 to 1.5.1.
- [Release notes](https://github.com/avaloniaui/Avalonia.NameGenerator/releases)
- [Commits](https://github.com/avaloniaui/Avalonia.NameGenerator/compare/1.4.2...1.5.1)

---
updated-dependencies:
- dependency-name: XamlNameReferenceGenerator
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 19:00:08 +01:00
dependabot[bot]
ab676d58ea nuget: bump System.IdentityModel.Tokens.Jwt from 6.25.0 to 6.25.1 (#4043)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.25.0 to 6.25.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.25.0...6.25.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 18:01:21 +01:00
Ac_K
2372c194f1 ava: Cleanup Input classes (#4042)
* ava: Cleanup Input classes

This PR just cleanup all Input classes for consistencies.

* Addresses TSRBerry's feedback
2022-12-06 15:32:14 +00:00
Mary-nyan
40311310d1 amadeus: Add missing compressor effect from REV11 (#4010)
* amadeus: Add missing compressor effect from REV11

This was in my reversing notes but seems I completely forgot to
implement it

Also took the opportunity to simplify the Limiter effect a bit.

* Remove some outdated comment

* Address gdkchan's comments
2022-12-06 15:04:25 +01:00
gdkchan
dde9bb5c69 Fix storage buffer access when match fails (#4037)
* Fix storage buffer access when match fails

* Shader cache version bump
2022-12-06 03:36:54 +00:00
MetrosexualGarbodor
266338a7c9 Change default Vsync toggle hotkey to F1 instead of Tab (#3995) 2022-12-06 02:09:26 +00:00
dependabot[bot]
90156eea4c nuget: bump Microsoft.CodeAnalysis.CSharp from 4.2.0 to 4.4.0 (#4025)
Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.2.0 to 4.4.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/compare/v4.2.0...Visual-Studio-2019-Version-16.0-Preview-4.4)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 01:51:33 +00:00
Isaac Marovitz
071c01c235 Fix Sorting Regression (#4032)
* Fix sorting regression + Remove unsued sort

* Fix GTK

* Attempt 2 to fix GTK

* Whoopsie

* Fix whitspace
2022-12-06 01:40:06 +00:00
gdkchan
de06ffb0f7 Fix shaders with global memory access from unknown locations (#4029)
* Fix shaders with global memory access from unknown locations

* Shader cache version bump
2022-12-06 01:09:24 +00:00
Andrew Glaze
8a7de35e3f Update 'OpenGL Log Level' to 'Graphics Backend Log Level' (#3996)
* Update 'OpenGL Log Level' to 'Graphics Backend Log Level'

* update other locals and change keys
2022-12-06 00:48:41 +00:00
Isaac Marovitz
121296834a Ava GUI: Several UI Fixes (#3991)
* Fix accessability violations in ListView

* Use accent colour for favourite star

* Hide progress bar when its done

* App Data Formating

- Added space before storage unit
- Changed so minutes have 0 decimals, and hours and days have 1

* Fix theming

* Fix mismatched corner radius

* Fix acceability violations in GridView

* More consistency between Grid and List View

* Fix margin

* Let whitespace defocus controls
2022-12-05 22:04:18 +00:00
gdkchan
bbb24d8c7e Restrict shader storage buffer search when match fails (#4011)
* Restrict storage buffer search when match fails

* Shader cache version bump
2022-12-05 19:11:32 +00:00
Andrey Sukharev
4da44e09cb Make structs readonly when applicable (#4002)
* Make all structs readonly when applicable. It should reduce amount of needless defensive copies

* Make structs with trivial boilerplate equality code record structs

* Remove unnecessary readonly modifiers from TextureCreateInfo

* Make BitMap structs readonly too
2022-12-05 14:47:39 +01:00
Mary-nyan
ae13f0ab4d misc: Fix obsolete warnings in Ryujinx.Graphics.Vulkan (#4020)
Was caused by some merges after the Silk.NET update
2022-12-05 12:57:11 +00:00
dependabot[bot]
a2a35f1be6 nuget: bump Microsoft.NET.Test.Sdk from 16.8.0 to 17.4.0 (#3900)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.8.0 to 17.4.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v16.8.0...v17.4.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-05 08:17:40 +01:00
Shane Slattery
aedfadaaf7 Add InfoType.MesosphereCurrentProcess (#3792)
* Add InfoType.MesosphereCurrentProcess

* Make outHandle inlined

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-04 19:46:02 +00:00
Ethan Page
5c0fb0cec3 ui: Disallow checking for updates while emulation active (#3886)
* Disallow updating while game is running

* Reflected change on Avalonia

* Git has gone wonky

* Fix accidental indent
2022-12-04 20:17:11 +01:00
gdkchan
17a1cab5d2 Allow SNorm buffer texture formats on Vulkan (#3957)
* Allow SNorm buffer texture formats on Vulkan

* Shader cache version bump
2022-12-04 15:36:03 -03:00
gdkchan
73aed239c3 Implement non-MS to MS copies with draws (#3958)
* Implement non-MS to MS copies with draws, simplify MS to non-MS copies and supports any host sample count

* Remove unused program
2022-12-04 15:07:11 -03:00
riperiperi
9ac66336a2 GPU: Use lazy checks for specialization state (#4004)
* GPU: Use lazy checks for specialization state

This PR adds a new class, the SpecializationStateUpdater, that allows elements of specialization state to be updated individually, and signal the state is checked when it changes between draws, instead of building and checking it on every draw. This also avoids building spec state when

Most state updates have been moved behind the shader state update, so that their specialization state updates make it in before shaders are fetched.

Downside: Fields in GpuChannelGraphicsState are no longer readonly. To counteract copies that might be caused this I pass it as `ref` when possible, though maybe `in` would be better? Not really sure about the quirks of `in` and the difference probably won't show on a benchmark.

The result is around 2 extra FPS on SMO in the usual spot. Not much right now, but it will remove costs when we're doing more expensive specialization checks, such as fragment output type specialization for macos. It may also help more on other games with more draws.

* Address Feedback

* Oops
2022-12-04 18:41:17 +01:00
riperiperi
4965681e06 GPU: Swap bindings array instead of copying (#4003)
* GPU: Swap bindings array instead of copying

Reduces work on UpdateShaderState. Now the cost is a few reference moves for arrays, rather than copying data.

Downside: bindings arrays are no longer readonly.

* Micro optimisation

* Add missing docs

* Address Feedback
2022-12-04 18:18:40 +01:00
Andrey Sukharev
3868a00206 Use source generated regular expressions (#4005) 2022-12-04 00:43:23 +00:00
mageven
933e5144a9 Query Available RAM on macOS (#4000) 2022-12-04 00:25:07 +00:00
Ac_K
73a42c85c4 Add crowdin badge and information in readme (#3990)
* Add crowdin bagde and informations in readme.

* Update README.md

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update README.md

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-02 23:57:38 +01:00
Ac_K
39ba11054b Update Crowdin configuration file 2022-12-02 15:22:21 +01:00
Ac_K
c250e3392c Fix using in Ava 2022-12-02 15:08:57 +01:00
Ac_K
e56b069081 Update Crowdin configuration file 2022-12-02 14:54:56 +01:00
merry
204c031fef SDL2Driver: Invoke dispatcher on main thread (#3818) 2022-12-02 14:37:22 +01:00
Emmanuel Hansen
d9053bbe37 Avalonia - Save Manager (#3476)
* Add save manager to account selector

* add fallback to app metadata for titlename if app is not in gamelist

* Allow recovering lost accounts
2022-12-02 13:16:43 +00:00
Mary-nyan
c25e8427aa amadeus: Fix wrong SendCommands logic (#3969)
* amadeus: Fix wrong SendCommands logic

Might help with audio desync, might cause audio stutters, will see!

* Address gdkchan's comment
2022-12-02 13:01:19 +00:00
Isaac Marovitz
21a081b185 Add back locales removed in #3955 (#3983)
Add back `SettingsButtonSave` & `SettingsButtonClose` removed in #3955

Fixes #3982
2022-12-02 12:46:18 +00:00
Isaac Marovitz
b540ea80d1 Ava GUI: Make Dialogue More Intuitive (#3955)
* Adjust button position and locales

* Shortcuts + Highlight default action

* Update Locales - Corrections Welcome

* Move `Apply` button back to right side

* OS Reactive Button layout

* Fix reversed boolean :)

* Fix accented button styling
2022-12-02 03:31:21 +01:00
Mary
d692a9b83e Revert "nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 (#3976)"
This reverts commit 9677ddaa5d.

SixLabors.ImageShar switched to a shady and vague license starting with 2.x
without mentioning it on their changelog.

As a result we are staying on 1.x (licensed under Apache-2) and will
seak an alternative package.
2022-12-01 23:06:55 +01:00
dependabot[bot]
9677ddaa5d nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3 (#3976)
* nuget: bump SixLabors.ImageSharp from 1.0.4 to 2.1.3

Bumps [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) from 1.0.4 to 2.1.3.
- [Release notes](https://github.com/SixLabors/ImageSharp/releases)
- [Commits](https://github.com/SixLabors/ImageSharp/compare/v1.0.4...v2.1.3)

---
updated-dependencies:
- dependency-name: SixLabors.ImageSharp
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update for 2.x changes

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mary <mary@mary.zone>
2022-12-01 21:54:41 +00:00
Mary-nyan
ce92e8cd04 chore: Update Silk.NET to 2.16.0 (#3953) 2022-12-01 19:11:56 +01:00
Isaac Marovitz
456fc04007 Better SDL2 Audio Init Error Logging (#3967)
* Better SDL2 Audio Init Error Logging

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update SDL2HardwareDeviceDriver.cs

* Update SDL2HardwareDeviceDriver.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-01 17:53:13 +01:00
riperiperi
458452279c GPU: Track buffer migrations and flush source on incomplete copy (#3952)
* Track buffer migrations and flush source on incomplete copy

Makes sure that the modified range list is always from the latest iteration of the buffer, and flushes earlier iterations of a buffer if the data has not been migrated yet.

* Cleanup 1

* Reduce cost for redundant signal checks on Vulkan

* Only inherit the range list if there are pending ranges.

* Fix OpenGL

* Address Feedback

* Whoops
2022-12-01 16:30:13 +01:00
Mary-nyan
817b89767a infra: Add distribution files for macOS (#3934)
This upstream macOS packing and distribution files
2022-12-01 14:08:43 +01:00
TSRBerry
3fb583c98c Avalonia: Clean up leftover RenderTimer & Fix minimum and initial window size (#3935)
* ava: Cleanup RenderTimer

* ava: Remove ContentControl from RendererHost

* ava: Remove unused actual scale factor

* ava: Enable UseGpu for Linux

* ava: Set better initial size & Scale the window properly

* ava: Realign properties

* ava: Use explicit type & specify where the note applies
2022-11-30 23:34:25 +01:00
dependabot[bot]
d2686e0a5b nuget: bump DiscordRichPresence from 1.0.175 to 1.1.3.18 (#3965)
Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.0.175 to 1.1.3.18.
- [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases)
- [Commits](https://github.com/Lachee/discord-rpc-csharp/commits)

---
updated-dependencies:
- dependency-name: DiscordRichPresence
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-30 21:58:45 +00:00
gdkchan
4905101df1 Remove shader dependency on SPV_KHR_shader_ballot and SPV_KHR_subgroup_vote extensions (#3943)
* Remove shader dependency on SPV_KHR_shader_ballot and SPV_KHR_subgroup_vote extensions

* Shader cache version bump
2022-11-30 18:24:15 -03:00
gdkchan
8750b90a7f Ensure that vertex attribute buffer index is valid on GPU (#3942)
* Ensure that vertex attribute buffer index is valid on GPU

* Remove vertex buffer validation code from OpenGL

* Remove some fields that are no longer necessary
2022-11-30 18:06:40 -03:00
dependabot[bot]
af01100050 nuget: bump System.Management from 6.0.0 to 7.0.0 (#3949)
Bumps [System.Management](https://github.com/dotnet/runtime) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.0...v7.0.0)

---
updated-dependencies:
- dependency-name: System.Management
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-29 23:30:33 +01:00
EmulationFanatic
c0821fee1f Update README.MD (#3946)
Updates the readme to include usage of Metal via MoltenVK, updated game compatibility statistics.
2022-11-29 17:20:26 +01:00
merry
a5c2aead67 ConcurrentBitmap: Use Interlocked Or/And (#3937) 2022-11-29 13:47:57 +00:00
Mary-nyan
d41c95dcff chore: Update OpenTK to 4.7.5 (#3944) 2022-11-29 13:32:40 +00:00
TSRBerry
fbf2b09706 ava: Make dialogs using an overlay window work on Linux (#3938) 2022-11-29 06:33:46 +01:00
riperiperi
1fc0f569de GPU: Always draw polygon topology as triangle fan (#3932)
Polygon topology wasn't really supported and would only work on OpenGL on drivers that haven't removed it. As an alternative, this PR makes all cases of polygon topology use triangle fan. The topology type and transform feedback type have not been changed, as I don't think geo shader/tfb should be used with polygons.

The OpenGL spec states:
Only convex polygons are guaranteed to be drawn correctly by the GL.

For convex polygons, triangle fan is equivalent to polygon. I imagine this is probably how it works on device, as this get-out-of-jail-free card is too enticing to pass up.

This fixes the stat display in Pokemon S/V.
2022-11-28 19:18:22 -03:00
Mary-nyan
dff138229c amadeus: Fixes and initial 15.0.0 support (#3908)
* amadeus: Allow OOB read of GC-ADPCM coefficients

Fixes "Ninja Gaiden Sigma 2" and possibly "NINJA GAIDEN 3: Razor's Edge"

* amadeus: Fix wrong variable usage in delay effect

We should transform the delay line values, not the input.

* amadeus: Update GroupedBiquadFilterCommand documentation

* amadeus: Simplify PoolMapper alignment checks

* amadeus: Update Surround delay effect matrix to REV11

* amadeus: Add drop parameter support and use 32 bits integers for estimate time

Also implement accurate ExecuteAudioRendererRendering stub.

* Address gdkchan's comments

* Address gdkchan's other comments

* Address gdkchan's comment
2022-11-28 08:28:45 +01:00
Ac_K
472119c8da sfdnsres; Fix deserializer of AddrInfoSerialized when addresses are empty (#3924) 2022-11-28 02:53:57 +01:00
Mary-nyan
1865ea87e5 bsd: Fix eventfd broken logic (#3647)
* bsd: Fix eventfd broken logic

This commit fix eventfd logic being broken.

The following changes were made:
- EventFd IPC definition had argument inverted
- EventFd events weren't fired correctly
- Poll logic was wrong and unfinished for eventfd
- Reintroduce workaround from #3385 but in a safer way, and spawn 4
  threads.

* ipc: Rework a bit for multithreads

* Clean up debug logs

* Make server thread yield when managed lock isn't availaible

* Fix replyTargetHandle not being added in the proper locking scope

* Simplify some scopes

* Address gdkchan's comments

* Revert IPC workaround for now

* Reintroduce the EventFileDescriptor workaround
2022-11-27 20:18:05 +00:00
&Olga
18b61aff59 Unbreak bug_report.md (#3915)
* Unbreak bug_report.md

* Update bug_report.md
2022-11-27 20:11:51 +00:00
Luminoso-256
cb22629ac1 HLE: fix small issue in IPsmSession (#3909) 2022-11-27 01:10:42 +00:00
TSRBerry
6f0f99ee2b Avalonia: Fix OpenGL crashing on Linux (#3902)
* ava: Fix OpenGL crashing on Linux

Fixes a regression from #3901

* Fix formatting
2022-11-26 12:06:53 +01:00
TSRBerry
70f2da8fdf ava: Fix invisible vulkan window on Linux (#3901)
Co-authored-by: emmauss <emmausssss@gmail.com>

Co-authored-by: emmauss <emmausssss@gmail.com>
2022-11-25 17:40:44 +00:00
Ac_K
5d3ef7761b ava: Refactor Title Update Manager window (#3898)
* ava: Refactor TitleUpdate Manager window

* Update locale
2022-11-25 17:55:08 +01:00
riperiperi
476b4683cf Fix CB0 alignment with addresses used for 8/16-bit LDG/STG (#3897)
This replacement is meant to be done with the original identified byteOffset, not the one assigned later on by the below conditionals (that already has the constant offset added, for instance).

This fixes videos being pixelated in Xenoblade 3, and other regressions that might have happened since #3847.
2022-11-25 14:39:03 +00:00
Mary-nyan
5fb5079730 chore: Update Avalonia related dependencies (#3891) 2022-11-25 13:27:41 +00:00
Ac_K
3fbacd0f49 ava: Rework DLC Manager, Add various fixes and cleanup (#3896)
* Fixes Everything Part.2

* Change sorting, fix remove and heading
2022-11-25 12:41:34 +01:00
dependabot[bot]
7aa6abc120 nuget: bump SharpZipLib from 1.3.3 to 1.4.1 (#3893)
Bumps [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) from 1.3.3 to 1.4.1.
- [Release notes](https://github.com/icsharpcode/SharpZipLib/releases)
- [Changelog](https://github.com/icsharpcode/SharpZipLib/blob/master/docs/Changes.txt)
- [Commits](https://github.com/icsharpcode/SharpZipLib/compare/v1.3.3...v1.4.1)

---
updated-dependencies:
- dependency-name: SharpZipLib
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-24 20:30:17 +01:00
Mary-nyan
548bfd60a2 chore: Update Ryujinx.SDL2-CS to 2.24.2 (#3892)
* chore: Update Ryujinx.SDL2-CS to 2.24.2

* Disable SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS
2022-11-24 20:13:16 +01:00
riperiperi
65778a6b78 GPU: Don't trigger uploads for redundant buffer updates (#3828)
* Initial implementation

* Actually do The Thing

* Add remark about performance to IVirtualMemoryManager
2022-11-24 15:50:15 +01:00
Mary-nyan
f4e879a1e6 Reduce usage of Marshal.PtrToStructure and Marshal.StructureToPtr (#3805)
* common: Make BinaryReaderExtensions Read & Write take unamanged types

This allows us to not rely on Marshal.PtrToStructure and Marshal.StructureToPtr for those.

* common: Make MemoryHelper Read & Write takes unamanged types

* Update Marshal.SizeOf => Unsafe.SizeOf when appropriate and start moving software applet to unmanaged types
2022-11-24 15:26:29 +01:00
Ac_K
a1ddaa2736 ui: Fixes disposing on GTK/Avalonia and Firmware Messages on Avalonia (#3885)
* ui: Only wait on _exitEvent when MainLoop is active under GTK

This fixes a dispose issue under Horizon/GTK, we don't check if the ApplicationClient is null so it throw NCE. We don't check if the main loop is active and waiting an event which is set in the main loop... So that could lead to a freeze.

Everything works fine in GTK now.

Related issue: https://github.com/Ryujinx/Ryujinx/issues/3873

As a side note, same kind of issue appear in Avalonia UI too. Firmware's popup doesn't show anything and the emulator just freeze.

* TSRBerry's change

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Fix Avalonia crashing/freezing

* Add Avalonia OpenGL fixes

* Fix firmware popup on windows

* Fixes everything

* Add _initialized bool to VulkanRenderer and OpenGL Window

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-11-24 15:08:27 +01:00
Mary-nyan
008286b79f Ryujinx.Ava: Add missing redefinition of app name (#3890)
Before this, Ryujinx would possibly report as "Avalonia Application".
2022-11-24 14:52:39 +01:00
gdkchan
a0c77f8d11 Fix NRE on Avalonia for error applets with unknown error message (#3888) 2022-11-24 09:31:00 +01:00
riperiperi
ece36b274d GAL: Send all buffer assignments at once rather than individually (#3881)
* GAL: Send all buffer assignments at once rather than individually

The `(int first, BufferRange[] ranges)` method call has very significant performance implications when the bindings are spread out, which they generally always are in Vulkan. This change makes it so that these methods are only called a maximum of one time per draw.

Significantly improves GPU thread performance in Pokemon Scarlet/Violet.

* Address Feedback

Removed SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
2022-11-24 07:50:59 +00:00
riperiperi
f3cc2e5703 GPU: Access non-prefetch command buffers directly (#3882)
* GPU: Access non-prefetch command buffers directly

Saves allocating new arrays for them constantly - they can be quite small so it can be very wasteful. About 0.4% of GPU thread in SMO, but was a bit higher in S/V when I checked.

Assumes that non-prefetch command buffers won't be randomly clobbered before they finish executing, though that's probably a safe bet.

* Small change while I'm here

* Address feedback
2022-11-24 01:56:55 +00:00
riperiperi
5a39d3c4a1 GPU: Relax locking on Buffer Cache (#3883)
I did this on ncbuffer2 when we were using it for LDN 3, but I noticed that it can apply to the current buffer manager too, and it's an easy performance win.

The only buffer access that can come from another thread is the overlap search for buffers that have been unmapped. Everything else, including modifications, come from the main GPU thread. That means we only need to lock the range list when it's being modified, as that's the only time where we'll cause a race with the unmapped handler.

This has a significant performance improvements in situations where FIFO is high, like the other two PRs. Joined together they give a nice boost (73.6 master -> 79 -> 83 fps in SMO).
2022-11-24 01:41:16 +00:00
dependabot[bot]
cc51a03af9 nuget: bump Avalonia from 0.10.15 to 0.10.18 (#3817)
Bumps [Avalonia](https://github.com/AvaloniaUI/Avalonia) from 0.10.15 to 0.10.18.
- [Release notes](https://github.com/AvaloniaUI/Avalonia/releases)
- [Commits](https://github.com/AvaloniaUI/Avalonia/compare/0.10.15...0.10.18)

---
updated-dependencies:
- dependency-name: Avalonia
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-24 01:26:53 +00:00
Ac_K
567c64e149 ava: Fix JsonSerializer warnings (#3884)
Since we move to .NET7, JsonSerializer now needs to have explicit options as arguments, which leads to some warnings in Avalonia project. This is fixed by using our `JsonHelper` class.
2022-11-23 17:55:26 +00:00
Alex Barney
36f00985d3 Update to LibHac 0.17.0 (#3878)
* Update to LibHac 0.17.0

* Don't clear SD card saves when starting the emulator

This was an old workaround for errors that happened when a user's SD card encryption seed changed. SD card saves have been unencrypted for over a year, so we should be fine to remove the workaround.
2022-11-23 18:32:35 +01:00
WilliamWsyHK
748d87adcc Stub IFriendService: 1 (Cancel) (#3841)
* Add friend/Cancel. Closes #3824

* Update according to review comments.

* Add comment base on request

* Update Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-11-23 16:25:49 +01:00
Emmanuel Hansen
0fd47ff490 remove redundant logs (#3877) 2022-11-23 11:28:46 +01:00
gdkchan
f088c3d344 Do not update shader state for DrawTextures (#3876) 2022-11-21 18:16:00 +01:00
TSRBerry
905a191e28 Use upstream unicorn for Ryujinx.Tests.Unicorn (#3771)
* unicorn: Add modified ver of unicorns const gen

* unicorn: Use upstream consts

These consts were generated from the dev branch of unicorn

* unicorn: Split common consts into multiple enums

* unicorn: Remove arch prefix from consts

* unicorn: Add new windows dll

Windows 10 - MSVC x64 shared build

* unicorn: Use absolute path for const generation

* unicorn: Remove fspcr patch

* unicorn: Fix using the wrong file extension

For some reason _NativeLibraryExtension evaluates to ".so" even on Windows.

* unicorn: Add linux shared object again

* unicron: Add DllImportResolver

* unicorn: Try to import unicorn using an absolute path

* unicorn: Add clean target

* unicorn: Replace IsUnicornAvailable() methods

* unicorn: Skip tests instead of silently passing them if unicorn is missing

* unicorn: Write error message to stderr

* unicorn: Make Interface static

* unicron: Include prefixed unicorn libs (libunicorn.so)

Co-authored-by: merry <git@mary.rs>

* unicorn: Add lib prefix to shared object for linux

Co-authored-by: merry <git@mary.rs>
2022-11-20 20:18:21 +01:00
EmulationFanatic
ab0491817e Reword the description of the 6GB expand DRAM hack to be less tantalizing (#3870)
* Reword the hack to something less tantalizing

* Address feedback
2022-11-20 17:15:57 +00:00
gdkchan
5de6ae426e Unsubscribe MemoryUnmappedHandler even when GPU channel is destroyed (#3872) 2022-11-19 23:54:33 -03:00
gdkchan
69ced3a6e8 Fix shader cache on Vulkan when geometry shaders are inserted (#3868) 2022-11-19 10:24:23 +01:00
gdkchan
2e43d01d36 Move gl_Layer from vertex to geometry if GPU does not support it on vertex (#3866)
* Move gl_Layer from vertex to geometry if GPU does not support it on vertex

* Shader cache version bump

* PR feedback
2022-11-18 23:27:54 -03:00
riperiperi
7373ec5792 Vulkan: Clear dummy texture to (0,0,0,0) on creation (#3867)
This might fix an issue with AMD gpus on linux where the data could contain random garbage data. On the switch, it always samples as 0.
2022-11-18 23:11:34 -03:00
riperiperi
de162a648b Gpu: Fix thread safety of ReregisterRanges (#3865)
A quick fix to prevent reading the wrong value of Count when reregistering ranges for a new target buffer. Buffer flushes from another thread can modify the range list when the lock isn't active, which can change the count.

This prevents some crashes in Pokemon Scarlet/Violet. It's probably likely that buffer migration during flush is causing some other issues in this game, but this at least prevents the crashing.
2022-11-18 21:47:29 +01:00
riperiperi
131baebe2a Vulkan: Don't create preload command buffer outside a render pass (#3864)
* Vulkan: Don't create preload buffer outside a render pass

The preload command buffer is used to avoid render pass splits and barriers when updating buffer data. However, when a render pass is not active (for example, at the start of a pass, or during compute invocations) buffer uploads can be performed at any time, so the optimization isn't as useful.

This PR makes it so that the preload command buffer is only used for buffer updates outside of a render pass. It's still used for textures as I don't want to shake things up right now regarding how the preload buffer is obtained before some other changes, and texture updates are a lot rarer anyways.

Improves performance slightly in Pokemon Scarlet/Violet (43 -> 48), as it was switching to compute, writing a bunch of buffers inline, then dispatching, then flushing commands... It uses 1 command buffer instead of 2 every time it does this now. Maybe it would be nice to find a faster way to sync without creating so many command buffers in a short period of time.

* Address feedback
2022-11-18 14:58:56 +00:00
riperiperi
187372cbde Prune ForceDirty and CheckModified caches on unmap (#3862)
* Prune ForceDirty and CheckModified caches on unmap

Since we're now using this for modified checks on the HLE indirect draw method, I'm worried that leaving these to forever gather cache entries isn't the best idea for performance in the long term, and it could keep old buffer objects alive for longer than they should be.

This PR adds the ability to prune invalid entries before checking these caches, and queues it whenever gpu memory is unmapped. It also aligns modified checks to the page size, as I figured it would be possible for a huge number of overlapping over a game's runtime.

This prevents Super Mario Odyssey from having 10s of thousands of entries in the modified cache in Metro Kingdom, and them duplicating when entering and leaving a building (should be cleared, as they were unmapped).

* Address Feedback
2022-11-18 14:58:24 +00:00
TSRBerry
022d495335 am: Stub GetSaveDataSizeMax (#3857)
* am: Stub GetSaveDataSizeMax()

* am: Remove todo comment for GetSaveDataSizeMax()

* am: saveDataSize & journalDataSize should be of type long

* am: Add explanation for returning default values in GetSaveDataSizeMax()
2022-11-18 03:29:01 +00:00
Berkan Diler
c1372ed775 Use ReadOnlySpan<byte> compiler optimization in more places (#3853)
* Use ReadOnlySpan<byte> compiler optimization in more places

* Revert changes in ShaderBinaries.cs

* Remove unused using;

* Use ReadOnlySpan<byte> compiler optimization in more places
2022-11-18 03:10:44 +00:00
riperiperi
a16682cfd3 Allow _volatile to be set from MultiRegionHandle checks again (#3830)
* Allow _volatile to be set from MultiRegionHandle checks again

Tracking handles have a `_volatile` flag which indicates that the resource being tracked is modified every time it is used under a new sequence number. This is used to reduce the time spent reprotecting memory for tracking writes to commonly modified buffers, like constant buffers.

This optimisation works by detecting if a buffer is modified every time a check happens. If a buffer is checked but it is not dirty, then that data is likely not modified every sequence number, and should use memory protection for write tracking. If the opposite is the case all the time, it is faster to just assume it's dirty as we'd just be wasting time protecting the memory.

The new MultiRegionBitmap could not notify handles that they had been checked as part of the fast bitmap lookup, so bindings larger than 4096 bytes wouldn't trigger it at all. This meant that they would be subject to a ton of reprotection if they were modified often.

This does mean there are two separate sources for a _volatile set: VolatileOrDirty + _checkCount, and the bitmap check. These shouldn't interfere with each other, though.

This fixes performance regressions from #3775 in Pokemon Sword, and hopefully Yu-Gi-Oh! RUSH DUEL: Dawn of the Battle Royale. May affect other games.

* Fix stupid mistake
2022-11-18 02:54:20 +00:00
riperiperi
7c53b69c30 SPIR-V: Fix unscaling helper not being able to find Array textures (#3863)
The type in the `texOp` in the textureSize instruction doesn't have the exact type on SPIR-V (for example, it is missing the Array flag). This PR gives it the proper type before giving it to the unscaling helper.

This fixes the ground textures being broken on Pokemon Scarlet/Violet when scaling. It wasn't finding the texture, so the descriptor index it provided was -1...
2022-11-18 02:37:37 +00:00
riperiperi
33a4d7d1ba GPU: Eliminate CB0 accesses when storage buffer accesses are resolved (#3847)
* Eliminate CB0 accesses

Still some work to do, decouple from hle?

* Forgot the important part somehow

* Fix and improve alignment test

* Address Feedback

* Remove some complexity when checking storage buffer alignment

* Update Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-11-17 18:47:41 +01:00
Mary-nyan
391e08dd27 ci: Clean up Actions leftovers (#3859)
As title say.

Fix Avalonia build versions for PRs.

Also ensure that the --self-contained doesn't warn at build.
2022-11-17 18:30:54 +01:00
Matthew Wells
b5cf8b8af9 Capitalization to be consistent (#3860)
Thread ID Register, Floating-point Control Register, and Floating-point Status Register all had Register capitalized, so the Register in Processor State register should be capitalized.
2022-11-17 18:13:37 +01:00
Alberto Fanjul
55043c8afc Allow to start Ryujinx in wayland environment (#3516)
PrimaryMonitor is only available on X11

At some point it will be deprecated, this change support wayland
2022-11-17 13:02:43 +01:00
Mary-nyan
5d73a9f5fc Fix Fedora support (#3815)
For some reasons, my fresh installation of Fedora 36 (KDE) doesn't have a
symlink for libX11.so.

This commit fixes this by trying to import the library with its major
version or fallback to the normal way.
2022-11-17 00:18:29 +01:00
Logan Stromberg
2c9ab5e45f Prevent raw Unicode control codes from showing on software keyboard applet. (#3845)
* Revert "Add support for releasing a semaphore to DmaClass (#2926)"

This reverts commit 521a07e612.

* Revert "Revert "Add support for releasing a semaphore to DmaClass (#2926)""

This reverts commit ec8a5fd053.

* Strip non-visible control codes from strings before they are sent to the software keyboard to prevent ugly unicode blocks from being shown on the UI.

* remove debugging junk

* Initialize stringbuilder capacity at the start to prevent resizing (a tiny tiny microoptimization)

* Update remarks documentation. Remove unneeded imports.

* Removing a test that's actually just redundant

Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2022-11-16 23:53:17 +01:00
Wunk
d536cc8ae6 Update units of memory from decimal to binary prefixes (#3716)
`MB` and `GB` can either be interpreted as having base-10 units, or
base-2. `MiB` and `GiB` removes this discrepancy so that units of memory
are always interpreted using base-2 units.
2022-11-16 23:27:42 +01:00
Berkan Diler
d751da84f9 Use new C# 11 u8 string literals (#3854) 2022-11-16 16:30:12 -03:00
Berkan Diler
11aae9cfbc Make use of Random.Shared (#3852) 2022-11-16 15:34:18 -03:00
Berkan Diler
b96794e72b Use new LINQ Order() methods (#3851) 2022-11-16 15:17:03 -03:00
gdkchan
f1d1670b0b Implement HLE macro for DrawElementsIndirect (#3748)
* Implement HLE macro for DrawElementsIndirect

* Shader cache version bump

* Use GL_ARB_shader_draw_parameters extension on OpenGL

* Fix DrawIndexedIndirectCount on Vulkan when extension is not supported

* Implement DrawIndex

* Alignment

* Fix some validation errors

* Rename BaseIds to DrawParameters

* Fix incorrect index buffer and vertex buffer size in some cases

* Add HLE macros for DrawArraysInstanced and DrawElementsInstanced

* Perform a regular draw when indirect data is not modified

* Use non-indirect draw methods if indirect buffer was not GPU modified

* Only check if draw parameters match if the shader actually uses them

* Expose Macro HLE setting on GUI

* Reset FirstVertex and FirstInstance after draw

* Update shader cache version again since some people already tested this

* PR feedback

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2022-11-16 14:53:04 -03:00
EmulationFanatic
b8de72de8f It's REE-YOU-JINX (#3839) 2022-11-15 22:19:25 +01:00
TSRBerry
eebc39228d UI: Allow overriding graphics backend + Move command line parser into a new class (#3707)
* Ava: Keep command line args when restarting

* UI: Move common UI functions to ProgramHelper

Add command line option to override the configured graphics backend

* Ava: Add CleanupUpdate task back

* Remove unused usings

* Revert combining common UI functions

Rename ProgramHelper to CommandLineState
Move command line parsing to CommandLineState

* Rename CommandLineProfile to Profile

* Fix assigning the wrong array to Arguments
2022-11-12 20:36:36 -03:00
gdkchan
9daf029f35 Use vector transform feedback outputs if possible (#3832) 2022-11-12 20:20:40 -03:00
gdkchan
51a27032f0 Fix VertexId and InstanceId on Vulkan (#3833)
* Fix VertexId and InstanceId on Vulkan

* Shader cache version bump
2022-11-11 13:22:49 -03:00
gdkchan
a6a67a2b7a Minor improvement to Vulkan pipeline state and bindings management (#3829)
* Minor improvement to Vulkan pipeline state and bindings management

* Clean up buffer textures too

* Use glBindTextureUnit
2022-11-10 13:38:38 -03:00
Mary-nyan
c6d05301aa infra: Migrate to .NET 7 (#3795)
* Update readme to mention .NET 7

* infra: Migrate to .NET 7

.NET 7 is still in preview but this prepare for the release coming up
next month.

* Use Random.Shared in CreateRandom

* Move UInt128Utils.cs to Ryujinx.Common project

* Fix inverted parameters in System.UInt128 constructor

* Fix Visual Studio complains on  Ryujinx.Graphics.Vic

* time: Fix missing alignment enforcement in SystemClockContext

Fixes at least Smash

* time: Fix missing alignment enforcement in SteadyClockContext

Fix games (like recent version of Smash) using time shared memory

* Switch to .NET 7.0.100 release

* Enable Tiered PGO

* Ensure CreateId validity requirements are meet when doing random generation

Also enforce correct packing layout for other Mii structures.

This fix a Mario Kart 8 crashes related to the default Miis.
2022-11-09 20:22:43 +01:00
gdkchan
647de4cd31 Ensure all pending draws are done before compute dispatch (#3822) 2022-11-03 19:54:30 -03:00
gdkchan
f82309fa2d Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve (#3723)
* Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve

* FramebufferParams is no longer required there

* Implement Specialization Constants and merge CopyMS Shaders (#15)

* Vulkan: Initial Specialization Constants

* Replace with specialized helper shader

* Reimplement everything

Fix nonexistant interaction with Ryu pipeline caching
Decouple specialization info from data and relocate them
Generalize mapping and add type enum to better match spv types
Use local fixed scopes instead of global unmanaged allocs

* Fix misses in initial implementation

Use correct info variable in Create2DLayerView
Add ShaderStorageImageMultisample to required feature set

* Use texture for source image

* No point in using ReadOnlyMemory

* Apply formatting feedback

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Apply formatting suggestions on shader source

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Support conversion with samples count that does not match the requested count, other minor changes

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>
2022-11-02 18:17:19 -03:00
Mary-nyan
7d8e198c33 fix: Support FFmpeg 5.1.x for decoding (#3816)
For some reason FFmpeg 5.1.x reverted part of the changes made in 5.0.x
on AVCodec.

This fix decoding issues with it.
2022-11-02 09:26:50 +01:00
riperiperi
3d98e1361b GPU: Use a bitmap to track buffer modified flags. (#3775)
* Initial implementation

* Some improvements.

* Fix incorrect cast

* Performance improvement and improved correctness

* Add very fast path when all handles are checked.

* Slightly faster

* Add comment

* De-virtualize region handle

All region handles are now bitmap backed.

* Remove non-bitmap tracking

* Remove unused methods

* Add docs, remove unused methods

* Address Feedback

* Rename file
2022-10-29 22:07:37 +00:00
TSRBerry
141cf61ff7 CI: Run git_short_hash inside of bash (#3808) 2022-10-29 19:00:08 +00:00
Wunk
3fe3598d41 Vulkan: Replace VK_EXT_debug_report usage with VK_EXT_debug_utils (#3802)
* Vulkan: Replace `VK_EXT_debug_report` usage with `VK_EXT_debug_utils`

[VK_EXT_debug_report](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_report.html)
has been depreciated for quite some time now in favor of the much more
featureful
[VK_EXT_debug_utils](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html)
extension.

This PR converts our debug-report-callback into the newer
debug-messenger pattern.

`VK_EXT_debug_utils` adds some additional diagnostic tooling for marking
debug-label scopes for queue-operations, command-buffers, and assigning
name-labels to vulkan objects to aid in debugging(for a later PR).

* Vulkan: Fix `DebugMessenger` severity-flag classification

Extension bits between the two flags, for reference:

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugUtilsMessageSeverityFlagBitsEXT.html

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugReportFlagBitsEXT.html
2022-10-29 14:09:25 -03:00
gdkchan
59cdf310bd SPIR-V: Fix tessellation control shader output types (#3807)
* SPIR-V: Fix tessellation control shader output types

* Shader cache version bump
2022-10-29 13:45:30 -03:00
4767 changed files with 360514 additions and 286864 deletions

View File

@@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions ####
# Namespace preferences
csharp_style_namespace_declarations = block_scoped:warning
resharper_csharp_namespace_body = block_scoped
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
@@ -89,6 +93,7 @@ csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
csharp_style_prefer_readonly_struct = true
# Code-block preferences
csharp_prefer_braces = true:silent

61
.gitattributes vendored
View File

@@ -1,63 +1,4 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
* text=auto eol=lf

View File

@@ -1,43 +0,0 @@
---
name: Bug Report
about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression.
#assignees:
---
## Bug Report
[ If any section does not apply, replace its contents with "N/A". ]</br>
[ Lines between [ ] (square brackets) should be removed before posting. ]
### What's the issue you encountered?
[ Describe the issue in detail and what you were doing beforehand. ]</br>
[ Did you make any changes related to Ryujinx itself? ]</br>
[ If so, make sure to include details relating to what exactly you changed. ]
### How can the issue be reproduced?
[ Include a detailed step by step process for recreating your issue. ]
### Log file
[ Logs files can be found under ``Logs`` folder in Ryujinx program folder. ]</br>
[ If you don't include a crash report in instances of crash related issues, we will ask you one to provide one. ]
### Environment?
- Ryujinx version: 1.0.X</br>
[ Replace X's with the Ryujinx version at time of crash. ]
- Game version: X.X.X</br>
[ Replace X's with the game version at time of crash. ]
- System Specs:
- OS: *(e.g. Windows 10)*
- CPU: *(e.g. i7-6700)*
- GPU: *(e.g. NVIDIA RTX 2070)*
- RAM: *(e.g. 16GB)*
- Applied Mods : [ Yes (Which ones) / No ]
### Additional context?
Additional info about your environment:</br>
[ Any other information relevant to your issue. ]

86
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: Bug Report
description: File a bug report
title: "[Bug]"
labels: bug
body:
- type: textarea
id: issue
attributes:
label: Description of the issue
description: What's the issue you encountered?
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction steps
description: How can the issue be reproduced?
placeholder: Describe each step as precisely as possible
validations:
required: true
- type: textarea
id: log
attributes:
label: Log file
description: A log file will help our developers to better diagnose and fix the issue.
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
validations:
required: true
- type: input
id: os
attributes:
label: OS
placeholder: "e.g. Windows 10"
validations:
required: true
- type: input
id: ryujinx-version
attributes:
label: Ryujinx version
placeholder: "e.g. 1.0.470"
validations:
required: true
- type: input
id: game-version
attributes:
label: Game version
placeholder: "e.g. 1.1.1"
validations:
required: false
- type: input
id: cpu
attributes:
label: CPU
placeholder: "e.g. i7-6700"
validations:
required: false
- type: input
id: gpu
attributes:
label: GPU
placeholder: "e.g. NVIDIA RTX 2070"
validations:
required: false
- type: input
id: ram
attributes:
label: RAM
placeholder: "e.g. 16GB"
validations:
required: false
- type: textarea
id: mods
attributes:
label: List of applied mods
placeholder: You can list applied mods here.
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: Additional context?
description: |
- Additional info about your environment:
- Any other information relevant to your issue.
validations:
required: false

View File

@@ -1,34 +0,0 @@
---
name: Feature Request
about: Suggest a new feature for Ryujinx.
#assignees:
---
## Feature Request
[ If any section does not apply, replace its contents with "N/A". ]</br>
[ If you do not have the information needed for a section, replace its contents with "Unknown". ]</br>
[ Lines between [ ] (square brackets) are to be removed before posting. ]</br>
[ Please search for existing [feature requests](https://github.com/Ryujinx/Ryujinx/issues) before you make your own request. ]</br>
[ Duplicate requests will be marked as such and you will be referred to the original request. ]
### What feature are you suggesting?
#### Overview:
- [ Include the basic, high-level concepts for this feature here. ]
#### Smaller Details:
- [ These may include specific methods of implementation etc. ]
#### Nature of Request:
[ Remove all that do not apply to your request. ]
- Addition
- [ Ex: Addition of certain original features or features from other community projects. ]
- [ If you are suggesting porting features or including features from other projects, include what license they are distributed under and what, if any libraries those project use. ]
- Change
- Removal
- [Ex: Removal of certain features or implementation due to a specific issue/bug or because of low quality code, etc.]
### Why would this feature be useful?
[ If this is a feature for an end-user, how does it benefit the end-user? ]</br>
[ If this feature is for developers, what does it add to Ryujinx that did not already exist? ]

View File

@@ -0,0 +1,30 @@
name: Feature Request
description: Suggest a new feature for Ryujinx.
title: "[Feature Request]"
body:
- type: textarea
id: overview
attributes:
label: Overview
description: Include the basic, high-level concepts for this feature here.
validations:
required: true
- type: textarea
id: details
attributes:
label: Smaller details
description: These may include specific methods of implementation etc.
validations:
required: true
- type: textarea
id: request
attributes:
label: Nature of request
validations:
required: true
- type: textarea
id: feature
attributes:
label: Why would this feature be useful?
validations:
required: true

View File

@@ -1,34 +0,0 @@
---
name: Missing CPU Instruction
about: CPU Instruction is missing in Ryujinx.
#assignees:
---
## Missing CPU Instruction
[ If any section does not apply, replace its contents with "N/A". ]</br>
[ If you do not have the information needed for a section, replace its contents with "Unknown". ]</br>
[ Lines between [ ] (square brackets) are to be removed before posting. ]
[ Please search for existing [missing CPU instruction](https://github.com/Ryujinx/Ryujinx/issues) before you make your own issue. ]</br>
[ See the following [issue](https://github.com/Ryujinx/Ryujinx/issues/1405) as an example ]</br>
[ Duplicate issue will be marked as such and you will be referred to the original request. ]
### What CPU instruction is missing?
Requires the *INSTRUCTION* instruction.</br>
[ Replace *INSTRUCTION* by the instruction name, e.g. VADDL.U16 ]
```
*
```
[ Add the undefined instruction error message in the above code block ]
### Instruction name
```
*
```
[ Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block ]
### Required by:
[ Add our (games list database)[https://github.com/Ryujinx/Ryujinx-Games-List/issues] links of games who require this instruction ]

View File

@@ -0,0 +1,26 @@
name: Missing CPU Instruction
description: CPU Instruction is missing in Ryujinx.
title: "[CPU]"
labels: [cpu, not-implemented]
body:
- type: textarea
id: instruction
attributes:
label: CPU instruction
description: What CPU instruction is missing?
validations:
required: true
- type: textarea
id: name
attributes:
label: Instruction name
description: Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block
validations:
required: true
- type: textarea
id: required
attributes:
label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
validations:
required: true

View File

@@ -1,35 +0,0 @@
---
name: Missing Service Call
about: Service call is missing in Ryujinx.
#assignees:
---
## Missing Service Call
[ If any section does not apply, replace its contents with "N/A". ]</br>
[ If you do not have the information needed for a section, replace its contents with "Unknown". ]</br>
[ Lines between [ ] (square brackets) are to be removed before posting. ]
[ Please search for existing [missing service call](https://github.com/Ryujinx/Ryujinx/issues) before you make your own issue. ]</br>
[ See the following [issue](https://github.com/Ryujinx/Ryujinx/issues/1431) as an example ]</br>
[ Duplicate issue will be marked as such and you will be referred to the original request. ]
### What service call is missing?
*SERVICE* *INTERFACE*: *NUMBER* (*NAME*) is not implemented.</br>
[ Replace *SERVICE* by the service name, e.g. appletAE ]</br>
[ Replace *INTERFACE* by the interface name, e.g. IAllSystemAppletProxiesService ]</br>
[ Replace *NUMBER* by the call number, e.g. 100 ]</br>
[ Replace *NAME* by the call name, e.g. OpenSystemAppletProxy ]</br>
[ e.g. appletAE IAllSystemAppletProxiesService: 100 (OpenSystemAppletProxy) ]
[ Add related links to the specific call from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) ]
### Service description
```
*
```
[ Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block ]
### Required by:
[ Add our (games list database)[https://github.com/Ryujinx/Ryujinx-Games-List/issues] links of games who require this call ]

View File

@@ -0,0 +1,25 @@
name: Missing Service Call
description: Service call is missing in Ryujinx.
labels: not-implemented
body:
- type: textarea
id: instruction
attributes:
label: Service call
description: What service call is missing?
validations:
required: true
- type: textarea
id: name
attributes:
label: Service description
description: Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block
validations:
required: true
- type: textarea
id: required
attributes:
label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this service.
validations:
required: true

View File

@@ -51,26 +51,23 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1
dotnet-version: 7.0.x
- name: Get git short hash
id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
- name: Clear
run: dotnet clean && dotnet nuget locals all --clear
shell: bash
- name: Build
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test
run: dotnet test --no-build -c "${{ matrix.configuration }}"
- name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
if: github.event_name == 'pull_request'
- name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
if: github.event_name == 'pull_request'
- name: Publish Ryujinx.Ava
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
if: github.event_name == 'pull_request'
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v3
@@ -89,4 +86,4 @@ jobs:
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_ava
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request'

171
.github/workflows/flatpak.yml vendored Normal file
View File

@@ -0,0 +1,171 @@
name: Flatpak release job
on:
workflow_call:
inputs:
ryujinx_version:
required: true
type: string
concurrency: flatpak-release
jobs:
release:
runs-on: ubuntu-latest
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
GIT_COMMITTER_NAME: "RyujinxBot"
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
NUGET_SOURCES_DESTDIR: "nuget-sources"
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
steps:
- uses: actions/checkout@v3
with:
path: Ryujinx
- uses: actions/setup-dotnet@v3
with:
global-json-file: Ryujinx/global.json
- name: Get version info
id: version_info
working-directory: Ryujinx
run: |
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3
with:
repository: flathub/org.ryujinx.Ryujinx
token: ${{ secrets.RYUJINX_BOT_PAT }}
submodules: recursive
path: flathub
- name: Install dependencies
run: python -m pip install PyYAML lxml
- name: Restore Nuget packages
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
- name: Generate nuget_sources.json
shell: python
run: |
from pathlib import Path
import base64
import binascii
import json
import os
sources = []
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
name = path.parent.parent.name
version = path.parent.name
filename = '{}.{}.nupkg'.format(name, version)
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
with path.open() as fp:
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
sources.append({
'type': 'file',
'url': url,
'sha512': sha512,
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
'dest-filename': filename,
})
with open('flathub/nuget_sources.json', 'w') as fp:
json.dump(sources, fp, indent=4)
- name: Update flatpak metadata
id: metadata
env:
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
shell: python
run: |
import hashlib
import hmac
import json
import os
import yaml
from datetime import datetime
from lxml import etree
# Ensure we don't destroy multiline strings
def str_presenter(dumper, data):
if len(data.splitlines()) > 1:
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
with open(yaml_file, "r") as f:
data = yaml.safe_load(f)
for source in data["modules"][0]["sources"]:
if type(source) is str:
continue
if (
source["type"] == "git"
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
):
source["commit"] = os.environ['RYUJINX_GIT_HASH']
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
if is_same_version:
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
else:
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
if not is_same_version:
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
with open(yaml_file, "w") as f:
yaml.safe_dump(data, f, sort_keys=False)
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(xml_file, parser)
root = tree.getroot()
releases = root.find("releases")
element = etree.Element("release")
element.set("version", os.environ['RYUJINX_VERSION'])
element.set("date", datetime.now().date().isoformat())
releases.insert(0, element)
# Ensure 4 spaces
etree.indent(root, space=" ")
with open(xml_file, "wb") as f:
f.write(
etree.tostring(
tree,
pretty_print=True,
encoding="UTF-8",
doctype='<?xml version="1.0" encoding="UTF-8"?>',
)
)
- name: Push flatpak update
working-directory: flathub
env:
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
run: |
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
git add .
git commit -m "$COMMIT_MESSAGE"
git push origin master

View File

@@ -11,28 +11,24 @@ on:
- '*.yml'
- 'README.md'
concurrency: release
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
jobs:
release:
runs-on: windows-latest
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1
- name: Clear
run: dotnet clean && dotnet nuget locals all --clear
global-json-file: global.json
- name: Get version info
id: version_info
run: |
@@ -41,19 +37,19 @@ jobs:
shell: bash
- name: Configure for release
run: |
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
- name: Create output dir
run: "mkdir release_output"
- name: Publish Windows
run: |
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
- name: Packing Windows builds
run: |
pushd publish_windows
@@ -71,22 +67,34 @@ jobs:
- name: Publish Linux
run: |
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
- name: Packing Linux builds
run: |
pushd publish_linux
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd
pushd publish_linux_sdl2_headless
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd
pushd publish_linux_ava
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava"
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd
shell: bash
@@ -96,10 +104,28 @@ jobs:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip"
tag: ${{ steps.version_info.outputs.build_version }}
body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
allowUpdates: true
removeArtifacts: true
replacesArtifacts: true
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.RELEASE_TOKEN }}
- name: Create tag
uses: actions/github-script@v5
with:
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
sha: context.sha
})
flatpak_release:
uses: ./.github/workflows/flatpak.yml
needs: release
with:
ryujinx_version: "1.1.${{ github.run_number }}"
secrets: inherit

6
.gitignore vendored
View File

@@ -125,6 +125,9 @@ ClientBin/
packages/*
*.config
# Include nuget.config
!nuget.config
# RIA/Silverlight projects
Generated_Code/
@@ -167,3 +170,6 @@ launchSettings.json
# NetCore Publishing Profiles
PublishProfiles/
# Glade backup files
*.glade~

View File

@@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,56 +0,0 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Translation.Cache;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.CodeGen
{
/// <summary>
/// Represents a compiled function.
/// </summary>
readonly struct CompiledFunction
{
/// <summary>
/// Gets the machine code of the <see cref="CompiledFunction"/>.
/// </summary>
public byte[] Code { get; }
/// <summary>
/// Gets the <see cref="Unwinding.UnwindInfo"/> of the <see cref="CompiledFunction"/>.
/// </summary>
public UnwindInfo UnwindInfo { get; }
/// <summary>
/// Gets the <see cref="Linking.RelocInfo"/> of the <see cref="CompiledFunction"/>.
/// </summary>
public RelocInfo RelocInfo { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CompiledFunction"/> struct with the specified machine code,
/// unwind info and relocation info.
/// </summary>
/// <param name="code">Machine code</param>
/// <param name="unwindInfo">Unwind info</param>
/// <param name="relocInfo">Relocation info</param>
internal CompiledFunction(byte[] code, UnwindInfo unwindInfo, RelocInfo relocInfo)
{
Code = code;
UnwindInfo = unwindInfo;
RelocInfo = relocInfo;
}
/// <summary>
/// Maps the <see cref="CompiledFunction"/> onto the <see cref="JitCache"/> and returns a delegate of type
/// <typeparamref name="T"/> pointing to the mapped function.
/// </summary>
/// <typeparam name="T">Type of delegate</typeparam>
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T Map<T>()
{
IntPtr codePtr = JitCache.Map(this);
return Marshal.GetDelegateForFunctionPointer<T>(codePtr);
}
}
}

View File

@@ -1,305 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using System;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.CodeGen.Optimizations
{
static class ConstantFolding
{
public static void RunPass(Operation operation)
{
if (operation.Destination == default || operation.SourcesCount == 0)
{
return;
}
if (!AreAllSourcesConstant(operation))
{
return;
}
OperandType type = operation.Destination.Type;
switch (operation.Instruction)
{
case Instruction.Add:
if (operation.GetSource(0).Relocatable ||
operation.GetSource(1).Relocatable)
{
break;
}
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x + y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x + y);
}
break;
case Instruction.BitwiseAnd:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x & y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x & y);
}
break;
case Instruction.BitwiseExclusiveOr:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x ^ y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x ^ y);
}
break;
case Instruction.BitwiseNot:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => ~x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => ~x);
}
break;
case Instruction.BitwiseOr:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x | y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x | y);
}
break;
case Instruction.ConvertI64ToI32:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => x);
}
break;
case Instruction.Copy:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => x);
}
break;
case Instruction.Divide:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => y != 0 ? x / y : 0);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => y != 0 ? x / y : 0);
}
break;
case Instruction.DivideUI:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => y != 0 ? (int)((uint)x / (uint)y) : 0);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => y != 0 ? (long)((ulong)x / (ulong)y) : 0);
}
break;
case Instruction.Multiply:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x * y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x * y);
}
break;
case Instruction.Negate:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => -x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => -x);
}
break;
case Instruction.ShiftLeft:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x << y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x << (int)y);
}
break;
case Instruction.ShiftRightSI:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x >> y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x >> (int)y);
}
break;
case Instruction.ShiftRightUI:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => (int)((uint)x >> y));
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => (long)((ulong)x >> (int)y));
}
break;
case Instruction.SignExtend16:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => (short)x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => (short)x);
}
break;
case Instruction.SignExtend32:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => (int)x);
}
break;
case Instruction.SignExtend8:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => (sbyte)x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => (sbyte)x);
}
break;
case Instruction.ZeroExtend16:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => (ushort)x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => (ushort)x);
}
break;
case Instruction.ZeroExtend32:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => (uint)x);
}
break;
case Instruction.ZeroExtend8:
if (type == OperandType.I32)
{
EvaluateUnaryI32(operation, (x) => (byte)x);
}
else if (type == OperandType.I64)
{
EvaluateUnaryI64(operation, (x) => (byte)x);
}
break;
case Instruction.Subtract:
if (type == OperandType.I32)
{
EvaluateBinaryI32(operation, (x, y) => x - y);
}
else if (type == OperandType.I64)
{
EvaluateBinaryI64(operation, (x, y) => x - y);
}
break;
}
}
private static bool AreAllSourcesConstant(Operation operation)
{
for (int index = 0; index < operation.SourcesCount; index++)
{
Operand srcOp = operation.GetSource(index);
if (srcOp.Kind != OperandKind.Constant)
{
return false;
}
}
return true;
}
private static void EvaluateUnaryI32(Operation operation, Func<int, int> op)
{
int x = operation.GetSource(0).AsInt32();
operation.TurnIntoCopy(Const(op(x)));
}
private static void EvaluateUnaryI64(Operation operation, Func<long, long> op)
{
long x = operation.GetSource(0).AsInt64();
operation.TurnIntoCopy(Const(op(x)));
}
private static void EvaluateBinaryI32(Operation operation, Func<int, int, int> op)
{
int x = operation.GetSource(0).AsInt32();
int y = operation.GetSource(1).AsInt32();
operation.TurnIntoCopy(Const(op(x, y)));
}
private static void EvaluateBinaryI64(Operation operation, Func<long, long, long> op)
{
long x = operation.GetSource(0).AsInt64();
long y = operation.GetSource(1).AsInt64();
operation.TurnIntoCopy(Const(op(x, y)));
}
}
}

View File

@@ -1,266 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.CodeGen.Optimizations
{
static class Optimizer
{
public static void RunPass(ControlFlowGraph cfg)
{
// Scratch buffer used to store uses.
Span<Operation> buffer = default;
bool modified;
do
{
modified = false;
for (BasicBlock block = cfg.Blocks.Last; block != null; block = block.ListPrevious)
{
Operation node;
Operation prevNode;
for (node = block.Operations.Last; node != default; node = prevNode)
{
prevNode = node.ListPrevious;
if (IsUnused(node))
{
RemoveNode(block, node);
modified = true;
continue;
}
else if (node.Instruction == Instruction.Phi)
{
continue;
}
ConstantFolding.RunPass(node);
Simplification.RunPass(node);
if (DestIsLocalVar(node))
{
if (IsPropagableCompare(node))
{
modified |= PropagateCompare(ref buffer, node);
if (modified && IsUnused(node))
{
RemoveNode(block, node);
}
}
else if (IsPropagableCopy(node))
{
PropagateCopy(ref buffer, node);
RemoveNode(block, node);
modified = true;
}
}
}
}
}
while (modified);
}
public static void RemoveUnusedNodes(ControlFlowGraph cfg)
{
bool modified;
do
{
modified = false;
for (BasicBlock block = cfg.Blocks.Last; block != null; block = block.ListPrevious)
{
Operation node;
Operation prevNode;
for (node = block.Operations.Last; node != default; node = prevNode)
{
prevNode = node.ListPrevious;
if (IsUnused(node))
{
RemoveNode(block, node);
modified = true;
}
}
}
}
while (modified);
}
private static Span<Operation> GetUses(ref Span<Operation> buffer, Operand operand)
{
ReadOnlySpan<Operation> uses = operand.Uses;
if (buffer.Length < uses.Length)
{
buffer = Allocators.Default.AllocateSpan<Operation>((uint)uses.Length);
}
uses.CopyTo(buffer);
return buffer.Slice(0, uses.Length);
}
private static bool PropagateCompare(ref Span<Operation> buffer, Operation compOp)
{
// Try to propagate Compare operations into their BranchIf uses, when these BranchIf uses are in the form
// of:
//
// - BranchIf %x, 0x0, Equal ;; i.e BranchIfFalse %x
// - BranchIf %x, 0x0, NotEqual ;; i.e BranchIfTrue %x
//
// The commutative property of Equal and NotEqual is taken into consideration as well.
//
// For example:
//
// %x = Compare %a, %b, comp
// BranchIf %x, 0x0, NotEqual
//
// =>
//
// BranchIf %a, %b, comp
static bool IsZeroBranch(Operation operation, out Comparison compType)
{
compType = Comparison.Equal;
if (operation.Instruction != Instruction.BranchIf)
{
return false;
}
Operand src1 = operation.GetSource(0);
Operand src2 = operation.GetSource(1);
Operand comp = operation.GetSource(2);
compType = (Comparison)comp.AsInt32();
return (src1.Kind == OperandKind.Constant && src1.Value == 0) ||
(src2.Kind == OperandKind.Constant && src2.Value == 0);
}
bool modified = false;
Operand dest = compOp.Destination;
Operand src1 = compOp.GetSource(0);
Operand src2 = compOp.GetSource(1);
Operand comp = compOp.GetSource(2);
Comparison compType = (Comparison)comp.AsInt32();
Span<Operation> uses = GetUses(ref buffer, dest);
foreach (Operation use in uses)
{
// If operation is a BranchIf and has a constant value 0 in its RHS or LHS source operands.
if (IsZeroBranch(use, out Comparison otherCompType))
{
Comparison propCompType;
if (otherCompType == Comparison.NotEqual)
{
propCompType = compType;
}
else if (otherCompType == Comparison.Equal)
{
propCompType = compType.Invert();
}
else
{
continue;
}
use.SetSource(0, src1);
use.SetSource(1, src2);
use.SetSource(2, Const((int)propCompType));
modified = true;
}
}
return modified;
}
private static void PropagateCopy(ref Span<Operation> buffer, Operation copyOp)
{
// Propagate copy source operand to all uses of the destination operand.
Operand dest = copyOp.Destination;
Operand source = copyOp.GetSource(0);
Span<Operation> uses = GetUses(ref buffer, dest);
foreach (Operation use in uses)
{
for (int index = 0; index < use.SourcesCount; index++)
{
if (use.GetSource(index) == dest)
{
use.SetSource(index, source);
}
}
}
}
private static void RemoveNode(BasicBlock block, Operation node)
{
// Remove a node from the nodes list, and also remove itself
// from all the use lists on the operands that this node uses.
block.Operations.Remove(node);
for (int index = 0; index < node.SourcesCount; index++)
{
node.SetSource(index, default);
}
Debug.Assert(node.Destination == default || node.Destination.UsesCount == 0);
node.Destination = default;
}
private static bool IsUnused(Operation node)
{
return DestIsLocalVar(node) && node.Destination.UsesCount == 0 && !HasSideEffects(node);
}
private static bool DestIsLocalVar(Operation node)
{
return node.Destination != default && node.Destination.Kind == OperandKind.LocalVariable;
}
private static bool HasSideEffects(Operation node)
{
return node.Instruction == Instruction.Call
|| node.Instruction == Instruction.Tailcall
|| node.Instruction == Instruction.CompareAndSwap
|| node.Instruction == Instruction.CompareAndSwap16
|| node.Instruction == Instruction.CompareAndSwap8;
}
private static bool IsPropagableCompare(Operation operation)
{
return operation.Instruction == Instruction.Compare;
}
private static bool IsPropagableCopy(Operation operation)
{
if (operation.Instruction != Instruction.Copy)
{
return false;
}
return operation.Destination.Type == operation.GetSource(0).Type;
}
}
}

View File

@@ -1,19 +0,0 @@
namespace ARMeilleure.CodeGen.RegisterAllocators
{
struct AllocationResult
{
public int IntUsedRegisters { get; }
public int VecUsedRegisters { get; }
public int SpillRegionSize { get; }
public AllocationResult(
int intUsedRegisters,
int vecUsedRegisters,
int spillRegionSize)
{
IntUsedRegisters = intUsedRegisters;
VecUsedRegisters = vecUsedRegisters;
SpillRegionSize = spillRegionSize;
}
}
}

View File

@@ -1,259 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using System;
using System.Collections.Generic;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
namespace ARMeilleure.CodeGen.RegisterAllocators
{
class CopyResolver
{
private class ParallelCopy
{
private struct Copy
{
public Register Dest { get; }
public Register Source { get; }
public OperandType Type { get; }
public Copy(Register dest, Register source, OperandType type)
{
Dest = dest;
Source = source;
Type = type;
}
}
private readonly List<Copy> _copies;
public int Count => _copies.Count;
public ParallelCopy()
{
_copies = new List<Copy>();
}
public void AddCopy(Register dest, Register source, OperandType type)
{
_copies.Add(new Copy(dest, source, type));
}
public void Sequence(List<Operation> sequence)
{
Dictionary<Register, Register> locations = new Dictionary<Register, Register>();
Dictionary<Register, Register> sources = new Dictionary<Register, Register>();
Dictionary<Register, OperandType> types = new Dictionary<Register, OperandType>();
Queue<Register> pendingQueue = new Queue<Register>();
Queue<Register> readyQueue = new Queue<Register>();
foreach (Copy copy in _copies)
{
locations[copy.Source] = copy.Source;
sources[copy.Dest] = copy.Source;
types[copy.Dest] = copy.Type;
pendingQueue.Enqueue(copy.Dest);
}
foreach (Copy copy in _copies)
{
// If the destination is not used anywhere, we can assign it immediately.
if (!locations.ContainsKey(copy.Dest))
{
readyQueue.Enqueue(copy.Dest);
}
}
while (pendingQueue.TryDequeue(out Register current))
{
Register copyDest;
Register origSource;
Register copySource;
while (readyQueue.TryDequeue(out copyDest))
{
origSource = sources[copyDest];
copySource = locations[origSource];
OperandType type = types[copyDest];
EmitCopy(sequence, GetRegister(copyDest, type), GetRegister(copySource, type));
locations[origSource] = copyDest;
if (origSource == copySource && sources.ContainsKey(origSource))
{
readyQueue.Enqueue(origSource);
}
}
copyDest = current;
origSource = sources[copyDest];
copySource = locations[origSource];
if (copyDest != copySource)
{
OperandType type = types[copyDest];
type = type.IsInteger() ? OperandType.I64 : OperandType.V128;
EmitXorSwap(sequence, GetRegister(copyDest, type), GetRegister(copySource, type));
locations[origSource] = copyDest;
Register swapOther = copySource;
if (copyDest != locations[sources[copySource]])
{
// Find the other swap destination register.
// To do that, we search all the pending registers, and pick
// the one where the copy source register is equal to the
// current destination register being processed (copyDest).
foreach (Register pending in pendingQueue)
{
// Is this a copy of pending <- copyDest?
if (copyDest == locations[sources[pending]])
{
swapOther = pending;
break;
}
}
}
// The value that was previously at "copyDest" now lives on
// "copySource" thanks to the swap, now we need to update the
// location for the next copy that is supposed to copy the value
// that used to live on "copyDest".
locations[sources[swapOther]] = copySource;
}
}
}
private static void EmitCopy(List<Operation> sequence, Operand x, Operand y)
{
sequence.Add(Operation(Instruction.Copy, x, y));
}
private static void EmitXorSwap(List<Operation> sequence, Operand x, Operand y)
{
sequence.Add(Operation(Instruction.BitwiseExclusiveOr, x, x, y));
sequence.Add(Operation(Instruction.BitwiseExclusiveOr, y, y, x));
sequence.Add(Operation(Instruction.BitwiseExclusiveOr, x, x, y));
}
}
private Queue<Operation> _fillQueue = null;
private Queue<Operation> _spillQueue = null;
private ParallelCopy _parallelCopy = null;
public bool HasCopy { get; private set; }
public void AddSplit(LiveInterval left, LiveInterval right)
{
if (left.Local != right.Local)
{
throw new ArgumentException("Intervals of different variables are not allowed.");
}
OperandType type = left.Local.Type;
if (left.IsSpilled && !right.IsSpilled)
{
// Move from the stack to a register.
AddSplitFill(left, right, type);
}
else if (!left.IsSpilled && right.IsSpilled)
{
// Move from a register to the stack.
AddSplitSpill(left, right, type);
}
else if (!left.IsSpilled && !right.IsSpilled && left.Register != right.Register)
{
// Move from one register to another.
AddSplitCopy(left, right, type);
}
else if (left.SpillOffset != right.SpillOffset)
{
// This would be the stack-to-stack move case, but this is not supported.
throw new ArgumentException("Both intervals were spilled.");
}
}
private void AddSplitFill(LiveInterval left, LiveInterval right, OperandType type)
{
if (_fillQueue == null)
{
_fillQueue = new Queue<Operation>();
}
Operand register = GetRegister(right.Register, type);
Operand offset = Const(left.SpillOffset);
_fillQueue.Enqueue(Operation(Instruction.Fill, register, offset));
HasCopy = true;
}
private void AddSplitSpill(LiveInterval left, LiveInterval right, OperandType type)
{
if (_spillQueue == null)
{
_spillQueue = new Queue<Operation>();
}
Operand offset = Const(right.SpillOffset);
Operand register = GetRegister(left.Register, type);
_spillQueue.Enqueue(Operation(Instruction.Spill, default, offset, register));
HasCopy = true;
}
private void AddSplitCopy(LiveInterval left, LiveInterval right, OperandType type)
{
if (_parallelCopy == null)
{
_parallelCopy = new ParallelCopy();
}
_parallelCopy.AddCopy(right.Register, left.Register, type);
HasCopy = true;
}
public Operation[] Sequence()
{
List<Operation> sequence = new List<Operation>();
if (_spillQueue != null)
{
while (_spillQueue.TryDequeue(out Operation spillOp))
{
sequence.Add(spillOp);
}
}
_parallelCopy?.Sequence(sequence);
if (_fillQueue != null)
{
while (_fillQueue.TryDequeue(out Operation fillOp))
{
sequence.Add(fillOp);
}
}
return sequence.ToArray();
}
private static Operand GetRegister(Register reg, OperandType type)
{
return Register(reg.Index, reg.Type, type);
}
}
}

View File

@@ -1,454 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
namespace ARMeilleure.CodeGen.RegisterAllocators
{
class HybridAllocator : IRegisterAllocator
{
private struct BlockInfo
{
public bool HasCall { get; }
public int IntFixedRegisters { get; }
public int VecFixedRegisters { get; }
public BlockInfo(bool hasCall, int intFixedRegisters, int vecFixedRegisters)
{
HasCall = hasCall;
IntFixedRegisters = intFixedRegisters;
VecFixedRegisters = vecFixedRegisters;
}
}
private struct LocalInfo
{
public int Uses { get; set; }
public int UsesAllocated { get; set; }
public int Sequence { get; set; }
public Operand Temp { get; set; }
public Operand Register { get; set; }
public Operand SpillOffset { get; set; }
public OperandType Type { get; }
private int _first;
private int _last;
public bool IsBlockLocal => _first == _last;
public LocalInfo(OperandType type, int uses, int blkIndex)
{
Uses = uses;
Type = type;
UsesAllocated = 0;
Sequence = 0;
Temp = default;
Register = default;
SpillOffset = default;
_first = -1;
_last = -1;
SetBlockIndex(blkIndex);
}
public void SetBlockIndex(int blkIndex)
{
if (_first == -1 || blkIndex < _first)
{
_first = blkIndex;
}
if (_last == -1 || blkIndex > _last)
{
_last = blkIndex;
}
}
}
private const int MaxIROperands = 4;
// The "visited" state is stored in the MSB of the local's value.
private const ulong VisitedMask = 1ul << 63;
private BlockInfo[] _blockInfo;
private LocalInfo[] _localInfo;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsVisited(Operand local)
{
Debug.Assert(local.Kind == OperandKind.LocalVariable);
return (local.GetValueUnsafe() & VisitedMask) != 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetVisited(Operand local)
{
Debug.Assert(local.Kind == OperandKind.LocalVariable);
local.GetValueUnsafe() |= VisitedMask;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref LocalInfo GetLocalInfo(Operand local)
{
Debug.Assert(local.Kind == OperandKind.LocalVariable);
Debug.Assert(IsVisited(local), "Local variable not visited. Used before defined?");
return ref _localInfo[(uint)local.GetValueUnsafe() - 1];
}
public AllocationResult RunPass(ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks)
{
int intUsedRegisters = 0;
int vecUsedRegisters = 0;
int intFreeRegisters = regMasks.IntAvailableRegisters;
int vecFreeRegisters = regMasks.VecAvailableRegisters;
_blockInfo = new BlockInfo[cfg.Blocks.Count];
_localInfo = new LocalInfo[cfg.Blocks.Count * 3];
int localInfoCount = 0;
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
{
BasicBlock block = cfg.PostOrderBlocks[index];
int intFixedRegisters = 0;
int vecFixedRegisters = 0;
bool hasCall = false;
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
{
if (node.Instruction == Instruction.Call)
{
hasCall = true;
}
foreach (Operand source in node.SourcesUnsafe)
{
if (source.Kind == OperandKind.LocalVariable)
{
GetLocalInfo(source).SetBlockIndex(block.Index);
}
else if (source.Kind == OperandKind.Memory)
{
MemoryOperand memOp = source.GetMemory();
if (memOp.BaseAddress != default)
{
GetLocalInfo(memOp.BaseAddress).SetBlockIndex(block.Index);
}
if (memOp.Index != default)
{
GetLocalInfo(memOp.Index).SetBlockIndex(block.Index);
}
}
}
foreach (Operand dest in node.DestinationsUnsafe)
{
if (dest.Kind == OperandKind.LocalVariable)
{
if (IsVisited(dest))
{
GetLocalInfo(dest).SetBlockIndex(block.Index);
}
else
{
dest.NumberLocal(++localInfoCount);
if (localInfoCount > _localInfo.Length)
{
Array.Resize(ref _localInfo, localInfoCount * 2);
}
SetVisited(dest);
GetLocalInfo(dest) = new LocalInfo(dest.Type, UsesCount(dest), block.Index);
}
}
else if (dest.Kind == OperandKind.Register)
{
if (dest.Type.IsInteger())
{
intFixedRegisters |= 1 << dest.GetRegister().Index;
}
else
{
vecFixedRegisters |= 1 << dest.GetRegister().Index;
}
}
}
}
_blockInfo[block.Index] = new BlockInfo(hasCall, intFixedRegisters, vecFixedRegisters);
}
int sequence = 0;
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
{
BasicBlock block = cfg.PostOrderBlocks[index];
ref BlockInfo blkInfo = ref _blockInfo[block.Index];
int intLocalFreeRegisters = intFreeRegisters & ~blkInfo.IntFixedRegisters;
int vecLocalFreeRegisters = vecFreeRegisters & ~blkInfo.VecFixedRegisters;
int intCallerSavedRegisters = blkInfo.HasCall ? regMasks.IntCallerSavedRegisters : 0;
int vecCallerSavedRegisters = blkInfo.HasCall ? regMasks.VecCallerSavedRegisters : 0;
int intSpillTempRegisters = SelectSpillTemps(
intCallerSavedRegisters & ~blkInfo.IntFixedRegisters,
intLocalFreeRegisters);
int vecSpillTempRegisters = SelectSpillTemps(
vecCallerSavedRegisters & ~blkInfo.VecFixedRegisters,
vecLocalFreeRegisters);
intLocalFreeRegisters &= ~(intSpillTempRegisters | intCallerSavedRegisters);
vecLocalFreeRegisters &= ~(vecSpillTempRegisters | vecCallerSavedRegisters);
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
{
int intLocalUse = 0;
int vecLocalUse = 0;
Operand AllocateRegister(Operand local)
{
ref LocalInfo info = ref GetLocalInfo(local);
info.UsesAllocated++;
Debug.Assert(info.UsesAllocated <= info.Uses);
if (info.Register != default)
{
if (info.UsesAllocated == info.Uses)
{
Register reg = info.Register.GetRegister();
if (local.Type.IsInteger())
{
intLocalFreeRegisters |= 1 << reg.Index;
}
else
{
vecLocalFreeRegisters |= 1 << reg.Index;
}
}
return info.Register;
}
else
{
Operand temp = info.Temp;
if (temp == default || info.Sequence != sequence)
{
temp = local.Type.IsInteger()
? GetSpillTemp(local, intSpillTempRegisters, ref intLocalUse)
: GetSpillTemp(local, vecSpillTempRegisters, ref vecLocalUse);
info.Sequence = sequence;
info.Temp = temp;
}
Operation fillOp = Operation(Instruction.Fill, temp, info.SpillOffset);
block.Operations.AddBefore(node, fillOp);
return temp;
}
}
bool folded = false;
// If operation is a copy of a local and that local is living on the stack, we turn the copy into
// a fill, instead of inserting a fill before it.
if (node.Instruction == Instruction.Copy)
{
Operand source = node.GetSource(0);
if (source.Kind == OperandKind.LocalVariable)
{
ref LocalInfo info = ref GetLocalInfo(source);
if (info.Register == default)
{
Operation fillOp = Operation(Instruction.Fill, node.Destination, info.SpillOffset);
block.Operations.AddBefore(node, fillOp);
block.Operations.Remove(node);
node = fillOp;
folded = true;
}
}
}
if (!folded)
{
foreach (ref Operand source in node.SourcesUnsafe)
{
if (source.Kind == OperandKind.LocalVariable)
{
source = AllocateRegister(source);
}
else if (source.Kind == OperandKind.Memory)
{
MemoryOperand memOp = source.GetMemory();
if (memOp.BaseAddress != default)
{
memOp.BaseAddress = AllocateRegister(memOp.BaseAddress);
}
if (memOp.Index != default)
{
memOp.Index = AllocateRegister(memOp.Index);
}
}
}
}
int intLocalAsg = 0;
int vecLocalAsg = 0;
foreach (ref Operand dest in node.DestinationsUnsafe)
{
if (dest.Kind != OperandKind.LocalVariable)
{
continue;
}
ref LocalInfo info = ref GetLocalInfo(dest);
if (info.UsesAllocated == 0)
{
int mask = dest.Type.IsInteger()
? intLocalFreeRegisters
: vecLocalFreeRegisters;
if (info.IsBlockLocal && mask != 0)
{
int selectedReg = BitOperations.TrailingZeroCount(mask);
info.Register = Register(selectedReg, info.Type.ToRegisterType(), info.Type);
if (dest.Type.IsInteger())
{
intLocalFreeRegisters &= ~(1 << selectedReg);
intUsedRegisters |= 1 << selectedReg;
}
else
{
vecLocalFreeRegisters &= ~(1 << selectedReg);
vecUsedRegisters |= 1 << selectedReg;
}
}
else
{
info.Register = default;
info.SpillOffset = Const(stackAlloc.Allocate(dest.Type.GetSizeInBytes()));
}
}
info.UsesAllocated++;
Debug.Assert(info.UsesAllocated <= info.Uses);
if (info.Register != default)
{
dest = info.Register;
}
else
{
Operand temp = info.Temp;
if (temp == default || info.Sequence != sequence)
{
temp = dest.Type.IsInteger()
? GetSpillTemp(dest, intSpillTempRegisters, ref intLocalAsg)
: GetSpillTemp(dest, vecSpillTempRegisters, ref vecLocalAsg);
info.Sequence = sequence;
info.Temp = temp;
}
dest = temp;
Operation spillOp = Operation(Instruction.Spill, default, info.SpillOffset, temp);
block.Operations.AddAfter(node, spillOp);
node = spillOp;
}
}
sequence++;
intUsedRegisters |= intLocalAsg | intLocalUse;
vecUsedRegisters |= vecLocalAsg | vecLocalUse;
}
}
return new AllocationResult(intUsedRegisters, vecUsedRegisters, stackAlloc.TotalSize);
}
private static int SelectSpillTemps(int mask0, int mask1)
{
int selection = 0;
int count = 0;
while (count < MaxIROperands && mask0 != 0)
{
int mask = mask0 & -mask0;
selection |= mask;
mask0 &= ~mask;
count++;
}
while (count < MaxIROperands && mask1 != 0)
{
int mask = mask1 & -mask1;
selection |= mask;
mask1 &= ~mask;
count++;
}
Debug.Assert(count == MaxIROperands, "No enough registers for spill temps.");
return selection;
}
private static Operand GetSpillTemp(Operand local, int freeMask, ref int useMask)
{
int selectedReg = BitOperations.TrailingZeroCount(freeMask & ~useMask);
useMask |= 1 << selectedReg;
return Register(selectedReg, local.Type.ToRegisterType(), local.Type);
}
private static int UsesCount(Operand local)
{
return local.AssignmentsCount + local.UsesCount;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,394 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ARMeilleure.CodeGen.RegisterAllocators
{
unsafe readonly struct LiveInterval : IComparable<LiveInterval>
{
public const int NotFound = -1;
private struct Data
{
public int End;
public int SpillOffset;
public LiveRange FirstRange;
public LiveRange PrevRange;
public LiveRange CurrRange;
public LiveInterval Parent;
public UseList Uses;
public LiveIntervalList Children;
public Operand Local;
public Register Register;
public bool IsFixed;
}
private readonly Data* _data;
private ref int End => ref _data->End;
private ref LiveRange FirstRange => ref _data->FirstRange;
private ref LiveRange CurrRange => ref _data->CurrRange;
private ref LiveRange PrevRange => ref _data->PrevRange;
private ref LiveInterval Parent => ref _data->Parent;
private ref UseList Uses => ref _data->Uses;
private ref LiveIntervalList Children => ref _data->Children;
public Operand Local => _data->Local;
public ref Register Register => ref _data->Register;
public ref int SpillOffset => ref _data->SpillOffset;
public bool IsFixed => _data->IsFixed;
public bool IsEmpty => FirstRange == default;
public bool IsSplit => Children.Count != 0;
public bool IsSpilled => SpillOffset != -1;
public int UsesCount => Uses.Count;
public LiveInterval(Operand local = default, LiveInterval parent = default)
{
_data = Allocators.LiveIntervals.Allocate<Data>();
*_data = default;
_data->IsFixed = false;
_data->Local = local;
Parent = parent == default ? this : parent;
Uses = new UseList();
Children = new LiveIntervalList();
FirstRange = default;
CurrRange = default;
PrevRange = default;
SpillOffset = -1;
}
public LiveInterval(Register register) : this(local: default, parent: default)
{
_data->IsFixed = true;
Register = register;
}
public void Reset()
{
PrevRange = default;
CurrRange = FirstRange;
}
public void Forward(int position)
{
LiveRange prev = PrevRange;
LiveRange curr = CurrRange;
while (curr != default && curr.Start < position && !curr.Overlaps(position))
{
prev = curr;
curr = curr.Next;
}
PrevRange = prev;
CurrRange = curr;
}
public int GetStart()
{
Debug.Assert(!IsEmpty, "Empty LiveInterval cannot have a start position.");
return FirstRange.Start;
}
public void SetStart(int position)
{
if (FirstRange != default)
{
Debug.Assert(position != FirstRange.End);
FirstRange.Start = position;
}
else
{
FirstRange = new LiveRange(position, position + 1);
End = position + 1;
}
}
public int GetEnd()
{
Debug.Assert(!IsEmpty, "Empty LiveInterval cannot have an end position.");
return End;
}
public void AddRange(int start, int end)
{
Debug.Assert(start < end, $"Invalid range start position {start}, {end}");
if (FirstRange != default)
{
// If the new range ends exactly where the first range start, then coalesce together.
if (end == FirstRange.Start)
{
FirstRange.Start = start;
return;
}
// If the new range is already contained, then coalesce together.
else if (FirstRange.Overlaps(start, end))
{
FirstRange.Start = Math.Min(FirstRange.Start, start);
FirstRange.End = Math.Max(FirstRange.End, end);
End = Math.Max(End, end);
Debug.Assert(FirstRange.Next == default || !FirstRange.Overlaps(FirstRange.Next));
return;
}
}
FirstRange = new LiveRange(start, end, FirstRange);
End = Math.Max(End, end);
Debug.Assert(FirstRange.Next == default || !FirstRange.Overlaps(FirstRange.Next));
}
public void AddUsePosition(int position)
{
Uses.Add(position);
}
public bool Overlaps(int position)
{
LiveRange curr = CurrRange;
while (curr != default && curr.Start <= position)
{
if (curr.Overlaps(position))
{
return true;
}
curr = curr.Next;
}
return false;
}
public bool Overlaps(LiveInterval other)
{
return GetOverlapPosition(other) != NotFound;
}
public int GetOverlapPosition(LiveInterval other)
{
LiveRange a = CurrRange;
LiveRange b = other.CurrRange;
while (a != default)
{
while (b != default && b.Start < a.Start)
{
if (a.Overlaps(b))
{
return a.Start;
}
b = b.Next;
}
if (b == default)
{
break;
}
else if (a.Overlaps(b))
{
return a.Start;
}
a = a.Next;
}
return NotFound;
}
public ReadOnlySpan<LiveInterval> SplitChildren()
{
return Parent.Children.Span;
}
public ReadOnlySpan<int> UsePositions()
{
return Uses.Span;
}
public int FirstUse()
{
return Uses.FirstUse;
}
public int NextUseAfter(int position)
{
return Uses.NextUse(position);
}
public LiveInterval Split(int position)
{
LiveInterval result = new(Local, Parent);
result.End = End;
LiveRange prev = PrevRange;
LiveRange curr = CurrRange;
while (curr != default && curr.Start < position && !curr.Overlaps(position))
{
prev = curr;
curr = curr.Next;
}
if (curr.Start >= position)
{
prev.Next = default;
result.FirstRange = curr;
End = prev.End;
}
else
{
result.FirstRange = new LiveRange(position, curr.End, curr.Next);
curr.End = position;
curr.Next = default;
End = curr.End;
}
result.Uses = Uses.Split(position);
AddSplitChild(result);
Debug.Assert(!IsEmpty, "Left interval is empty after split.");
Debug.Assert(!result.IsEmpty, "Right interval is empty after split.");
// Make sure the iterator in the new split is pointing to the start.
result.Reset();
return result;
}
private void AddSplitChild(LiveInterval child)
{
Debug.Assert(!child.IsEmpty, "Trying to insert an empty interval.");
Parent.Children.Add(child);
}
public LiveInterval GetSplitChild(int position)
{
if (Overlaps(position))
{
return this;
}
foreach (LiveInterval splitChild in SplitChildren())
{
if (splitChild.Overlaps(position))
{
return splitChild;
}
else if (splitChild.GetStart() > position)
{
break;
}
}
return default;
}
public bool TrySpillWithSiblingOffset()
{
foreach (LiveInterval splitChild in SplitChildren())
{
if (splitChild.IsSpilled)
{
Spill(splitChild.SpillOffset);
return true;
}
}
return false;
}
public void Spill(int offset)
{
SpillOffset = offset;
}
public int CompareTo(LiveInterval interval)
{
if (FirstRange == default || interval.FirstRange == default)
{
return 0;
}
return GetStart().CompareTo(interval.GetStart());
}
public bool Equals(LiveInterval interval)
{
return interval._data == _data;
}
public override bool Equals(object obj)
{
return obj is LiveInterval interval && Equals(interval);
}
public static bool operator ==(LiveInterval a, LiveInterval b)
{
return a.Equals(b);
}
public static bool operator !=(LiveInterval a, LiveInterval b)
{
return !a.Equals(b);
}
public override int GetHashCode()
{
return HashCode.Combine((IntPtr)_data);
}
public override string ToString()
{
LiveInterval self = this;
IEnumerable<string> GetRanges()
{
LiveRange curr = self.CurrRange;
while (curr != default)
{
if (curr == self.CurrRange)
{
yield return "*" + curr;
}
else
{
yield return curr.ToString();
}
curr = curr.Next;
}
}
return string.Join(", ", GetRanges());
}
}
}

View File

@@ -1,47 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using System;
namespace ARMeilleure.CodeGen.RegisterAllocators
{
struct RegisterMasks
{
public int IntAvailableRegisters { get; }
public int VecAvailableRegisters { get; }
public int IntCallerSavedRegisters { get; }
public int VecCallerSavedRegisters { get; }
public int IntCalleeSavedRegisters { get; }
public int VecCalleeSavedRegisters { get; }
public RegisterMasks(
int intAvailableRegisters,
int vecAvailableRegisters,
int intCallerSavedRegisters,
int vecCallerSavedRegisters,
int intCalleeSavedRegisters,
int vecCalleeSavedRegisters)
{
IntAvailableRegisters = intAvailableRegisters;
VecAvailableRegisters = vecAvailableRegisters;
IntCallerSavedRegisters = intCallerSavedRegisters;
VecCallerSavedRegisters = vecCallerSavedRegisters;
IntCalleeSavedRegisters = intCalleeSavedRegisters;
VecCalleeSavedRegisters = vecCalleeSavedRegisters;
}
public int GetAvailableRegisters(RegisterType type)
{
if (type == RegisterType.Integer)
{
return IntAvailableRegisters;
}
else if (type == RegisterType.Vector)
{
return VecAvailableRegisters;
}
else
{
throw new ArgumentException($"Invalid register type \"{type}\".");
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,291 +0,0 @@
using System;
namespace ARMeilleure.CodeGen.X86
{
partial class Assembler
{
public static bool SupportsVexPrefix(X86Instruction inst)
{
return _instTable[(int)inst].Flags.HasFlag(InstructionFlags.Vex);
}
private const int BadOp = 0;
[Flags]
private enum InstructionFlags
{
None = 0,
RegOnly = 1 << 0,
Reg8Src = 1 << 1,
Reg8Dest = 1 << 2,
RexW = 1 << 3,
Vex = 1 << 4,
PrefixBit = 16,
PrefixMask = 7 << PrefixBit,
Prefix66 = 1 << PrefixBit,
PrefixF3 = 2 << PrefixBit,
PrefixF2 = 4 << PrefixBit
}
private readonly struct InstructionInfo
{
public int OpRMR { get; }
public int OpRMImm8 { get; }
public int OpRMImm32 { get; }
public int OpRImm64 { get; }
public int OpRRM { get; }
public InstructionFlags Flags { get; }
public InstructionInfo(
int opRMR,
int opRMImm8,
int opRMImm32,
int opRImm64,
int opRRM,
InstructionFlags flags)
{
OpRMR = opRMR;
OpRMImm8 = opRMImm8;
OpRMImm32 = opRMImm32;
OpRImm64 = opRImm64;
OpRRM = opRRM;
Flags = flags;
}
}
private readonly static InstructionInfo[] _instTable;
static Assembler()
{
_instTable = new InstructionInfo[(int)X86Instruction.Count];
// Name RM/R RM/I8 RM/I32 R/I64 R/RM Flags
Add(X86Instruction.Add, new InstructionInfo(0x00000001, 0x00000083, 0x00000081, BadOp, 0x00000003, InstructionFlags.None));
Add(X86Instruction.Addpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Addps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex));
Add(X86Instruction.Addsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Addss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f58, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Aesdec, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38de, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Aesdeclast, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38df, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Aesenc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38dc, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Aesenclast, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38dd, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Aesimc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38db, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.And, new InstructionInfo(0x00000021, 0x04000083, 0x04000081, BadOp, 0x00000023, InstructionFlags.None));
Add(X86Instruction.Andnpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Andnps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex));
Add(X86Instruction.Andpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Andps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex));
Add(X86Instruction.Blendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3815, InstructionFlags.Prefix66));
Add(X86Instruction.Blendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3814, InstructionFlags.Prefix66));
Add(X86Instruction.Bsr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbd, InstructionFlags.None));
Add(X86Instruction.Bswap, new InstructionInfo(0x00000fc8, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RegOnly));
Add(X86Instruction.Call, new InstructionInfo(0x020000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Cmovcc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f40, InstructionFlags.None));
Add(X86Instruction.Cmp, new InstructionInfo(0x00000039, 0x07000083, 0x07000081, BadOp, 0x0000003b, InstructionFlags.None));
Add(X86Instruction.Cmppd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Cmpps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex));
Add(X86Instruction.Cmpsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Cmpss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Cmpxchg, new InstructionInfo(0x00000fb1, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Cmpxchg16b, new InstructionInfo(0x01000fc7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RexW));
Add(X86Instruction.Cmpxchg8, new InstructionInfo(0x00000fb0, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Reg8Src));
Add(X86Instruction.Comisd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Comiss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstructionFlags.Vex));
Add(X86Instruction.Crc32, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f1, InstructionFlags.PrefixF2));
Add(X86Instruction.Crc32_16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f1, InstructionFlags.PrefixF2 | InstructionFlags.Prefix66));
Add(X86Instruction.Crc32_8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38f0, InstructionFlags.PrefixF2 | InstructionFlags.Reg8Src));
Add(X86Instruction.Cvtdq2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Cvtdq2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex));
Add(X86Instruction.Cvtpd2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Cvtpd2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Cvtps2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Cvtps2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex));
Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Cvtsd2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Cvtsi2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Cvtss2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Cvtss2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Div, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x060000f7, InstructionFlags.None));
Add(X86Instruction.Divpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Divps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex));
Add(X86Instruction.Divsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Divss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Gf2p8affineqb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3ace, InstructionFlags.Prefix66));
Add(X86Instruction.Haddpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Haddps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7c, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Idiv, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x070000f7, InstructionFlags.None));
Add(X86Instruction.Imul, new InstructionInfo(BadOp, 0x0000006b, 0x00000069, BadOp, 0x00000faf, InstructionFlags.None));
Add(X86Instruction.Imul128, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x050000f7, InstructionFlags.None));
Add(X86Instruction.Insertps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a21, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Jmp, new InstructionInfo(0x040000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Ldmxcsr, new InstructionInfo(0x02000fae, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex));
Add(X86Instruction.Lea, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x0000008d, InstructionFlags.None));
Add(X86Instruction.Maxpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Maxps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex));
Add(X86Instruction.Maxsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Maxss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5f, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Minpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Minps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex));
Add(X86Instruction.Minsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Minss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5d, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Mov, new InstructionInfo(0x00000089, BadOp, 0x000000c7, 0x000000b8, 0x0000008b, InstructionFlags.None));
Add(X86Instruction.Mov16, new InstructionInfo(0x00000089, BadOp, 0x000000c7, BadOp, 0x0000008b, InstructionFlags.Prefix66));
Add(X86Instruction.Mov8, new InstructionInfo(0x00000088, 0x000000c6, BadOp, BadOp, 0x0000008a, InstructionFlags.Reg8Src | InstructionFlags.Reg8Dest));
Add(X86Instruction.Movd, new InstructionInfo(0x00000f7e, BadOp, BadOp, BadOp, 0x00000f6e, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Movdqu, new InstructionInfo(0x00000f7f, BadOp, BadOp, BadOp, 0x00000f6f, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Movhlps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f12, InstructionFlags.Vex));
Add(X86Instruction.Movlhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f16, InstructionFlags.Vex));
Add(X86Instruction.Movq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f7e, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Movsd, new InstructionInfo(0x00000f11, BadOp, BadOp, BadOp, 0x00000f10, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Movss, new InstructionInfo(0x00000f11, BadOp, BadOp, BadOp, 0x00000f10, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Movsx16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbf, InstructionFlags.None));
Add(X86Instruction.Movsx32, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000063, InstructionFlags.None));
Add(X86Instruction.Movsx8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbe, InstructionFlags.Reg8Src));
Add(X86Instruction.Movzx16, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb7, InstructionFlags.None));
Add(X86Instruction.Movzx8, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb6, InstructionFlags.Reg8Src));
Add(X86Instruction.Mul128, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x040000f7, InstructionFlags.None));
Add(X86Instruction.Mulpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Mulps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex));
Add(X86Instruction.Mulsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Mulss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f59, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Neg, new InstructionInfo(0x030000f7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Not, new InstructionInfo(0x020000f7, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Or, new InstructionInfo(0x00000009, 0x01000083, 0x01000081, BadOp, 0x0000000b, InstructionFlags.None));
Add(X86Instruction.Paddb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffc, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Paddd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffe, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Paddq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fd4, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Paddw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffd, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Palignr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0f, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pand, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fdb, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pandn, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fdf, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pavgb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe0, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pavgw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe3, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3810, InstructionFlags.Prefix66));
Add(X86Instruction.Pclmulqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a44, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpeqb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f74, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpeqd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f76, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpeqq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3829, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpeqw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f75, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpgtb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f64, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpgtd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f66, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpgtq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3837, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pcmpgtw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f65, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pextrb, new InstructionInfo(0x000f3a14, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pextrd, new InstructionInfo(0x000f3a16, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pextrq, new InstructionInfo(0x000f3a16, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.RexW | InstructionFlags.Prefix66));
Add(X86Instruction.Pextrw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc5, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pinsrb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a20, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pinsrd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a22, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pinsrq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a22, InstructionFlags.Vex | InstructionFlags.RexW | InstructionFlags.Prefix66));
Add(X86Instruction.Pinsrw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc4, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmaxsb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmaxsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383d, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmaxsw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fee, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmaxub, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fde, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmaxud, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383f, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmaxuw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383e, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pminsb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3838, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pminsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3839, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pminsw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fea, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pminub, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fda, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pminud, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383b, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pminuw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f383a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmovsxbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3820, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmovsxdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3825, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmovsxwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3823, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmovzxbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3830, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmovzxdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3835, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmovzxwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3833, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmulld, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3840, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pmullw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fd5, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pop, new InstructionInfo(0x0000008f, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Popcnt, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fb8, InstructionFlags.PrefixF3));
Add(X86Instruction.Por, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000feb, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pshufb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3800, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pshufd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f70, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pslld, new InstructionInfo(BadOp, 0x06000f72, BadOp, BadOp, 0x00000ff2, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Pslldq, new InstructionInfo(BadOp, 0x07000f73, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psllq, new InstructionInfo(BadOp, 0x06000f73, BadOp, BadOp, 0x00000ff3, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psllw, new InstructionInfo(BadOp, 0x06000f71, BadOp, BadOp, 0x00000ff1, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psrad, new InstructionInfo(BadOp, 0x04000f72, BadOp, BadOp, 0x00000fe2, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psraw, new InstructionInfo(BadOp, 0x04000f71, BadOp, BadOp, 0x00000fe1, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psrld, new InstructionInfo(BadOp, 0x02000f72, BadOp, BadOp, 0x00000fd2, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psrlq, new InstructionInfo(BadOp, 0x02000f73, BadOp, BadOp, 0x00000fd3, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psrldq, new InstructionInfo(BadOp, 0x03000f73, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psrlw, new InstructionInfo(BadOp, 0x02000f71, BadOp, BadOp, 0x00000fd1, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psubb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ff8, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psubd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffa, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psubq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ffb, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Psubw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000ff9, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpckhbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f68, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpckhdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpckhqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6d, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpckhwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f69, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpcklbw, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f60, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpckldq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f62, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpcklqdq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f6c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Punpcklwd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f61, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Push, new InstructionInfo(BadOp, 0x0000006a, 0x00000068, BadOp, 0x060000ff, InstructionFlags.None));
Add(X86Instruction.Pxor, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fef, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Rcpps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f53, InstructionFlags.Vex));
Add(X86Instruction.Rcpss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f53, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Ror, new InstructionInfo(0x010000d3, 0x010000c1, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Roundpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a09, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Roundps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a08, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Roundsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0b, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Roundss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a0a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Rsqrtps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f52, InstructionFlags.Vex));
Add(X86Instruction.Rsqrtss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f52, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Sar, new InstructionInfo(0x070000d3, 0x070000c1, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Setcc, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f90, InstructionFlags.Reg8Dest));
Add(X86Instruction.Sha256Msg1, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cc, InstructionFlags.None));
Add(X86Instruction.Sha256Msg2, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cd, InstructionFlags.None));
Add(X86Instruction.Sha256Rnds2, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38cb, InstructionFlags.None));
Add(X86Instruction.Shl, new InstructionInfo(0x040000d3, 0x040000c1, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Shr, new InstructionInfo(0x050000d3, 0x050000c1, BadOp, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Shufpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc6, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Shufps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc6, InstructionFlags.Vex));
Add(X86Instruction.Sqrtpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Sqrtps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex));
Add(X86Instruction.Sqrtsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Sqrtss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f51, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Stmxcsr, new InstructionInfo(0x03000fae, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex));
Add(X86Instruction.Sub, new InstructionInfo(0x00000029, 0x05000083, 0x05000081, BadOp, 0x0000002b, InstructionFlags.None));
Add(X86Instruction.Subpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Subps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex));
Add(X86Instruction.Subsd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.PrefixF2));
Add(X86Instruction.Subss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5c, InstructionFlags.Vex | InstructionFlags.PrefixF3));
Add(X86Instruction.Test, new InstructionInfo(0x00000085, BadOp, 0x000000f7, BadOp, BadOp, InstructionFlags.None));
Add(X86Instruction.Unpckhpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Unpckhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex));
Add(X86Instruction.Unpcklpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Unpcklps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex));
Add(X86Instruction.Vblendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4b, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex));
static void Add(X86Instruction inst, in InstructionInfo info)
{
_instTable[(int)inst] = info;
}
}
}
}

View File

@@ -1,104 +0,0 @@
using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.IntermediateRepresentation;
using System.IO;
using System.Numerics;
namespace ARMeilleure.CodeGen.X86
{
class CodeGenContext
{
private readonly Stream _stream;
private readonly Operand[] _blockLabels;
public int StreamOffset => (int)_stream.Length;
public AllocationResult AllocResult { get; }
public Assembler Assembler { get; }
public BasicBlock CurrBlock { get; private set; }
public int CallArgsRegionSize { get; }
public int XmmSaveRegionSize { get; }
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
{
_stream = new MemoryStream();
_blockLabels = new Operand[blocksCount];
AllocResult = allocResult;
Assembler = new Assembler(_stream, relocatable);
CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
XmmSaveRegionSize = xmmSaveRegionSize;
}
private static int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
{
// We need to add 8 bytes to the total size, as the call to this function already pushed 8 bytes (the
// return address).
int intMask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.IntUsedRegisters;
int vecMask = CallingConvention.GetVecCalleeSavedRegisters() & allocResult.VecUsedRegisters;
xmmSaveRegionSize = BitOperations.PopCount((uint)vecMask) * 16;
int calleeSaveRegionSize = BitOperations.PopCount((uint)intMask) * 8 + xmmSaveRegionSize + 8;
int argsCount = maxCallArgs;
if (argsCount < 0)
{
// When the function has no calls, argsCount is -1. In this case, we don't need to allocate the shadow
// space.
argsCount = 0;
}
else if (argsCount < 4)
{
// The ABI mandates that the space for at least 4 arguments is reserved on the stack (this is called
// shadow space).
argsCount = 4;
}
// TODO: Align XMM save region to 16 bytes because unwinding on Windows requires it.
int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;
// TODO: Instead of always multiplying by 16 (the largest possible size of a variable, since a V128 has 16
// bytes), we should calculate the exact size consumed by the arguments passed to the called functions on
// the stack.
int callArgsAndFrameSize = frameSize + argsCount * 16;
// Ensure that the Stack Pointer will be aligned to 16 bytes.
callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;
return callArgsAndFrameSize - frameSize;
}
public void EnterBlock(BasicBlock block)
{
Assembler.MarkLabel(GetLabel(block));
CurrBlock = block;
}
public void JumpTo(BasicBlock target)
{
Assembler.Jmp(GetLabel(target));
}
public void JumpTo(X86Condition condition, BasicBlock target)
{
Assembler.Jcc(condition, GetLabel(target));
}
private Operand GetLabel(BasicBlock block)
{
ref Operand label = ref _blockLabels[block.Index];
if (label == default)
{
label = Operand.Factory.Label();
}
return label;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,89 +0,0 @@
using System;
using System.Runtime.Intrinsics.X86;
namespace ARMeilleure.CodeGen.X86
{
static class HardwareCapabilities
{
static HardwareCapabilities()
{
if (!X86Base.IsSupported)
{
return;
}
(int maxNum, _, _, _) = X86Base.CpuId(0x00000000, 0x00000000);
(_, _, int ecx1, int edx1) = X86Base.CpuId(0x00000001, 0x00000000);
FeatureInfo1Edx = (FeatureFlags1Edx)edx1;
FeatureInfo1Ecx = (FeatureFlags1Ecx)ecx1;
if (maxNum >= 7)
{
(_, int ebx7, int ecx7, _) = X86Base.CpuId(0x00000007, 0x00000000);
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
}
}
[Flags]
public enum FeatureFlags1Edx
{
Sse = 1 << 25,
Sse2 = 1 << 26
}
[Flags]
public enum FeatureFlags1Ecx
{
Sse3 = 1 << 0,
Pclmulqdq = 1 << 1,
Ssse3 = 1 << 9,
Fma = 1 << 12,
Sse41 = 1 << 19,
Sse42 = 1 << 20,
Popcnt = 1 << 23,
Aes = 1 << 25,
Avx = 1 << 28,
F16c = 1 << 29
}
[Flags]
public enum FeatureFlags7Ebx
{
Avx2 = 1 << 5,
Sha = 1 << 29
}
[Flags]
public enum FeatureFlags7Ecx
{
Gfni = 1 << 8,
}
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
public static bool SupportsSse3 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse3);
public static bool SupportsPclmulqdq => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Pclmulqdq);
public static bool SupportsSsse3 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Ssse3);
public static bool SupportsFma => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Fma);
public static bool SupportsSse41 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse41);
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx);
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);
public static bool ForceLegacySse { get; set; }
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
}
}

View File

@@ -1,14 +0,0 @@
namespace ARMeilleure.CodeGen.X86
{
struct IntrinsicInfo
{
public X86Instruction Inst { get; }
public IntrinsicType Type { get; }
public IntrinsicInfo(X86Instruction inst, IntrinsicType type)
{
Inst = inst;
Type = type;
}
}
}

View File

@@ -1,199 +0,0 @@
using ARMeilleure.Common;
using ARMeilleure.IntermediateRepresentation;
namespace ARMeilleure.CodeGen.X86
{
static class IntrinsicTable
{
private const int BadOp = 0;
private static IntrinsicInfo[] _intrinTable;
static IntrinsicTable()
{
_intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))];
Add(Intrinsic.X86Addpd, new IntrinsicInfo(X86Instruction.Addpd, IntrinsicType.Binary));
Add(Intrinsic.X86Addps, new IntrinsicInfo(X86Instruction.Addps, IntrinsicType.Binary));
Add(Intrinsic.X86Addsd, new IntrinsicInfo(X86Instruction.Addsd, IntrinsicType.Binary));
Add(Intrinsic.X86Addss, new IntrinsicInfo(X86Instruction.Addss, IntrinsicType.Binary));
Add(Intrinsic.X86Aesdec, new IntrinsicInfo(X86Instruction.Aesdec, IntrinsicType.Binary));
Add(Intrinsic.X86Aesdeclast, new IntrinsicInfo(X86Instruction.Aesdeclast, IntrinsicType.Binary));
Add(Intrinsic.X86Aesenc, new IntrinsicInfo(X86Instruction.Aesenc, IntrinsicType.Binary));
Add(Intrinsic.X86Aesenclast, new IntrinsicInfo(X86Instruction.Aesenclast, IntrinsicType.Binary));
Add(Intrinsic.X86Aesimc, new IntrinsicInfo(X86Instruction.Aesimc, IntrinsicType.Unary));
Add(Intrinsic.X86Andnpd, new IntrinsicInfo(X86Instruction.Andnpd, IntrinsicType.Binary));
Add(Intrinsic.X86Andnps, new IntrinsicInfo(X86Instruction.Andnps, IntrinsicType.Binary));
Add(Intrinsic.X86Andpd, new IntrinsicInfo(X86Instruction.Andpd, IntrinsicType.Binary));
Add(Intrinsic.X86Andps, new IntrinsicInfo(X86Instruction.Andps, IntrinsicType.Binary));
Add(Intrinsic.X86Blendvpd, new IntrinsicInfo(X86Instruction.Blendvpd, IntrinsicType.Ternary));
Add(Intrinsic.X86Blendvps, new IntrinsicInfo(X86Instruction.Blendvps, IntrinsicType.Ternary));
Add(Intrinsic.X86Cmppd, new IntrinsicInfo(X86Instruction.Cmppd, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpps, new IntrinsicInfo(X86Instruction.Cmpps, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpsd, new IntrinsicInfo(X86Instruction.Cmpsd, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpss, new IntrinsicInfo(X86Instruction.Cmpss, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Comisdeq, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_));
Add(Intrinsic.X86Comisdge, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_));
Add(Intrinsic.X86Comisdlt, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_));
Add(Intrinsic.X86Comisseq, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_));
Add(Intrinsic.X86Comissge, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_));
Add(Intrinsic.X86Comisslt, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_));
Add(Intrinsic.X86Crc32, new IntrinsicInfo(X86Instruction.Crc32, IntrinsicType.Crc32));
Add(Intrinsic.X86Crc32_16, new IntrinsicInfo(X86Instruction.Crc32_16, IntrinsicType.Crc32));
Add(Intrinsic.X86Crc32_8, new IntrinsicInfo(X86Instruction.Crc32_8, IntrinsicType.Crc32));
Add(Intrinsic.X86Cvtdq2pd, new IntrinsicInfo(X86Instruction.Cvtdq2pd, IntrinsicType.Unary));
Add(Intrinsic.X86Cvtdq2ps, new IntrinsicInfo(X86Instruction.Cvtdq2ps, IntrinsicType.Unary));
Add(Intrinsic.X86Cvtpd2dq, new IntrinsicInfo(X86Instruction.Cvtpd2dq, IntrinsicType.Unary));
Add(Intrinsic.X86Cvtpd2ps, new IntrinsicInfo(X86Instruction.Cvtpd2ps, IntrinsicType.Unary));
Add(Intrinsic.X86Cvtps2dq, new IntrinsicInfo(X86Instruction.Cvtps2dq, IntrinsicType.Unary));
Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary));
Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr));
Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary));
Add(Intrinsic.X86Cvtsi2sd, new IntrinsicInfo(X86Instruction.Cvtsi2sd, IntrinsicType.BinaryGpr));
Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr));
Add(Intrinsic.X86Cvtsi2ss, new IntrinsicInfo(X86Instruction.Cvtsi2ss, IntrinsicType.BinaryGpr));
Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary));
Add(Intrinsic.X86Cvtss2si, new IntrinsicInfo(X86Instruction.Cvtss2si, IntrinsicType.UnaryToGpr));
Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary));
Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary));
Add(Intrinsic.X86Divsd, new IntrinsicInfo(X86Instruction.Divsd, IntrinsicType.Binary));
Add(Intrinsic.X86Divss, new IntrinsicInfo(X86Instruction.Divss, IntrinsicType.Binary));
Add(Intrinsic.X86Gf2p8affineqb, new IntrinsicInfo(X86Instruction.Gf2p8affineqb, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
Add(Intrinsic.X86Maxss, new IntrinsicInfo(X86Instruction.Maxss, IntrinsicType.Binary));
Add(Intrinsic.X86Minpd, new IntrinsicInfo(X86Instruction.Minpd, IntrinsicType.Binary));
Add(Intrinsic.X86Minps, new IntrinsicInfo(X86Instruction.Minps, IntrinsicType.Binary));
Add(Intrinsic.X86Minsd, new IntrinsicInfo(X86Instruction.Minsd, IntrinsicType.Binary));
Add(Intrinsic.X86Minss, new IntrinsicInfo(X86Instruction.Minss, IntrinsicType.Binary));
Add(Intrinsic.X86Movhlps, new IntrinsicInfo(X86Instruction.Movhlps, IntrinsicType.Binary));
Add(Intrinsic.X86Movlhps, new IntrinsicInfo(X86Instruction.Movlhps, IntrinsicType.Binary));
Add(Intrinsic.X86Movss, new IntrinsicInfo(X86Instruction.Movss, IntrinsicType.Binary));
Add(Intrinsic.X86Mulpd, new IntrinsicInfo(X86Instruction.Mulpd, IntrinsicType.Binary));
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits.
Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits.
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
Add(Intrinsic.X86Paddw, new IntrinsicInfo(X86Instruction.Paddw, IntrinsicType.Binary));
Add(Intrinsic.X86Palignr, new IntrinsicInfo(X86Instruction.Palignr, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Pand, new IntrinsicInfo(X86Instruction.Pand, IntrinsicType.Binary));
Add(Intrinsic.X86Pandn, new IntrinsicInfo(X86Instruction.Pandn, IntrinsicType.Binary));
Add(Intrinsic.X86Pavgb, new IntrinsicInfo(X86Instruction.Pavgb, IntrinsicType.Binary));
Add(Intrinsic.X86Pavgw, new IntrinsicInfo(X86Instruction.Pavgw, IntrinsicType.Binary));
Add(Intrinsic.X86Pblendvb, new IntrinsicInfo(X86Instruction.Pblendvb, IntrinsicType.Ternary));
Add(Intrinsic.X86Pclmulqdq, new IntrinsicInfo(X86Instruction.Pclmulqdq, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Pcmpeqb, new IntrinsicInfo(X86Instruction.Pcmpeqb, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpeqd, new IntrinsicInfo(X86Instruction.Pcmpeqd, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpeqq, new IntrinsicInfo(X86Instruction.Pcmpeqq, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpeqw, new IntrinsicInfo(X86Instruction.Pcmpeqw, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpgtb, new IntrinsicInfo(X86Instruction.Pcmpgtb, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpgtd, new IntrinsicInfo(X86Instruction.Pcmpgtd, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpgtq, new IntrinsicInfo(X86Instruction.Pcmpgtq, IntrinsicType.Binary));
Add(Intrinsic.X86Pcmpgtw, new IntrinsicInfo(X86Instruction.Pcmpgtw, IntrinsicType.Binary));
Add(Intrinsic.X86Pmaxsb, new IntrinsicInfo(X86Instruction.Pmaxsb, IntrinsicType.Binary));
Add(Intrinsic.X86Pmaxsd, new IntrinsicInfo(X86Instruction.Pmaxsd, IntrinsicType.Binary));
Add(Intrinsic.X86Pmaxsw, new IntrinsicInfo(X86Instruction.Pmaxsw, IntrinsicType.Binary));
Add(Intrinsic.X86Pmaxub, new IntrinsicInfo(X86Instruction.Pmaxub, IntrinsicType.Binary));
Add(Intrinsic.X86Pmaxud, new IntrinsicInfo(X86Instruction.Pmaxud, IntrinsicType.Binary));
Add(Intrinsic.X86Pmaxuw, new IntrinsicInfo(X86Instruction.Pmaxuw, IntrinsicType.Binary));
Add(Intrinsic.X86Pminsb, new IntrinsicInfo(X86Instruction.Pminsb, IntrinsicType.Binary));
Add(Intrinsic.X86Pminsd, new IntrinsicInfo(X86Instruction.Pminsd, IntrinsicType.Binary));
Add(Intrinsic.X86Pminsw, new IntrinsicInfo(X86Instruction.Pminsw, IntrinsicType.Binary));
Add(Intrinsic.X86Pminub, new IntrinsicInfo(X86Instruction.Pminub, IntrinsicType.Binary));
Add(Intrinsic.X86Pminud, new IntrinsicInfo(X86Instruction.Pminud, IntrinsicType.Binary));
Add(Intrinsic.X86Pminuw, new IntrinsicInfo(X86Instruction.Pminuw, IntrinsicType.Binary));
Add(Intrinsic.X86Pmovsxbw, new IntrinsicInfo(X86Instruction.Pmovsxbw, IntrinsicType.Unary));
Add(Intrinsic.X86Pmovsxdq, new IntrinsicInfo(X86Instruction.Pmovsxdq, IntrinsicType.Unary));
Add(Intrinsic.X86Pmovsxwd, new IntrinsicInfo(X86Instruction.Pmovsxwd, IntrinsicType.Unary));
Add(Intrinsic.X86Pmovzxbw, new IntrinsicInfo(X86Instruction.Pmovzxbw, IntrinsicType.Unary));
Add(Intrinsic.X86Pmovzxdq, new IntrinsicInfo(X86Instruction.Pmovzxdq, IntrinsicType.Unary));
Add(Intrinsic.X86Pmovzxwd, new IntrinsicInfo(X86Instruction.Pmovzxwd, IntrinsicType.Unary));
Add(Intrinsic.X86Pmulld, new IntrinsicInfo(X86Instruction.Pmulld, IntrinsicType.Binary));
Add(Intrinsic.X86Pmullw, new IntrinsicInfo(X86Instruction.Pmullw, IntrinsicType.Binary));
Add(Intrinsic.X86Popcnt, new IntrinsicInfo(X86Instruction.Popcnt, IntrinsicType.PopCount));
Add(Intrinsic.X86Por, new IntrinsicInfo(X86Instruction.Por, IntrinsicType.Binary));
Add(Intrinsic.X86Pshufb, new IntrinsicInfo(X86Instruction.Pshufb, IntrinsicType.Binary));
Add(Intrinsic.X86Pshufd, new IntrinsicInfo(X86Instruction.Pshufd, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Pslld, new IntrinsicInfo(X86Instruction.Pslld, IntrinsicType.Binary));
Add(Intrinsic.X86Pslldq, new IntrinsicInfo(X86Instruction.Pslldq, IntrinsicType.Binary));
Add(Intrinsic.X86Psllq, new IntrinsicInfo(X86Instruction.Psllq, IntrinsicType.Binary));
Add(Intrinsic.X86Psllw, new IntrinsicInfo(X86Instruction.Psllw, IntrinsicType.Binary));
Add(Intrinsic.X86Psrad, new IntrinsicInfo(X86Instruction.Psrad, IntrinsicType.Binary));
Add(Intrinsic.X86Psraw, new IntrinsicInfo(X86Instruction.Psraw, IntrinsicType.Binary));
Add(Intrinsic.X86Psrld, new IntrinsicInfo(X86Instruction.Psrld, IntrinsicType.Binary));
Add(Intrinsic.X86Psrlq, new IntrinsicInfo(X86Instruction.Psrlq, IntrinsicType.Binary));
Add(Intrinsic.X86Psrldq, new IntrinsicInfo(X86Instruction.Psrldq, IntrinsicType.Binary));
Add(Intrinsic.X86Psrlw, new IntrinsicInfo(X86Instruction.Psrlw, IntrinsicType.Binary));
Add(Intrinsic.X86Psubb, new IntrinsicInfo(X86Instruction.Psubb, IntrinsicType.Binary));
Add(Intrinsic.X86Psubd, new IntrinsicInfo(X86Instruction.Psubd, IntrinsicType.Binary));
Add(Intrinsic.X86Psubq, new IntrinsicInfo(X86Instruction.Psubq, IntrinsicType.Binary));
Add(Intrinsic.X86Psubw, new IntrinsicInfo(X86Instruction.Psubw, IntrinsicType.Binary));
Add(Intrinsic.X86Punpckhbw, new IntrinsicInfo(X86Instruction.Punpckhbw, IntrinsicType.Binary));
Add(Intrinsic.X86Punpckhdq, new IntrinsicInfo(X86Instruction.Punpckhdq, IntrinsicType.Binary));
Add(Intrinsic.X86Punpckhqdq, new IntrinsicInfo(X86Instruction.Punpckhqdq, IntrinsicType.Binary));
Add(Intrinsic.X86Punpckhwd, new IntrinsicInfo(X86Instruction.Punpckhwd, IntrinsicType.Binary));
Add(Intrinsic.X86Punpcklbw, new IntrinsicInfo(X86Instruction.Punpcklbw, IntrinsicType.Binary));
Add(Intrinsic.X86Punpckldq, new IntrinsicInfo(X86Instruction.Punpckldq, IntrinsicType.Binary));
Add(Intrinsic.X86Punpcklqdq, new IntrinsicInfo(X86Instruction.Punpcklqdq, IntrinsicType.Binary));
Add(Intrinsic.X86Punpcklwd, new IntrinsicInfo(X86Instruction.Punpcklwd, IntrinsicType.Binary));
Add(Intrinsic.X86Pxor, new IntrinsicInfo(X86Instruction.Pxor, IntrinsicType.Binary));
Add(Intrinsic.X86Rcpps, new IntrinsicInfo(X86Instruction.Rcpps, IntrinsicType.Unary));
Add(Intrinsic.X86Rcpss, new IntrinsicInfo(X86Instruction.Rcpss, IntrinsicType.Unary));
Add(Intrinsic.X86Roundpd, new IntrinsicInfo(X86Instruction.Roundpd, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Roundps, new IntrinsicInfo(X86Instruction.Roundps, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Roundsd, new IntrinsicInfo(X86Instruction.Roundsd, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Roundss, new IntrinsicInfo(X86Instruction.Roundss, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Rsqrtps, new IntrinsicInfo(X86Instruction.Rsqrtps, IntrinsicType.Unary));
Add(Intrinsic.X86Rsqrtss, new IntrinsicInfo(X86Instruction.Rsqrtss, IntrinsicType.Unary));
Add(Intrinsic.X86Sha256Msg1, new IntrinsicInfo(X86Instruction.Sha256Msg1, IntrinsicType.Binary));
Add(Intrinsic.X86Sha256Msg2, new IntrinsicInfo(X86Instruction.Sha256Msg2, IntrinsicType.Binary));
Add(Intrinsic.X86Sha256Rnds2, new IntrinsicInfo(X86Instruction.Sha256Rnds2, IntrinsicType.Ternary));
Add(Intrinsic.X86Shufpd, new IntrinsicInfo(X86Instruction.Shufpd, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Shufps, new IntrinsicInfo(X86Instruction.Shufps, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Sqrtpd, new IntrinsicInfo(X86Instruction.Sqrtpd, IntrinsicType.Unary));
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
Add(Intrinsic.X86Subss, new IntrinsicInfo(X86Instruction.Subss, IntrinsicType.Binary));
Add(Intrinsic.X86Unpckhpd, new IntrinsicInfo(X86Instruction.Unpckhpd, IntrinsicType.Binary));
Add(Intrinsic.X86Unpckhps, new IntrinsicInfo(X86Instruction.Unpckhps, IntrinsicType.Binary));
Add(Intrinsic.X86Unpcklpd, new IntrinsicInfo(X86Instruction.Unpcklpd, IntrinsicType.Binary));
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma));
Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary));
Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary));
}
private static void Add(Intrinsic intrin, IntrinsicInfo info)
{
_intrinTable[(int)intrin] = info;
}
public static IntrinsicInfo GetInfo(Intrinsic intrin)
{
return _intrinTable[(int)intrin];
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,228 +0,0 @@
namespace ARMeilleure.CodeGen.X86
{
enum X86Instruction
{
None,
Add,
Addpd,
Addps,
Addsd,
Addss,
Aesdec,
Aesdeclast,
Aesenc,
Aesenclast,
Aesimc,
And,
Andnpd,
Andnps,
Andpd,
Andps,
Blendvpd,
Blendvps,
Bsr,
Bswap,
Call,
Cmovcc,
Cmp,
Cmppd,
Cmpps,
Cmpsd,
Cmpss,
Cmpxchg,
Cmpxchg16b,
Cmpxchg8,
Comisd,
Comiss,
Crc32,
Crc32_16,
Crc32_8,
Cvtdq2pd,
Cvtdq2ps,
Cvtpd2dq,
Cvtpd2ps,
Cvtps2dq,
Cvtps2pd,
Cvtsd2si,
Cvtsd2ss,
Cvtsi2sd,
Cvtsi2ss,
Cvtss2sd,
Cvtss2si,
Div,
Divpd,
Divps,
Divsd,
Divss,
Gf2p8affineqb,
Haddpd,
Haddps,
Idiv,
Imul,
Imul128,
Insertps,
Jmp,
Ldmxcsr,
Lea,
Maxpd,
Maxps,
Maxsd,
Maxss,
Minpd,
Minps,
Minsd,
Minss,
Mov,
Mov16,
Mov8,
Movd,
Movdqu,
Movhlps,
Movlhps,
Movq,
Movsd,
Movss,
Movsx16,
Movsx32,
Movsx8,
Movzx16,
Movzx8,
Mul128,
Mulpd,
Mulps,
Mulsd,
Mulss,
Neg,
Not,
Or,
Paddb,
Paddd,
Paddq,
Paddw,
Palignr,
Pand,
Pandn,
Pavgb,
Pavgw,
Pblendvb,
Pclmulqdq,
Pcmpeqb,
Pcmpeqd,
Pcmpeqq,
Pcmpeqw,
Pcmpgtb,
Pcmpgtd,
Pcmpgtq,
Pcmpgtw,
Pextrb,
Pextrd,
Pextrq,
Pextrw,
Pinsrb,
Pinsrd,
Pinsrq,
Pinsrw,
Pmaxsb,
Pmaxsd,
Pmaxsw,
Pmaxub,
Pmaxud,
Pmaxuw,
Pminsb,
Pminsd,
Pminsw,
Pminub,
Pminud,
Pminuw,
Pmovsxbw,
Pmovsxdq,
Pmovsxwd,
Pmovzxbw,
Pmovzxdq,
Pmovzxwd,
Pmulld,
Pmullw,
Pop,
Popcnt,
Por,
Pshufb,
Pshufd,
Pslld,
Pslldq,
Psllq,
Psllw,
Psrad,
Psraw,
Psrld,
Psrlq,
Psrldq,
Psrlw,
Psubb,
Psubd,
Psubq,
Psubw,
Punpckhbw,
Punpckhdq,
Punpckhqdq,
Punpckhwd,
Punpcklbw,
Punpckldq,
Punpcklqdq,
Punpcklwd,
Push,
Pxor,
Rcpps,
Rcpss,
Ror,
Roundpd,
Roundps,
Roundsd,
Roundss,
Rsqrtps,
Rsqrtss,
Sar,
Setcc,
Sha256Msg1,
Sha256Msg2,
Sha256Rnds2,
Shl,
Shr,
Shufpd,
Shufps,
Sqrtpd,
Sqrtps,
Sqrtsd,
Sqrtss,
Stmxcsr,
Sub,
Subpd,
Subps,
Subsd,
Subss,
Test,
Unpckhpd,
Unpckhps,
Unpcklpd,
Unpcklps,
Vblendvpd,
Vblendvps,
Vcvtph2ps,
Vcvtps2ph,
Vfmadd231ps,
Vfmadd231sd,
Vfmadd231ss,
Vfmsub231sd,
Vfmsub231ss,
Vfnmadd231ps,
Vfnmadd231sd,
Vfnmadd231ss,
Vfnmsub231sd,
Vfnmsub231ss,
Vpblendvb,
Xor,
Xorpd,
Xorps,
Count
}
}

View File

@@ -1,261 +0,0 @@
using ARMeilleure.Diagnostics;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace ARMeilleure.Common
{
/// <summary>
/// Represents a table of guest address to a value.
/// </summary>
/// <typeparam name="TEntry">Type of the value</typeparam>
unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
{
/// <summary>
/// Represents a level in an <see cref="AddressTable{TEntry}"/>.
/// </summary>
public readonly struct Level
{
/// <summary>
/// Gets the index of the <see cref="Level"/> in the guest address.
/// </summary>
public int Index { get; }
/// <summary>
/// Gets the length of the <see cref="Level"/> in the guest address.
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the mask which masks the bits used by the <see cref="Level"/>.
/// </summary>
public ulong Mask => ((1ul << Length) - 1) << Index;
/// <summary>
/// Initializes a new instance of the <see cref="Level"/> structure with the specified
/// <paramref name="index"/> and <paramref name="length"/>.
/// </summary>
/// <param name="index">Index of the <see cref="Level"/></param>
/// <param name="length">Length of the <see cref="Level"/></param>
public Level(int index, int length)
{
(Index, Length) = (index, length);
}
/// <summary>
/// Gets the value of the <see cref="Level"/> from the specified guest <paramref name="address"/>.
/// </summary>
/// <param name="address">Guest address</param>
/// <returns>Value of the <see cref="Level"/> from the specified guest <paramref name="address"/></returns>
public int GetValue(ulong address)
{
return (int)((address & Mask) >> Index);
}
}
private bool _disposed;
private TEntry** _table;
private readonly List<IntPtr> _pages;
/// <summary>
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
public ulong Mask { get; }
/// <summary>
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
public Level[] Levels { get; }
/// <summary>
/// Gets or sets the default fill value of newly created leaf pages.
/// </summary>
public TEntry Fill { get; set; }
/// <summary>
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public IntPtr Base
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
lock (_pages)
{
return (IntPtr)GetRootPage();
}
}
}
/// <summary>
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
/// <see cref="Level"/>.
/// </summary>
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public AddressTable(Level[] levels)
{
if (levels == null)
{
throw new ArgumentNullException(nameof(levels));
}
if (levels.Length < 2)
{
throw new ArgumentException("Table must be at least 2 levels deep.", nameof(levels));
}
_pages = new List<IntPtr>(capacity: 16);
Levels = levels;
Mask = 0;
foreach (var level in Levels)
{
Mask |= level.Mask;
}
}
/// <summary>
/// Determines if the specified <paramref name="address"/> is in the range of the
/// <see cref="AddressTable{TEntry}"/>.
/// </summary>
/// <param name="address">Guest address</param>
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
public bool IsValid(ulong address)
{
return (address & ~Mask) == 0;
}
/// <summary>
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
/// </summary>
/// <param name="address">Guest address</param>
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
public ref TEntry GetValue(ulong address)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
if (!IsValid(address))
{
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
}
lock (_pages)
{
return ref GetPage(address)[Levels[^1].GetValue(address)];
}
}
/// <summary>
/// Gets the leaf page for the specified guest <paramref name="address"/>.
/// </summary>
/// <param name="address">Guest address</param>
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
private TEntry* GetPage(ulong address)
{
TEntry** page = GetRootPage();
for (int i = 0; i < Levels.Length - 1; i++)
{
ref Level level = ref Levels[i];
ref TEntry* nextPage = ref page[level.GetValue(address)];
if (nextPage == null)
{
ref Level nextLevel = ref Levels[i + 1];
nextPage = i == Levels.Length - 2 ?
(TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true) :
(TEntry*)Allocate(1 << nextLevel.Length, IntPtr.Zero, leaf: false);
}
page = (TEntry**)nextPage;
}
return (TEntry*)page;
}
/// <summary>
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
/// </summary>
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
private TEntry** GetRootPage()
{
if (_table == null)
{
_table = (TEntry**)Allocate(1 << Levels[0].Length, fill: IntPtr.Zero, leaf: false);
}
return _table;
}
/// <summary>
/// Allocates a block of memory of the specified type and length.
/// </summary>
/// <typeparam name="T">Type of elements</typeparam>
/// <param name="length">Number of elements</param>
/// <param name="fill">Fill value</param>
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
/// <returns>Allocated block</returns>
private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
{
var size = sizeof(T) * length;
var page = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
var span = new Span<T>((void*)page, length);
span.Fill(fill);
_pages.Add(page);
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
return page;
}
/// <summary>
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
/// instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
foreach (var page in _pages)
{
Marshal.FreeHGlobal(page);
}
_disposed = true;
}
}
/// <summary>
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
~AddressTable()
{
Dispose(false);
}
}
}

View File

@@ -1,61 +0,0 @@
using System.Numerics;
namespace ARMeilleure.Common
{
static class BitUtils
{
private static readonly sbyte[] HbsNibbleLut;
static BitUtils()
{
HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
}
public static long FillWithOnes(int bits)
{
return bits == 64 ? -1L : (1L << bits) - 1;
}
public static int HighestBitSet(int value)
{
return 31 - BitOperations.LeadingZeroCount((uint)value);
}
public static int HighestBitSetNibble(int value)
{
return HbsNibbleLut[value];
}
public static long Replicate(long bits, int size)
{
long output = 0;
for (int bit = 0; bit < 64; bit += size)
{
output |= bits << bit;
}
return output;
}
public static int RotateRight(int bits, int shift, int size)
{
return (int)RotateRight((uint)bits, shift, size);
}
public static uint RotateRight(uint bits, int shift, int size)
{
return (bits >> shift) | (bits << (size - shift));
}
public static long RotateRight(long bits, int shift, int size)
{
return (long)RotateRight((ulong)bits, shift, size);
}
public static ulong RotateRight(ulong bits, int shift, int size)
{
return (bits >> shift) | (bits << (size - shift));
}
}
}

View File

@@ -1,101 +0,0 @@
using System;
namespace ARMeilleure.Common
{
/// <summary>
/// Represents a numeric counter which can be used for instrumentation of compiled code.
/// </summary>
/// <typeparam name="T">Type of the counter</typeparam>
class Counter<T> : IDisposable where T : unmanaged
{
private bool _disposed;
/// <summary>
/// Index in the <see cref="EntryTable{T}"/>
/// </summary>
private readonly int _index;
private readonly EntryTable<T> _countTable;
/// <summary>
/// Initializes a new instance of the <see cref="Counter{T}"/> class from the specified
/// <see cref="EntryTable{T}"/> instance and index.
/// </summary>
/// <param name="countTable"><see cref="EntryTable{T}"/> instance</param>
/// <exception cref="ArgumentNullException"><paramref name="countTable"/> is <see langword="null"/></exception>
/// <exception cref="ArgumentException"><typeparamref name="T"/> is unsupported</exception>
public Counter(EntryTable<T> countTable)
{
if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) &&
typeof(T) != typeof(short) && typeof(T) != typeof(ushort) &&
typeof(T) != typeof(int) && typeof(T) != typeof(uint) &&
typeof(T) != typeof(long) && typeof(T) != typeof(ulong) &&
typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) &&
typeof(T) != typeof(float) && typeof(T) != typeof(double))
{
throw new ArgumentException("Counter does not support the specified type.");
}
_countTable = countTable ?? throw new ArgumentNullException(nameof(countTable));
_index = countTable.Allocate();
}
/// <summary>
/// Gets a reference to the value of the counter.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="Counter{T}"/> instance was disposed</exception>
/// <remarks>
/// This can refer to freed memory if the owning <see cref="EntryTable{TEntry}"/> is disposed.
/// </remarks>
public ref T Value
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
return ref _countTable.GetValue(_index);
}
}
/// <summary>
/// Releases all resources used by the <see cref="Counter{T}"/> instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all unmanaged and optionally managed resources used by the <see cref="Counter{T}"/> instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resources</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
try
{
// The index into the EntryTable is essentially an unmanaged resource since we allocate and free the
// resource ourselves.
_countTable.Free(_index);
}
catch (ObjectDisposedException)
{
// Can happen because _countTable may be disposed before the Counter instance.
}
_disposed = true;
}
}
/// <summary>
/// Frees resources used by the <see cref="Counter{T}"/> instance.
/// </summary>
~Counter()
{
Dispose(false);
}
}
}

View File

@@ -1,197 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
namespace ARMeilleure.Common
{
/// <summary>
/// Represents an expandable table of the type <typeparamref name="TEntry"/>, whose entries will remain at the same
/// address through out the table's lifetime.
/// </summary>
/// <typeparam name="TEntry">Type of the entry in the table</typeparam>
class EntryTable<TEntry> : IDisposable where TEntry : unmanaged
{
private bool _disposed;
private int _freeHint;
private readonly int _pageCapacity; // Number of entries per page.
private readonly int _pageLogCapacity;
private readonly Dictionary<int, IntPtr> _pages;
private readonly BitMap _allocated;
/// <summary>
/// Initializes a new instance of the <see cref="EntryTable{TEntry}"/> class with the desired page size in
/// bytes.
/// </summary>
/// <param name="pageSize">Desired page size in bytes</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="pageSize"/> is less than 0</exception>
/// <exception cref="ArgumentException"><typeparamref name="TEntry"/>'s size is zero</exception>
/// <remarks>
/// The actual page size may be smaller or larger depending on the size of <typeparamref name="TEntry"/>.
/// </remarks>
public unsafe EntryTable(int pageSize = 4096)
{
if (pageSize < 0)
{
throw new ArgumentOutOfRangeException(nameof(pageSize), "Page size cannot be negative.");
}
if (sizeof(TEntry) == 0)
{
throw new ArgumentException("Size of TEntry cannot be zero.");
}
_allocated = new BitMap(NativeAllocator.Instance);
_pages = new Dictionary<int, IntPtr>();
_pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry)));
_pageCapacity = 1 << _pageLogCapacity;
}
/// <summary>
/// Allocates an entry in the <see cref="EntryTable{TEntry}"/>.
/// </summary>
/// <returns>Index of entry allocated in the table</returns>
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public int Allocate()
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
lock (_allocated)
{
if (_allocated.IsSet(_freeHint))
{
_freeHint = _allocated.FindFirstUnset();
}
int index = _freeHint++;
var page = GetPage(index);
_allocated.Set(index);
GetValue(page, index) = default;
return index;
}
}
/// <summary>
/// Frees the entry at the specified <paramref name="index"/>.
/// </summary>
/// <param name="index">Index of entry to free</param>
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public void Free(int index)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
lock (_allocated)
{
if (_allocated.IsSet(index))
{
_allocated.Clear(index);
_freeHint = index;
}
}
}
/// <summary>
/// Gets a reference to the entry at the specified allocated <paramref name="index"/>.
/// </summary>
/// <param name="index">Index of the entry</param>
/// <returns>Reference to the entry at the specified <paramref name="index"/></returns>
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
/// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
public ref TEntry GetValue(int index)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
lock (_allocated)
{
if (!_allocated.IsSet(index))
{
throw new ArgumentException("Entry at the specified index was not allocated", nameof(index));
}
var page = GetPage(index);
return ref GetValue(page, index);
}
}
/// <summary>
/// Gets a reference to the entry at using the specified <paramref name="index"/> from the specified
/// <paramref name="page"/>.
/// </summary>
/// <param name="page">Page to use</param>
/// <param name="index">Index to use</param>
/// <returns>Reference to the entry</returns>
private ref TEntry GetValue(Span<TEntry> page, int index)
{
return ref page[index & (_pageCapacity - 1)];
}
/// <summary>
/// Gets the page for the specified <see cref="index"/>.
/// </summary>
/// <param name="index">Index to use</param>
/// <returns>Page for the specified <see cref="index"/></returns>
private unsafe Span<TEntry> GetPage(int index)
{
var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity);
if (!_pages.TryGetValue(pageIndex, out IntPtr page))
{
page = (IntPtr)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity);
_pages.Add(pageIndex, page);
}
return new Span<TEntry>((void*)page, _pageCapacity);
}
/// <summary>
/// Releases all resources used by the <see cref="EntryTable{TEntry}"/> instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all unmanaged and optionally managed resources used by the <see cref="EntryTable{TEntry}"/>
/// instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected unsafe virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_allocated.Dispose();
foreach (var page in _pages.Values)
{
NativeAllocator.Instance.Free((void*)page);
}
_disposed = true;
}
}
/// <summary>
/// Frees resources used by the <see cref="EntryTable{TEntry}"/> instance.
/// </summary>
~EntryTable()
{
Dispose(false);
}
}
}

View File

@@ -1,167 +0,0 @@
using ARMeilleure.Common;
namespace ARMeilleure.Decoders
{
static class DecoderHelper
{
static DecoderHelper()
{
Imm8ToFP32Table = BuildImm8ToFP32Table();
Imm8ToFP64Table = BuildImm8ToFP64Table();
}
public static readonly uint[] Imm8ToFP32Table;
public static readonly ulong[] Imm8ToFP64Table;
private static uint[] BuildImm8ToFP32Table()
{
uint[] tbl = new uint[256];
for (int idx = 0; idx < 256; idx++)
{
tbl[idx] = ExpandImm8ToFP32((uint)idx);
}
return tbl;
}
private static ulong[] BuildImm8ToFP64Table()
{
ulong[] tbl = new ulong[256];
for (int idx = 0; idx < 256; idx++)
{
tbl[idx] = ExpandImm8ToFP64((ulong)idx);
}
return tbl;
}
// abcdefgh -> aBbbbbbc defgh000 00000000 00000000 (B = ~b)
private static uint ExpandImm8ToFP32(uint imm)
{
uint MoveBit(uint bits, int from, int to)
{
return ((bits >> from) & 1U) << to;
}
return MoveBit(imm, 7, 31) | MoveBit(~imm, 6, 30) |
MoveBit(imm, 6, 29) | MoveBit( imm, 6, 28) |
MoveBit(imm, 6, 27) | MoveBit( imm, 6, 26) |
MoveBit(imm, 6, 25) | MoveBit( imm, 5, 24) |
MoveBit(imm, 4, 23) | MoveBit( imm, 3, 22) |
MoveBit(imm, 2, 21) | MoveBit( imm, 1, 20) |
MoveBit(imm, 0, 19);
}
// abcdefgh -> aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000 (B = ~b)
private static ulong ExpandImm8ToFP64(ulong imm)
{
ulong MoveBit(ulong bits, int from, int to)
{
return ((bits >> from) & 1UL) << to;
}
return MoveBit(imm, 7, 63) | MoveBit(~imm, 6, 62) |
MoveBit(imm, 6, 61) | MoveBit( imm, 6, 60) |
MoveBit(imm, 6, 59) | MoveBit( imm, 6, 58) |
MoveBit(imm, 6, 57) | MoveBit( imm, 6, 56) |
MoveBit(imm, 6, 55) | MoveBit( imm, 6, 54) |
MoveBit(imm, 5, 53) | MoveBit( imm, 4, 52) |
MoveBit(imm, 3, 51) | MoveBit( imm, 2, 50) |
MoveBit(imm, 1, 49) | MoveBit( imm, 0, 48);
}
public struct BitMask
{
public long WMask;
public long TMask;
public int Pos;
public int Shift;
public bool IsUndefined;
public static BitMask Invalid => new BitMask { IsUndefined = true };
}
public static BitMask DecodeBitMask(int opCode, bool immediate)
{
int immS = (opCode >> 10) & 0x3f;
int immR = (opCode >> 16) & 0x3f;
int n = (opCode >> 22) & 1;
int sf = (opCode >> 31) & 1;
int length = BitUtils.HighestBitSet((~immS & 0x3f) | (n << 6));
if (length < 1 || (sf == 0 && n != 0))
{
return BitMask.Invalid;
}
int size = 1 << length;
int levels = size - 1;
int s = immS & levels;
int r = immR & levels;
if (immediate && s == levels)
{
return BitMask.Invalid;
}
long wMask = BitUtils.FillWithOnes(s + 1);
long tMask = BitUtils.FillWithOnes(((s - r) & levels) + 1);
if (r > 0)
{
wMask = BitUtils.RotateRight(wMask, r, size);
wMask &= BitUtils.FillWithOnes(size);
}
return new BitMask()
{
WMask = BitUtils.Replicate(wMask, size),
TMask = BitUtils.Replicate(tMask, size),
Pos = immS,
Shift = immR
};
}
public static long DecodeImm24_2(int opCode)
{
return ((long)opCode << 40) >> 38;
}
public static long DecodeImm26_2(int opCode)
{
return ((long)opCode << 38) >> 36;
}
public static long DecodeImmS19_2(int opCode)
{
return (((long)opCode << 40) >> 43) & ~3;
}
public static long DecodeImmS14_2(int opCode)
{
return (((long)opCode << 45) >> 48) & ~3;
}
public static bool VectorArgumentsInvalid(bool q, params int[] args)
{
if (q)
{
for (int i = 0; i < args.Length; i++)
{
if ((args[i] & 1) == 1)
{
return true;
}
}
}
return false;
}
}
}

View File

@@ -1,6 +0,0 @@
namespace ARMeilleure.Decoders;
interface IOpCode32Exception
{
int Id { get; }
}

View File

@@ -1,18 +0,0 @@
using ARMeilleure.Instructions;
namespace ARMeilleure.Decoders
{
struct InstDescriptor
{
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);
public InstName Name { get; }
public InstEmitter Emitter { get; }
public InstDescriptor(InstName name, InstEmitter emitter)
{
Name = name;
Emitter = emitter;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,84 +0,0 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
namespace ARMeilleure.Diagnostics
{
static class Symbols
{
private struct RangedSymbol
{
public readonly ulong Start;
public readonly ulong End;
public readonly ulong ElementSize;
public readonly string Name;
public RangedSymbol(ulong start, ulong end, ulong elemSize, string name)
{
Start = start;
End = end;
ElementSize = elemSize;
Name = name;
}
}
private static readonly ConcurrentDictionary<ulong, string> _symbols;
private static readonly List<RangedSymbol> _rangedSymbols;
static Symbols()
{
_symbols = new ConcurrentDictionary<ulong, string>();
_rangedSymbols = new List<RangedSymbol>();
}
public static string Get(ulong address)
{
string result;
if (_symbols.TryGetValue(address, out result))
{
return result;
}
lock (_rangedSymbols)
{
foreach (RangedSymbol symbol in _rangedSymbols)
{
if (address >= symbol.Start && address <= symbol.End)
{
ulong diff = address - symbol.Start;
ulong rem = diff % symbol.ElementSize;
result = symbol.Name + "_" + diff / symbol.ElementSize;
if (rem != 0)
{
result += "+" + rem;
}
_symbols.TryAdd(address, result);
return result;
}
}
}
return null;
}
[Conditional("M_DEBUG")]
public static void Add(ulong address, string name)
{
_symbols.TryAdd(address, name);
}
[Conditional("M_DEBUG")]
public static void Add(ulong address, ulong size, ulong elemSize, string name)
{
lock (_rangedSymbols)
{
_rangedSymbols.Add(new RangedSymbol(address, address + size, elemSize, name));
}
}
}
}

View File

@@ -1,67 +0,0 @@
using System.Diagnostics.Tracing;
using System.Threading;
namespace ARMeilleure.Diagnostics
{
[EventSource(Name = "ARMeilleure")]
class TranslatorEventSource : EventSource
{
public static readonly TranslatorEventSource Log = new();
private int _rejitQueue;
private ulong _funcTabSize;
private ulong _funcTabLeafSize;
private PollingCounter _rejitQueueCounter;
private PollingCounter _funcTabSizeCounter;
private PollingCounter _funcTabLeafSizeCounter;
public TranslatorEventSource()
{
_rejitQueueCounter = new PollingCounter("rejit-queue-length", this, () => _rejitQueue)
{
DisplayName = "Rejit Queue Length"
};
_funcTabSizeCounter = new PollingCounter("addr-tab-alloc", this, () => _funcTabSize / 1024d / 1024d)
{
DisplayName = "AddressTable Total Bytes Allocated",
DisplayUnits = "MB"
};
_funcTabLeafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _funcTabLeafSize / 1024d / 1024d)
{
DisplayName = "AddressTable Total Leaf Bytes Allocated",
DisplayUnits = "MB"
};
}
public void RejitQueueAdd(int count)
{
Interlocked.Add(ref _rejitQueue, count);
}
public void AddressTableAllocated(int bytes, bool leaf)
{
_funcTabSize += (uint)bytes;
if (leaf)
{
_funcTabLeafSize += (uint)bytes;
}
}
protected override void Dispose(bool disposing)
{
_rejitQueueCounter.Dispose();
_rejitQueueCounter = null;
_funcTabLeafSizeCounter.Dispose();
_funcTabLeafSizeCounter = null;
_funcTabSizeCounter.Dispose();
_funcTabSizeCounter = null;
base.Dispose(disposing);
}
}
}

View File

@@ -1,54 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitHashHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Crc32b(ArmEmitterContext context)
{
EmitCrc32Call(context, ByteSizeLog2, false);
}
public static void Crc32h(ArmEmitterContext context)
{
EmitCrc32Call(context, HWordSizeLog2, false);
}
public static void Crc32w(ArmEmitterContext context)
{
EmitCrc32Call(context, WordSizeLog2, false);
}
public static void Crc32cb(ArmEmitterContext context)
{
EmitCrc32Call(context, ByteSizeLog2, true);
}
public static void Crc32ch(ArmEmitterContext context)
{
EmitCrc32Call(context, HWordSizeLog2, true);
}
public static void Crc32cw(ArmEmitterContext context)
{
EmitCrc32Call(context, WordSizeLog2, true);
}
private static void EmitCrc32Call(ArmEmitterContext context, int size, bool c)
{
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
Operand d = EmitCrc32(context, n, m, size, c);
EmitAluStore(context, d);
}
}
}

View File

@@ -1,119 +0,0 @@
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
namespace ARMeilleure.Instructions
{
static class InstEmitHashHelper
{
public const uint Crc32RevPoly = 0xedb88320;
public const uint Crc32cRevPoly = 0x82f63b78;
public static Operand EmitCrc32(ArmEmitterContext context, Operand crc, Operand value, int size, bool castagnoli)
{
Debug.Assert(crc.Type.IsInteger() && value.Type.IsInteger());
Debug.Assert(size >= 0 && size < 4);
Debug.Assert((size < 3) || (value.Type == OperandType.I64));
if (castagnoli && Optimizations.UseSse42)
{
// The CRC32 instruction does not have an immediate variant, so ensure both inputs are in registers.
value = (value.Kind == OperandKind.Constant) ? context.Copy(value) : value;
crc = (crc.Kind == OperandKind.Constant) ? context.Copy(crc) : crc;
Intrinsic op = size switch
{
0 => Intrinsic.X86Crc32_8,
1 => Intrinsic.X86Crc32_16,
_ => Intrinsic.X86Crc32,
};
return (size == 3) ? context.ConvertI64ToI32(context.AddIntrinsicLong(op, crc, value)) : context.AddIntrinsicInt(op, crc, value);
}
else if (Optimizations.UsePclmulqdq)
{
return size switch
{
3 => EmitCrc32Optimized64(context, crc, value, castagnoli),
_ => EmitCrc32Optimized(context, crc, value, castagnoli, size),
};
}
else
{
string name = (size, castagnoli) switch
{
(0, false) => nameof(SoftFallback.Crc32b),
(1, false) => nameof(SoftFallback.Crc32h),
(2, false) => nameof(SoftFallback.Crc32w),
(3, false) => nameof(SoftFallback.Crc32x),
(0, true) => nameof(SoftFallback.Crc32cb),
(1, true) => nameof(SoftFallback.Crc32ch),
(2, true) => nameof(SoftFallback.Crc32cw),
(3, true) => nameof(SoftFallback.Crc32cx),
_ => throw new ArgumentOutOfRangeException(nameof(size))
};
return context.Call(typeof(SoftFallback).GetMethod(name), crc, value);
}
}
private static Operand EmitCrc32Optimized(ArmEmitterContext context, Operand crc, Operand data, bool castagnoli, int size)
{
long mu = castagnoli ? 0x0DEA713F1 : 0x1F7011641; // mu' = floor(x^64/P(x))'
long polynomial = castagnoli ? 0x105EC76F0 : 0x1DB710641; // P'(x) << 1
crc = context.VectorInsert(context.VectorZero(), crc, 0);
switch (size)
{
case 0: data = context.VectorInsert8(context.VectorZero(), data, 0); break;
case 1: data = context.VectorInsert16(context.VectorZero(), data, 0); break;
case 2: data = context.VectorInsert(context.VectorZero(), data, 0); break;
}
int bitsize = 8 << size;
Operand tmp = context.AddIntrinsic(Intrinsic.X86Pxor, crc, data);
tmp = context.AddIntrinsic(Intrinsic.X86Psllq, tmp, Const(64 - bitsize));
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, mu), Const(0));
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, polynomial), Const(0));
if (bitsize < 32)
{
crc = context.AddIntrinsic(Intrinsic.X86Pslldq, crc, Const((64 - bitsize) / 8));
tmp = context.AddIntrinsic(Intrinsic.X86Pxor, tmp, crc);
}
return context.VectorExtract(OperandType.I32, tmp, 2);
}
private static Operand EmitCrc32Optimized64(ArmEmitterContext context, Operand crc, Operand data, bool castagnoli)
{
long mu = castagnoli ? 0x0DEA713F1 : 0x1F7011641; // mu' = floor(x^64/P(x))'
long polynomial = castagnoli ? 0x105EC76F0 : 0x1DB710641; // P'(x) << 1
crc = context.VectorInsert(context.VectorZero(), crc, 0);
data = context.VectorInsert(context.VectorZero(), data, 0);
Operand tmp = context.AddIntrinsic(Intrinsic.X86Pxor, crc, data);
Operand res = context.AddIntrinsic(Intrinsic.X86Pslldq, tmp, Const(4));
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, res, X86GetScalar(context, mu), Const(0));
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, polynomial), Const(0));
tmp = context.AddIntrinsic(Intrinsic.X86Pxor, tmp, res);
tmp = context.AddIntrinsic(Intrinsic.X86Psllq, tmp, Const(32));
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, mu), Const(1));
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, polynomial), Const(0));
return context.VectorExtract(OperandType.I32, tmp, 2);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,785 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
using Func2I = Func<Operand, Operand, Operand>;
static partial class InstEmit
{
public static void Cmeq_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: true);
}
public static void Cmeq_V(ArmEmitterContext context)
{
if (Optimizations.UseSse41)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m;
if (op is OpCodeSimdReg binOp)
{
m = GetVec(binOp.Rm);
}
else
{
m = context.VectorZero();
}
Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
Operand res = context.AddIntrinsic(cmpInst, n, m);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: false);
}
}
public static void Cmge_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqual(op1, op2), scalar: true);
}
public static void Cmge_V(ArmEmitterContext context)
{
if (Optimizations.UseSse42)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m;
if (op is OpCodeSimdReg binOp)
{
m = GetVec(binOp.Rm);
}
else
{
m = context.VectorZero();
}
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
Operand res = context.AddIntrinsic(cmpInst, m, n);
Operand mask = X86GetAllElements(context, -1L);
res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqual(op1, op2), scalar: false);
}
}
public static void Cmgt_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreater(op1, op2), scalar: true);
}
public static void Cmgt_V(ArmEmitterContext context)
{
if (Optimizations.UseSse42)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m;
if (op is OpCodeSimdReg binOp)
{
m = GetVec(binOp.Rm);
}
else
{
m = context.VectorZero();
}
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
Operand res = context.AddIntrinsic(cmpInst, n, m);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreater(op1, op2), scalar: false);
}
}
public static void Cmhi_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterUI(op1, op2), scalar: true);
}
public static void Cmhi_V(ArmEmitterContext context)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
if (Optimizations.UseSse41 && op.Size < 3)
{
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Intrinsic maxInst = X86PmaxuInstruction[op.Size];
Operand res = context.AddIntrinsic(maxInst, m, n);
Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
res = context.AddIntrinsic(cmpInst, res, m);
Operand mask = X86GetAllElements(context, -1L);
res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterUI(op1, op2), scalar: false);
}
}
public static void Cmhs_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqualUI(op1, op2), scalar: true);
}
public static void Cmhs_V(ArmEmitterContext context)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
if (Optimizations.UseSse41 && op.Size < 3)
{
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Intrinsic maxInst = X86PmaxuInstruction[op.Size];
Operand res = context.AddIntrinsic(maxInst, n, m);
Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
res = context.AddIntrinsic(cmpInst, res, n);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqualUI(op1, op2), scalar: false);
}
}
public static void Cmle_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareLessOrEqual(op1, op2), scalar: true);
}
public static void Cmle_V(ArmEmitterContext context)
{
if (Optimizations.UseSse42)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
Operand res = context.AddIntrinsic(cmpInst, n, context.VectorZero());
Operand mask = X86GetAllElements(context, -1L);
res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareLessOrEqual(op1, op2), scalar: false);
}
}
public static void Cmlt_S(ArmEmitterContext context)
{
EmitCmpOp(context, (op1, op2) => context.ICompareLess(op1, op2), scalar: true);
}
public static void Cmlt_V(ArmEmitterContext context)
{
if (Optimizations.UseSse42)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
Operand res = context.AddIntrinsic(cmpInst, context.VectorZero(), n);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitCmpOp(context, (op1, op2) => context.ICompareLess(op1, op2), scalar: false);
}
}
public static void Cmtst_S(ArmEmitterContext context)
{
EmitCmtstOp(context, scalar: true);
}
public static void Cmtst_V(ArmEmitterContext context)
{
EmitCmtstOp(context, scalar: false);
}
public static void Facge_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, absolute: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true, absolute: true);
}
}
public static void Facge_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, absolute: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false, absolute: true);
}
}
public static void Facgt_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true, absolute: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true, absolute: true);
}
}
public static void Facgt_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false, absolute: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false, absolute: true);
}
}
public static void Fccmp_S(ArmEmitterContext context)
{
EmitFccmpOrFccmpe(context, signalNaNs: false);
}
public static void Fccmpe_S(ArmEmitterContext context)
{
EmitFccmpOrFccmpe(context, signalNaNs: true);
}
public static void Fcmeq_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: true);
}
}
public static void Fcmeq_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: false);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: false);
}
}
public static void Fcmge_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true);
}
}
public static void Fcmge_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false);
}
}
public static void Fcmgt_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true);
}
}
public static void Fcmgt_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false);
}
}
public static void Fcmle_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: true);
}
}
public static void Fcmle_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: false);
}
}
public static void Fcmlt_S(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: true);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: true);
}
}
public static void Fcmlt_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: false);
}
else
{
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: false);
}
}
public static void Fcmp_S(ArmEmitterContext context)
{
EmitFcmpOrFcmpe(context, signalNaNs: false);
}
public static void Fcmpe_S(ArmEmitterContext context)
{
EmitFcmpOrFcmpe(context, signalNaNs: true);
}
private static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs)
{
OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp;
Operand lblTrue = Label();
Operand lblEnd = Label();
context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond));
EmitSetNzcv(context, op.Nzcv);
context.Branch(lblEnd);
context.MarkLabel(lblTrue);
EmitFcmpOrFcmpe(context, signalNaNs);
context.MarkLabel(lblEnd);
}
private static void EmitSetNzcv(ArmEmitterContext context, int nzcv)
{
Operand Extract(int value, int bit)
{
if (bit != 0)
{
value >>= bit;
}
value &= 1;
return Const(value);
}
SetFlag(context, PState.VFlag, Extract(nzcv, 0));
SetFlag(context, PState.CFlag, Extract(nzcv, 1));
SetFlag(context, PState.ZFlag, Extract(nzcv, 2));
SetFlag(context, PState.NFlag, Extract(nzcv, 3));
}
private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false;
if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2))
{
Operand n = GetVec(op.Rn);
Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm);
CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
Operand lblNaN = Label();
Operand lblEnd = Label();
if (op.Size == 0)
{
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered));
Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask);
context.BranchIfFalse(lblNaN, isOrdered);
Operand nCopy = context.Copy(n);
Operand mCopy = cmpWithZero ? context.VectorZero() : context.Copy(m);
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, nCopy, mCopy);
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, nCopy, mCopy);
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, nCopy, mCopy);
SetFlag(context, PState.VFlag, Const(0));
SetFlag(context, PState.CFlag, cf);
SetFlag(context, PState.ZFlag, zf);
SetFlag(context, PState.NFlag, nf);
}
else /* if (op.Size == 1) */
{
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered));
Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask);
context.BranchIfFalse(lblNaN, isOrdered);
Operand nCopy = context.Copy(n);
Operand mCopy = cmpWithZero ? context.VectorZero() : context.Copy(m);
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, nCopy, mCopy);
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, nCopy, mCopy);
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, nCopy, mCopy);
SetFlag(context, PState.VFlag, Const(0));
SetFlag(context, PState.CFlag, cf);
SetFlag(context, PState.ZFlag, zf);
SetFlag(context, PState.NFlag, nf);
}
context.Branch(lblEnd);
context.MarkLabel(lblNaN);
SetFlag(context, PState.VFlag, Const(1));
SetFlag(context, PState.CFlag, Const(1));
SetFlag(context, PState.ZFlag, Const(0));
SetFlag(context, PState.NFlag, Const(0));
context.MarkLabel(lblEnd);
}
else
{
OperandType type = op.Size != 0 ? OperandType.FP64 : OperandType.FP32;
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
Operand me;
if (cmpWithZero)
{
me = op.Size == 0 ? ConstF(0f) : ConstF(0d);
}
else
{
me = context.VectorExtract(type, GetVec(op.Rm), 0);
}
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
EmitSetNzcv(context, nzcv);
}
}
private static void EmitSetNzcv(ArmEmitterContext context, Operand nzcv)
{
Operand Extract(Operand value, int bit)
{
if (bit != 0)
{
value = context.ShiftRightUI(value, Const(bit));
}
value = context.BitwiseAnd(value, Const(1));
return value;
}
SetFlag(context, PState.VFlag, Extract(nzcv, 0));
SetFlag(context, PState.CFlag, Extract(nzcv, 1));
SetFlag(context, PState.ZFlag, Extract(nzcv, 2));
SetFlag(context, PState.NFlag, Extract(nzcv, 3));
}
private static void EmitCmpOp(ArmEmitterContext context, Func2I emitCmp, bool scalar)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand res = context.VectorZero();
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
Operand me;
if (op is OpCodeSimdReg binOp)
{
me = EmitVectorExtractSx(context, binOp.Rm, index, op.Size);
}
else
{
me = Const(0L);
}
Operand isTrue = emitCmp(ne, me);
Operand mask = context.ConditionalSelect(isTrue, Const(szMask), Const(0L));
res = EmitVectorInsert(context, res, mask, index, op.Size);
}
context.Copy(GetVec(op.Rd), res);
}
private static void EmitCmtstOp(ArmEmitterContext context, bool scalar)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand res = context.VectorZero();
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size);
Operand test = context.BitwiseAnd(ne, me);
Operand isTrue = context.ICompareNotEqual(test, Const(0L));
Operand mask = context.ConditionalSelect(isTrue, Const(szMask), Const(0L));
res = EmitVectorInsert(context, res, mask, index, op.Size);
}
context.Copy(GetVec(op.Rd), res);
}
private static void EmitCmpOpF(ArmEmitterContext context, string name, bool scalar, bool absolute = false)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand res = context.VectorZero();
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = !scalar ? op.GetBytesCount() >> sizeF + 2 : 1;
for (int index = 0; index < elems; index++)
{
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
Operand me;
if (op is OpCodeSimdReg binOp)
{
me = context.VectorExtract(type, GetVec(binOp.Rm), index);
}
else
{
me = sizeF == 0 ? ConstF(0f) : ConstF(0d);
}
if (absolute)
{
ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
me = EmitUnaryMathCall(context, nameof(Math.Abs), me);
}
Operand e = EmitSoftFloatCall(context, name, ne, me);
res = context.VectorInsert(res, e, index);
}
context.Copy(GetVec(op.Rd), res);
}
private static void EmitSse2OrAvxCmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar, bool absolute = false)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m = op is OpCodeSimdReg binOp ? GetVec(binOp.Rm) : context.VectorZero();
int sizeF = op.Size & 1;
if (sizeF == 0)
{
if (absolute)
{
Operand mask = scalar ? X86GetScalar(context, int.MaxValue) : X86GetAllElements(context, int.MaxValue);
n = context.AddIntrinsic(Intrinsic.X86Andps, n, mask);
m = context.AddIntrinsic(Intrinsic.X86Andps, m, mask);
}
Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps;
Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
if (scalar)
{
res = context.VectorZeroUpper96(res);
}
else if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else /* if (sizeF == 1) */
{
if (absolute)
{
Operand mask = scalar ? X86GetScalar(context, long.MaxValue) : X86GetAllElements(context, long.MaxValue);
n = context.AddIntrinsic(Intrinsic.X86Andpd, n, mask);
m = context.AddIntrinsic(Intrinsic.X86Andpd, m, mask);
}
Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd;
Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
if (scalar)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
}
}
}

View File

@@ -1,391 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
using Func2I = Func<Operand, Operand, Operand>;
static partial class InstEmit32
{
public static void Vceq_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, false);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), false);
}
}
public static void Vceq_I(ArmEmitterContext context)
{
EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, false, false);
}
public static void Vceq_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, true);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), true);
}
}
else
{
EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, true, false);
}
}
public static void Vcge_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), false);
}
}
public static void Vcge_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, false, !op.U);
}
public static void Vcge_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), true);
}
}
else
{
EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, true, true);
}
}
public static void Vcgt_V(ArmEmitterContext context)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, false);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), false);
}
}
public static void Vcgt_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, false, !op.U);
}
public static void Vcgt_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, true);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), true);
}
}
else
{
EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, true, true);
}
}
public static void Vcle_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThanOrEqual, true);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLEFpscr), true);
}
}
else
{
EmitCmpOpI32(context, context.ICompareLessOrEqual, context.ICompareLessOrEqualUI, true, true);
}
}
public static void Vclt_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThan, true);
}
else
{
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLTFpscr), true);
}
}
else
{
EmitCmpOpI32(context, context.ICompareLess, context.ICompareLessUI, true, true);
}
}
private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero)
{
if (zero)
{
EmitVectorUnaryOpF32(context, (m) =>
{
Operand zeroOp = m.Type == OperandType.FP64 ? ConstF(0.0d) : ConstF(0.0f);
return EmitSoftFloatCallDefaultFpscr(context, name, m, zeroOp);
});
}
else
{
EmitVectorBinaryOpF32(context, (n, m) =>
{
return EmitSoftFloatCallDefaultFpscr(context, name, n, m);
});
}
}
private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
{
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
}
private static void EmitCmpOpI32(
ArmEmitterContext context,
Func2I signedOp,
Func2I unsignedOp,
bool zero,
bool signed)
{
if (zero)
{
if (signed)
{
EmitVectorUnaryOpSx32(context, (m) =>
{
OperandType type = m.Type;
Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
return ZerosOrOnes(context, signedOp(m, zeroV), type);
});
}
else
{
EmitVectorUnaryOpZx32(context, (m) =>
{
OperandType type = m.Type;
Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
return ZerosOrOnes(context, unsignedOp(m, zeroV), type);
});
}
}
else
{
if (signed)
{
EmitVectorBinaryOpSx32(context, (n, m) => ZerosOrOnes(context, signedOp(n, m), n.Type));
}
else
{
EmitVectorBinaryOpZx32(context, (n, m) => ZerosOrOnes(context, unsignedOp(n, m), n.Type));
}
}
}
public static void Vcmp(ArmEmitterContext context)
{
EmitVcmpOrVcmpe(context, false);
}
public static void Vcmpe(ArmEmitterContext context)
{
EmitVcmpOrVcmpe(context, true);
}
private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
bool cmpWithZero = (op.Opc & 2) != 0;
int sizeF = op.Size & 1;
if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2))
{
CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
bool doubleSize = sizeF != 0;
int shift = doubleSize ? 1 : 2;
Operand m = GetVecA32(op.Vm >> shift);
Operand n = GetVecA32(op.Vd >> shift);
n = EmitSwapScalar(context, n, op.Vd, doubleSize);
m = cmpWithZero ? context.VectorZero() : EmitSwapScalar(context, m, op.Vm, doubleSize);
Operand lblNaN = Label();
Operand lblEnd = Label();
if (!doubleSize)
{
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered));
Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask);
context.BranchIfFalse(lblNaN, isOrdered);
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, n, m);
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, n, m);
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, n, m);
SetFpFlag(context, FPState.VFlag, Const(0));
SetFpFlag(context, FPState.CFlag, cf);
SetFpFlag(context, FPState.ZFlag, zf);
SetFpFlag(context, FPState.NFlag, nf);
}
else
{
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered));
Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask);
context.BranchIfFalse(lblNaN, isOrdered);
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, n, m);
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, n, m);
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, n, m);
SetFpFlag(context, FPState.VFlag, Const(0));
SetFpFlag(context, FPState.CFlag, cf);
SetFpFlag(context, FPState.ZFlag, zf);
SetFpFlag(context, FPState.NFlag, nf);
}
context.Branch(lblEnd);
context.MarkLabel(lblNaN);
SetFpFlag(context, FPState.VFlag, Const(1));
SetFpFlag(context, FPState.CFlag, Const(1));
SetFpFlag(context, FPState.ZFlag, Const(0));
SetFpFlag(context, FPState.NFlag, Const(0));
context.MarkLabel(lblEnd);
}
else
{
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
Operand ne = ExtractScalar(context, type, op.Vd);
Operand me;
if (cmpWithZero)
{
me = sizeF == 0 ? ConstF(0f) : ConstF(0d);
}
else
{
me = ExtractScalar(context, type, op.Vm);
}
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
EmitSetFpscrNzcv(context, nzcv);
}
}
private static void EmitSetFpscrNzcv(ArmEmitterContext context, Operand nzcv)
{
Operand Extract(Operand value, int bit)
{
if (bit != 0)
{
value = context.ShiftRightUI(value, Const(bit));
}
value = context.BitwiseAnd(value, Const(1));
return value;
}
SetFpFlag(context, FPState.VFlag, Extract(nzcv, 0));
SetFpFlag(context, FPState.CFlag, Extract(nzcv, 1));
SetFpFlag(context, FPState.ZFlag, Extract(nzcv, 2));
SetFpFlag(context, FPState.NFlag, Extract(nzcv, 3));
}
private static void EmitSse2OrAvxCmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
int sizeF = op.Size & 1;
Intrinsic inst = (sizeF == 0) ? Intrinsic.X86Cmpps : Intrinsic.X86Cmppd;
if (zero)
{
EmitVectorUnaryOpSimd32(context, (m) =>
{
return context.AddIntrinsic(inst, m, context.VectorZero(), Const((int)cond));
});
}
else
{
EmitVectorBinaryOpSimd32(context, (n, m) =>
{
return context.AddIntrinsic(inst, n, m, Const((int)cond));
});
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,712 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
private static int FlipVdBits(int vd, bool lowBit)
{
if (lowBit)
{
// Move the low bit to the top.
return ((vd & 0x1) << 4) | (vd >> 1);
}
else
{
// Move the high bit to the bottom.
return ((vd & 0xf) << 1) | (vd >> 4);
}
}
private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
{
MethodInfo info;
if (op1.Type == OperandType.FP64)
{
info = unsigned
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32));
}
else
{
info = unsigned
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
}
return context.Call(info, op1);
}
public static void Vcvt_V(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
bool unsigned = (op.Opc & 1) != 0;
bool toInteger = (op.Opc & 2) != 0;
OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
if (toInteger)
{
if (Optimizations.UseSse41)
{
EmitSse41ConvertVector32(context, FPRoundingMode.TowardsZero, !unsigned);
}
else
{
EmitVectorUnaryOpF32(context, (op1) =>
{
return EmitSaturateFloatToInt(context, op1, unsigned);
});
}
}
else
{
if (Optimizations.UseSse2)
{
EmitVectorUnaryOpSimd32(context, (n) =>
{
if (unsigned)
{
Operand mask = X86GetAllElements(context, 0x47800000);
Operand res = context.AddIntrinsic(Intrinsic.X86Psrld, n, Const(16));
res = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res);
res = context.AddIntrinsic(Intrinsic.X86Mulps, res, mask);
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pslld, n, Const(16));
res2 = context.AddIntrinsic(Intrinsic.X86Psrld, res2, Const(16));
res2 = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res2);
return context.AddIntrinsic(Intrinsic.X86Addps, res, res2);
}
else
{
return context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, n);
}
});
}
else
{
if (unsigned)
{
EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
}
else
{
EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
}
}
}
}
public static void Vcvt_FD(ArmEmitterContext context)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
int vm = op.Vm;
int vd;
if (op.Size == 3)
{
vd = FlipVdBits(op.Vd, false);
// Double to single.
Operand fp = ExtractScalar(context, OperandType.FP64, vm);
Operand res = context.ConvertToFP(OperandType.FP32, fp);
InsertScalar(context, vd, res);
}
else
{
vd = FlipVdBits(op.Vd, true);
// Single to double.
Operand fp = ExtractScalar(context, OperandType.FP32, vm);
Operand res = context.ConvertToFP(OperandType.FP64, fp);
InsertScalar(context, vd, res);
}
}
// VCVT (floating-point to integer, floating-point) | VCVT (integer to floating-point, floating-point).
public static void Vcvt_FI(ArmEmitterContext context)
{
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
bool toInteger = (op.Opc2 & 0b100) != 0;
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
if (toInteger)
{
bool unsigned = (op.Opc2 & 1) == 0;
bool roundWithFpscr = op.Opc != 1;
if (!roundWithFpscr && Optimizations.UseSse41)
{
EmitSse41ConvertInt32(context, FPRoundingMode.TowardsZero, !unsigned);
}
else
{
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
// TODO: Fast Path.
if (roundWithFpscr)
{
toConvert = EmitRoundByRMode(context, toConvert);
}
// Round towards zero.
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
InsertScalar(context, op.Vd, asInteger);
}
}
else
{
bool unsigned = op.Opc == 0;
Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm);
Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned);
InsertScalar(context, op.Vd, asFloat);
}
}
private static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
{
IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
string name = nameof(Math.Round);
MethodInfo info = (op.Size & 1) == 0
? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) })
: typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) });
return context.Call(info, n, Const((int)roundMode));
}
private static FPRoundingMode RMToRoundMode(int rm)
{
FPRoundingMode roundMode;
switch (rm)
{
case 0b00:
roundMode = FPRoundingMode.ToNearestAway;
break;
case 0b01:
roundMode = FPRoundingMode.ToNearest;
break;
case 0b10:
roundMode = FPRoundingMode.TowardsPlusInfinity;
break;
case 0b11:
roundMode = FPRoundingMode.TowardsMinusInfinity;
break;
default:
throw new ArgumentOutOfRangeException(nameof(rm));
}
return roundMode;
}
// VCVTA/M/N/P (floating-point).
public static void Vcvt_RM(ArmEmitterContext context)
{
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; // toInteger == true (opCode<18> == 1 => Opc2<2> == 1).
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
bool unsigned = op.Opc == 0;
int rm = op.Opc2 & 3;
if (Optimizations.UseSse41)
{
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
}
else
{
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
switch (rm)
{
case 0b00: // Away
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
break;
case 0b01: // Nearest
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
break;
case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
break;
}
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
InsertScalar(context, op.Vd, asInteger);
}
}
public static void Vcvt_TB(ArmEmitterContext context)
{
OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp;
if (Optimizations.UseF16c)
{
Debug.Assert(!Optimizations.ForceLegacySse);
if (op.Op)
{
Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
if (op.Size == 1)
{
res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res);
}
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
res = context.VectorExtract16(res, 0);
InsertScalar16(context, op.Vd, op.T, res);
}
else
{
Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T));
res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res);
if (op.Size == 1)
{
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
}
res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0);
InsertScalar(context, op.Vd, res);
}
}
else
{
if (op.Op)
{
// Convert to half.
Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
MethodInfo method = op.Size == 1
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
context.StoreToContext();
Operand res = context.Call(method, src);
context.LoadFromContext();
InsertScalar16(context, op.Vd, op.T, res);
}
else
{
// Convert from half.
Operand src = ExtractScalar16(context, op.Vm, op.T);
MethodInfo method = op.Size == 1
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
context.StoreToContext();
Operand res = context.Call(method, src);
context.LoadFromContext();
InsertScalar(context, op.Vd, res);
}
}
}
// VRINTA/M/N/P (floating-point).
public static void Vrint_RM(ArmEmitterContext context)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
int rm = op.Opc2 & 3;
if (Optimizations.UseSse41)
{
EmitScalarUnaryOpSimd32(context, (m) =>
{
FPRoundingMode roundMode = RMToRoundMode(rm);
if (roundMode != FPRoundingMode.ToNearestAway)
{
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
}
else
{
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: true);
}
});
}
else
{
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
switch (rm)
{
case 0b00: // Away
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
break;
case 0b01: // Nearest
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
break;
case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
break;
}
InsertScalar(context, op.Vd, toConvert);
}
}
// VRINTA (vector).
public static void Vrinta_V(ArmEmitterContext context)
{
if (Optimizations.UseSse41)
{
EmitVectorUnaryOpSimd32(context, (m) =>
{
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: false);
});
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
}
}
// VRINTM (vector).
public static void Vrintm_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorUnaryOpSimd32(context, (m) =>
{
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.TowardsMinusInfinity)));
});
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Floor), m));
}
}
// VRINTN (vector).
public static void Vrintn_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorUnaryOpSimd32(context, (m) =>
{
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
});
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.ToEven, m));
}
}
// VRINTP (vector).
public static void Vrintp_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorUnaryOpSimd32(context, (m) =>
{
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.TowardsPlusInfinity)));
});
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Ceiling), m));
}
}
// VRINTZ (floating-point).
public static void Vrint_Z(ArmEmitterContext context)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
if (Optimizations.UseSse2)
{
EmitScalarUnaryOpSimd32(context, (m) =>
{
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(FPRoundingMode.TowardsZero)));
});
}
else
{
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
}
}
// VRINTX (floating-point).
public static void Vrintx_S(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) =>
{
return EmitRoundByRMode(context, op1);
});
}
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
{
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
if (signed)
{
return context.ConvertToFP(type, value);
}
else
{
return context.ConvertToFPUI(type, value);
}
}
private static void EmitSse41ConvertInt32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed)
{
// A port of the similar round function in InstEmitSimdCvt.
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
bool doubleSize = (op.Size & 1) != 0;
int shift = doubleSize ? 1 : 2;
Operand n = GetVecA32(op.Vm >> shift);
n = EmitSwapScalar(context, n, op.Vm, doubleSize);
if (!doubleSize)
{
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand zero = context.VectorZero();
Operand nCmp;
Operand nIntOrLong2 = default;
if (!signed)
{
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
}
int fpMaxVal = 0x4F000000; // 2.14748365E9f (2147483648)
Operand fpMaxValMask = X86GetScalar(context, fpMaxVal);
Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes);
if (!signed)
{
nRes = context.AddIntrinsic(Intrinsic.X86Subss, nRes, fpMaxValMask);
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes);
}
nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
Operand nInt = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, nRes);
Operand dRes;
if (signed)
{
dRes = context.BitwiseExclusiveOr(nIntOrLong, nInt);
}
else
{
dRes = context.BitwiseExclusiveOr(nIntOrLong2, nInt);
dRes = context.Add(dRes, nIntOrLong);
}
InsertScalar(context, op.Vd, dRes);
}
else
{
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand zero = context.VectorZero();
Operand nCmp;
Operand nIntOrLong2 = default;
if (!signed)
{
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
}
long fpMaxVal = 0x41E0000000000000L; // 2147483648.0000000d (2147483648)
Operand fpMaxValMask = X86GetScalar(context, fpMaxVal);
Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes);
if (!signed)
{
nRes = context.AddIntrinsic(Intrinsic.X86Subsd, nRes, fpMaxValMask);
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes);
}
nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
Operand nLong = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, nRes);
nLong = context.ConvertI64ToI32(nLong);
Operand dRes;
if (signed)
{
dRes = context.BitwiseExclusiveOr(nIntOrLong, nLong);
}
else
{
dRes = context.BitwiseExclusiveOr(nIntOrLong2, nLong);
dRes = context.Add(dRes, nIntOrLong);
}
InsertScalar(context, op.Vd, dRes);
}
}
private static void EmitSse41ConvertVector32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
EmitVectorUnaryOpSimd32(context, (n) =>
{
int sizeF = op.Size & 1;
if (sizeF == 0)
{
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
Operand zero = context.VectorZero();
Operand nCmp;
if (!signed)
{
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
}
Operand fpMaxValMask = X86GetAllElements(context, 0x4F000000); // 2.14748365E9f (2147483648)
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
Operand nInt2 = default;
if (!signed)
{
nRes = context.AddIntrinsic(Intrinsic.X86Subps, nRes, fpMaxValMask);
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
nInt2 = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
}
nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
if (signed)
{
return context.AddIntrinsic(Intrinsic.X86Pxor, nInt, nRes);
}
else
{
Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nInt2, nRes);
return context.AddIntrinsic(Intrinsic.X86Paddd, dRes, nInt);
}
}
else /* if (sizeF == 1) */
{
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
Operand zero = context.VectorZero();
Operand nCmp;
if (!signed)
{
nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
}
Operand fpMaxValMask = X86GetAllElements(context, 0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808)
Operand nLong = InstEmit.EmitSse2CvtDoubleToInt64OpF(context, nRes, false);
Operand nLong2 = default;
if (!signed)
{
nRes = context.AddIntrinsic(Intrinsic.X86Subpd, nRes, fpMaxValMask);
nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
nLong2 = InstEmit.EmitSse2CvtDoubleToInt64OpF(context, nRes, false);
}
nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
if (signed)
{
return context.AddIntrinsic(Intrinsic.X86Pxor, nLong, nRes);
}
else
{
Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nLong2, nRes);
return context.AddIntrinsic(Intrinsic.X86Paddq, dRes, nLong);
}
}
});
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,543 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit
{
public static void And_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand res = context.AddIntrinsic(Intrinsic.X86Pand, n, m);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorBinaryOpZx(context, (op1, op2) => context.BitwiseAnd(op1, op2));
}
}
public static void Bic_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand res = context.AddIntrinsic(Intrinsic.X86Pandn, m, n);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorBinaryOpZx(context, (op1, op2) =>
{
return context.BitwiseAnd(op1, context.BitwiseNot(op2));
});
}
}
public static void Bic_Vi(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
int eSize = 8 << op.Size;
Operand d = GetVec(op.Rd);
Operand imm = eSize switch {
16 => X86GetAllElements(context, (short)~op.Immediate),
32 => X86GetAllElements(context, (int)~op.Immediate),
_ => throw new InvalidOperationException($"Invalid element size {eSize}.")
};
Operand res = context.AddIntrinsic(Intrinsic.X86Pand, d, imm);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorImmBinaryOp(context, (op1, op2) =>
{
return context.BitwiseAnd(op1, context.BitwiseNot(op2));
});
}
}
public static void Bif_V(ArmEmitterContext context)
{
EmitBifBit(context, notRm: true);
}
public static void Bit_V(ArmEmitterContext context)
{
EmitBifBit(context, notRm: false);
}
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
if (Optimizations.UseSse2)
{
Operand d = GetVec(op.Rd);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, d);
if (notRm)
{
res = context.AddIntrinsic(Intrinsic.X86Pandn, m, res);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Pand, m, res);
}
res = context.AddIntrinsic(Intrinsic.X86Pxor, d, res);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(d, res);
}
else
{
Operand res = context.VectorZero();
int elems = op.RegisterSize == RegisterSize.Simd128 ? 2 : 1;
for (int index = 0; index < elems; index++)
{
Operand d = EmitVectorExtractZx(context, op.Rd, index, 3);
Operand n = EmitVectorExtractZx(context, op.Rn, index, 3);
Operand m = EmitVectorExtractZx(context, op.Rm, index, 3);
if (notRm)
{
m = context.BitwiseNot(m);
}
Operand e = context.BitwiseExclusiveOr(d, n);
e = context.BitwiseAnd(e, m);
e = context.BitwiseExclusiveOr(e, d);
res = EmitVectorInsert(context, res, e, index, 3);
}
context.Copy(GetVec(op.Rd), res);
}
}
public static void Bsl_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand d = GetVec(op.Rd);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
res = context.AddIntrinsic(Intrinsic.X86Pand, res, d);
res = context.AddIntrinsic(Intrinsic.X86Pxor, res, m);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(d, res);
}
else
{
EmitVectorTernaryOpZx(context, (op1, op2, op3) =>
{
return context.BitwiseExclusiveOr(
context.BitwiseAnd(op1,
context.BitwiseExclusiveOr(op2, op3)), op3);
});
}
}
public static void Eor_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorBinaryOpZx(context, (op1, op2) => context.BitwiseExclusiveOr(op1, op2));
}
}
public static void Not_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand mask = X86GetAllElements(context, -1L);
Operand res = context.AddIntrinsic(Intrinsic.X86Pandn, n, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorUnaryOpZx(context, (op1) => context.BitwiseNot(op1));
}
}
public static void Orn_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand mask = X86GetAllElements(context, -1L);
Operand res = context.AddIntrinsic(Intrinsic.X86Pandn, m, mask);
res = context.AddIntrinsic(Intrinsic.X86Por, res, n);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorBinaryOpZx(context, (op1, op2) =>
{
return context.BitwiseOr(op1, context.BitwiseNot(op2));
});
}
}
public static void Orr_V(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
Operand res = context.AddIntrinsic(Intrinsic.X86Por, n, m);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorBinaryOpZx(context, (op1, op2) => context.BitwiseOr(op1, op2));
}
}
public static void Orr_Vi(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
int eSize = 8 << op.Size;
Operand d = GetVec(op.Rd);
Operand imm = eSize switch {
16 => X86GetAllElements(context, (short)op.Immediate),
32 => X86GetAllElements(context, (int)op.Immediate),
_ => throw new InvalidOperationException($"Invalid element size {eSize}.")
};
Operand res = context.AddIntrinsic(Intrinsic.X86Por, d, imm);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitVectorImmBinaryOp(context, (op1, op2) => context.BitwiseOr(op1, op2));
}
}
public static void Rbit_V(ArmEmitterContext context)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
if (Optimizations.UseGfni)
{
const long bitMatrix =
(0b10000000L << 56) |
(0b01000000L << 48) |
(0b00100000L << 40) |
(0b00010000L << 32) |
(0b00001000L << 24) |
(0b00000100L << 16) |
(0b00000010L << 8) |
(0b00000001L << 0);
Operand vBitMatrix = X86GetAllElements(context, bitMatrix);
Operand res = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, GetVec(op.Rn), vBitMatrix, Const(0));
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
Operand res = context.VectorZero();
int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8;
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0);
Operand de = EmitReverseBits8Op(context, ne);
res = EmitVectorInsert(context, res, de, index, 0);
}
context.Copy(GetVec(op.Rd), res);
}
}
private static Operand EmitReverseBits8Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaul)), Const(1)),
context.ShiftLeft (context.BitwiseAnd(op, Const(0x55ul)), Const(1)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccul)), Const(2)),
context.ShiftLeft (context.BitwiseAnd(val, Const(0x33ul)), Const(2)));
return context.BitwiseOr(context.ShiftRightUI(val, Const(4)),
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0ful)), Const(4)));
}
public static void Rev16_V(ArmEmitterContext context)
{
if (Optimizations.UseSsse3)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
const long maskE0 = 06L << 56 | 07L << 48 | 04L << 40 | 05L << 32 | 02L << 24 | 03L << 16 | 00L << 8 | 01L << 0;
const long maskE1 = 14L << 56 | 15L << 48 | 12L << 40 | 13L << 32 | 10L << 24 | 11L << 16 | 08L << 8 | 09L << 0;
Operand mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitRev_V(context, containerSize: 1);
}
}
public static void Rev32_V(ArmEmitterContext context)
{
if (Optimizations.UseSsse3)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand mask;
if (op.Size == 0)
{
const long maskE0 = 04L << 56 | 05L << 48 | 06L << 40 | 07L << 32 | 00L << 24 | 01L << 16 | 02L << 8 | 03L << 0;
const long maskE1 = 12L << 56 | 13L << 48 | 14L << 40 | 15L << 32 | 08L << 24 | 09L << 16 | 10L << 8 | 11L << 0;
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
}
else /* if (op.Size == 1) */
{
const long maskE0 = 05L << 56 | 04L << 48 | 07L << 40 | 06L << 32 | 01L << 24 | 00L << 16 | 03L << 8 | 02L << 0;
const long maskE1 = 13L << 56 | 12L << 48 | 15L << 40 | 14L << 32 | 09L << 24 | 08L << 16 | 11L << 8 | 10L << 0;
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
}
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitRev_V(context, containerSize: 2);
}
}
public static void Rev64_V(ArmEmitterContext context)
{
if (Optimizations.UseSsse3)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand n = GetVec(op.Rn);
Operand mask;
if (op.Size == 0)
{
const long maskE0 = 00L << 56 | 01L << 48 | 02L << 40 | 03L << 32 | 04L << 24 | 05L << 16 | 06L << 8 | 07L << 0;
const long maskE1 = 08L << 56 | 09L << 48 | 10L << 40 | 11L << 32 | 12L << 24 | 13L << 16 | 14L << 8 | 15L << 0;
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
}
else if (op.Size == 1)
{
const long maskE0 = 01L << 56 | 00L << 48 | 03L << 40 | 02L << 32 | 05L << 24 | 04L << 16 | 07L << 8 | 06L << 0;
const long maskE1 = 09L << 56 | 08L << 48 | 11L << 40 | 10L << 32 | 13L << 24 | 12L << 16 | 15L << 8 | 14L << 0;
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
}
else /* if (op.Size == 2) */
{
const long maskE0 = 03L << 56 | 02L << 48 | 01L << 40 | 00L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0;
const long maskE1 = 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 15L << 24 | 14L << 16 | 13L << 8 | 12L << 0;
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
}
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
if (op.RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
context.Copy(GetVec(op.Rd), res);
}
else
{
EmitRev_V(context, containerSize: 3);
}
}
private static void EmitRev_V(ArmEmitterContext context, int containerSize)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
Operand res = context.VectorZero();
int elems = op.GetBytesCount() >> op.Size;
int containerMask = (1 << (containerSize - op.Size)) - 1;
for (int index = 0; index < elems; index++)
{
int revIndex = index ^ containerMask;
Operand ne = EmitVectorExtractZx(context, op.Rn, revIndex, op.Size);
res = EmitVectorInsert(context, res, ne, index, op.Size);
}
context.Copy(GetVec(op.Rd), res);
}
}
}

View File

@@ -1,221 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Vand_I(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pand, n, m));
}
else
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, op2));
}
}
public static void Vbic_I(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pandn, m, n));
}
else
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, context.BitwiseNot(op2)));
}
}
public static void Vbic_II(ArmEmitterContext context)
{
OpCode32SimdImm op = (OpCode32SimdImm)context.CurrOp;
long immediate = op.Immediate;
// Replicate fields to fill the 64-bits, if size is < 64-bits.
switch (op.Size)
{
case 0: immediate *= 0x0101010101010101L; break;
case 1: immediate *= 0x0001000100010001L; break;
case 2: immediate *= 0x0000000100000001L; break;
}
Operand imm = Const(immediate);
Operand res = GetVecA32(op.Qd);
if (op.Q)
{
for (int elem = 0; elem < 2; elem++)
{
Operand de = EmitVectorExtractZx(context, op.Qd, elem, 3);
res = EmitVectorInsert(context, res, context.BitwiseAnd(de, context.BitwiseNot(imm)), elem, 3);
}
}
else
{
Operand de = EmitVectorExtractZx(context, op.Qd, op.Vd & 1, 3);
res = EmitVectorInsert(context, res, context.BitwiseAnd(de, context.BitwiseNot(imm)), op.Vd & 1, 3);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vbif(ArmEmitterContext context)
{
EmitBifBit(context, true);
}
public static void Vbit(ArmEmitterContext context)
{
EmitBifBit(context, false);
}
public static void Vbsl(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorTernaryOpSimd32(context, (d, n, m) =>
{
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
res = context.AddIntrinsic(Intrinsic.X86Pand, res, d);
return context.AddIntrinsic(Intrinsic.X86Pxor, res, m);
});
}
else
{
EmitVectorTernaryOpZx32(context, (op1, op2, op3) =>
{
return context.BitwiseExclusiveOr(
context.BitwiseAnd(op1,
context.BitwiseExclusiveOr(op2, op3)), op3);
});
}
}
public static void Veor_I(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pxor, n, m));
}
else
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseExclusiveOr(op1, op2));
}
}
public static void Vorn_I(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
Operand mask = context.VectorOne();
EmitVectorBinaryOpSimd32(context, (n, m) =>
{
m = context.AddIntrinsic(Intrinsic.X86Pandn, m, mask);
return context.AddIntrinsic(Intrinsic.X86Por, n, m);
});
}
else
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, context.BitwiseNot(op2)));
}
}
public static void Vorr_I(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Por, n, m));
}
else
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, op2));
}
}
public static void Vorr_II(ArmEmitterContext context)
{
OpCode32SimdImm op = (OpCode32SimdImm)context.CurrOp;
long immediate = op.Immediate;
// Replicate fields to fill the 64-bits, if size is < 64-bits.
switch (op.Size)
{
case 0: immediate *= 0x0101010101010101L; break;
case 1: immediate *= 0x0001000100010001L; break;
case 2: immediate *= 0x0000000100000001L; break;
}
Operand imm = Const(immediate);
Operand res = GetVecA32(op.Qd);
if (op.Q)
{
for (int elem = 0; elem < 2; elem++)
{
Operand de = EmitVectorExtractZx(context, op.Qd, elem, 3);
res = EmitVectorInsert(context, res, context.BitwiseOr(de, imm), elem, 3);
}
}
else
{
Operand de = EmitVectorExtractZx(context, op.Qd, op.Vd & 1, 3);
res = EmitVectorInsert(context, res, context.BitwiseOr(de, imm), op.Vd & 1, 3);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vtst(ArmEmitterContext context)
{
EmitVectorBinaryOpZx32(context, (op1, op2) =>
{
Operand isZero = context.ICompareEqual(context.BitwiseAnd(op1, op2), Const(0));
return context.ConditionalSelect(isZero, Const(0), Const(-1));
});
}
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
if (Optimizations.UseSse2)
{
EmitVectorTernaryOpSimd32(context, (d, n, m) =>
{
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, d);
res = context.AddIntrinsic((notRm) ? Intrinsic.X86Pandn : Intrinsic.X86Pand, m, res);
return context.AddIntrinsic(Intrinsic.X86Pxor, d, res);
});
}
else
{
EmitVectorTernaryOpZx32(context, (d, n, m) =>
{
if (notRm)
{
m = context.BitwiseNot(m);
}
return context.BitwiseExclusiveOr(
context.BitwiseAnd(m,
context.BitwiseExclusiveOr(d, n)), d);
});
}
}
}
}

View File

@@ -1,595 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
#region "Masks"
// Same as InstEmitSimdMove, as the instructions do the same thing.
private static readonly long[] _masksE0_Uzp = new long[]
{
13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0
};
private static readonly long[] _masksE1_Uzp = new long[]
{
15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0,
15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0
};
#endregion
public static void Vmov_I(ArmEmitterContext context)
{
EmitVectorImmUnaryOp32(context, (op1) => op1);
}
public static void Vmvn_I(ArmEmitterContext context)
{
if (Optimizations.UseSse2)
{
EmitVectorUnaryOpSimd32(context, (op1) =>
{
Operand mask = X86GetAllElements(context, -1L);
return context.AddIntrinsic(Intrinsic.X86Pandn, op1, mask);
});
}
else
{
EmitVectorUnaryOpZx32(context, (op1) => context.BitwiseNot(op1));
}
}
public static void Vmvn_II(ArmEmitterContext context)
{
EmitVectorImmUnaryOp32(context, (op1) => context.BitwiseNot(op1));
}
public static void Vmov_GS(ArmEmitterContext context)
{
OpCode32SimdMovGp op = (OpCode32SimdMovGp)context.CurrOp;
Operand vec = GetVecA32(op.Vn >> 2);
if (op.Op == 1)
{
// To general purpose.
Operand value = context.VectorExtract(OperandType.I32, vec, op.Vn & 0x3);
SetIntA32(context, op.Rt, value);
}
else
{
// From general purpose.
Operand value = GetIntA32(context, op.Rt);
context.Copy(vec, context.VectorInsert(vec, value, op.Vn & 0x3));
}
}
public static void Vmov_G1(ArmEmitterContext context)
{
OpCode32SimdMovGpElem op = (OpCode32SimdMovGpElem)context.CurrOp;
int index = op.Index + ((op.Vd & 1) << (3 - op.Size));
if (op.Op == 1)
{
// To general purpose.
Operand value = EmitVectorExtract32(context, op.Vd >> 1, index, op.Size, !op.U);
SetIntA32(context, op.Rt, value);
}
else
{
// From general purpose.
Operand vec = GetVecA32(op.Vd >> 1);
Operand value = GetIntA32(context, op.Rt);
context.Copy(vec, EmitVectorInsert(context, vec, value, index, op.Size));
}
}
public static void Vmov_G2(ArmEmitterContext context)
{
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
Operand vec = GetVecA32(op.Vm >> 2);
int vm1 = op.Vm + 1;
bool sameOwnerVec = (op.Vm >> 2) == (vm1 >> 2);
Operand vec2 = sameOwnerVec ? vec : GetVecA32(vm1 >> 2);
if (op.Op == 1)
{
// To general purpose.
Operand lowValue = context.VectorExtract(OperandType.I32, vec, op.Vm & 3);
SetIntA32(context, op.Rt, lowValue);
Operand highValue = context.VectorExtract(OperandType.I32, vec2, vm1 & 3);
SetIntA32(context, op.Rt2, highValue);
}
else
{
// From general purpose.
Operand lowValue = GetIntA32(context, op.Rt);
Operand resultVec = context.VectorInsert(vec, lowValue, op.Vm & 3);
Operand highValue = GetIntA32(context, op.Rt2);
if (sameOwnerVec)
{
context.Copy(vec, context.VectorInsert(resultVec, highValue, vm1 & 3));
}
else
{
context.Copy(vec, resultVec);
context.Copy(vec2, context.VectorInsert(vec2, highValue, vm1 & 3));
}
}
}
public static void Vmov_GD(ArmEmitterContext context)
{
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
Operand vec = GetVecA32(op.Vm >> 1);
if (op.Op == 1)
{
// To general purpose.
Operand value = context.VectorExtract(OperandType.I64, vec, op.Vm & 1);
SetIntA32(context, op.Rt, context.ConvertI64ToI32(value));
SetIntA32(context, op.Rt2, context.ConvertI64ToI32(context.ShiftRightUI(value, Const(32))));
}
else
{
// From general purpose.
Operand lowValue = GetIntA32(context, op.Rt);
Operand highValue = GetIntA32(context, op.Rt2);
Operand value = context.BitwiseOr(
context.ZeroExtend32(OperandType.I64, lowValue),
context.ShiftLeft(context.ZeroExtend32(OperandType.I64, highValue), Const(32)));
context.Copy(vec, context.VectorInsert(vec, value, op.Vm & 1));
}
}
public static void Vmovl(ArmEmitterContext context)
{
OpCode32SimdLong op = (OpCode32SimdLong)context.CurrOp;
Operand res = context.VectorZero();
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
if (op.Size == 2)
{
if (op.U)
{
me = context.ZeroExtend32(OperandType.I64, me);
}
else
{
me = context.SignExtend32(OperandType.I64, me);
}
}
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vtbl(ArmEmitterContext context)
{
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
bool extension = op.Opc == 1;
int length = op.Length + 1;
if (Optimizations.UseSsse3)
{
Operand d = GetVecA32(op.Qd);
Operand m = EmitMoveDoubleWordToSide(context, GetVecA32(op.Qm), op.Vm, 0);
Operand res;
Operand mask = X86GetAllElements(context, 0x0707070707070707L);
// Fast path for single register table.
{
Operand n = EmitMoveDoubleWordToSide(context, GetVecA32(op.Qn), op.Vn, 0);
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask);
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m);
res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask);
}
for (int index = 1; index < length; index++)
{
int newVn = (op.Vn + index) & 0x1F;
(int qn, int ind) = GetQuadwordAndSubindex(newVn, op.RegisterSize);
Operand ni = EmitMoveDoubleWordToSide(context, GetVecA32(qn), newVn, 0);
Operand idxMask = X86GetAllElements(context, 0x0808080808080808L * index);
Operand mSubMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, idxMask);
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mSubMask, mask);
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, mSubMask);
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask);
res = context.AddIntrinsic(Intrinsic.X86Por, res, res2);
}
if (extension)
{
Operand idxMask = X86GetAllElements(context, (0x0808080808080808L * length) - 0x0101010101010101L);
Operand zeroMask = context.VectorZero();
Operand mPosMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, idxMask);
Operand mNegMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, zeroMask, m);
Operand mMask = context.AddIntrinsic(Intrinsic.X86Por, mPosMask, mNegMask);
Operand dMask = context.AddIntrinsic(Intrinsic.X86Pand, EmitMoveDoubleWordToSide(context, d, op.Vd, 0), mMask);
res = context.AddIntrinsic(Intrinsic.X86Por, res, dMask);
}
res = EmitMoveDoubleWordToSide(context, res, 0, op.Vd);
context.Copy(d, EmitDoubleWordInsert(context, d, res, op.Vd));
}
else
{
int elems = op.GetBytesCount() >> op.Size;
(int Qx, int Ix)[] tableTuples = new (int, int)[length];
for (int i = 0; i < length; i++)
{
tableTuples[i] = GetQuadwordAndSubindex(op.Vn + i, op.RegisterSize);
}
int byteLength = length * 8;
Operand res = GetVecA32(op.Qd);
Operand m = GetVecA32(op.Qm);
for (int index = 0; index < elems; index++)
{
Operand selectedIndex = context.ZeroExtend8(OperandType.I32, context.VectorExtract8(m, index + op.Im));
Operand inRange = context.ICompareLess(selectedIndex, Const(byteLength));
Operand elemRes = default; // Note: This is I64 for ease of calculation.
// TODO: Branching rather than conditional select.
// Get indexed byte.
// To simplify (ha) the il, we get bytes from every vector and use a nested conditional select to choose the right result.
// This does have to extract `length` times for every element but certainly not as bad as it could be.
// Which vector number is the index on.
Operand vecIndex = context.ShiftRightUI(selectedIndex, Const(3));
// What should we shift by to extract it.
Operand subVecIndexShift = context.ShiftLeft(context.BitwiseAnd(selectedIndex, Const(7)), Const(3));
for (int i = 0; i < length; i++)
{
(int qx, int ix) = tableTuples[i];
// Get the whole vector, we'll get a byte out of it.
Operand lookupResult;
if (qx == op.Qd)
{
// Result contains the current state of the vector.
lookupResult = context.VectorExtract(OperandType.I64, res, ix);
}
else
{
lookupResult = EmitVectorExtract32(context, qx, ix, 3, false); // I64
}
lookupResult = context.ShiftRightUI(lookupResult, subVecIndexShift); // Get the relevant byte from this vector.
if (i == 0)
{
elemRes = lookupResult; // First result is always default.
}
else
{
Operand isThisElem = context.ICompareEqual(vecIndex, Const(i));
elemRes = context.ConditionalSelect(isThisElem, lookupResult, elemRes);
}
}
Operand fallback = (extension) ? context.ZeroExtend32(OperandType.I64, EmitVectorExtract32(context, op.Qd, index + op.Id, 0, false)) : Const(0L);
res = EmitVectorInsert(context, res, context.ConditionalSelect(inRange, elemRes, fallback), index + op.Id, 0);
}
context.Copy(GetVecA32(op.Qd), res);
}
}
public static void Vtrn(ArmEmitterContext context)
{
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
if (Optimizations.UseSsse3)
{
EmitVectorShuffleOpSimd32(context, (m, d) =>
{
Operand mask = default;
if (op.Size < 3)
{
long maskE0 = EvenMasks[op.Size];
long maskE1 = OddMasks[op.Size];
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
}
if (op.Size < 3)
{
d = context.AddIntrinsic(Intrinsic.X86Pshufb, d, mask);
m = context.AddIntrinsic(Intrinsic.X86Pshufb, m, mask);
}
Operand resD = context.AddIntrinsic(X86PunpcklInstruction[op.Size], d, m);
Operand resM = context.AddIntrinsic(X86PunpckhInstruction[op.Size], d, m);
return (resM, resD);
});
}
else
{
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
bool overlap = op.Qm == op.Qd;
Operand resD = GetVecA32(op.Qd);
Operand resM = GetVecA32(op.Qm);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand d2 = EmitVectorExtract32(context, op.Qd, pairIndex + 1 + op.Id, op.Size, false);
Operand m1 = EmitVectorExtract32(context, op.Qm, pairIndex + op.Im, op.Size, false);
resD = EmitVectorInsert(context, resD, m1, pairIndex + 1 + op.Id, op.Size);
if (overlap)
{
resM = resD;
}
resM = EmitVectorInsert(context, resM, d2, pairIndex + op.Im, op.Size);
if (overlap)
{
resD = resM;
}
}
context.Copy(GetVecA32(op.Qd), resD);
if (!overlap)
{
context.Copy(GetVecA32(op.Qm), resM);
}
}
}
public static void Vzip(ArmEmitterContext context)
{
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
if (Optimizations.UseSse2)
{
EmitVectorShuffleOpSimd32(context, (m, d) =>
{
if (op.RegisterSize == RegisterSize.Simd128)
{
Operand resD = context.AddIntrinsic(X86PunpcklInstruction[op.Size], d, m);
Operand resM = context.AddIntrinsic(X86PunpckhInstruction[op.Size], d, m);
return (resM, resD);
}
else
{
Operand res = context.AddIntrinsic(X86PunpcklInstruction[op.Size], d, m);
Operand resD = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, res, context.VectorZero());
Operand resM = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, res, context.VectorZero());
return (resM, resD);
}
});
}
else
{
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
bool overlap = op.Qm == op.Qd;
Operand resD = GetVecA32(op.Qd);
Operand resM = GetVecA32(op.Qm);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand dRowD = EmitVectorExtract32(context, op.Qd, index + op.Id, op.Size, false);
Operand mRowD = EmitVectorExtract32(context, op.Qm, index + op.Im, op.Size, false);
Operand dRowM = EmitVectorExtract32(context, op.Qd, index + op.Id + pairs, op.Size, false);
Operand mRowM = EmitVectorExtract32(context, op.Qm, index + op.Im + pairs, op.Size, false);
resD = EmitVectorInsert(context, resD, dRowD, pairIndex + op.Id, op.Size);
resD = EmitVectorInsert(context, resD, mRowD, pairIndex + 1 + op.Id, op.Size);
if (overlap)
{
resM = resD;
}
resM = EmitVectorInsert(context, resM, dRowM, pairIndex + op.Im, op.Size);
resM = EmitVectorInsert(context, resM, mRowM, pairIndex + 1 + op.Im, op.Size);
if (overlap)
{
resD = resM;
}
}
context.Copy(GetVecA32(op.Qd), resD);
if (!overlap)
{
context.Copy(GetVecA32(op.Qm), resM);
}
}
}
public static void Vuzp(ArmEmitterContext context)
{
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
if (Optimizations.UseSsse3)
{
EmitVectorShuffleOpSimd32(context, (m, d) =>
{
if (op.RegisterSize == RegisterSize.Simd128)
{
Operand mask = default;
if (op.Size < 3)
{
long maskE0 = EvenMasks[op.Size];
long maskE1 = OddMasks[op.Size];
mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
d = context.AddIntrinsic(Intrinsic.X86Pshufb, d, mask);
m = context.AddIntrinsic(Intrinsic.X86Pshufb, m, mask);
}
Operand resD = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, d, m);
Operand resM = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, d, m);
return (resM, resD);
}
else
{
Intrinsic punpcklInst = X86PunpcklInstruction[op.Size];
Operand res = context.AddIntrinsic(punpcklInst, d, m);
if (op.Size < 2)
{
long maskE0 = _masksE0_Uzp[op.Size];
long maskE1 = _masksE1_Uzp[op.Size];
Operand mask = X86GetScalar(context, maskE0);
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
res = context.AddIntrinsic(Intrinsic.X86Pshufb, res, mask);
}
Operand resD = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, res, context.VectorZero());
Operand resM = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, res, context.VectorZero());
return (resM, resD);
}
});
}
else
{
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
bool overlap = op.Qm == op.Qd;
Operand resD = GetVecA32(op.Qd);
Operand resM = GetVecA32(op.Qm);
for (int index = 0; index < elems; index++)
{
Operand dIns, mIns;
if (index >= pairs)
{
int pairIndex = index - pairs;
dIns = EmitVectorExtract32(context, op.Qm, (pairIndex << 1) + op.Im, op.Size, false);
mIns = EmitVectorExtract32(context, op.Qm, ((pairIndex << 1) | 1) + op.Im, op.Size, false);
}
else
{
dIns = EmitVectorExtract32(context, op.Qd, (index << 1) + op.Id, op.Size, false);
mIns = EmitVectorExtract32(context, op.Qd, ((index << 1) | 1) + op.Id, op.Size, false);
}
resD = EmitVectorInsert(context, resD, dIns, index + op.Id, op.Size);
if (overlap)
{
resM = resD;
}
resM = EmitVectorInsert(context, resM, mIns, index + op.Im, op.Size);
if (overlap)
{
resD = resM;
}
}
context.Copy(GetVecA32(op.Qd), resD);
if (!overlap)
{
context.Copy(GetVecA32(op.Qm), resM);
}
}
}
private static void EmitVectorShuffleOpSimd32(ArmEmitterContext context, Func<Operand, Operand, (Operand, Operand)> shuffleFunc)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
Operand m = GetVecA32(op.Qm);
Operand d = GetVecA32(op.Qd);
Operand initialM = m;
Operand initialD = d;
if (!op.Q) // Register swap: move relevant doubleword to side 0, for consistency.
{
m = EmitMoveDoubleWordToSide(context, m, op.Vm, 0);
d = EmitMoveDoubleWordToSide(context, d, op.Vd, 0);
}
(Operand resM, Operand resD) = shuffleFunc(m, d);
bool overlap = op.Qm == op.Qd;
if (!op.Q) // Register insert.
{
resM = EmitDoubleWordInsert(context, initialM, EmitMoveDoubleWordToSide(context, resM, 0, op.Vm), op.Vm);
resD = EmitDoubleWordInsert(context, overlap ? resM : initialD, EmitMoveDoubleWordToSide(context, resD, 0, op.Vd), op.Vd);
}
if (!overlap)
{
context.Copy(initialM, resM);
}
context.Copy(initialD, resD);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,211 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit
{
private const int DczSizeLog2 = 4; // Log2 size in words
public const int DczSizeInBytes = 4 << DczSizeLog2;
public static void Isb(ArmEmitterContext context)
{
// Execute as no-op.
}
public static void Mrs(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
MethodInfo info;
switch (GetPackedId(op))
{
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
default: throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
SetIntOrZR(context, op.Rt, context.Call(info));
}
public static void Msr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
MethodInfo info;
switch (GetPackedId(op))
{
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
context.Call(info, GetIntOrZR(context, op.Rt));
}
public static void Nop(ArmEmitterContext context)
{
// Do nothing.
}
public static void Sys(ArmEmitterContext context)
{
// This instruction is used to do some operations on the CPU like cache invalidation,
// address translation and the like.
// We treat it as no-op here since we don't have any cache being emulated anyway.
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
switch (GetPackedId(op))
{
case 0b11_011_0111_0100_001:
{
// DC ZVA
Operand t = GetIntOrZR(context, op.Rt);
for (long offset = 0; offset < DczSizeInBytes; offset += 8)
{
Operand address = context.Add(t, Const(offset));
InstEmitMemoryHelper.EmitStore(context, address, RegisterConsts.ZeroIndex, 3);
}
break;
}
// No-op
case 0b11_011_0111_1110_001: // DC CIVAC
break;
case 0b11_011_0111_0101_001: // IC IVAU
Operand target = Register(op.Rt, RegisterType.Integer, OperandType.I64);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
break;
}
}
private static int GetPackedId(OpCodeSystem op)
{
int id;
id = op.Op2 << 0;
id |= op.CRm << 3;
id |= op.CRn << 7;
id |= op.Op1 << 11;
id |= op.Op0 << 14;
return id;
}
private static void EmitGetNzcv(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand nzcv = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
SetIntOrZR(context, op.Rt, nzcv);
}
private static void EmitGetFpcr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpcr = Const(0);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
{
fpcr = context.BitwiseOr(fpcr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
}
}
SetIntOrZR(context, op.Rt, fpcr);
}
private static void EmitGetFpsr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpsr = Const(0);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
{
fpsr = context.BitwiseOr(fpsr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
}
}
SetIntOrZR(context, op.Rt, fpsr);
}
private static void EmitSetNzcv(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand nzcv = GetIntOrZR(context, op.Rt);
nzcv = context.ConvertI64ToI32(nzcv);
SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.VFlag)), Const(1)));
SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.CFlag)), Const(1)));
SetFlag(context, PState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.ZFlag)), Const(1)));
SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.NFlag)), Const(1)));
}
private static void EmitSetFpcr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpcr = GetIntOrZR(context, op.Rt);
fpcr = context.ConvertI64ToI32(fpcr);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
{
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
}
}
}
private static void EmitSetFpsr(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand fpsr = GetIntOrZR(context, op.Rt);
fpsr = context.ConvertI64ToI32(fpsr);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
{
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
}
}
}
}
}

View File

@@ -1,326 +0,0 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Reflection;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Mcr(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
if (op.Coproc != 15 || op.Opc1 != 0)
{
InstEmit.Und(context);
return;
}
MethodInfo info;
switch (op.CRn)
{
case 13: // Process and Thread Info.
if (op.CRm != 0)
{
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
}
switch (op.Opc2)
{
case 2:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break;
default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
}
break;
case 7:
switch (op.CRm) // Cache and Memory barrier.
{
case 10:
switch (op.Opc2)
{
case 5: // Data Memory Barrier Register.
return; // No-op.
default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
}
default:
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
}
default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
context.Call(info, GetIntA32(context, op.Rt));
}
public static void Mrc(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
if (op.Coproc != 15 || op.Opc1 != 0)
{
InstEmit.Und(context);
return;
}
MethodInfo info;
switch (op.CRn)
{
case 13: // Process and Thread Info.
if (op.CRm != 0)
{
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
}
switch (op.Opc2)
{
case 2:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break;
case 3:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break;
default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
}
break;
default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
}
if (op.Rt == RegisterAlias.Aarch32Pc)
{
// Special behavior: copy NZCV flags into APSR.
EmitSetNzcv(context, context.Call(info));
return;
}
else
{
SetIntA32(context, op.Rt, context.Call(info));
}
}
public static void Mrrc(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
if (op.Coproc != 15)
{
InstEmit.Und(context);
return;
}
int opc = op.MrrcOp;
MethodInfo info;
switch (op.CRm)
{
case 14: // Timer.
switch (opc)
{
case 0:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
default:
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
}
break;
default:
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
}
Operand result = context.Call(info);
SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
}
public static void Mrs(ArmEmitterContext context)
{
OpCode32Mrs op = (OpCode32Mrs)context.CurrOp;
if (op.R)
{
throw new NotImplementedException("SPSR");
}
else
{
Operand spsr = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag)));
// TODO: Remaining flags.
SetIntA32(context, op.Rd, spsr);
}
}
public static void Msr(ArmEmitterContext context)
{
OpCode32MsrReg op = (OpCode32MsrReg)context.CurrOp;
if (op.R)
{
throw new NotImplementedException("SPSR");
}
else
{
if ((op.Mask & 8) != 0)
{
Operand value = GetIntA32(context, op.Rn);
EmitSetNzcv(context, value);
Operand q = context.BitwiseAnd(context.ShiftRightUI(value, Const((int)PState.QFlag)), Const(1));
SetFlag(context, PState.QFlag, q);
}
if ((op.Mask & 4) != 0)
{
throw new NotImplementedException("APSR_g");
}
if ((op.Mask & 2) != 0)
{
throw new NotImplementedException("CPSR_x");
}
if ((op.Mask & 1) != 0)
{
throw new NotImplementedException("CPSR_c");
}
}
}
public static void Nop(ArmEmitterContext context) { }
public static void Vmrs(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
if (op.Rt == RegisterAlias.Aarch32Pc && op.Sreg == 0b0001)
{
// Special behavior: copy NZCV flags into APSR.
SetFlag(context, PState.VFlag, GetFpFlag(FPState.VFlag));
SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag));
SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
return;
}
switch (op.Sreg)
{
case 0b0000: // FPSID
throw new NotImplementedException("Supervisor Only");
case 0b0001: // FPSCR
EmitGetFpscr(context); return;
case 0b0101: // MVFR2
throw new NotImplementedException("MVFR2");
case 0b0110: // MVFR1
throw new NotImplementedException("MVFR1");
case 0b0111: // MVFR0
throw new NotImplementedException("MVFR0");
case 0b1000: // FPEXC
throw new NotImplementedException("Supervisor Only");
default:
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
}
}
public static void Vmsr(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
switch (op.Sreg)
{
case 0b0000: // FPSID
throw new NotImplementedException("Supervisor Only");
case 0b0001: // FPSCR
EmitSetFpscr(context); return;
case 0b0101: // MVFR2
throw new NotImplementedException("MVFR2");
case 0b0110: // MVFR1
throw new NotImplementedException("MVFR1");
case 0b0111: // MVFR0
throw new NotImplementedException("MVFR0");
case 0b1000: // FPEXC
throw new NotImplementedException("Supervisor Only");
default:
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
}
}
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
{
Operand v = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.VFlag)), Const(1));
Operand c = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.CFlag)), Const(1));
Operand z = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.ZFlag)), Const(1));
Operand n = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.NFlag)), Const(1));
SetFlag(context, PState.VFlag, v);
SetFlag(context, PState.CFlag, c);
SetFlag(context, PState.ZFlag, z);
SetFlag(context, PState.NFlag, n);
}
private static void EmitGetFpscr(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
Operand fpscr = Const(0);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
{
fpscr = context.BitwiseOr(fpscr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
}
}
SetIntA32(context, op.Rt, fpscr);
}
private static void EmitSetFpscr(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
Operand fpscr = GetIntA32(context, op.Rt);
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
{
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
}
}
}
}
}

View File

@@ -1,225 +0,0 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
namespace ARMeilleure.Instructions
{
static class NativeInterface
{
private class ThreadContext
{
public ExecutionContext Context { get; }
public IMemoryManager Memory { get; }
public Translator Translator { get; }
public ThreadContext(ExecutionContext context, IMemoryManager memory, Translator translator)
{
Context = context;
Memory = memory;
Translator = translator;
}
}
[ThreadStatic]
private static ThreadContext Context;
public static void RegisterThread(ExecutionContext context, IMemoryManager memory, Translator translator)
{
Context = new ThreadContext(context, memory, translator);
}
public static void UnregisterThread()
{
Context = null;
}
public static void Break(ulong address, int imm)
{
Statistics.PauseTimer();
GetContext().OnBreak(address, imm);
Statistics.ResumeTimer();
}
public static void SupervisorCall(ulong address, int imm)
{
Statistics.PauseTimer();
GetContext().OnSupervisorCall(address, imm);
Statistics.ResumeTimer();
}
public static void Undefined(ulong address, int opCode)
{
Statistics.PauseTimer();
GetContext().OnUndefined(address, opCode);
Statistics.ResumeTimer();
}
#region "System registers"
public static ulong GetCtrEl0()
{
return (ulong)GetContext().CtrEl0;
}
public static ulong GetDczidEl0()
{
return (ulong)GetContext().DczidEl0;
}
public static ulong GetTpidrEl0()
{
return (ulong)GetContext().TpidrEl0;
}
public static uint GetTpidrEl032()
{
return (uint)GetContext().TpidrEl0;
}
public static ulong GetTpidrroEl0()
{
return (ulong)GetContext().TpidrroEl0;
}
public static uint GetTpidr32()
{
return (uint)GetContext().TpidrroEl0;
}
public static ulong GetCntfrqEl0()
{
return GetContext().CntfrqEl0;
}
public static ulong GetCntpctEl0()
{
return GetContext().CntpctEl0;
}
public static ulong GetCntvctEl0()
{
return GetContext().CntvctEl0;
}
public static void SetTpidrEl0(ulong value)
{
GetContext().TpidrEl0 = (long)value;
}
public static void SetTpidrEl032(uint value)
{
GetContext().TpidrEl0 = (long)value;
}
#endregion
#region "Read"
public static byte ReadByte(ulong address)
{
return GetMemoryManager().ReadTracked<byte>(address);
}
public static ushort ReadUInt16(ulong address)
{
return GetMemoryManager().ReadTracked<ushort>(address);
}
public static uint ReadUInt32(ulong address)
{
return GetMemoryManager().ReadTracked<uint>(address);
}
public static ulong ReadUInt64(ulong address)
{
return GetMemoryManager().ReadTracked<ulong>(address);
}
public static V128 ReadVector128(ulong address)
{
return GetMemoryManager().ReadTracked<V128>(address);
}
#endregion
#region "Write"
public static void WriteByte(ulong address, byte value)
{
GetMemoryManager().Write(address, value);
}
public static void WriteUInt16(ulong address, ushort value)
{
GetMemoryManager().Write(address, value);
}
public static void WriteUInt32(ulong address, uint value)
{
GetMemoryManager().Write(address, value);
}
public static void WriteUInt64(ulong address, ulong value)
{
GetMemoryManager().Write(address, value);
}
public static void WriteVector128(ulong address, V128 value)
{
GetMemoryManager().Write(address, value);
}
#endregion
public static void EnqueueForRejit(ulong address)
{
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
}
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
{
GetMemoryManager().SignalMemoryTracking(address, size, write);
}
public static void ThrowInvalidMemoryAccess(ulong address)
{
throw new InvalidAccessException(address);
}
public static ulong GetFunctionAddress(ulong address)
{
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
return (ulong)function.FuncPtr.ToInt64();
}
public static void InvalidateCacheLine(ulong address)
{
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
}
public static bool CheckSynchronization()
{
Statistics.PauseTimer();
ExecutionContext context = GetContext();
context.CheckInterrupt();
Statistics.ResumeTimer();
return context.Running;
}
public static ExecutionContext GetContext()
{
return Context.Context;
}
public static IMemoryManager GetMemoryManager()
{
return Context.Memory;
}
}
}

View File

@@ -1,166 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
{
class BasicBlock : IEquatable<BasicBlock>, IIntrusiveListNode<BasicBlock>
{
private const uint MaxSuccessors = 2;
private int _succCount;
private BasicBlock _succ0;
private BasicBlock _succ1;
private HashSet<BasicBlock> _domFrontiers;
public int Index { get; set; }
public BasicBlockFrequency Frequency { get; set; }
public BasicBlock ListPrevious { get; set; }
public BasicBlock ListNext { get; set; }
public IntrusiveList<Operation> Operations { get; }
public List<BasicBlock> Predecessors { get; }
public BasicBlock ImmediateDominator { get; set; }
public int SuccessorsCount => _succCount;
public HashSet<BasicBlock> DominanceFrontiers
{
get
{
if (_domFrontiers == null)
{
_domFrontiers = new HashSet<BasicBlock>();
}
return _domFrontiers;
}
}
public BasicBlock() : this(index: -1) { }
public BasicBlock(int index)
{
Operations = new IntrusiveList<Operation>();
Predecessors = new List<BasicBlock>();
Index = index;
}
public void AddSuccessor(BasicBlock block)
{
if (block == null)
{
ThrowNull(nameof(block));
}
if ((uint)_succCount + 1 > MaxSuccessors)
{
ThrowSuccessorOverflow();
}
block.Predecessors.Add(this);
GetSuccessorUnsafe(_succCount++) = block;
}
public void RemoveSuccessor(int index)
{
if ((uint)index >= (uint)_succCount)
{
ThrowOutOfRange(nameof(index));
}
ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
oldBlock.Predecessors.Remove(this);
oldBlock = null;
if (index == 0)
{
_succ0 = _succ1;
}
_succCount--;
}
public BasicBlock GetSuccessor(int index)
{
if ((uint)index >= (uint)_succCount)
{
ThrowOutOfRange(nameof(index));
}
return GetSuccessorUnsafe(index);
}
private ref BasicBlock GetSuccessorUnsafe(int index)
{
return ref Unsafe.Add(ref _succ0, index);
}
public void SetSuccessor(int index, BasicBlock block)
{
if (block == null)
{
ThrowNull(nameof(block));
}
if ((uint)index >= (uint)_succCount)
{
ThrowOutOfRange(nameof(index));
}
ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
oldBlock.Predecessors.Remove(this);
block.Predecessors.Add(this);
oldBlock = block;
}
public void Append(Operation node)
{
Operation last = Operations.Last;
// Append node before terminal or to end if no terminal.
if (last == default)
{
Operations.AddLast(node);
return;
}
switch (last.Instruction)
{
case Instruction.Return:
case Instruction.Tailcall:
case Instruction.BranchIf:
Operations.AddBefore(last, node);
break;
default:
Operations.AddLast(node);
break;
}
}
private static void ThrowNull(string name) => throw new ArgumentNullException(name);
private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
public bool Equals(BasicBlock other)
{
return other == this;
}
public override bool Equals(object obj)
{
return Equals(obj as BasicBlock);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}

View File

@@ -1,177 +0,0 @@
namespace ARMeilleure.IntermediateRepresentation
{
enum Intrinsic : ushort
{
X86Addpd,
X86Addps,
X86Addsd,
X86Addss,
X86Aesdec,
X86Aesdeclast,
X86Aesenc,
X86Aesenclast,
X86Aesimc,
X86Andnpd,
X86Andnps,
X86Andpd,
X86Andps,
X86Blendvpd,
X86Blendvps,
X86Cmppd,
X86Cmpps,
X86Cmpsd,
X86Cmpss,
X86Comisdeq,
X86Comisdge,
X86Comisdlt,
X86Comisseq,
X86Comissge,
X86Comisslt,
X86Crc32,
X86Crc32_16,
X86Crc32_8,
X86Cvtdq2pd,
X86Cvtdq2ps,
X86Cvtpd2dq,
X86Cvtpd2ps,
X86Cvtps2dq,
X86Cvtps2pd,
X86Cvtsd2si,
X86Cvtsd2ss,
X86Cvtsi2sd,
X86Cvtsi2si,
X86Cvtsi2ss,
X86Cvtss2sd,
X86Cvtss2si,
X86Divpd,
X86Divps,
X86Divsd,
X86Divss,
X86Gf2p8affineqb,
X86Haddpd,
X86Haddps,
X86Insertps,
X86Maxpd,
X86Maxps,
X86Maxsd,
X86Maxss,
X86Minpd,
X86Minps,
X86Minsd,
X86Minss,
X86Movhlps,
X86Movlhps,
X86Movss,
X86Mulpd,
X86Mulps,
X86Mulsd,
X86Mulss,
X86Mxcsrmb,
X86Mxcsrub,
X86Paddb,
X86Paddd,
X86Paddq,
X86Paddw,
X86Palignr,
X86Pand,
X86Pandn,
X86Pavgb,
X86Pavgw,
X86Pblendvb,
X86Pclmulqdq,
X86Pcmpeqb,
X86Pcmpeqd,
X86Pcmpeqq,
X86Pcmpeqw,
X86Pcmpgtb,
X86Pcmpgtd,
X86Pcmpgtq,
X86Pcmpgtw,
X86Pmaxsb,
X86Pmaxsd,
X86Pmaxsw,
X86Pmaxub,
X86Pmaxud,
X86Pmaxuw,
X86Pminsb,
X86Pminsd,
X86Pminsw,
X86Pminub,
X86Pminud,
X86Pminuw,
X86Pmovsxbw,
X86Pmovsxdq,
X86Pmovsxwd,
X86Pmovzxbw,
X86Pmovzxdq,
X86Pmovzxwd,
X86Pmulld,
X86Pmullw,
X86Popcnt,
X86Por,
X86Pshufb,
X86Pshufd,
X86Pslld,
X86Pslldq,
X86Psllq,
X86Psllw,
X86Psrad,
X86Psraw,
X86Psrld,
X86Psrlq,
X86Psrldq,
X86Psrlw,
X86Psubb,
X86Psubd,
X86Psubq,
X86Psubw,
X86Punpckhbw,
X86Punpckhdq,
X86Punpckhqdq,
X86Punpckhwd,
X86Punpcklbw,
X86Punpckldq,
X86Punpcklqdq,
X86Punpcklwd,
X86Pxor,
X86Rcpps,
X86Rcpss,
X86Roundpd,
X86Roundps,
X86Roundsd,
X86Roundss,
X86Rsqrtps,
X86Rsqrtss,
X86Sha256Msg1,
X86Sha256Msg2,
X86Sha256Rnds2,
X86Shufpd,
X86Shufps,
X86Sqrtpd,
X86Sqrtps,
X86Sqrtsd,
X86Sqrtss,
X86Subpd,
X86Subps,
X86Subsd,
X86Subss,
X86Unpckhpd,
X86Unpckhps,
X86Unpcklpd,
X86Unpcklps,
X86Vcvtph2ps,
X86Vcvtps2ph,
X86Vfmadd231ps,
X86Vfmadd231sd,
X86Vfmadd231ss,
X86Vfmsub231sd,
X86Vfmsub231ss,
X86Vfnmadd231ps,
X86Vfnmadd231sd,
X86Vfnmadd231ss,
X86Vfnmsub231sd,
X86Vfnmsub231ss,
X86Xorpd,
X86Xorps
}
}

View File

@@ -1,10 +0,0 @@
namespace ARMeilleure.IntermediateRepresentation
{
enum Multiplier
{
x1 = 0,
x2 = 1,
x4 = 2,
x8 = 3
}
}

View File

@@ -1,587 +0,0 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.Common;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
{
unsafe struct Operand : IEquatable<Operand>
{
internal struct Data
{
public byte Kind;
public byte Type;
public byte SymbolType;
public byte Padding; // Unused space.
public ushort AssignmentsCount;
public ushort AssignmentsCapacity;
public uint UsesCount;
public uint UsesCapacity;
public Operation* Assignments;
public Operation* Uses;
public ulong Value;
public ulong SymbolValue;
}
private Data* _data;
public OperandKind Kind
{
get => (OperandKind)_data->Kind;
private set => _data->Kind = (byte)value;
}
public OperandType Type
{
get => (OperandType)_data->Type;
private set => _data->Type = (byte)value;
}
public ulong Value
{
get => _data->Value;
private set => _data->Value = value;
}
public Symbol Symbol
{
get
{
Debug.Assert(Kind != OperandKind.Memory);
return new Symbol((SymbolType)_data->SymbolType, _data->SymbolValue);
}
private set
{
Debug.Assert(Kind != OperandKind.Memory);
if (value.Type == SymbolType.None)
{
_data->SymbolType = (byte)SymbolType.None;
}
else
{
_data->SymbolType = (byte)value.Type;
_data->SymbolValue = value.Value;
}
}
}
public ReadOnlySpan<Operation> Assignments
{
get
{
Debug.Assert(Kind != OperandKind.Memory);
return new ReadOnlySpan<Operation>(_data->Assignments, _data->AssignmentsCount);
}
}
public ReadOnlySpan<Operation> Uses
{
get
{
Debug.Assert(Kind != OperandKind.Memory);
return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
}
}
public int UsesCount => (int)_data->UsesCount;
public int AssignmentsCount => _data->AssignmentsCount;
public bool Relocatable => Symbol.Type != SymbolType.None;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Register GetRegister()
{
Debug.Assert(Kind == OperandKind.Register);
return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MemoryOperand GetMemory()
{
Debug.Assert(Kind == OperandKind.Memory);
return new MemoryOperand(this);
}
public int GetLocalNumber()
{
Debug.Assert(Kind == OperandKind.LocalVariable);
return (int)Value;
}
public byte AsByte()
{
return (byte)Value;
}
public short AsInt16()
{
return (short)Value;
}
public int AsInt32()
{
return (int)Value;
}
public long AsInt64()
{
return (long)Value;
}
public float AsFloat()
{
return BitConverter.Int32BitsToSingle((int)Value);
}
public double AsDouble()
{
return BitConverter.Int64BitsToDouble((long)Value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref ulong GetValueUnsafe()
{
return ref _data->Value;
}
internal void NumberLocal(int number)
{
if (Kind != OperandKind.LocalVariable)
{
throw new InvalidOperationException("The operand is not a local variable.");
}
Value = (ulong)number;
}
public void AddAssignment(Operation operation)
{
if (Kind == OperandKind.LocalVariable)
{
Add(operation, ref _data->Assignments, ref _data->AssignmentsCount, ref _data->AssignmentsCapacity);
}
else if (Kind == OperandKind.Memory)
{
MemoryOperand memOp = GetMemory();
Operand addr = memOp.BaseAddress;
Operand index = memOp.Index;
if (addr != default)
{
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
}
if (index != default)
{
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
}
}
}
public void RemoveAssignment(Operation operation)
{
if (Kind == OperandKind.LocalVariable)
{
Remove(operation, ref _data->Assignments, ref _data->AssignmentsCount);
}
else if (Kind == OperandKind.Memory)
{
MemoryOperand memOp = GetMemory();
Operand addr = memOp.BaseAddress;
Operand index = memOp.Index;
if (addr != default)
{
Remove(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount);
}
if (index != default)
{
Remove(operation, ref index._data->Assignments, ref index._data->AssignmentsCount);
}
}
}
public void AddUse(Operation operation)
{
if (Kind == OperandKind.LocalVariable)
{
Add(operation, ref _data->Uses, ref _data->UsesCount, ref _data->UsesCapacity);
}
else if (Kind == OperandKind.Memory)
{
MemoryOperand memOp = GetMemory();
Operand addr = memOp.BaseAddress;
Operand index = memOp.Index;
if (addr != default)
{
Add(operation, ref addr._data->Uses, ref addr._data->UsesCount, ref addr._data->UsesCapacity);
}
if (index != default)
{
Add(operation, ref index._data->Uses, ref index._data->UsesCount, ref index._data->UsesCapacity);
}
}
}
public void RemoveUse(Operation operation)
{
if (Kind == OperandKind.LocalVariable)
{
Remove(operation, ref _data->Uses, ref _data->UsesCount);
}
else if (Kind == OperandKind.Memory)
{
MemoryOperand memOp = GetMemory();
Operand addr = memOp.BaseAddress;
Operand index = memOp.Index;
if (addr != default)
{
Remove(operation, ref addr._data->Uses, ref addr._data->UsesCount);
}
if (index != default)
{
Remove(operation, ref index._data->Uses, ref index._data->UsesCount);
}
}
}
private static void New<T>(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged
{
count = 0;
capacity = initialCapacity;
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
{
count = 0;
capacity = initialCapacity;
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
{
if (count < capacity)
{
data[(uint)count++] = item;
return;
}
// Could not add item in the fast path, fallback onto the slow path.
ExpandAdd(item, ref data, ref count, ref capacity);
static void ExpandAdd(T item, ref T* data, ref ushort count, ref ushort capacity)
{
ushort newCount = checked((ushort)(count + 1));
ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
var oldSpan = new Span<T>(data, count);
capacity = newCapacity;
data = Allocators.References.Allocate<T>(capacity);
oldSpan.CopyTo(new Span<T>(data, count));
data[count] = item;
count = newCount;
}
}
private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
{
if (count < capacity)
{
data[count++] = item;
return;
}
// Could not add item in the fast path, fallback onto the slow path.
ExpandAdd(item, ref data, ref count, ref capacity);
static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
{
uint newCount = checked(count + 1);
uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
if (newCapacity <= capacity)
{
throw new OverflowException();
}
var oldSpan = new Span<T>(data, (int)count);
capacity = newCapacity;
data = Allocators.References.Allocate<T>(capacity);
oldSpan.CopyTo(new Span<T>(data, (int)count));
data[count] = item;
count = newCount;
}
}
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
{
var span = new Span<T>(data, count);
for (int i = 0; i < span.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(span[i], item))
{
if (i + 1 < count)
{
span.Slice(i + 1).CopyTo(span.Slice(i));
}
count--;
return;
}
}
}
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
{
var span = new Span<T>(data, (int)count);
for (int i = 0; i < span.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(span[i], item))
{
if (i + 1 < count)
{
span.Slice(i + 1).CopyTo(span.Slice(i));
}
count--;
return;
}
}
}
public override int GetHashCode()
{
if (Kind == OperandKind.LocalVariable)
{
return base.GetHashCode();
}
else
{
return (int)Value ^ ((int)Kind << 16) ^ ((int)Type << 20);
}
}
public bool Equals(Operand operand)
{
return operand._data == _data;
}
public override bool Equals(object obj)
{
return obj is Operand operand && Equals(operand);
}
public static bool operator ==(Operand a, Operand b)
{
return a.Equals(b);
}
public static bool operator !=(Operand a, Operand b)
{
return !a.Equals(b);
}
public static class Factory
{
private const int InternTableSize = 256;
private const int InternTableProbeLength = 8;
[ThreadStatic]
private static Data* _internTable;
private static Data* InternTable
{
get
{
if (_internTable == null)
{
_internTable = (Data*)NativeAllocator.Instance.Allocate((uint)sizeof(Data) * InternTableSize);
// Make sure the table is zeroed.
new Span<Data>(_internTable, InternTableSize).Clear();
}
return _internTable;
}
}
private static Operand Make(OperandKind kind, OperandType type, ulong value, Symbol symbol = default)
{
Debug.Assert(kind != OperandKind.None);
Data* data = null;
// If constant or register, then try to look up in the intern table before allocating.
if (kind == OperandKind.Constant || kind == OperandKind.Register)
{
uint hash = (uint)HashCode.Combine(kind, type, value);
// Look in the next InternTableProbeLength slots for a match.
for (uint i = 0; i < InternTableProbeLength; i++)
{
Operand interned = new();
interned._data = &InternTable[(hash + i) % InternTableSize];
// If slot matches the allocation request then return that slot.
if (interned.Kind == kind && interned.Type == type && interned.Value == value && interned.Symbol == symbol)
{
return interned;
}
// Otherwise if the slot is not occupied, we store in that slot.
else if (interned.Kind == OperandKind.None)
{
data = interned._data;
break;
}
}
}
// If we could not get a slot from the intern table, we allocate somewhere else and store there.
if (data == null)
{
data = Allocators.Operands.Allocate<Data>();
}
*data = default;
Operand result = new();
result._data = data;
result.Value = value;
result.Kind = kind;
result.Type = type;
if (kind != OperandKind.Memory)
{
result.Symbol = symbol;
}
// If local variable, then the use and def list is initialized with default sizes.
if (kind == OperandKind.LocalVariable)
{
New(ref result._data->Assignments, ref result._data->AssignmentsCount, ref result._data->AssignmentsCapacity, 1);
New(ref result._data->Uses, ref result._data->UsesCount, ref result._data->UsesCapacity, 4);
}
return result;
}
public static Operand Const(OperandType type, long value)
{
Debug.Assert(type is OperandType.I32 or OperandType.I64);
return type == OperandType.I32 ? Const((int)value) : Const(value);
}
public static Operand Const(bool value)
{
return Const(value ? 1 : 0);
}
public static Operand Const(int value)
{
return Const((uint)value);
}
public static Operand Const(uint value)
{
return Make(OperandKind.Constant, OperandType.I32, value);
}
public static Operand Const(long value)
{
return Const(value, symbol: default);
}
public static Operand Const<T>(ref T reference, Symbol symbol = default)
{
return Const((long)Unsafe.AsPointer(ref reference), symbol);
}
public static Operand Const(long value, Symbol symbol)
{
return Make(OperandKind.Constant, OperandType.I64, (ulong)value, symbol);
}
public static Operand Const(ulong value)
{
return Make(OperandKind.Constant, OperandType.I64, value);
}
public static Operand ConstF(float value)
{
return Make(OperandKind.Constant, OperandType.FP32, (ulong)BitConverter.SingleToInt32Bits(value));
}
public static Operand ConstF(double value)
{
return Make(OperandKind.Constant, OperandType.FP64, (ulong)BitConverter.DoubleToInt64Bits(value));
}
public static Operand Label()
{
return Make(OperandKind.Label, OperandType.None, 0);
}
public static Operand Local(OperandType type)
{
return Make(OperandKind.LocalVariable, type, 0);
}
public static Operand Register(int index, RegisterType regType, OperandType type)
{
return Make(OperandKind.Register, type, (ulong)((int)regType << 24 | index));
}
public static Operand Undef()
{
return Make(OperandKind.Undefined, OperandType.None, 0);
}
public static Operand MemoryOp(
OperandType type,
Operand baseAddress,
Operand index = default,
Multiplier scale = Multiplier.x1,
int displacement = 0)
{
Operand result = Make(OperandKind.Memory, type, 0);
MemoryOperand memory = result.GetMemory();
memory.BaseAddress = baseAddress;
memory.Index = index;
memory.Scale = scale;
memory.Displacement = displacement;
return result;
}
}
}
}

View File

@@ -1,51 +0,0 @@
using System;
namespace ARMeilleure.IntermediateRepresentation
{
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 RegisterType ToRegisterType(this OperandType type)
{
switch (type)
{
case OperandType.FP32: return RegisterType.Vector;
case OperandType.FP64: return RegisterType.Vector;
case OperandType.I32: return RegisterType.Integer;
case OperandType.I64: return RegisterType.Integer;
case OperandType.V128: return RegisterType.Vector;
}
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
}
public static int GetSizeInBytes(this OperandType type)
{
switch (type)
{
case OperandType.FP32: return 4;
case OperandType.FP64: return 8;
case OperandType.I32: return 4;
case OperandType.I64: return 8;
case OperandType.V128: return 16;
}
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
}
}
}

View File

@@ -1,37 +0,0 @@
using ARMeilleure.Translation;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.IntermediateRepresentation
{
struct PhiOperation
{
private readonly Operation _operation;
public PhiOperation(Operation operation)
{
_operation = operation;
}
public int SourcesCount => _operation.SourcesCount / 2;
public BasicBlock GetBlock(ControlFlowGraph cfg, int index)
{
return cfg.PostOrderBlocks[cfg.PostOrderMap[_operation.GetSource(index * 2).AsInt32()]];
}
public void SetBlock(int index, BasicBlock block)
{
_operation.SetSource(index * 2, Const(block.Index));
}
public Operand GetSource(int index)
{
return _operation.GetSource(index * 2 + 1);
}
public void SetSource(int index, Operand operand)
{
_operation.SetSource(index * 2 + 1, operand);
}
}
}

View File

@@ -1,43 +0,0 @@
using System;
namespace ARMeilleure.IntermediateRepresentation
{
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;
}
}
}

View File

@@ -1,8 +0,0 @@
namespace ARMeilleure.Memory
{
public interface IJitMemoryAllocator
{
IJitMemoryBlock Allocate(ulong size);
IJitMemoryBlock Reserve(ulong size);
}
}

View File

@@ -1,76 +0,0 @@
using System;
namespace ARMeilleure.Memory
{
public interface IMemoryManager
{
int AddressSpaceBits { get; }
IntPtr PageTablePointer { get; }
MemoryManagerType Type { get; }
event Action<ulong, ulong> UnmapEvent;
/// <summary>
/// Reads data from CPU mapped memory.
/// </summary>
/// <typeparam name="T">Type of the data being read</typeparam>
/// <param name="va">Virtual address of the data in memory</param>
/// <returns>The data</returns>
T Read<T>(ulong va) where T : unmanaged;
/// <summary>
/// Reads data from CPU mapped memory, with read tracking
/// </summary>
/// <typeparam name="T">Type of the data being read</typeparam>
/// <param name="va">Virtual address of the data in memory</param>
/// <returns>The data</returns>
T ReadTracked<T>(ulong va) where T : unmanaged;
/// <summary>
/// Writes data to CPU mapped memory.
/// </summary>
/// <typeparam name="T">Type of the data being written</typeparam>
/// <param name="va">Virtual address to write the data into</param>
/// <param name="value">Data to be written</param>
void Write<T>(ulong va, T value) where T : unmanaged;
/// <summary>
/// Gets a read-only span of data from CPU mapped memory.
/// </summary>
/// <param name="va">Virtual address of the data</param>
/// <param name="size">Size of the data</param>
/// <param name="tracked">True if read tracking is triggered on the span</param>
/// <returns>A read-only span of the data</returns>
ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false);
/// <summary>
/// Gets a reference for the given type at the specified virtual memory address.
/// </summary>
/// <remarks>
/// The data must be located at a contiguous memory region.
/// </remarks>
/// <typeparam name="T">Type of the data to get the reference</typeparam>
/// <param name="va">Virtual address of the data</param>
/// <returns>A reference to the data in memory</returns>
ref T GetRef<T>(ulong va) where T : unmanaged;
/// <summary>
/// Checks if the page at a given CPU virtual address is mapped.
/// </summary>
/// <param name="va">Virtual address to check</param>
/// <returns>True if the address is mapped, false otherwise</returns>
bool IsMapped(ulong va);
/// <summary>
/// Alerts the memory tracking that a given region has been read from or written to.
/// This should be called before read/write is performed.
/// </summary>
/// <param name="va">Virtual address of the region</param>
/// <param name="size">Size of the region</param>
/// <param name="write">True if the region was written, false if read</param>
/// <param name="precise">True if the access is precise, false otherwise</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false);
}
}

View File

@@ -1,58 +0,0 @@
using System;
namespace ARMeilleure.Memory
{
class ReservedRegion
{
private const int DefaultGranularity = 65536; // Mapping granularity in Windows.
public IJitMemoryBlock Block { get; }
public IntPtr Pointer => Block.Pointer;
private readonly ulong _maxSize;
private readonly ulong _sizeGranularity;
private ulong _currentSize;
public ReservedRegion(IJitMemoryAllocator allocator, ulong maxSize, ulong granularity = 0)
{
if (granularity == 0)
{
granularity = DefaultGranularity;
}
Block = allocator.Reserve(maxSize);
_maxSize = maxSize;
_sizeGranularity = granularity;
_currentSize = 0;
}
public void ExpandIfNeeded(ulong desiredSize)
{
if (desiredSize > _maxSize)
{
throw new OutOfMemoryException();
}
if (desiredSize > _currentSize)
{
// Lock, and then check again. We only want to commit once.
lock (this)
{
if (desiredSize >= _currentSize)
{
ulong overflowBytes = desiredSize - _currentSize;
ulong moreToCommit = (((_sizeGranularity - 1) + overflowBytes) / _sizeGranularity) * _sizeGranularity; // Round up.
Block.Commit(_currentSize, moreToCommit);
_currentSize += moreToCommit;
}
}
}
}
public void Dispose()
{
Block.Dispose();
}
}
}

View File

@@ -1,48 +0,0 @@
using ARMeilleure.CodeGen.X86;
namespace ARMeilleure
{
public static class Optimizations
{
public static bool FastFP { get; set; } = true;
public static bool AllowLcqInFunctionTable { get; set; } = true;
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
public static bool UseSseIfAvailable { get; set; } = true;
public static bool UseSse2IfAvailable { get; set; } = true;
public static bool UseSse3IfAvailable { get; set; } = true;
public static bool UseSsse3IfAvailable { get; set; } = true;
public static bool UseSse41IfAvailable { get; set; } = true;
public static bool UseSse42IfAvailable { get; set; } = true;
public static bool UsePopCntIfAvailable { get; set; } = true;
public static bool UseAvxIfAvailable { get; set; } = true;
public static bool UseF16cIfAvailable { get; set; } = true;
public static bool UseFmaIfAvailable { get; set; } = true;
public static bool UseAesniIfAvailable { get; set; } = true;
public static bool UsePclmulqdqIfAvailable { get; set; } = true;
public static bool UseShaIfAvailable { get; set; } = true;
public static bool UseGfniIfAvailable { get; set; } = true;
public static bool ForceLegacySse
{
get => HardwareCapabilities.ForceLegacySse;
set => HardwareCapabilities.ForceLegacySse = value;
}
internal static bool UseSse => UseSseIfAvailable && HardwareCapabilities.SupportsSse;
internal static bool UseSse2 => UseSse2IfAvailable && HardwareCapabilities.SupportsSse2;
internal static bool UseSse3 => UseSse3IfAvailable && HardwareCapabilities.SupportsSse3;
internal static bool UseSsse3 => UseSsse3IfAvailable && HardwareCapabilities.SupportsSsse3;
internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41;
internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42;
internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt;
internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse;
internal static bool UseF16c => UseF16cIfAvailable && HardwareCapabilities.SupportsF16c;
internal static bool UseFma => UseFmaIfAvailable && HardwareCapabilities.SupportsFma;
internal static bool UseAesni => UseAesniIfAvailable && HardwareCapabilities.SupportsAesni;
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && HardwareCapabilities.SupportsPclmulqdq;
internal static bool UseSha => UseShaIfAvailable && HardwareCapabilities.SupportsSha;
internal static bool UseGfni => UseGfniIfAvailable && HardwareCapabilities.SupportsGfni;
}
}

View File

@@ -1,338 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Signal
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SignalHandlerRange
{
public int IsActive;
public nuint RangeAddress;
public nuint RangeEndAddress;
public IntPtr ActionPointer;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SignalHandlerConfig
{
/// <summary>
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
/// </summary>
public int StructAddressOffset;
/// <summary>
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
/// </summary>
public int StructWriteOffset;
/// <summary>
/// The sigaction handler that was registered before this one. (unix only)
/// </summary>
public nuint UnixOldSigaction;
/// <summary>
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
/// </summary>
public int UnixOldSigaction3Arg;
public SignalHandlerRange Range0;
public SignalHandlerRange Range1;
public SignalHandlerRange Range2;
public SignalHandlerRange Range3;
public SignalHandlerRange Range4;
public SignalHandlerRange Range5;
public SignalHandlerRange Range6;
public SignalHandlerRange Range7;
}
public static class NativeSignalHandler
{
private delegate void UnixExceptionHandler(int sig, IntPtr info, IntPtr ucontext);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int VectoredExceptionHandler(IntPtr exceptionInfo);
private const int MaxTrackedRanges = 8;
private const int StructAddressOffset = 0;
private const int StructWriteOffset = 4;
private const int UnixOldSigaction = 8;
private const int UnixOldSigaction3Arg = 16;
private const int RangeOffset = 20;
private const int EXCEPTION_CONTINUE_SEARCH = 0;
private const int EXCEPTION_CONTINUE_EXECUTION = -1;
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
private const ulong PageSize = 0x1000;
private const ulong PageMask = PageSize - 1;
private static IntPtr _handlerConfig;
private static IntPtr _signalHandlerPtr;
private static IntPtr _signalHandlerHandle;
private static readonly object _lock = new object();
private static bool _initialized;
static NativeSignalHandler()
{
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
ref SignalHandlerConfig config = ref GetConfigRef();
config = new SignalHandlerConfig();
}
public static void InitializeSignalHandler()
{
if (_initialized) return;
lock (_lock)
{
if (_initialized) return;
bool unix = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS();
ref SignalHandlerConfig config = ref GetConfigRef();
if (unix)
{
// Unix siginfo struct locations.
// NOTE: These are incredibly likely to be different between kernel version and architectures.
config.StructAddressOffset = OperatingSystem.IsMacOS() ? 24 : 16; // si_addr
config.StructWriteOffset = 8; // si_code
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
SigAction old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
config.UnixOldSigaction3Arg = old.sa_flags & 4;
}
else
{
config.StructAddressOffset = 40; // ExceptionInformation1
config.StructWriteOffset = 32; // ExceptionInformation0
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig));
_signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
}
_initialized = true;
}
}
private static unsafe ref SignalHandlerConfig GetConfigRef()
{
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
}
public static unsafe bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
{
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
for (int i = 0; i < MaxTrackedRanges; i++)
{
if (ranges[i].IsActive == 0)
{
ranges[i].RangeAddress = address;
ranges[i].RangeEndAddress = endAddress;
ranges[i].ActionPointer = action;
ranges[i].IsActive = 1;
return true;
}
}
return false;
}
public static unsafe bool RemoveTrackedRegion(nuint address)
{
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
for (int i = 0; i < MaxTrackedRanges; i++)
{
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
{
ranges[i].IsActive = 0;
return true;
}
}
return false;
}
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite)
{
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
context.Copy(inRegionLocal, Const(0));
Operand endLabel = Label();
for (int i = 0; i < MaxTrackedRanges; i++)
{
ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
Operand nextLabel = Label();
Operand isActive = context.Load(OperandType.I32, Const((ulong)signalStructPtr + rangeBaseOffset));
context.BranchIfFalse(nextLabel, isActive);
Operand rangeAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 4));
Operand rangeEndAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 12));
// Is the fault address within this tracked region?
Operand inRange = context.BitwiseAnd(
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI)
);
// Only call tracking if in range.
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~PageMask));
// Call the tracking action, with the pointer's relative offset to the base address.
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
context.Copy(inRegionLocal, Const(0));
Operand skipActionLabel = Label();
// Tracking action should be non-null to call it, otherwise assume false return.
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(PageSize), isWrite, Const(0));
context.Copy(inRegionLocal, result);
context.MarkLabel(skipActionLabel);
// If the tracking action returns false or does not exist, it might be an invalid access due to a partial overlap on Windows.
if (OperatingSystem.IsWindows())
{
context.BranchIfTrue(endLabel, inRegionLocal);
context.Copy(inRegionLocal, WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context));
}
context.Branch(endLabel);
context.MarkLabel(nextLabel);
}
context.MarkLabel(endLabel);
return context.Copy(inRegionLocal);
}
private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
{
EmitterContext context = new EmitterContext();
// (int sig, SigInfo* sigInfo, void* ucontext)
Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1);
Operand structAddressOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructAddressOffset));
Operand structWriteOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructWriteOffset));
Operand faultAddress = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset)));
Operand writeFlag = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset)));
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
Operand endLabel = Label();
context.BranchIfTrue(endLabel, isInRegion);
Operand unixOldSigaction = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction));
Operand unixOldSigaction3Arg = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction3Arg));
Operand threeArgLabel = Label();
context.BranchIfTrue(threeArgLabel, unixOldSigaction3Arg);
context.Call(unixOldSigaction, OperandType.None, context.LoadArgument(OperandType.I32, 0));
context.Branch(endLabel);
context.MarkLabel(threeArgLabel);
context.Call(unixOldSigaction,
OperandType.None,
context.LoadArgument(OperandType.I32, 0),
sigInfoPtr,
context.LoadArgument(OperandType.I64, 2)
);
context.MarkLabel(endLabel);
context.Return();
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq).Map<UnixExceptionHandler>();
}
private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr)
{
EmitterContext context = new EmitterContext();
// (ExceptionPointers* exceptionInfo)
Operand exceptionInfoPtr = context.LoadArgument(OperandType.I64, 0);
Operand exceptionRecordPtr = context.Load(OperandType.I64, exceptionInfoPtr);
// First thing's first - this catches a number of exceptions, but we only want access violations.
Operand validExceptionLabel = Label();
Operand exceptionCode = context.Load(OperandType.I32, exceptionRecordPtr);
context.BranchIf(validExceptionLabel, exceptionCode, Const(EXCEPTION_ACCESS_VIOLATION), Comparison.Equal);
context.Return(Const(EXCEPTION_CONTINUE_SEARCH)); // Don't handle this one.
context.MarkLabel(validExceptionLabel);
// Next, read the address of the invalid access, and whether it is a write or not.
Operand structAddressOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructAddressOffset));
Operand structWriteOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructWriteOffset));
Operand faultAddress = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset)));
Operand writeFlag = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset)));
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
Operand endLabel = Label();
// If the region check result is false, then run the next vectored exception handler.
context.BranchIfTrue(endLabel, isInRegion);
context.Return(Const(EXCEPTION_CONTINUE_SEARCH));
context.MarkLabel(endLabel);
// Otherwise, return to execution.
context.Return(Const(EXCEPTION_CONTINUE_EXECUTION));
// Compile and return the function.
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map<VectoredExceptionHandler>();
}
}
}

View File

@@ -1,84 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Signal
{
public struct NativeWriteLoopState
{
public int Running;
public int Error;
}
public static class TestMethods
{
public delegate bool DebugPartialUnmap();
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
public delegate void DebugNativeWriteLoop(IntPtr nativeWriteLoopPtr, IntPtr writePtr);
public static DebugPartialUnmap GenerateDebugPartialUnmap()
{
EmitterContext context = new EmitterContext();
var result = WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context);
context.Return(result);
// Compile and return the function.
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map<DebugPartialUnmap>();
}
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(IntPtr structPtr)
{
EmitterContext context = new EmitterContext();
var result = WindowsPartialUnmapHandler.EmitThreadLocalMapIntGetOrReserve(context, structPtr, context.LoadArgument(OperandType.I32, 0), context.LoadArgument(OperandType.I32, 1));
context.Return(result);
// Compile and return the function.
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map<DebugThreadLocalMapGetOrReserve>();
}
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop()
{
EmitterContext context = new EmitterContext();
// Loop a write to the target address until "running" is false.
Operand structPtr = context.Copy(context.LoadArgument(OperandType.I64, 0));
Operand writePtr = context.Copy(context.LoadArgument(OperandType.I64, 1));
Operand loopLabel = Label();
context.MarkLabel(loopLabel);
context.Store(writePtr, Const(12345));
Operand running = context.Load(OperandType.I32, structPtr);
context.BranchIfTrue(loopLabel, running);
context.Return();
// Compile and return the function.
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq).Map<DebugNativeWriteLoop>();
}
}
}

View File

@@ -1,68 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Signal
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct SigSet
{
fixed long sa_mask[16];
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SigAction
{
public IntPtr sa_handler;
public SigSet sa_mask;
public int sa_flags;
public IntPtr sa_restorer;
}
static class UnixSignalHandlerRegistration
{
private const int SIGSEGV = 11;
private const int SIGBUS = 10;
private const int SA_SIGINFO = 0x00000004;
[DllImport("libc", SetLastError = true)]
private static extern int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
[DllImport("libc", SetLastError = true)]
private static extern int sigemptyset(ref SigSet set);
public static SigAction RegisterExceptionHandler(IntPtr action)
{
SigAction sig = new SigAction
{
sa_handler = action,
sa_flags = SA_SIGINFO
};
sigemptyset(ref sig.sa_mask);
int result = sigaction(SIGSEGV, ref sig, out SigAction old);
if (result != 0)
{
throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
}
if (OperatingSystem.IsMacOS())
{
result = sigaction(SIGBUS, ref sig, out SigAction oldb);
if (result != 0)
{
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
}
}
return old;
}
public static bool RestoreExceptionHandler(SigAction oldAction)
{
return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0);
}
}
}

View File

@@ -1,44 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Signal
{
unsafe class WindowsSignalHandlerRegistration
{
[DllImport("kernel32.dll")]
private static extern IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
[DllImport("kernel32.dll")]
private static extern ulong RemoveVectoredExceptionHandler(IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
private static IntPtr _getCurrentThreadIdPtr;
public static IntPtr RegisterExceptionHandler(IntPtr action)
{
return AddVectoredExceptionHandler(1, action);
}
public static bool RemoveExceptionHandler(IntPtr handle)
{
return RemoveVectoredExceptionHandler(handle) != 0;
}
public static IntPtr GetCurrentThreadIdFunc()
{
if (_getCurrentThreadIdPtr == IntPtr.Zero)
{
IntPtr handle = LoadLibrary("kernel32.dll");
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
}
return _getCurrentThreadIdPtr;
}
}
}

View File

@@ -1,164 +0,0 @@
using ARMeilleure.Memory;
using System;
namespace ARMeilleure.State
{
public class ExecutionContext
{
private const int MinCountForCheck = 4000;
private NativeContext _nativeContext;
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
private bool _interrupted;
private readonly ICounter _counter;
public ulong Pc => _nativeContext.GetPc();
public uint CtrEl0 => 0x8444c004;
public uint DczidEl0 => 0x00000004;
public ulong CntfrqEl0 => _counter.Frequency;
public ulong CntpctEl0 => _counter.Counter;
// CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
public ulong CntvctEl0 => CntpctEl0;
public long TpidrEl0 { get; set; }
public long TpidrroEl0 { get; set; }
public uint Pstate
{
get => _nativeContext.GetPstate();
set => _nativeContext.SetPstate(value);
}
public FPSR Fpsr
{
get => (FPSR)_nativeContext.GetFPState((uint)FPSR.Mask);
set => _nativeContext.SetFPState((uint)value, (uint)FPSR.Mask);
}
public FPCR Fpcr
{
get => (FPCR)_nativeContext.GetFPState((uint)FPCR.Mask);
set => _nativeContext.SetFPState((uint)value, (uint)FPCR.Mask);
}
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
public FPSCR Fpscr
{
get => (FPSCR)_nativeContext.GetFPState((uint)FPSCR.Mask);
set => _nativeContext.SetFPState((uint)value, (uint)FPSCR.Mask);
}
public bool IsAarch32 { get; set; }
internal ExecutionMode ExecutionMode
{
get
{
if (IsAarch32)
{
return GetPstateFlag(PState.TFlag)
? ExecutionMode.Aarch32Thumb
: ExecutionMode.Aarch32Arm;
}
else
{
return ExecutionMode.Aarch64;
}
}
}
public bool Running
{
get => _nativeContext.GetRunning();
private set => _nativeContext.SetRunning(value);
}
private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback;
public ExecutionContext(
IJitMemoryAllocator allocator,
ICounter counter,
ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null,
ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null)
{
_nativeContext = new NativeContext(allocator);
_counter = counter;
_interruptCallback = interruptCallback;
_breakCallback = breakCallback;
_supervisorCallback = supervisorCallback;
_undefinedCallback = undefinedCallback;
Running = true;
_nativeContext.SetCounter(MinCountForCheck);
}
public ulong GetX(int index) => _nativeContext.GetX(index);
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
public V128 GetV(int index) => _nativeContext.GetV(index);
public void SetV(int index, V128 value) => _nativeContext.SetV(index, value);
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
internal void CheckInterrupt()
{
if (_interrupted)
{
_interrupted = false;
_interruptCallback?.Invoke(this);
}
_nativeContext.SetCounter(MinCountForCheck);
}
public void RequestInterrupt()
{
_interrupted = true;
}
internal void OnBreak(ulong address, int imm)
{
_breakCallback?.Invoke(this, address, imm);
}
internal void OnSupervisorCall(ulong address, int imm)
{
_supervisorCallback?.Invoke(this, address, imm);
}
internal void OnUndefined(ulong address, int opCode)
{
_undefinedCallback?.Invoke(this, address, opCode);
}
public void StopRunning()
{
Running = false;
_nativeContext.SetCounter(0);
}
public void Dispose()
{
_nativeContext.Dispose();
}
}
}

View File

@@ -1,251 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using System;
using System.Runtime.CompilerServices;
namespace ARMeilleure.State
{
class NativeContext : IDisposable
{
private unsafe struct NativeCtxStorage
{
public fixed ulong X[RegisterConsts.IntRegsCount];
public fixed ulong V[RegisterConsts.VecRegsCount * 2];
public fixed uint Flags[RegisterConsts.FlagsCount];
public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
public int Counter;
public ulong DispatchAddress;
public ulong ExclusiveAddress;
public ulong ExclusiveValueLow;
public ulong ExclusiveValueHigh;
public int Running;
}
private static NativeCtxStorage _dummyStorage = new NativeCtxStorage();
private readonly IJitMemoryBlock _block;
public IntPtr BasePtr => _block.Pointer;
public NativeContext(IJitMemoryAllocator allocator)
{
_block = allocator.Allocate((ulong)Unsafe.SizeOf<NativeCtxStorage>());
GetStorage().ExclusiveAddress = ulong.MaxValue;
}
public ulong GetPc()
{
// TODO: More precise tracking of PC value.
return GetStorage().DispatchAddress;
}
public unsafe ulong GetX(int index)
{
if ((uint)index >= RegisterConsts.IntRegsCount)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return GetStorage().X[index];
}
public unsafe void SetX(int index, ulong value)
{
if ((uint)index >= RegisterConsts.IntRegsCount)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
GetStorage().X[index] = value;
}
public unsafe V128 GetV(int index)
{
if ((uint)index >= RegisterConsts.VecRegsCount)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return new V128(GetStorage().V[index * 2 + 0], GetStorage().V[index * 2 + 1]);
}
public unsafe void SetV(int index, V128 value)
{
if ((uint)index >= RegisterConsts.VecRegsCount)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
GetStorage().V[index * 2 + 0] = value.Extract<ulong>(0);
GetStorage().V[index * 2 + 1] = value.Extract<ulong>(1);
}
public unsafe bool GetPstateFlag(PState flag)
{
if ((uint)flag >= RegisterConsts.FlagsCount)
{
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
}
return GetStorage().Flags[(int)flag] != 0;
}
public unsafe void SetPstateFlag(PState flag, bool value)
{
if ((uint)flag >= RegisterConsts.FlagsCount)
{
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
}
GetStorage().Flags[(int)flag] = value ? 1u : 0u;
}
public unsafe uint GetPstate()
{
uint value = 0;
for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
{
value |= GetStorage().Flags[flag] != 0 ? 1u << flag : 0u;
}
return value;
}
public unsafe void SetPstate(uint value)
{
for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
{
uint bit = 1u << flag;
GetStorage().Flags[flag] = (value & bit) == bit ? 1u : 0u;
}
}
public unsafe bool GetFPStateFlag(FPState flag)
{
if ((uint)flag >= RegisterConsts.FpFlagsCount)
{
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
}
return GetStorage().FpFlags[(int)flag] != 0;
}
public unsafe void SetFPStateFlag(FPState flag, bool value)
{
if ((uint)flag >= RegisterConsts.FpFlagsCount)
{
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
}
GetStorage().FpFlags[(int)flag] = value ? 1u : 0u;
}
public unsafe uint GetFPState(uint mask = uint.MaxValue)
{
uint value = 0;
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
uint bit = 1u << flag;
if ((mask & bit) == bit)
{
value |= GetStorage().FpFlags[flag] != 0 ? bit : 0u;
}
}
return value;
}
public unsafe void SetFPState(uint value, uint mask = uint.MaxValue)
{
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
{
uint bit = 1u << flag;
if ((mask & bit) == bit)
{
GetStorage().FpFlags[flag] = (value & bit) == bit ? 1u : 0u;
}
}
}
public int GetCounter() => GetStorage().Counter;
public void SetCounter(int value) => GetStorage().Counter = value;
public bool GetRunning() => GetStorage().Running != 0;
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
public unsafe static int GetRegisterOffset(Register reg)
{
if (reg.Type == RegisterType.Integer)
{
if ((uint)reg.Index >= RegisterConsts.IntRegsCount)
{
throw new ArgumentException("Invalid register.");
}
return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[reg.Index]);
}
else if (reg.Type == RegisterType.Vector)
{
if ((uint)reg.Index >= RegisterConsts.VecRegsCount)
{
throw new ArgumentException("Invalid register.");
}
return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[reg.Index * 2]);
}
else if (reg.Type == RegisterType.Flag)
{
if ((uint)reg.Index >= RegisterConsts.FlagsCount)
{
throw new ArgumentException("Invalid register.");
}
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Flags[reg.Index]);
}
else /* if (reg.Type == RegisterType.FpFlag) */
{
if ((uint)reg.Index >= RegisterConsts.FpFlagsCount)
{
throw new ArgumentException("Invalid register.");
}
return StorageOffset(ref _dummyStorage, ref _dummyStorage.FpFlags[reg.Index]);
}
}
public static int GetCounterOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
}
public static int GetDispatchAddressOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DispatchAddress);
}
public static int GetExclusiveAddressOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveAddress);
}
public static int GetExclusiveValueOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveValueLow);
}
public static int GetRunningOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
}
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
{
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
}
private unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer);
public void Dispose() => _block.Dispose();
}
}

View File

@@ -1,219 +0,0 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.Diagnostics;
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using ARMeilleure.State;
using ARMeilleure.Translation.PTC;
using System;
using System.Collections.Generic;
using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Translation
{
class ArmEmitterContext : EmitterContext
{
private readonly Dictionary<ulong, Operand> _labels;
private OpCode _optOpLastCompare;
private OpCode _optOpLastFlagSet;
private Operand _optCmpTempN;
private Operand _optCmpTempM;
private Block _currBlock;
public Block CurrBlock
{
get
{
return _currBlock;
}
set
{
_currBlock = value;
ResetBlockState();
}
}
public OpCode CurrOp { get; set; }
public IMemoryManager Memory { get; }
public bool HasPtc { get; }
public EntryTable<uint> CountTable { get; }
public AddressTable<ulong> FunctionTable { get; }
public TranslatorStubs Stubs { get; }
public ulong EntryAddress { get; }
public bool HighCq { get; }
public Aarch32Mode Mode { get; }
private int _ifThenBlockStateIndex = 0;
private Condition[] _ifThenBlockState = { };
public bool IsInIfThenBlock => _ifThenBlockStateIndex < _ifThenBlockState.Length;
public Condition CurrentIfThenBlockCond => _ifThenBlockState[_ifThenBlockStateIndex];
public ArmEmitterContext(
IMemoryManager memory,
EntryTable<uint> countTable,
AddressTable<ulong> funcTable,
TranslatorStubs stubs,
ulong entryAddress,
bool highCq,
Aarch32Mode mode)
{
HasPtc = Ptc.State != PtcState.Disabled;
Memory = memory;
CountTable = countTable;
FunctionTable = funcTable;
Stubs = stubs;
EntryAddress = entryAddress;
HighCq = highCq;
Mode = mode;
_labels = new Dictionary<ulong, Operand>();
}
public override Operand Call(MethodInfo info, params Operand[] callArgs)
{
if (!HasPtc)
{
return base.Call(info, callArgs);
}
else
{
int index = Delegates.GetDelegateIndex(info);
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
OperandType returnType = GetOperandType(info.ReturnType);
Symbol symbol = new Symbol(SymbolType.DelegateTable, (ulong)index);
Symbols.Add((ulong)funcPtr.ToInt64(), info.Name);
return Call(Const(funcPtr.ToInt64(), symbol), returnType, callArgs);
}
}
public Operand GetLabel(ulong address)
{
if (!_labels.TryGetValue(address, out Operand label))
{
label = Label();
_labels.Add(address, label);
}
return label;
}
public void MarkComparison(Operand n, Operand m)
{
_optOpLastCompare = CurrOp;
_optCmpTempN = Copy(n);
_optCmpTempM = Copy(m);
}
public void MarkFlagSet(PState stateFlag)
{
// Set this only if any of the NZCV flag bits were modified.
// This is used to ensure that when emiting a direct IL branch
// instruction for compare + branch sequences, we're not expecting
// to use comparison values from an old instruction, when in fact
// the flags were already overwritten by another instruction further along.
if (stateFlag >= PState.VFlag)
{
_optOpLastFlagSet = CurrOp;
}
}
private void ResetBlockState()
{
_optOpLastCompare = null;
_optOpLastFlagSet = null;
}
public Operand TryGetComparisonResult(Condition condition)
{
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
{
return default;
}
Operand n = _optCmpTempN;
Operand m = _optCmpTempM;
InstName cmpName = _optOpLastCompare.Instruction.Name;
if (cmpName == InstName.Subs)
{
switch (condition)
{
case Condition.Eq: return ICompareEqual (n, m);
case Condition.Ne: return ICompareNotEqual (n, m);
case Condition.GeUn: return ICompareGreaterOrEqualUI(n, m);
case Condition.LtUn: return ICompareLessUI (n, m);
case Condition.GtUn: return ICompareGreaterUI (n, m);
case Condition.LeUn: return ICompareLessOrEqualUI (n, m);
case Condition.Ge: return ICompareGreaterOrEqual (n, m);
case Condition.Lt: return ICompareLess (n, m);
case Condition.Gt: return ICompareGreater (n, m);
case Condition.Le: return ICompareLessOrEqual (n, m);
}
}
else if (cmpName == InstName.Adds && _optOpLastCompare is IOpCodeAluImm op)
{
// There are several limitations that needs to be taken into account for CMN comparisons:
// - The unsigned comparisons are not valid, as they depend on the
// carry flag value, and they will have different values for addition and
// subtraction. For addition, it's carry, and for subtraction, it's borrow.
// So, we need to make sure we're not doing a unsigned compare for the CMN case.
// - We can only do the optimization for the immediate variants,
// because when the second operand value is exactly INT_MIN, we can't
// negate the value as theres no positive counterpart.
// Such invalid values can't be encoded on the immediate encodings.
if (op.RegisterSize == RegisterSize.Int32)
{
m = Const((int)-op.Immediate);
}
else
{
m = Const(-op.Immediate);
}
switch (condition)
{
case Condition.Eq: return ICompareEqual (n, m);
case Condition.Ne: return ICompareNotEqual (n, m);
case Condition.Ge: return ICompareGreaterOrEqual(n, m);
case Condition.Lt: return ICompareLess (n, m);
case Condition.Gt: return ICompareGreater (n, m);
case Condition.Le: return ICompareLessOrEqual (n, m);
}
}
return default;
}
public void SetIfThenBlockState(Condition[] state)
{
_ifThenBlockState = state;
_ifThenBlockStateIndex = 0;
}
public void AdvanceIfThenBlockState()
{
if (IsInIfThenBlock)
{
_ifThenBlockStateIndex++;
}
}
}
}

View File

@@ -1,26 +0,0 @@
using ARMeilleure.CodeGen.Unwinding;
using System;
using System.Diagnostics.CodeAnalysis;
namespace ARMeilleure.Translation.Cache
{
struct CacheEntry : IComparable<CacheEntry>
{
public int Offset { get; }
public int Size { get; }
public UnwindInfo UnwindInfo { get; }
public CacheEntry(int offset, int size, UnwindInfo unwindInfo)
{
Offset = offset;
Size = size;
UnwindInfo = unwindInfo;
}
public int CompareTo([AllowNull] CacheEntry other)
{
return Offset.CompareTo(other.Offset);
}
}
}

View File

@@ -1,96 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ARMeilleure.Translation.Cache
{
class CacheMemoryAllocator
{
private struct MemoryBlock : IComparable<MemoryBlock>
{
public int Offset { get; }
public int Size { get; }
public MemoryBlock(int offset, int size)
{
Offset = offset;
Size = size;
}
public int CompareTo([AllowNull] MemoryBlock other)
{
return Offset.CompareTo(other.Offset);
}
}
private readonly List<MemoryBlock> _blocks = new List<MemoryBlock>();
public CacheMemoryAllocator(int capacity)
{
_blocks.Add(new MemoryBlock(0, capacity));
}
public int Allocate(int size)
{
for (int i = 0; i < _blocks.Count; i++)
{
MemoryBlock block = _blocks[i];
if (block.Size > size)
{
_blocks[i] = new MemoryBlock(block.Offset + size, block.Size - size);
return block.Offset;
}
else if (block.Size == size)
{
_blocks.RemoveAt(i);
return block.Offset;
}
}
// We don't have enough free memory to perform the allocation.
return -1;
}
public void Free(int offset, int size)
{
Insert(new MemoryBlock(offset, size));
}
private void Insert(MemoryBlock block)
{
int index = _blocks.BinarySearch(block);
if (index < 0)
{
index = ~index;
}
if (index < _blocks.Count)
{
MemoryBlock next = _blocks[index];
int endOffs = block.Offset + block.Size;
if (next.Offset == endOffs)
{
block = new MemoryBlock(block.Offset, block.Size + next.Size);
_blocks.RemoveAt(index);
}
}
if (index > 0)
{
MemoryBlock prev = _blocks[index - 1];
if (prev.Offset + prev.Size == block.Offset)
{
block = new MemoryBlock(block.Offset - prev.Size, block.Size + prev.Size);
_blocks.RemoveAt(--index);
}
}
_blocks.Insert(index, block);
}
}
}

View File

@@ -1,184 +0,0 @@
using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Memory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation.Cache
{
static class JitCache
{
private const int PageSize = 4 * 1024;
private const int PageMask = PageSize - 1;
private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 2047 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static CacheMemoryAllocator _cacheAllocator;
private static readonly List<CacheEntry> _cacheEntries = new List<CacheEntry>();
private static readonly object _lock = new object();
private static bool _initialized;
public static IntPtr Base => _jitRegion.Pointer;
public static void Initialize(IJitMemoryAllocator allocator)
{
if (_initialized) return;
lock (_lock)
{
if (_initialized) return;
_jitRegion = new ReservedRegion(allocator, CacheSize);
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
if (OperatingSystem.IsWindows())
{
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(PageSize));
}
_initialized = true;
}
}
public static IntPtr Map(CompiledFunction func)
{
byte[] code = func.Code;
lock (_lock)
{
Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length);
IntPtr funcPtr = _jitRegion.Pointer + funcOffset;
ReprotectAsWritable(funcOffset, code.Length);
Marshal.Copy(code, 0, funcPtr, code.Length);
ReprotectAsExecutable(funcOffset, code.Length);
Add(funcOffset, code.Length, func.UnwindInfo);
return funcPtr;
}
}
public static void Unmap(IntPtr pointer)
{
lock (_lock)
{
Debug.Assert(_initialized);
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
bool result = TryFind(funcOffset, out CacheEntry entry);
Debug.Assert(result);
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
Remove(funcOffset);
}
}
private static void ReprotectAsWritable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~PageMask;
int regionEnd = (endOffs + PageMask) & ~PageMask;
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static void ReprotectAsExecutable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~PageMask;
int regionEnd = (endOffs + PageMask) & ~PageMask;
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static int Allocate(int codeSize)
{
codeSize = AlignCodeSize(codeSize);
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset < 0)
{
throw new OutOfMemoryException("JIT Cache exhausted.");
}
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
return allocOffset;
}
private static int AlignCodeSize(int codeSize)
{
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
}
private static void Add(int offset, int size, UnwindInfo unwindInfo)
{
CacheEntry entry = new CacheEntry(offset, size, unwindInfo);
int index = _cacheEntries.BinarySearch(entry);
if (index < 0)
{
index = ~index;
}
_cacheEntries.Insert(index, entry);
}
private static void Remove(int offset)
{
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index < 0)
{
index = ~index - 1;
}
if (index >= 0)
{
_cacheEntries.RemoveAt(index);
}
}
public static bool TryFind(int offset, out CacheEntry entry)
{
lock (_lock)
{
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index < 0)
{
index = ~index - 1;
}
if (index >= 0)
{
entry = _cacheEntries[index];
return true;
}
}
entry = default;
return false;
}
}
}

View File

@@ -1,188 +0,0 @@
// https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/build/exception-handling-x64.md
using ARMeilleure.CodeGen.Unwinding;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation.Cache
{
static class JitUnwindWindows
{
private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
private struct RuntimeFunction
{
public uint BeginAddress;
public uint EndAddress;
public uint UnwindData;
}
private struct UnwindInfo
{
public byte VersionAndFlags;
public byte SizeOfProlog;
public byte CountOfUnwindCodes;
public byte FrameRegister;
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
}
private enum UnwindOp
{
PushNonvol = 0,
AllocLarge = 1,
AllocSmall = 2,
SetFpreg = 3,
SaveNonvol = 4,
SaveNonvolFar = 5,
SaveXmm128 = 8,
SaveXmm128Far = 9,
PushMachframe = 10
}
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static unsafe extern bool RtlInstallFunctionTableCallback(
ulong tableIdentifier,
ulong baseAddress,
uint length,
GetRuntimeFunctionCallback callback,
IntPtr context,
string outOfProcessCallbackDll);
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
private static int _sizeOfRuntimeFunction;
private unsafe static RuntimeFunction* _runtimeFunction;
private unsafe static UnwindInfo* _unwindInfo;
public static void InstallFunctionTableHandler(IntPtr codeCachePointer, uint codeCacheLength, IntPtr workBufferPtr)
{
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
bool result;
unsafe
{
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
result = RtlInstallFunctionTableCallback(
codeCachePtr | 3,
codeCachePtr,
codeCacheLength,
_getRuntimeFunctionCallback,
codeCachePointer,
null);
}
if (!result)
{
throw new InvalidOperationException("Failure installing function table callback.");
}
}
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context)
{
int offset = (int)((long)controlPc - context.ToInt64());
if (!JitCache.TryFind(offset, out CacheEntry funcEntry))
{
return null; // Not found.
}
var unwindInfo = funcEntry.UnwindInfo;
int codeIndex = 0;
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
{
var entry = unwindInfo.PushEntries[index];
switch (entry.PseudoOp)
{
case UnwindPseudoOp.SaveXmm128:
{
int stackOffset = entry.StackOffsetOrAllocSize;
Debug.Assert(stackOffset % 16 == 0);
if (stackOffset <= 0xFFFF0)
{
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
}
else
{
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
}
break;
}
case UnwindPseudoOp.AllocStack:
{
int allocSize = entry.StackOffsetOrAllocSize;
Debug.Assert(allocSize % 8 == 0);
if (allocSize <= 128)
{
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1);
}
else if (allocSize <= 0x7FFF8)
{
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
}
else
{
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
}
break;
}
case UnwindPseudoOp.PushReg:
{
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
break;
}
default: throw new NotImplementedException($"({nameof(entry.PseudoOp)} = {entry.PseudoOp})");
}
}
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
_unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
_unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
_unwindInfo->FrameRegister = 0;
_runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
_runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
_runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
return _runtimeFunction;
}
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
{
return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12));
}
}
}

View File

@@ -1,55 +0,0 @@
using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Optimizations;
using ARMeilleure.CodeGen.X86;
using ARMeilleure.Diagnostics;
using ARMeilleure.IntermediateRepresentation;
namespace ARMeilleure.Translation
{
static class Compiler
{
public static CompiledFunction Compile(
ControlFlowGraph cfg,
OperandType[] argTypes,
OperandType retType,
CompilerOptions options)
{
CompilerContext cctx = new(cfg, argTypes, retType, options);
if (options.HasFlag(CompilerOptions.Optimize))
{
Logger.StartPass(PassName.TailMerge);
TailMerge.RunPass(cctx);
Logger.EndPass(PassName.TailMerge, cfg);
}
if (options.HasFlag(CompilerOptions.SsaForm))
{
Logger.StartPass(PassName.Dominance);
Dominance.FindDominators(cfg);
Dominance.FindDominanceFrontiers(cfg);
Logger.EndPass(PassName.Dominance);
Logger.StartPass(PassName.SsaConstruction);
Ssa.Construct(cfg);
Logger.EndPass(PassName.SsaConstruction, cfg);
}
else
{
Logger.StartPass(PassName.RegisterToLocal);
RegisterToLocal.Rename(cfg);
Logger.EndPass(PassName.RegisterToLocal, cfg);
}
return CodeGenerator.Generate(cctx);
}
}
}

View File

@@ -1,26 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
namespace ARMeilleure.Translation
{
struct CompilerContext
{
public ControlFlowGraph Cfg { get; }
public OperandType[] FuncArgTypes { get; }
public OperandType FuncReturnType { get; }
public CompilerOptions Options { get; }
public CompilerContext(
ControlFlowGraph cfg,
OperandType[] funcArgTypes,
OperandType funcReturnType,
CompilerOptions options)
{
Cfg = cfg;
FuncArgTypes = funcArgTypes;
FuncReturnType = funcReturnType;
Options = options;
}
}
}

View File

@@ -1,107 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace ARMeilleure.Translation
{
static class DelegateHelper
{
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
private static readonly ModuleBuilder _modBuilder;
private static readonly Dictionary<string, Type> _delegateTypesCache;
static DelegateHelper()
{
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
_delegateTypesCache = new Dictionary<string, Type>();
}
public static Delegate GetDelegate(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
Type returnType = info.ReturnType;
Type delegateType = GetDelegateType(parameters, returnType);
return Delegate.CreateDelegate(delegateType, info);
}
private static Type GetDelegateType(Type[] parameters, Type returnType)
{
string key = GetFunctionSignatureKey(parameters, returnType);
if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
{
delegateType = MakeDelegateType(parameters, returnType, key);
_delegateTypesCache.TryAdd(key, delegateType);
}
return delegateType;
}
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
{
string sig = GetTypeName(returnType);
foreach (Type type in parameters)
{
sig += '_' + GetTypeName(type);
}
return sig;
}
private static string GetTypeName(Type type)
{
return type.FullName.Replace(".", string.Empty);
}
private const MethodAttributes CtorAttributes =
MethodAttributes.RTSpecialName |
MethodAttributes.HideBySig |
MethodAttributes.Public;
private const TypeAttributes DelegateTypeAttributes =
TypeAttributes.Class |
TypeAttributes.Public |
TypeAttributes.Sealed |
TypeAttributes.AnsiClass |
TypeAttributes.AutoClass;
private const MethodImplAttributes ImplAttributes =
MethodImplAttributes.Runtime |
MethodImplAttributes.Managed;
private const MethodAttributes InvokeAttributes =
MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.NewSlot |
MethodAttributes.Virtual;
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
{
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
return builder.CreateTypeInfo();
}
}
}

View File

@@ -1,273 +0,0 @@
using ARMeilleure.Instructions;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ARMeilleure.Translation
{
static class Delegates
{
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
{
if (index >= 0 && index < _delegates.Count)
{
funcPtr = _delegates.Values[index].FuncPtr; // O(1).
return true;
}
else
{
funcPtr = default;
return false;
}
}
public static IntPtr GetDelegateFuncPtrByIndex(int index)
{
if (index < 0 || index >= _delegates.Count)
{
throw new ArgumentOutOfRangeException($"({nameof(index)} = {index})");
}
return _delegates.Values[index].FuncPtr; // O(1).
}
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
string key = GetKey(info);
if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)).
{
throw new KeyNotFoundException($"({nameof(key)} = {key})");
}
return dlgInfo.FuncPtr;
}
public static int GetDelegateIndex(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
string key = GetKey(info);
int index = _delegates.IndexOfKey(key); // O(log(n)).
if (index == -1)
{
throw new KeyNotFoundException($"({nameof(key)} = {key})");
}
return index;
}
private static void SetDelegateInfo(MethodInfo info)
{
string key = GetKey(info);
Delegate dlg = DelegateHelper.GetDelegate(info);
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
}
private static string GetKey(MethodInfo info)
{
return $"{info.DeclaringType.Name}.{info.Name}";
}
private static readonly SortedList<string, DelegateInfo> _delegates;
static Delegates()
{
_delegates = new SortedList<string, DelegateInfo>();
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) }));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) }));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) }));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) }));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) }));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) }));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
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.FPCompare)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
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.FPMaxNum)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
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.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.FPRSqrtStepFused)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
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.FPCompare)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
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.FPMaxNum)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
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.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.FPRSqrtStepFused)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)));
}
}
}

View File

@@ -1,6 +0,0 @@
using System;
namespace ARMeilleure.Translation
{
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
}

View File

@@ -1,754 +0,0 @@
using System;
using System.Collections.Generic;
namespace ARMeilleure.Translation
{
/// <summary>
/// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
/// </summary>
/// <typeparam name="K">Key</typeparam>
/// <typeparam name="V">Value</typeparam>
class IntervalTree<K, V> where K : IComparable<K>
{
private const int ArrayGrowthSize = 32;
private const bool Black = true;
private const bool Red = false;
private IntervalTreeNode<K, V> _root = null;
private int _count = 0;
public int Count => _count;
#region Public Methods
/// <summary>
/// Gets the values of the interval whose key is <paramref name="key"/>.
/// </summary>
/// <param name="key">Key of the node value to get</param>
/// <param name="value">Value with the given <paramref name="key"/></param>
/// <returns>True if the key is on the dictionary, false otherwise</returns>
public bool TryGet(K key, out V value)
{
IntervalTreeNode<K, V> node = GetNode(key);
if (node == null)
{
value = default;
return false;
}
value = node.Value;
return true;
}
/// <summary>
/// Returns the start addresses of the intervals whose start and end keys overlap the given range.
/// </summary>
/// <param name="start">Start of the range</param>
/// <param name="end">End of the range</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
/// <returns>Number of intervals found</returns>
public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0)
{
GetKeys(_root, start, end, ref overlaps, ref overlapCount);
return overlapCount;
}
/// <summary>
/// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
/// </summary>
/// <param name="start">Start of the range to add</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to add</param>
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
}
/// <summary>
/// Gets an existing or adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
/// </summary>
/// <param name="start">Start of the range to add</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to add</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
public V GetOrAdd(K start, K end, V value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
return node.Value;
}
/// <summary>
/// Removes a value from the tree, searching for it with <paramref name="key"/>.
/// </summary>
/// <param name="key">Key of the node to remove</param>
/// <returns>Number of deleted values</returns>
public int Remove(K key)
{
int removed = Delete(key);
_count -= removed;
return removed;
}
/// <summary>
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
/// </summary>
/// <returns>A list of all values sorted by Key Order</returns>
public List<V> AsList()
{
List<V> list = new List<V>();
AddToList(_root, list);
return list;
}
#endregion
#region Private Methods (BST)
/// <summary>
/// Adds all values that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
/// </summary>
/// <param name="node">The node to search for values within</param>
/// <param name="list">The list to add values to</param>
private void AddToList(IntervalTreeNode<K, V> node, List<V> list)
{
if (node == null)
{
return;
}
AddToList(node.Left, list);
list.Add(node.Value);
AddToList(node.Right, list);
}
/// <summary>
/// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
/// </summary>
/// <param name="key">Key of the node to get</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
/// <returns>Node reference in the tree</returns>
private IntervalTreeNode<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
IntervalTreeNode<K, V> node = _root;
while (node != null)
{
int cmp = key.CompareTo(node.Start);
if (cmp < 0)
{
node = node.Left;
}
else if (cmp > 0)
{
node = node.Right;
}
else
{
return node;
}
}
return null;
}
/// <summary>
/// Retrieve all keys that overlap the given start and end keys.
/// </summary>
/// <param name="start">Start of the range</param>
/// <param name="end">End of the range</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <param name="overlapCount">Overlaps count to update</param>
private void GetKeys(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount)
{
if (node == null || start.CompareTo(node.Max) >= 0)
{
return;
}
GetKeys(node.Left, start, end, ref overlaps, ref overlapCount);
bool endsOnRight = end.CompareTo(node.Start) > 0;
if (endsOnRight)
{
if (start.CompareTo(node.End) < 0)
{
if (overlaps.Length >= overlapCount)
{
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
}
overlaps[overlapCount++] = node.Start;
}
GetKeys(node.Right, start, end, ref overlaps, ref overlapCount);
}
}
/// <summary>
/// Propagate an increase in max value starting at the given node, heading up the tree.
/// This should only be called if the max increases - not for rebalancing or removals.
/// </summary>
/// <param name="node">The node to start propagating from</param>
private void PropagateIncrease(IntervalTreeNode<K, V> node)
{
K max = node.Max;
IntervalTreeNode<K, V> ptr = node;
while ((ptr = ptr.Parent) != null)
{
if (max.CompareTo(ptr.Max) > 0)
{
ptr.Max = max;
}
else
{
break;
}
}
}
/// <summary>
/// Propagate recalculating max value starting at the given node, heading up the tree.
/// This fully recalculates the max value from all children when there is potential for it to decrease.
/// </summary>
/// <param name="node">The node to start propagating from</param>
private void PropagateFull(IntervalTreeNode<K, V> node)
{
IntervalTreeNode<K, V> ptr = node;
do
{
K max = ptr.End;
if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0)
{
max = ptr.Left.Max;
}
if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0)
{
max = ptr.Right.Max;
}
ptr.Max = max;
} while ((ptr = ptr.Parent) != null);
}
/// <summary>
/// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
/// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
/// Each node can contain multiple values, and has an end address which is the maximum of all those values.
/// Post insertion, the "max" value of the node and all parents are updated.
/// </summary>
/// <param name="start">Start of the range to insert</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to insert</param>
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
/// <param name="outNode">Node that was inserted or modified</param>
/// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns>
private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode)
{
IntervalTreeNode<K, V> parent = null;
IntervalTreeNode<K, V> node = _root;
while (node != null)
{
parent = node;
int cmp = start.CompareTo(node.Start);
if (cmp < 0)
{
node = node.Left;
}
else if (cmp > 0)
{
node = node.Right;
}
else
{
outNode = node;
if (updateFactoryCallback != null)
{
// Replace
node.Value = updateFactoryCallback(start, node.Value);
int endCmp = end.CompareTo(node.End);
if (endCmp > 0)
{
node.End = end;
if (end.CompareTo(node.Max) > 0)
{
node.Max = end;
PropagateIncrease(node);
RestoreBalanceAfterInsertion(node);
}
}
else if (endCmp < 0)
{
node.End = end;
PropagateFull(node);
}
}
return false;
}
}
IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent);
if (newNode.Parent == null)
{
_root = newNode;
}
else if (start.CompareTo(parent.Start) < 0)
{
parent.Left = newNode;
}
else
{
parent.Right = newNode;
}
PropagateIncrease(newNode);
_count++;
RestoreBalanceAfterInsertion(newNode);
outNode = newNode;
return true;
}
/// <summary>
/// Removes the value from the dictionary after searching for it with <paramref name="key"/>.
/// </summary>
/// <param name="key">Key to search for</param>
/// <returns>Number of deleted values</returns>
private int Delete(K key)
{
IntervalTreeNode<K, V> nodeToDelete = GetNode(key);
if (nodeToDelete == null)
{
return 0;
}
IntervalTreeNode<K, V> replacementNode;
if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
{
replacementNode = nodeToDelete;
}
else
{
replacementNode = PredecessorOf(nodeToDelete);
}
IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
if (tmp != null)
{
tmp.Parent = ParentOf(replacementNode);
}
if (ParentOf(replacementNode) == null)
{
_root = tmp;
}
else if (replacementNode == LeftOf(ParentOf(replacementNode)))
{
ParentOf(replacementNode).Left = tmp;
}
else
{
ParentOf(replacementNode).Right = tmp;
}
if (replacementNode != nodeToDelete)
{
nodeToDelete.Start = replacementNode.Start;
nodeToDelete.Value = replacementNode.Value;
nodeToDelete.End = replacementNode.End;
nodeToDelete.Max = replacementNode.Max;
}
PropagateFull(replacementNode);
if (tmp != null && ColorOf(replacementNode) == Black)
{
RestoreBalanceAfterRemoval(tmp);
}
return 1;
}
/// <summary>
/// Returns the node with the largest key where <paramref name="node"/> is considered the root node.
/// </summary>
/// <param name="node">Root Node</param>
/// <returns>Node with the maximum key in the tree of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> Maximum(IntervalTreeNode<K, V> node)
{
IntervalTreeNode<K, V> tmp = node;
while (tmp.Right != null)
{
tmp = tmp.Right;
}
return tmp;
}
/// <summary>
/// Finds the node whose key is immediately less than <paramref name="node"/>.
/// </summary>
/// <param name="node">Node to find the predecessor of</param>
/// <returns>Predecessor of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> PredecessorOf(IntervalTreeNode<K, V> node)
{
if (node.Left != null)
{
return Maximum(node.Left);
}
IntervalTreeNode<K, V> parent = node.Parent;
while (parent != null && node == parent.Left)
{
node = parent;
parent = parent.Parent;
}
return parent;
}
#endregion
#region Private Methods (RBL)
private void RestoreBalanceAfterRemoval(IntervalTreeNode<K, V> balanceNode)
{
IntervalTreeNode<K, V> ptr = balanceNode;
while (ptr != _root && ColorOf(ptr) == Black)
{
if (ptr == LeftOf(ParentOf(ptr)))
{
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ptr));
if (ColorOf(sibling) == Red)
{
SetColor(sibling, Black);
SetColor(ParentOf(ptr), Red);
RotateLeft(ParentOf(ptr));
sibling = RightOf(ParentOf(ptr));
}
if (ColorOf(LeftOf(sibling)) == Black && ColorOf(RightOf(sibling)) == Black)
{
SetColor(sibling, Red);
ptr = ParentOf(ptr);
}
else
{
if (ColorOf(RightOf(sibling)) == Black)
{
SetColor(LeftOf(sibling), Black);
SetColor(sibling, Red);
RotateRight(sibling);
sibling = RightOf(ParentOf(ptr));
}
SetColor(sibling, ColorOf(ParentOf(ptr)));
SetColor(ParentOf(ptr), Black);
SetColor(RightOf(sibling), Black);
RotateLeft(ParentOf(ptr));
ptr = _root;
}
}
else
{
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ptr));
if (ColorOf(sibling) == Red)
{
SetColor(sibling, Black);
SetColor(ParentOf(ptr), Red);
RotateRight(ParentOf(ptr));
sibling = LeftOf(ParentOf(ptr));
}
if (ColorOf(RightOf(sibling)) == Black && ColorOf(LeftOf(sibling)) == Black)
{
SetColor(sibling, Red);
ptr = ParentOf(ptr);
}
else
{
if (ColorOf(LeftOf(sibling)) == Black)
{
SetColor(RightOf(sibling), Black);
SetColor(sibling, Red);
RotateLeft(sibling);
sibling = LeftOf(ParentOf(ptr));
}
SetColor(sibling, ColorOf(ParentOf(ptr)));
SetColor(ParentOf(ptr), Black);
SetColor(LeftOf(sibling), Black);
RotateRight(ParentOf(ptr));
ptr = _root;
}
}
}
SetColor(ptr, Black);
}
private void RestoreBalanceAfterInsertion(IntervalTreeNode<K, V> balanceNode)
{
SetColor(balanceNode, Red);
while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red)
{
if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode))))
{
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ParentOf(balanceNode)));
if (ColorOf(sibling) == Red)
{
SetColor(ParentOf(balanceNode), Black);
SetColor(sibling, Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
balanceNode = ParentOf(ParentOf(balanceNode));
}
else
{
if (balanceNode == RightOf(ParentOf(balanceNode)))
{
balanceNode = ParentOf(balanceNode);
RotateLeft(balanceNode);
}
SetColor(ParentOf(balanceNode), Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
RotateRight(ParentOf(ParentOf(balanceNode)));
}
}
else
{
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ParentOf(balanceNode)));
if (ColorOf(sibling) == Red)
{
SetColor(ParentOf(balanceNode), Black);
SetColor(sibling, Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
balanceNode = ParentOf(ParentOf(balanceNode));
}
else
{
if (balanceNode == LeftOf(ParentOf(balanceNode)))
{
balanceNode = ParentOf(balanceNode);
RotateRight(balanceNode);
}
SetColor(ParentOf(balanceNode), Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
RotateLeft(ParentOf(ParentOf(balanceNode)));
}
}
}
SetColor(_root, Black);
}
private void RotateLeft(IntervalTreeNode<K, V> node)
{
if (node != null)
{
IntervalTreeNode<K, V> right = RightOf(node);
node.Right = LeftOf(right);
if (node.Right != null)
{
node.Right.Parent = node;
}
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
right.Parent = nodeParent;
if (nodeParent == null)
{
_root = right;
}
else if (node == LeftOf(nodeParent))
{
nodeParent.Left = right;
}
else
{
nodeParent.Right = right;
}
right.Left = node;
node.Parent = right;
PropagateFull(node);
}
}
private void RotateRight(IntervalTreeNode<K, V> node)
{
if (node != null)
{
IntervalTreeNode<K, V> left = LeftOf(node);
node.Left = RightOf(left);
if (node.Left != null)
{
node.Left.Parent = node;
}
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
left.Parent = nodeParent;
if (nodeParent == null)
{
_root = left;
}
else if (node == RightOf(nodeParent))
{
nodeParent.Right = left;
}
else
{
nodeParent.Left = left;
}
left.Right = node;
node.Parent = left;
PropagateFull(node);
}
}
#endregion
#region Safety-Methods
// These methods save memory by allowing us to forego sentinel nil nodes, as well as serve as protection against NullReferenceExceptions.
/// <summary>
/// Returns the color of <paramref name="node"/>, or Black if it is null.
/// </summary>
/// <param name="node">Node</param>
/// <returns>The boolean color of <paramref name="node"/>, or black if null</returns>
private static bool ColorOf(IntervalTreeNode<K, V> node)
{
return node == null || node.Color;
}
/// <summary>
/// Sets the color of <paramref name="node"/> node to <paramref name="color"/>.
/// <br></br>
/// This method does nothing if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to set the color of</param>
/// <param name="color">Color (Boolean)</param>
private static void SetColor(IntervalTreeNode<K, V> node, bool color)
{
if (node != null)
{
node.Color = color;
}
}
/// <summary>
/// This method returns the left node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to retrieve the left child from</param>
/// <returns>Left child of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> LeftOf(IntervalTreeNode<K, V> node)
{
return node?.Left;
}
/// <summary>
/// This method returns the right node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to retrieve the right child from</param>
/// <returns>Right child of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> RightOf(IntervalTreeNode<K, V> node)
{
return node?.Right;
}
/// <summary>
/// Returns the parent node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to retrieve the parent from</param>
/// <returns>Parent of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> ParentOf(IntervalTreeNode<K, V> node)
{
return node?.Parent;
}
#endregion
public bool ContainsKey(K key)
{
return GetNode(key) != null;
}
public void Clear()
{
_root = null;
_count = 0;
}
}
/// <summary>
/// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
/// </summary>
/// <typeparam name="K">Key type of the node</typeparam>
/// <typeparam name="V">Value type of the node</typeparam>
class IntervalTreeNode<K, V>
{
public bool Color = true;
public IntervalTreeNode<K, V> Left = null;
public IntervalTreeNode<K, V> Right = null;
public IntervalTreeNode<K, V> Parent = null;
/// <summary>
/// The start of the range.
/// </summary>
public K Start;
/// <summary>
/// The end of the range.
/// </summary>
public K End;
/// <summary>
/// The maximum end value of this node and all its children.
/// </summary>
public K Max;
/// <summary>
/// Value stored on this node.
/// </summary>
public V Value;
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
{
Start = start;
End = end;
Max = end;
Value = value;
Parent = parent;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,416 +0,0 @@
using ARMeilleure.State;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC
{
public static class PtcProfiler
{
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project.
private const int SaveInterval = 30; // Seconds.
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
private static readonly System.Timers.Timer _timer;
private static readonly ulong _outerHeaderMagic;
private static readonly ManualResetEvent _waitEvent;
private static readonly object _lock;
private static bool _disposed;
private static Hash128 _lastHash;
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
internal static bool Enabled { get; private set; }
public static ulong StaticCodeStart { internal get; set; }
public static ulong StaticCodeSize { internal get; set; }
static PtcProfiler()
{
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer.Elapsed += PreSave;
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
_waitEvent = new ManualResetEvent(true);
_lock = new object();
_disposed = false;
ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
Enabled = false;
}
internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq)
{
if (IsAddressInStaticCodeRange(address))
{
Debug.Assert(!highCq);
lock (_lock)
{
ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false));
}
}
}
internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
{
if (IsAddressInStaticCodeRange(address))
{
Debug.Assert(highCq);
lock (_lock)
{
Debug.Assert(ProfiledFuncs.ContainsKey(address));
ProfiledFuncs[address] = new FuncProfile(mode, highCq: true);
}
}
}
internal static bool IsAddressInStaticCodeRange(ulong address)
{
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
}
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
{
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
foreach (var profiledFunc in ProfiledFuncs)
{
if (!funcs.ContainsKey(profiledFunc.Key))
{
profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value));
}
}
return profiledFuncsToTranslate;
}
internal static void ClearEntries()
{
ProfiledFuncs.Clear();
ProfiledFuncs.TrimExcess();
}
internal static void PreLoad()
{
_lastHash = default;
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
{
if (!Load(fileNameActual, false))
{
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
{
Load(fileNameBackup, true);
}
}
}
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
{
Load(fileNameBackup, true);
}
}
private static bool Load(string fileName, bool isBackup)
{
using (FileStream compressedStream = new(fileName, FileMode.Open))
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
{
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
if (!outerHeader.IsHeaderValid())
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (outerHeader.Magic != _outerHeaderMagic)
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (outerHeader.InfoFileVersion != InternalVersion)
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (outerHeader.Endianness != Ptc.GetEndianness())
{
InvalidateCompressedStream(compressedStream);
return false;
}
using (MemoryStream stream = new MemoryStream())
{
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
try
{
deflateStream.CopyTo(stream);
}
catch
{
InvalidateCompressedStream(compressedStream);
return false;
}
Debug.Assert(stream.Position == stream.Length);
stream.Seek(0L, SeekOrigin.Begin);
Hash128 expectedHash = DeserializeStructure<Hash128>(stream);
Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
if (actualHash != expectedHash)
{
InvalidateCompressedStream(compressedStream);
return false;
}
ProfiledFuncs = Deserialize(stream);
Debug.Assert(stream.Position == stream.Length);
_lastHash = actualHash;
}
}
long fileSize = new FileInfo(fileName).Length;
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
return true;
}
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream)
{
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
}
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
{
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
}
private static void InvalidateCompressedStream(FileStream compressedStream)
{
compressedStream.SetLength(0L);
}
private static void PreSave(object source, System.Timers.ElapsedEventArgs e)
{
_waitEvent.Reset();
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
{
File.Copy(fileNameActual, fileNameBackup, true);
}
Save(fileNameActual);
_waitEvent.Set();
}
private static void Save(string fileName)
{
int profiledFuncsCount;
OuterHeader outerHeader = new OuterHeader();
outerHeader.Magic = _outerHeaderMagic;
outerHeader.InfoFileVersion = InternalVersion;
outerHeader.Endianness = Ptc.GetEndianness();
outerHeader.SetHeaderHash();
using (MemoryStream stream = new MemoryStream())
{
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
lock (_lock)
{
Serialize(stream, ProfiledFuncs);
profiledFuncsCount = ProfiledFuncs.Count;
}
Debug.Assert(stream.Position == stream.Length);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
stream.Seek(0L, SeekOrigin.Begin);
SerializeStructure(stream, hash);
if (hash == _lastHash)
{
return;
}
using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
{
try
{
SerializeStructure(compressedStream, outerHeader);
stream.WriteTo(deflateStream);
_lastHash = hash;
}
catch
{
compressedStream.Position = 0L;
_lastHash = default;
}
if (compressedStream.Position < compressedStream.Length)
{
compressedStream.SetLength(compressedStream.Position);
}
}
}
long fileSize = new FileInfo(fileName).Length;
if (fileSize != 0L)
{
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
}
}
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
{
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]
private struct OuterHeader
{
public ulong Magic;
public uint InfoFileVersion;
public bool Endianness;
public Hash128 HeaderHash;
public void SetHeaderHash()
{
Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>()));
}
public bool IsHeaderValid()
{
Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
internal struct FuncProfile
{
public ExecutionMode Mode;
public bool HighCq;
public FuncProfile(ExecutionMode mode, bool highCq)
{
Mode = mode;
HighCq = highCq;
}
}
internal static void Start()
{
if (Ptc.State == PtcState.Enabled ||
Ptc.State == PtcState.Continuing)
{
Enabled = true;
_timer.Enabled = true;
}
}
public static void Stop()
{
Enabled = false;
if (!_disposed)
{
_timer.Enabled = false;
}
}
internal static void Wait()
{
_waitEvent.WaitOne();
}
public static void Dispose()
{
if (!_disposed)
{
_disposed = true;
_timer.Elapsed -= PreSave;
_timer.Dispose();
Wait();
_waitEvent.Dispose();
}
}
}
}

View File

@@ -1,394 +0,0 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using System;
using System.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
namespace ARMeilleure.Translation
{
static class RegisterUsage
{
private const int RegsCount = 32;
private const int RegsMask = RegsCount - 1;
private struct RegisterMask : IEquatable<RegisterMask>
{
public long IntMask => Mask.GetElement(0);
public long VecMask => Mask.GetElement(1);
public Vector128<long> Mask { get; }
public RegisterMask(Vector128<long> mask)
{
Mask = mask;
}
public RegisterMask(long intMask, long vecMask)
{
Mask = Vector128.Create(intMask, vecMask);
}
public static RegisterMask operator &(RegisterMask x, RegisterMask y)
{
if (Sse2.IsSupported)
{
return new RegisterMask(Sse2.And(x.Mask, y.Mask));
}
return new RegisterMask(x.IntMask & y.IntMask, x.VecMask & y.VecMask);
}
public static RegisterMask operator |(RegisterMask x, RegisterMask y)
{
if (Sse2.IsSupported)
{
return new RegisterMask(Sse2.Or(x.Mask, y.Mask));
}
return new RegisterMask(x.IntMask | y.IntMask, x.VecMask | y.VecMask);
}
public static RegisterMask operator ~(RegisterMask x)
{
if (Sse2.IsSupported)
{
return new RegisterMask(Sse2.AndNot(x.Mask, Vector128<long>.AllBitsSet));
}
return new RegisterMask(~x.IntMask, ~x.VecMask);
}
public static bool operator ==(RegisterMask x, RegisterMask y)
{
return x.Equals(y);
}
public static bool operator !=(RegisterMask x, RegisterMask y)
{
return !x.Equals(y);
}
public override bool Equals(object obj)
{
return obj is RegisterMask regMask && Equals(regMask);
}
public bool Equals(RegisterMask other)
{
return Mask.Equals(other.Mask);
}
public override int GetHashCode()
{
return Mask.GetHashCode();
}
}
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
{
// Compute local register inputs and outputs used inside blocks.
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
{
for (int index = 0; index < node.SourcesCount; index++)
{
Operand source = node.GetSource(index);
if (source.Kind == OperandKind.Register)
{
Register register = source.GetRegister();
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
}
}
if (node.Destination != default && node.Destination.Kind == OperandKind.Register)
{
localOutputs[block.Index] |= GetMask(node.Destination.GetRegister());
}
}
}
// Compute global register inputs and outputs used across blocks.
RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Count];
RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count];
RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Count];
bool modified;
bool firstPass = true;
do
{
modified = false;
// Compute register outputs.
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
{
BasicBlock block = cfg.PostOrderBlocks[index];
if (block.Predecessors.Count != 0 && !HasContextLoad(block))
{
BasicBlock predecessor = block.Predecessors[0];
RegisterMask cmnOutputs = localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index];
RegisterMask outputs = globalOutputs[predecessor.Index];
for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++)
{
predecessor = block.Predecessors[pIndex];
cmnOutputs &= localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index];
outputs |= globalOutputs[predecessor.Index];
}
globalInputs[block.Index] |= outputs & ~cmnOutputs;
if (!firstPass)
{
cmnOutputs &= globalCmnOutputs[block.Index];
}
modified |= Exchange(globalCmnOutputs, block.Index, cmnOutputs);
outputs |= localOutputs[block.Index];
modified |= Exchange(globalOutputs, block.Index, globalOutputs[block.Index] | outputs);
}
else
{
modified |= Exchange(globalOutputs, block.Index, localOutputs[block.Index]);
}
}
// Compute register inputs.
for (int index = 0; index < cfg.PostOrderBlocks.Length; index++)
{
BasicBlock block = cfg.PostOrderBlocks[index];
RegisterMask inputs = localInputs[block.Index];
for (int i = 0; i < block.SuccessorsCount; i++)
{
inputs |= globalInputs[block.GetSuccessor(i).Index];
}
inputs &= ~globalCmnOutputs[block.Index];
modified |= Exchange(globalInputs, block.Index, globalInputs[block.Index] | inputs);
}
firstPass = false;
}
while (modified);
// Insert load and store context instructions where needed.
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
bool hasContextLoad = HasContextLoad(block);
if (hasContextLoad)
{
block.Operations.Remove(block.Operations.First);
}
Operand arg = default;
// The only block without any predecessor should be the entry block.
// It always needs a context load as it is the first block to run.
if (block.Predecessors.Count == 0 || hasContextLoad)
{
long vecMask = globalInputs[block.Index].VecMask;
long intMask = globalInputs[block.Index].IntMask;
if (vecMask != 0 || intMask != 0)
{
arg = Local(OperandType.I64);
Operation loadArg = block.Operations.AddFirst(Operation(Instruction.LoadArgument, arg, Const(0)));
LoadLocals(block, vecMask, RegisterType.Vector, mode, loadArg, arg);
LoadLocals(block, intMask, RegisterType.Integer, mode, loadArg, arg);
}
}
bool hasContextStore = HasContextStore(block);
if (hasContextStore)
{
block.Operations.Remove(block.Operations.Last);
}
if (EndsWithReturn(block) || hasContextStore)
{
long vecMask = globalOutputs[block.Index].VecMask;
long intMask = globalOutputs[block.Index].IntMask;
if (vecMask != 0 || intMask != 0)
{
if (arg == default)
{
arg = Local(OperandType.I64);
block.Append(Operation(Instruction.LoadArgument, arg, Const(0)));
}
StoreLocals(block, intMask, RegisterType.Integer, mode, arg);
StoreLocals(block, vecMask, RegisterType.Vector, mode, arg);
}
}
}
}
private static bool HasContextLoad(BasicBlock block)
{
return StartsWith(block, Instruction.LoadFromContext) && block.Operations.First.SourcesCount == 0;
}
private static bool HasContextStore(BasicBlock block)
{
return EndsWith(block, Instruction.StoreToContext) && block.Operations.Last.SourcesCount == 0;
}
private static bool StartsWith(BasicBlock block, Instruction inst)
{
if (block.Operations.Count > 0)
{
Operation first = block.Operations.First;
return first != default && first.Instruction == inst;
}
return false;
}
private static bool EndsWith(BasicBlock block, Instruction inst)
{
if (block.Operations.Count > 0)
{
Operation last = block.Operations.Last;
return last != default && last.Instruction == inst;
}
return false;
}
private static RegisterMask GetMask(Register register)
{
long intMask = 0;
long vecMask = 0;
switch (register.Type)
{
case RegisterType.Flag: intMask = (1L << RegsCount) << register.Index; break;
case RegisterType.Integer: intMask = 1L << register.Index; break;
case RegisterType.FpFlag: vecMask = (1L << RegsCount) << register.Index; break;
case RegisterType.Vector: vecMask = 1L << register.Index; break;
}
return new RegisterMask(intMask, vecMask);
}
private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value)
{
ref RegisterMask curValue = ref masks[blkIndex];
bool changed = curValue != value;
curValue = value;
return changed;
}
private static void LoadLocals(
BasicBlock block,
long inputs,
RegisterType baseType,
ExecutionMode mode,
Operation loadArg,
Operand arg)
{
while (inputs != 0)
{
int bit = 63 - BitOperations.LeadingZeroCount((ulong)inputs);
Operand dest = GetRegFromBit(bit, baseType, mode);
Operand offset = Const((long)NativeContext.GetRegisterOffset(dest.GetRegister()));
Operand addr = Local(OperandType.I64);
block.Operations.AddAfter(loadArg, Operation(Instruction.Load, dest, addr));
block.Operations.AddAfter(loadArg, Operation(Instruction.Add, addr, arg, offset));
inputs &= ~(1L << bit);
}
}
private static void StoreLocals(
BasicBlock block,
long outputs,
RegisterType baseType,
ExecutionMode mode,
Operand arg)
{
while (outputs != 0)
{
int bit = BitOperations.TrailingZeroCount(outputs);
Operand source = GetRegFromBit(bit, baseType, mode);
Operand offset = Const((long)NativeContext.GetRegisterOffset(source.GetRegister()));
Operand addr = Local(OperandType.I64);
block.Append(Operation(Instruction.Add, addr, arg, offset));
block.Append(Operation(Instruction.Store, default, addr, source));
outputs &= ~(1L << bit);
}
}
private static Operand GetRegFromBit(int bit, RegisterType baseType, ExecutionMode mode)
{
if (bit < RegsCount)
{
return Register(bit, baseType, GetOperandType(baseType, mode));
}
else if (baseType == RegisterType.Integer)
{
return Register(bit & RegsMask, RegisterType.Flag, OperandType.I32);
}
else if (baseType == RegisterType.Vector)
{
return Register(bit & RegsMask, RegisterType.FpFlag, OperandType.I32);
}
else
{
throw new ArgumentOutOfRangeException(nameof(bit));
}
}
private static OperandType GetOperandType(RegisterType type, ExecutionMode mode)
{
switch (type)
{
case RegisterType.Flag: return OperandType.I32;
case RegisterType.FpFlag: return OperandType.I32;
case RegisterType.Integer: return (mode == ExecutionMode.Aarch64) ? OperandType.I64 : OperandType.I32;
case RegisterType.Vector: return OperandType.V128;
}
throw new ArgumentException($"Invalid register type \"{type}\".");
}
private static bool EndsWithReturn(BasicBlock block)
{
Operation last = block.Operations.Last;
return last != default && last.Instruction == Instruction.Return;
}
}
}

View File

@@ -1,289 +0,0 @@
using ARMeilleure.Common;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Translation
{
static partial class Ssa
{
private class DefMap
{
private readonly Dictionary<int, Operand> _map;
private readonly BitMap _phiMasks;
public DefMap()
{
_map = new Dictionary<int, Operand>();
_phiMasks = new BitMap(Allocators.Default, RegisterConsts.TotalCount);
}
public bool TryAddOperand(int key, Operand operand)
{
return _map.TryAdd(key, operand);
}
public bool TryGetOperand(int key, out Operand operand)
{
return _map.TryGetValue(key, out operand);
}
public bool AddPhi(int key)
{
return _phiMasks.Set(key);
}
public bool HasPhi(int key)
{
return _phiMasks.IsSet(key);
}
}
public static void Construct(ControlFlowGraph cfg)
{
var globalDefs = new DefMap[cfg.Blocks.Count];
var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount];
var dfPhiBlocks = new Queue<BasicBlock>();
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
globalDefs[block.Index] = new DefMap();
}
// First pass, get all defs and locals uses.
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
{
for (int index = 0; index < node.SourcesCount; index++)
{
Operand src = node.GetSource(index);
if (TryGetId(src, out int srcKey))
{
Operand local = localDefs[srcKey];
if (local == default)
{
local = src;
}
node.SetSource(index, local);
}
}
Operand dest = node.Destination;
if (TryGetId(dest, out int destKey))
{
Operand local = Local(dest.Type);
localDefs[destKey] = local;
node.Destination = local;
}
}
for (int key = 0; key < localDefs.Length; key++)
{
Operand local = localDefs[key];
if (local == default)
{
continue;
}
globalDefs[block.Index].TryAddOperand(key, local);
dfPhiBlocks.Enqueue(block);
while (dfPhiBlocks.TryDequeue(out BasicBlock dfPhiBlock))
{
foreach (BasicBlock domFrontier in dfPhiBlock.DominanceFrontiers)
{
if (globalDefs[domFrontier.Index].AddPhi(key))
{
dfPhiBlocks.Enqueue(domFrontier);
}
}
}
}
Array.Clear(localDefs);
}
// Second pass, rename variables with definitions on different blocks.
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
{
for (int index = 0; index < node.SourcesCount; index++)
{
Operand src = node.GetSource(index);
if (TryGetId(src, out int key))
{
Operand local = localDefs[key];
if (local == default)
{
local = FindDef(globalDefs, block, src);
localDefs[key] = local;
}
node.SetSource(index, local);
}
}
}
Array.Clear(localDefs, 0, localDefs.Length);
}
}
private static Operand FindDef(DefMap[] globalDefs, BasicBlock current, Operand operand)
{
if (globalDefs[current.Index].HasPhi(GetId(operand)))
{
return InsertPhi(globalDefs, current, operand);
}
if (current != current.ImmediateDominator)
{
return FindDefOnPred(globalDefs, current.ImmediateDominator, operand);
}
return Undef();
}
private static Operand FindDefOnPred(DefMap[] globalDefs, BasicBlock current, Operand operand)
{
BasicBlock previous;
do
{
DefMap defMap = globalDefs[current.Index];
int key = GetId(operand);
if (defMap.TryGetOperand(key, out Operand lastDef))
{
return lastDef;
}
if (defMap.HasPhi(key))
{
return InsertPhi(globalDefs, current, operand);
}
previous = current;
current = current.ImmediateDominator;
}
while (previous != current);
return Undef();
}
private static Operand InsertPhi(DefMap[] globalDefs, BasicBlock block, Operand operand)
{
// This block has a Phi that has not been materialized yet, but that
// would define a new version of the variable we're looking for. We need
// to materialize the Phi, add all the block/operand pairs into the Phi, and
// then use the definition from that Phi.
Operand local = Local(operand.Type);
Operation operation = Operation.Factory.PhiOperation(local, block.Predecessors.Count);
AddPhi(block, operation);
globalDefs[block.Index].TryAddOperand(GetId(operand), local);
PhiOperation phi = operation.AsPhi();
for (int index = 0; index < block.Predecessors.Count; index++)
{
BasicBlock predecessor = block.Predecessors[index];
phi.SetBlock(index, predecessor);
phi.SetSource(index, FindDefOnPred(globalDefs, predecessor, operand));
}
return local;
}
private static void AddPhi(BasicBlock block, Operation phi)
{
Operation node = block.Operations.First;
if (node != default)
{
while (node.ListNext != default && node.ListNext.Instruction == Instruction.Phi)
{
node = node.ListNext;
}
}
if (node != default && node.Instruction == Instruction.Phi)
{
block.Operations.AddAfter(node, phi);
}
else
{
block.Operations.AddFirst(phi);
}
}
private static bool TryGetId(Operand operand, out int result)
{
if (operand != default)
{
if (operand.Kind == OperandKind.Register)
{
Register reg = operand.GetRegister();
if (reg.Type == RegisterType.Integer)
{
result = reg.Index;
}
else if (reg.Type == RegisterType.Vector)
{
result = RegisterConsts.IntRegsCount + reg.Index;
}
else if (reg.Type == RegisterType.Flag)
{
result = RegisterConsts.IntAndVecRegsCount + reg.Index;
}
else /* if (reg.Type == RegisterType.FpFlag) */
{
result = RegisterConsts.FpFlagsOffset + reg.Index;
}
return true;
}
else if (operand.Kind == OperandKind.LocalVariable && operand.GetLocalNumber() > 0)
{
result = RegisterConsts.TotalCount + operand.GetLocalNumber() - 1;
return true;
}
}
result = -1;
return false;
}
private static int GetId(Operand operand)
{
if (!TryGetId(operand, out int key))
{
Debug.Fail("OperandKind must be Register or a numbered LocalVariable.");
}
return key;
}
}
}

View File

@@ -1,30 +0,0 @@
using ARMeilleure.Common;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
class TranslatedFunction
{
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
public Counter<uint> CallCounter { get; }
public ulong GuestSize { get; }
public bool HighCq { get; }
public IntPtr FuncPtr { get; }
public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq)
{
_func = func;
CallCounter = callCounter;
GuestSize = guestSize;
HighCq = highCq;
FuncPtr = Marshal.GetFunctionPointerForDelegate(func);
}
public ulong Execute(State.ExecutionContext context)
{
return _func(context.NativeContextPtr);
}
}
}

View File

@@ -1,539 +0,0 @@
using ARMeilleure.CodeGen;
using ARMeilleure.Common;
using ARMeilleure.Decoders;
using ARMeilleure.Diagnostics;
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using ARMeilleure.Signal;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC;
using Ryujinx.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Translation
{
public class Translator
{
private static readonly AddressTable<ulong>.Level[] Levels64Bit =
new AddressTable<ulong>.Level[]
{
new(31, 17),
new(23, 8),
new(15, 8),
new( 7, 8),
new( 2, 5)
};
private static readonly AddressTable<ulong>.Level[] Levels32Bit =
new AddressTable<ulong>.Level[]
{
new(31, 17),
new(23, 8),
new(15, 8),
new( 7, 8),
new( 1, 6)
};
private readonly IJitMemoryAllocator _allocator;
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
internal TranslatorCache<TranslatedFunction> Functions { get; }
internal AddressTable<ulong> FunctionTable { get; }
internal EntryTable<uint> CountTable { get; }
internal TranslatorStubs Stubs { get; }
internal TranslatorQueue Queue { get; }
internal IMemoryManager Memory { get; }
private volatile int _threadCount;
// FIXME: Remove this once the init logic of the emulator will be redone.
public static readonly ManualResetEvent IsReadyForTranslation = new(false);
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
{
_allocator = allocator;
Memory = memory;
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
Queue = new TranslatorQueue();
JitCache.Initialize(allocator);
CountTable = new EntryTable<uint>();
Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? Levels64Bit : Levels32Bit);
Stubs = new TranslatorStubs(this);
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
if (memory.Type.IsHostMapped())
{
NativeSignalHandler.InitializeSignalHandler();
}
}
public void Execute(State.ExecutionContext context, ulong address)
{
if (Interlocked.Increment(ref _threadCount) == 1)
{
IsReadyForTranslation.WaitOne();
if (Ptc.State == PtcState.Enabled)
{
Debug.Assert(Functions.Count == 0);
Ptc.LoadTranslations(this);
Ptc.MakeAndSaveTranslations(this);
}
PtcProfiler.Start();
Ptc.Disable();
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
// etc). All threads are normal priority except from the last, which just fills as much of the last core
// as the os lets it with a low priority. If we only have one rejit thread, it should be normal priority
// as highCq code is performance critical.
//
// TODO: Use physical cores rather than logical. This only really makes sense for processors with
// hyperthreading. Requires OS specific code.
int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3);
int threadCount = Math.Min(4, unboundedThreadCount);
for (int i = 0; i < threadCount; i++)
{
bool last = i != 0 && i == unboundedThreadCount - 1;
Thread backgroundTranslatorThread = new Thread(BackgroundTranslate)
{
Name = "CPU.BackgroundTranslatorThread." + i,
Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal
};
backgroundTranslatorThread.Start();
}
}
Statistics.InitializeTimer();
NativeInterface.RegisterThread(context, Memory, this);
if (Optimizations.UseUnmanagedDispatchLoop)
{
Stubs.DispatchLoop(context.NativeContextPtr, address);
}
else
{
do
{
address = ExecuteSingle(context, address);
}
while (context.Running && address != 0);
}
NativeInterface.UnregisterThread();
if (Interlocked.Decrement(ref _threadCount) == 0)
{
ClearJitCache();
Queue.Dispose();
Stubs.Dispose();
FunctionTable.Dispose();
CountTable.Dispose();
}
}
private ulong ExecuteSingle(State.ExecutionContext context, ulong address)
{
TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode);
Statistics.StartTimer();
ulong nextAddr = func.Execute(context);
Statistics.StopTimer(address);
return nextAddr;
}
public ulong Step(State.ExecutionContext context, ulong address)
{
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
address = func.Execute(context);
EnqueueForDeletion(address, func);
return address;
}
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
{
if (!Functions.TryGetValue(address, out TranslatedFunction func))
{
func = Translate(address, mode, highCq: false);
TranslatedFunction oldFunc = Functions.GetOrAdd(address, func.GuestSize, func);
if (oldFunc != func)
{
JitCache.Unmap(func.FuncPtr);
func = oldFunc;
}
if (PtcProfiler.Enabled)
{
PtcProfiler.AddEntry(address, mode, highCq: false);
}
RegisterFunction(address, func);
}
return func;
}
internal void RegisterFunction(ulong guestAddress, TranslatedFunction func)
{
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
{
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr);
}
}
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false)
{
var context = new ArmEmitterContext(
Memory,
CountTable,
FunctionTable,
Stubs,
address,
highCq,
mode: Aarch32Mode.User);
Logger.StartPass(PassName.Decoding);
Block[] blocks = Decoder.Decode(Memory, address, mode, highCq, singleStep ? DecoderMode.SingleInstruction : DecoderMode.MultipleBlocks);
Logger.EndPass(PassName.Decoding);
Logger.StartPass(PassName.Translation);
EmitSynchronization(context);
if (blocks[0].Address != address)
{
context.Branch(context.GetLabel(address));
}
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
ulong funcSize = funcRange.End - funcRange.Start;
Logger.EndPass(PassName.Translation, cfg);
Logger.StartPass(PassName.RegisterUsage);
RegisterUsage.RunPass(cfg, mode);
Logger.EndPass(PassName.RegisterUsage);
var retType = OperandType.I64;
var argTypes = new OperandType[] { OperandType.I64 };
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
if (context.HasPtc && !singleStep)
{
options |= CompilerOptions.Relocatable;
}
CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options);
if (context.HasPtc && !singleStep)
{
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
}
GuestFunction func = compiledFunc.Map<GuestFunction>();
Allocators.ResetAll();
return new TranslatedFunction(func, counter, funcSize, highCq);
}
private void BackgroundTranslate()
{
while (_threadCount != 0 && Queue.TryDequeue(out RejitRequest request))
{
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
Functions.AddOrUpdate(request.Address, func.GuestSize, func, (key, oldFunc) =>
{
EnqueueForDeletion(key, oldFunc);
return func;
});
if (PtcProfiler.Enabled)
{
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
}
RegisterFunction(request.Address, func);
}
}
private struct Range
{
public ulong Start { get; }
public ulong End { get; }
public Range(ulong start, ulong end)
{
Start = start;
End = end;
}
}
private static ControlFlowGraph EmitAndGetCFG(
ArmEmitterContext context,
Block[] blocks,
out Range range,
out Counter<uint> counter)
{
counter = null;
ulong rangeStart = ulong.MaxValue;
ulong rangeEnd = 0;
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{
Block block = blocks[blkIndex];
if (!block.Exit)
{
if (rangeStart > block.Address)
{
rangeStart = block.Address;
}
if (rangeEnd < block.EndAddress)
{
rangeEnd = block.EndAddress;
}
}
if (block.Address == context.EntryAddress && !context.HighCq)
{
EmitRejitCheck(context, out counter);
}
context.CurrBlock = block;
context.MarkLabel(context.GetLabel(block.Address));
if (block.Exit)
{
// Left option here as it may be useful if we need to return to managed rather than tail call in
// future. (eg. for debug)
bool useReturns = false;
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
}
else
{
for (int opcIndex = 0; opcIndex < block.OpCodes.Count; opcIndex++)
{
OpCode opCode = block.OpCodes[opcIndex];
context.CurrOp = opCode;
bool isLastOp = opcIndex == block.OpCodes.Count - 1;
if (isLastOp && block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address)
{
EmitSynchronization(context);
}
Operand lblPredicateSkip = default;
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
{
lblPredicateSkip = Label();
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Invert());
}
if (opCode is OpCode32 op && op.Cond < Condition.Al)
{
lblPredicateSkip = Label();
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Invert());
}
if (opCode.Instruction.Emitter != null)
{
opCode.Instruction.Emitter(context);
}
else
{
throw new InvalidOperationException($"Invalid instruction \"{opCode.Instruction.Name}\".");
}
if (lblPredicateSkip != default)
{
context.MarkLabel(lblPredicateSkip);
}
if (context.IsInIfThenBlock && opCode.Instruction.Name != InstName.It)
{
context.AdvanceIfThenBlockState();
}
}
}
}
range = new Range(rangeStart, rangeEnd);
return context.GetControlFlowGraph();
}
internal static void EmitRejitCheck(ArmEmitterContext context, out Counter<uint> counter)
{
const int MinsCallForRejit = 100;
counter = new Counter<uint>(context.CountTable);
Operand lblEnd = Label();
Operand address = !context.HasPtc ?
Const(ref counter.Value) :
Const(ref counter.Value, Ptc.CountTableSymbol);
Operand curCount = context.Load(OperandType.I32, address);
Operand count = context.Add(curCount, Const(1));
context.Store(address, count);
context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
context.MarkLabel(lblEnd);
}
internal static void EmitSynchronization(EmitterContext context)
{
long countOffs = NativeContext.GetCounterOffset();
Operand lblNonZero = Label();
Operand lblExit = Label();
Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
Operand count = context.Load(OperandType.I32, countAddr);
context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);
Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
context.Return(Const(0L));
context.MarkLabel(lblNonZero);
count = context.Subtract(count, Const(1));
context.Store(countAddr, count);
context.MarkLabel(lblExit);
}
public void InvalidateJitCacheRegion(ulong address, ulong size)
{
ulong[] overlapAddresses = Array.Empty<ulong>();
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
if (overlapsCount != 0)
{
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
ClearRejitQueue(allowRequeue: true);
}
for (int index = 0; index < overlapsCount; index++)
{
ulong overlapAddress = overlapAddresses[index];
if (Functions.TryGetValue(overlapAddress, out TranslatedFunction overlap))
{
Functions.Remove(overlapAddress);
Volatile.Write(ref FunctionTable.GetValue(overlapAddress), FunctionTable.Fill);
EnqueueForDeletion(overlapAddress, overlap);
}
}
// TODO: Remove overlapping functions from the JitCache aswell.
// This should be done safely, with a mechanism to ensure the function is not being executed.
}
internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
{
Queue.Enqueue(guestAddress, mode);
}
private void EnqueueForDeletion(ulong guestAddress, TranslatedFunction func)
{
_oldFuncs.Enqueue(new(guestAddress, func));
}
private void ClearJitCache()
{
// Ensure no attempt will be made to compile new functions due to rejit.
ClearRejitQueue(allowRequeue: false);
List<TranslatedFunction> functions = Functions.AsList();
foreach (var func in functions)
{
JitCache.Unmap(func.FuncPtr);
func.CallCounter?.Dispose();
}
Functions.Clear();
while (_oldFuncs.TryDequeue(out var kv))
{
JitCache.Unmap(kv.Value.FuncPtr);
kv.Value.CallCounter?.Dispose();
}
}
private void ClearRejitQueue(bool allowRequeue)
{
if (!allowRequeue)
{
Queue.Clear();
return;
}
lock (Queue.Sync)
{
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
{
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
{
Volatile.Write(ref func.CallCounter.Value, 0);
}
}
}
}
}
}

View File

@@ -1,248 +0,0 @@
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Translation
{
/// <summary>
/// Represents a stub manager.
/// </summary>
class TranslatorStubs : IDisposable
{
private static readonly Lazy<IntPtr> _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
private bool _disposed;
private readonly Translator _translator;
private readonly Lazy<IntPtr> _dispatchStub;
private readonly Lazy<DispatcherFunction> _dispatchLoop;
/// <summary>
/// Gets the dispatch stub.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
public IntPtr DispatchStub
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
return _dispatchStub.Value;
}
}
/// <summary>
/// Gets the slow dispatch stub.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
public IntPtr SlowDispatchStub
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
return _slowDispatchStub.Value;
}
}
/// <summary>
/// Gets the dispatch loop function.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
public DispatcherFunction DispatchLoop
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
return _dispatchLoop.Value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
/// <see cref="Translator"/> instance.
/// </summary>
/// <param name="translator"><see cref="Translator"/> instance to use</param>
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
public TranslatorStubs(Translator translator)
{
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
}
/// <summary>
/// Releases all resources used by the <see cref="TranslatorStubs"/> instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all unmanaged and optionally managed resources used by the <see cref="TranslatorStubs"/> instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (_dispatchStub.IsValueCreated)
{
JitCache.Unmap(_dispatchStub.Value);
}
if (_dispatchLoop.IsValueCreated)
{
JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
}
_disposed = true;
}
}
/// <summary>
/// Frees resources used by the <see cref="TranslatorStubs"/> instance.
/// </summary>
~TranslatorStubs()
{
Dispose(false);
}
/// <summary>
/// Generates a <see cref="DispatchStub"/>.
/// </summary>
/// <returns>Generated <see cref="DispatchStub"/></returns>
private IntPtr GenerateDispatchStub()
{
var context = new EmitterContext();
Operand lblFallback = Label();
Operand lblEnd = Label();
// Load the target guest address from the native context.
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand guestAddress = context.Load(OperandType.I64,
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
// Check if guest address is within range of the AddressTable.
Operand masked = context.BitwiseAnd(guestAddress, Const(~_translator.FunctionTable.Mask));
context.BranchIfTrue(lblFallback, masked);
Operand index = default;
Operand page = Const((long)_translator.FunctionTable.Base);
for (int i = 0; i < _translator.FunctionTable.Levels.Length; i++)
{
ref var level = ref _translator.FunctionTable.Levels[i];
// level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
// be encoded as an immediate on x86's bitwise and operation.
Operand mask = Const(level.Mask >> level.Index);
index = context.BitwiseAnd(context.ShiftRightUI(guestAddress, Const(level.Index)), mask);
if (i < _translator.FunctionTable.Levels.Length - 1)
{
page = context.Load(OperandType.I64, context.Add(page, context.ShiftLeft(index, Const(3))));
context.BranchIfFalse(lblFallback, page);
}
}
Operand hostAddress;
Operand hostAddressAddr = context.Add(page, context.ShiftLeft(index, Const(3)));
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
context.Tailcall(hostAddress, nativeContext);
context.MarkLabel(lblFallback);
hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
context.Tailcall(hostAddress, nativeContext);
var cfg = context.GetControlFlowGraph();
var retType = OperandType.I64;
var argTypes = new[] { OperandType.I64 };
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
return Marshal.GetFunctionPointerForDelegate(func);
}
/// <summary>
/// Generates a <see cref="SlowDispatchStub"/>.
/// </summary>
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
private static IntPtr GenerateSlowDispatchStub()
{
var context = new EmitterContext();
// Load the target guest address from the native context.
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand guestAddress = context.Load(OperandType.I64,
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
MethodInfo getFuncAddress = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress));
Operand hostAddress = context.Call(getFuncAddress, guestAddress);
context.Tailcall(hostAddress, nativeContext);
var cfg = context.GetControlFlowGraph();
var retType = OperandType.I64;
var argTypes = new[] { OperandType.I64 };
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
return Marshal.GetFunctionPointerForDelegate(func);
}
/// <summary>
/// Generates a <see cref="DispatchLoop"/> function.
/// </summary>
/// <returns><see cref="DispatchLoop"/> function</returns>
private DispatcherFunction GenerateDispatchLoop()
{
var context = new EmitterContext();
Operand beginLbl = Label();
Operand endLbl = Label();
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand guestAddress = context.Copy(
context.AllocateLocal(OperandType.I64),
context.LoadArgument(OperandType.I64, 1));
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
context.MarkLabel(beginLbl);
context.Store(dispatchAddress, guestAddress);
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
context.BranchIfFalse(endLbl, guestAddress);
context.BranchIfFalse(endLbl, context.Load(OperandType.I32, runningAddress));
context.Branch(beginLbl);
context.MarkLabel(endLbl);
context.Return();
var cfg = context.GetControlFlowGraph();
var retType = OperandType.None;
var argTypes = new[] { OperandType.I64, OperandType.I64 };
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<DispatcherFunction>();
}
}
}

53
Directory.Packages.props Normal file
View File

@@ -0,0 +1,53 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="0.10.19" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DynamicData" Version="7.13.5" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageVersion Include="LibHac" Version="0.18.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
<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.26.3-build25" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.0" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.1" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
</ItemGroup>
</Project>

View File

@@ -21,6 +21,10 @@
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
alt="">
</a>
<a href="https://crwd.in/ryujinx">
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
alt="">
</a>
<a href="https://discord.com/invite/VkQYXAZ">
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
alt="Discord">
@@ -36,18 +40,20 @@
## Compatibility
As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable.
As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable.
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
## Usage
To run this emulator, your PC must be equipped with at least 8GB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
To run this emulator, your PC must be equipped with at least 8GiB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
## Latest build
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
@@ -62,7 +68,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
If you wish to build the emulator yourself, follow these steps:
### Step 1
Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0).
Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
### Step 2
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
@@ -90,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
- **GPU**
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
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. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI.
- **Input**

View File

@@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.OpenAL" Version="4.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,171 +0,0 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using Ryujinx.Memory;
using Ryujinx.SDL2.Common;
using System;
using System.Collections.Concurrent;
using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
using static SDL2.SDL;
namespace Ryujinx.Audio.Backends.SDL2
{
public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent;
private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions;
public SDL2HardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
_sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>();
SDL2Driver.Instance.Initialize();
}
public static bool IsSupported => IsSupportedInternal();
private static bool IsSupportedInternal()
{
uint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null);
if (device != 0)
{
SDL_CloseAudioDevice(device);
}
return device != 0;
}
public ManualResetEvent GetUpdateRequiredEvent()
{
return _updateRequiredEvent;
}
public ManualResetEvent GetPauseEvent()
{
return _pauseEvent;
}
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
{
if (channelCount == 0)
{
channelCount = 2;
}
if (sampleRate == 0)
{
sampleRate = Constants.TargetSampleRate;
}
if (direction != Direction.Output)
{
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
}
SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
_sessions.TryAdd(session, 0);
return session;
}
internal bool Unregister(SDL2HardwareDeviceSession session)
{
return _sessions.TryRemove(session, out _);
}
private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
{
return new SDL_AudioSpec
{
channels = (byte)requestedChannelCount,
format = GetSDL2Format(requestedSampleFormat),
freq = (int)requestedSampleRate,
samples = (ushort)sampleCount
};
}
internal static ushort GetSDL2Format(SampleFormat format)
{
return format switch
{
SampleFormat.PcmInt8 => AUDIO_S8,
SampleFormat.PcmInt16 => AUDIO_S16,
SampleFormat.PcmInt32 => AUDIO_S32,
SampleFormat.PcmFloat => AUDIO_F32,
_ => throw new ArgumentException($"Unsupported sample format {format}"),
};
}
internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL_AudioCallback callback)
{
SDL_AudioSpec desired = GetSDL2Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, sampleCount);
desired.callback = callback;
uint device = SDL_OpenAudioDevice(IntPtr.Zero, 0, ref desired, out SDL_AudioSpec got, 0);
if (device == 0)
{
return 0;
}
bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels;
if (!isValid)
{
SDL_CloseAudioDevice(device);
return 0;
}
return device;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
foreach (SDL2HardwareDeviceSession session in _sessions.Keys)
{
session.Dispose();
}
SDL2Driver.Instance.Dispose();
_pauseEvent.Dispose();
}
}
public bool SupportsSampleRate(uint sampleRate)
{
return true;
}
public bool SupportsSampleFormat(SampleFormat sampleFormat)
{
return sampleFormat != SampleFormat.PcmInt24;
}
public bool SupportsChannelCount(uint channelCount)
{
return true;
}
public bool SupportsDirection(Direction direction)
{
// TODO: add direction input when supported.
return direction == Direction.Output;
}
}
}

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