mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2026-03-13 12:28:42 +00:00
Compare commits
395 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aae4595bdb | ||
|
|
880fd3cfcb | ||
|
|
f679f25e08 | ||
|
|
c2709b3bdd | ||
|
|
2b6e81deea | ||
|
|
7271f1b18e | ||
|
|
5fda543f84 | ||
|
|
95c06de4c1 | ||
|
|
49c63ea077 | ||
|
|
531da8a1c0 | ||
|
|
5cbdfbc7a4 | ||
|
|
e0544dd9c7 | ||
|
|
aa784c3e5e | ||
|
|
9205077590 | ||
|
|
0ed40c7175 | ||
|
|
40d47b7aa2 | ||
|
|
ec0bb74968 | ||
|
|
42f7f98666 | ||
|
|
95bad6995c | ||
|
|
3d42995822 | ||
|
|
9095941fd1 | ||
|
|
ba71141bdc | ||
|
|
0a0675a7f6 | ||
|
|
a7c6e6a8cf | ||
|
|
0bc8151c7e | ||
|
|
40c17673f5 | ||
|
|
a8950d6ac4 | ||
|
|
162798b026 | ||
|
|
1b28ecd63e | ||
|
|
895d9b53bc | ||
|
|
0e06aace45 | ||
|
|
adf4ebcd60 | ||
|
|
470a8031a4 | ||
|
|
5440d4ad5c | ||
|
|
dde208b480 | ||
|
|
4c3d2d5d75 | ||
|
|
fab11ba3f1 | ||
|
|
332891b5ff | ||
|
|
7df4fcada7 | ||
|
|
d6698680be | ||
|
|
e5c9838b0b | ||
|
|
f8ec878796 | ||
|
|
9ff21f9ab6 | ||
|
|
aa021085cf | ||
|
|
1f5d881860 | ||
|
|
1f664100bd | ||
|
|
1f5e1ffa80 | ||
|
|
264438ff19 | ||
|
|
3b8ac1641a | ||
|
|
4250732353 | ||
|
|
4d1579acbf | ||
|
|
6279f5e430 | ||
|
|
b7d2bff6aa | ||
|
|
7c327fecb3 | ||
|
|
cc1a933a2f | ||
|
|
dd574146fb | ||
|
|
2c94ac455e | ||
|
|
e18d258fa0 | ||
|
|
36f10df775 | ||
|
|
680e548022 | ||
|
|
21c4176157 | ||
|
|
3b4ff2d6d9 | ||
|
|
12504f280c | ||
|
|
250fc51374 | ||
|
|
206e0882c2 | ||
|
|
609abc8b9b | ||
|
|
cee7121058 | ||
|
|
cd124bda58 | ||
|
|
9f12e50a54 | ||
|
|
097562bc6c | ||
|
|
db4242c5dc | ||
|
|
4dd77316f7 | ||
|
|
3f98369a17 | ||
|
|
c26aeefe03 | ||
|
|
666e05f5cb | ||
|
|
8d9d508dc7 | ||
|
|
e27f5522e2 | ||
|
|
add2a9d151 | ||
|
|
9e50dd99d7 | ||
|
|
0dec91bb42 | ||
|
|
d9b63353b0 | ||
|
|
eabd0ec93f | ||
|
|
138d5dc64a | ||
|
|
3e68a87d63 | ||
|
|
69b6ef7a4a | ||
|
|
40e87c634e | ||
|
|
79d1c190db | ||
|
|
2bc88467eb | ||
|
|
baf8752e74 | ||
|
|
d5e4378aea | ||
|
|
6dbcdfea47 | ||
|
|
c5258cf082 | ||
|
|
5c89e22bb9 | ||
|
|
11ecff2ff0 | ||
|
|
4c3f09644a | ||
|
|
e187a8870a | ||
|
|
a64fee29dc | ||
|
|
9ef94c8292 | ||
|
|
915d6d044c | ||
|
|
a4780ab33b | ||
|
|
a947a45d81 | ||
|
|
9db73f74cf | ||
|
|
a1efd87c45 | ||
|
|
49be977588 | ||
|
|
c95be55091 | ||
|
|
63dedbda86 | ||
|
|
c532118d94 | ||
|
|
52d6f2e656 | ||
|
|
c9bc4eaf58 | ||
|
|
3249f8ff41 | ||
|
|
1b41b285ac | ||
|
|
f5a6f45b27 | ||
|
|
210557951b | ||
|
|
4c2d9ff3ff | ||
|
|
8198b99935 | ||
|
|
460f96967d | ||
|
|
7ca779a26d | ||
|
|
b5032b3c91 | ||
|
|
f0a3dff136 | ||
|
|
f659dcb9d8 | ||
|
|
a34fb0e939 | ||
|
|
21ce8a9b80 | ||
|
|
9ecbee8032 | ||
|
|
80519af67d | ||
|
|
26e30faff3 | ||
|
|
0992310b76 | ||
|
|
009c1101d2 | ||
|
|
ba95ee54ab | ||
|
|
4ce4299ca2 | ||
|
|
17620d18db | ||
|
|
9f1cf6458c | ||
|
|
67b4e63cff | ||
|
|
c05c688ee8 | ||
|
|
b2623dc27d | ||
|
|
5131b71437 | ||
|
|
7870423671 | ||
|
|
b72916fbc1 | ||
|
|
da073fce61 | ||
|
|
1fc90e57d2 | ||
|
|
eafcc314a9 | ||
|
|
6e9bd4de13 | ||
|
|
05a41b31bc | ||
|
|
eed17f963e | ||
|
|
c09c0c002d | ||
|
|
d56d335c0b | ||
|
|
23c844b2aa | ||
|
|
81691b9e37 | ||
|
|
2dc422bc14 | ||
|
|
a80fa5e33f | ||
|
|
954e995321 | ||
|
|
dad9ab6bb6 | ||
|
|
f0562b9c75 | ||
|
|
b8556530f2 | ||
|
|
4f3af839be | ||
|
|
155736c986 | ||
|
|
dba908dc78 | ||
|
|
ecee34a50c | ||
|
|
9b5a0c3889 | ||
|
|
80b4972139 | ||
|
|
5d85468302 | ||
|
|
9b1cc2cec6 | ||
|
|
e691622f0a | ||
|
|
f663a5cd38 | ||
|
|
f7c2e867f4 | ||
|
|
cedd200745 | ||
|
|
58207685c0 | ||
|
|
095ad923ad | ||
|
|
f07ae7d53f | ||
|
|
c308f09722 | ||
|
|
f1eef29409 | ||
|
|
1f8d66db7c | ||
|
|
c3a5716a95 | ||
|
|
1f1e2a7f03 | ||
|
|
e54f9dc4b4 | ||
|
|
edfd4d70c0 | ||
|
|
fc43aecbbd | ||
|
|
58d7a1fe97 | ||
|
|
7aa430f1a5 | ||
|
|
6bf460e104 | ||
|
|
efb135b74c | ||
|
|
a707842e14 | ||
|
|
a5a9b9bc8b | ||
|
|
17078ad929 | ||
|
|
32450d45de | ||
|
|
ed7a0474c6 | ||
|
|
fe9c49949a | ||
|
|
052b23c83c | ||
|
|
e4f68592c3 | ||
|
|
1dcd44b94f | ||
|
|
61b1ce252f | ||
|
|
5f38086f94 | ||
|
|
7bae440d3a | ||
|
|
f1943fd0b6 | ||
|
|
ec8d4f3af5 | ||
|
|
b3f0978869 | ||
|
|
f614d2c435 | ||
|
|
40c9416097 | ||
|
|
618c8edc79 | ||
|
|
99fc4fa61b | ||
|
|
f6d5499a16 | ||
|
|
26bf13a65d | ||
|
|
96cf242bcf | ||
|
|
59755818ef | ||
|
|
f8beeeb7d3 | ||
|
|
cb250162cb | ||
|
|
7528f94536 | ||
|
|
43081c16c4 | ||
|
|
780627e7b0 | ||
|
|
9044cb38d1 | ||
|
|
a53cfdab78 | ||
|
|
c7f9962dde | ||
|
|
296c4a3d01 | ||
|
|
e7cf4e6eaf | ||
|
|
a1a4771ac1 | ||
|
|
2fd819613f | ||
|
|
ad6ff6ce99 | ||
|
|
dc30d94852 | ||
|
|
4f293f8cbe | ||
|
|
32a1cd83fd | ||
|
|
e3d0ccf8d5 | ||
|
|
c14844d12c | ||
|
|
7fea26e97e | ||
|
|
7b7f62c776 | ||
|
|
423dbc8888 | ||
|
|
6adf15e479 | ||
|
|
2747f12591 | ||
|
|
a47824f961 | ||
|
|
8474d52778 | ||
|
|
dd7a924596 | ||
|
|
a76eaf9a9a | ||
|
|
009e6bcd1b | ||
|
|
eb2cc159fa | ||
|
|
bb89e36fd8 | ||
|
|
de3134adbe | ||
|
|
36d53819a4 | ||
|
|
ae4324032a | ||
|
|
f449895e6d | ||
|
|
410be95ab6 | ||
|
|
cff9046fc7 | ||
|
|
86fd0643c2 | ||
|
|
43a83a401e | ||
|
|
f0e27a23a5 | ||
|
|
e68650237d | ||
|
|
1faff14e73 | ||
|
|
784cf9d594 | ||
|
|
64263c5218 | ||
|
|
065c4e520d | ||
|
|
139a930407 | ||
|
|
719dc97bbd | ||
|
|
41bba5310a | ||
|
|
8071c8c8c0 | ||
|
|
b402b4e7f6 | ||
|
|
93df366b2c | ||
|
|
cd3a15aea5 | ||
|
|
070136b3f7 | ||
|
|
08ab47c6c0 | ||
|
|
85faa9d8fa | ||
|
|
dca5b14493 | ||
|
|
4d2c8e2a44 | ||
|
|
8fa248ceb4 | ||
|
|
30862b5ffd | ||
|
|
9f57747c57 | ||
|
|
fe29a2ff6e | ||
|
|
e9a173e00c | ||
|
|
a11784fcbf | ||
|
|
fd36c8deca | ||
|
|
70638340b3 | ||
|
|
4b495f3333 | ||
|
|
934b5a64e5 | ||
|
|
cee667b491 | ||
|
|
94a64f2aea | ||
|
|
2355c2af62 | ||
|
|
5e0f8e8738 | ||
|
|
d16288a2a8 | ||
|
|
600f86dc7b | ||
|
|
7210c17c5e | ||
|
|
a16854e55a | ||
|
|
3e455a90a1 | ||
|
|
e4413542b2 | ||
|
|
8c720783f5 | ||
|
|
8734ea9dd4 | ||
|
|
c586e6d2b7 | ||
|
|
3a4eeb77fe | ||
|
|
51b3953cfc | ||
|
|
610eecc1c1 | ||
|
|
492056abf6 | ||
|
|
ee6e682ab4 | ||
|
|
6f60e102a2 | ||
|
|
eeb2af9953 | ||
|
|
550747eac6 | ||
|
|
3ffceab1fb | ||
|
|
b9f2a96595 | ||
|
|
cbaa845f5d | ||
|
|
9e2681f2d7 | ||
|
|
81fae0d1a6 | ||
|
|
38519f3b9a | ||
|
|
7f27aabbd0 | ||
|
|
e876c43ce9 | ||
|
|
8639245533 | ||
|
|
d6b86a6629 | ||
|
|
8f2b7b5b8e | ||
|
|
fc4b7cba2c | ||
|
|
08831eecf7 | ||
|
|
c6a139a6e7 | ||
|
|
02714a1291 | ||
|
|
09c9686498 | ||
|
|
b6614c6ad5 | ||
|
|
b1d4b174a6 | ||
|
|
2b23463daa | ||
|
|
9dfe81770a | ||
|
|
52c115a1f8 | ||
|
|
e20abbf9cc | ||
|
|
76671d63d4 | ||
|
|
3d1a0bf374 | ||
|
|
c20f3fbebd | ||
|
|
0d3b82477e | ||
|
|
470be03c2f | ||
|
|
c963b3c804 | ||
|
|
a4fdfb5f94 | ||
|
|
37d27c4c99 | ||
|
|
f906eb06c2 | ||
|
|
219f63ff4e | ||
|
|
1cca3e99ab | ||
|
|
55a23e5ec8 | ||
|
|
479d1fd8b0 | ||
|
|
cb70e7bb30 | ||
|
|
c200a7b7c6 | ||
|
|
6268170a10 | ||
|
|
ee0f9b03a4 | ||
|
|
f93c5f006a | ||
|
|
295fbd0542 | ||
|
|
d7310d7a1c | ||
|
|
8c50943a2e | ||
|
|
ec4cd57ccf | ||
|
|
5a085cba0f | ||
|
|
1a1d33a018 | ||
|
|
0fbcd630bc | ||
|
|
f4d731ae20 | ||
|
|
8ac53c66b4 | ||
|
|
0f50de72be | ||
|
|
df758eddd1 | ||
|
|
5f32a8ed94 | ||
|
|
535fbec675 | ||
|
|
6fe88115a3 | ||
|
|
475fa4d390 | ||
|
|
edf7e628ca | ||
|
|
ba5c0cf5d8 | ||
|
|
403e67d983 | ||
|
|
c6f1908e0f | ||
|
|
851d81d24a | ||
|
|
459c4caeba | ||
|
|
539b22ef7b | ||
|
|
872f036d64 | ||
|
|
dca96122bf | ||
|
|
e752959109 | ||
|
|
cf01664698 | ||
|
|
b283a4adcd | ||
|
|
8428bb6541 | ||
|
|
9a0330f7f8 | ||
|
|
57fc996337 | ||
|
|
1f3b860f06 | ||
|
|
abe3c02ab4 | ||
|
|
45b417b2b4 | ||
|
|
d076339e3e | ||
|
|
837836431d | ||
|
|
9f555db5cd | ||
|
|
bf7fa60dfc | ||
|
|
752b93d3b7 | ||
|
|
f23b2878cc | ||
|
|
e211c3f00a | ||
|
|
d3709a753f | ||
|
|
ab676d58ea | ||
|
|
2372c194f1 | ||
|
|
40311310d1 | ||
|
|
dde9bb5c69 | ||
|
|
266338a7c9 | ||
|
|
90156eea4c | ||
|
|
071c01c235 | ||
|
|
de06ffb0f7 | ||
|
|
8a7de35e3f | ||
|
|
121296834a | ||
|
|
bbb24d8c7e | ||
|
|
4da44e09cb | ||
|
|
ae13f0ab4d | ||
|
|
a2a35f1be6 | ||
|
|
aedfadaaf7 | ||
|
|
5c0fb0cec3 | ||
|
|
17a1cab5d2 | ||
|
|
73aed239c3 | ||
|
|
9ac66336a2 | ||
|
|
4965681e06 | ||
|
|
3868a00206 | ||
|
|
933e5144a9 | ||
|
|
73a42c85c4 | ||
|
|
39ba11054b |
@@ -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
61
.gitattributes
vendored
@@ -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
|
||||
|
||||
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,43 +0,0 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Something doesn't work correctly in Ryujinx. Game-specific issues should be posted at https://github.com/Ryujinx/Ryujinx-Games-List instead, 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. 16GiB)*
|
||||
- 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
86
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal 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
|
||||
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -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? ]
|
||||
30
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal 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
|
||||
@@ -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 ]
|
||||
26
.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml
vendored
Normal file
26
.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml
vendored
Normal 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
|
||||
35
.github/ISSUE_TEMPLATE/missing_service_call.md
vendored
35
.github/ISSUE_TEMPLATE/missing_service_call.md
vendored
@@ -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 ]
|
||||
25
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
Normal file
25
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
Normal 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
|
||||
98
.github/workflows/build.yml
vendored
98
.github/workflows/build.yml
vendored
@@ -18,10 +18,16 @@ on:
|
||||
- '*.yml'
|
||||
- 'README.md'
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }} (${{ matrix.configuration }})
|
||||
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 35
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
@@ -33,7 +39,7 @@ jobs:
|
||||
RELEASE_ZIP_OS_NAME: linux_x64
|
||||
|
||||
- os: macOS-latest
|
||||
OS_NAME: MacOS x64
|
||||
OS_NAME: macOS x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||
RELEASE_ZIP_OS_NAME: osx_x64
|
||||
|
||||
@@ -43,47 +49,107 @@ jobs:
|
||||
RELEASE_ZIP_OS_NAME: win_x64
|
||||
|
||||
fail-fast: false
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
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
|
||||
|
||||
- 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 true
|
||||
if: github.event_name == 'pull_request'
|
||||
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' && matrix.os != 'macOS-latest'
|
||||
|
||||
- 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 true
|
||||
if: github.event_name == 'pull_request'
|
||||
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' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Publish 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 Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
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' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Set executable bit
|
||||
run: |
|
||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_sdl2_headless
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
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' && matrix.os != 'macOS-latest'
|
||||
|
||||
build_macos:
|
||||
name: macOS Universal (${{ matrix.configuration }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [ Debug, Release ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Setup LLVM 14
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||
rm apple-codesign.tar.gz
|
||||
mv rcodesign $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish macOS
|
||||
run: |
|
||||
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish_ava/*.tar.gz"
|
||||
if: github.event_name == 'pull_request'
|
||||
172
.github/workflows/flatpak.yml
vendored
Normal file
172
.github/workflows/flatpak.yml
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
name: Flatpak release job
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ryujinx_version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
|
||||
concurrency: flatpak-release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
timeout-minutes: 35
|
||||
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
|
||||
3
.github/workflows/nightly_pr_comment.yml
vendored
3
.github/workflows/nightly_pr_comment.yml
vendored
@@ -7,6 +7,7 @@ jobs:
|
||||
pr_comment:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
@@ -65,4 +66,4 @@ jobs:
|
||||
} else {
|
||||
core.info(`Creating a comment`);
|
||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||
}
|
||||
}
|
||||
175
.github/workflows/release.yml
vendored
175
.github/workflows/release.yml
vendored
@@ -11,77 +11,118 @@ 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:
|
||||
tag:
|
||||
name: Create tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- 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
|
||||
})
|
||||
|
||||
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"
|
||||
name: Release ${{ matrix.OS_NAME }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 35
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
OS_NAME: Linux x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||
RELEASE_ZIP_OS_NAME: linux_x64
|
||||
|
||||
- os: windows-latest
|
||||
OS_NAME: Windows x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||
RELEASE_ZIP_OS_NAME: win_x64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
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
|
||||
|
||||
- name: Publish
|
||||
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 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 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 Ryujinx.Ava --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/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 "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_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 "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_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
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
pushd publish_windows
|
||||
pushd publish_gtk
|
||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
popd
|
||||
|
||||
pushd publish_windows_sdl2_headless
|
||||
pushd publish_sdl2_headless
|
||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
popd
|
||||
|
||||
pushd publish_windows_ava
|
||||
pushd publish_ava
|
||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- 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 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 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 Ryujinx.Ava --self-contained true
|
||||
|
||||
- name: Packing Linux builds
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd publish_linux
|
||||
pushd publish_gtk
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_linux_sdl2_headless
|
||||
pushd publish_sdl2_headless
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_linux_ava
|
||||
pushd publish_ava
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
shell: bash
|
||||
@@ -92,10 +133,78 @@ 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)."
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
removeArtifacts: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Setup LLVM 14
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||
rm apple-codesign.tar.gz
|
||||
mv rcodesign $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure for release
|
||||
run: |
|
||||
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: Publish macOS
|
||||
run: |
|
||||
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||
|
||||
- name: Pushing new release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
artifacts: "publish_ava/*.tar.gz"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
flatpak_release:
|
||||
uses: ./.github/workflows/flatpak.yml
|
||||
needs: release
|
||||
with:
|
||||
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||
secrets: inherit
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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~
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace ARMeilleure.Decoders;
|
||||
|
||||
interface IOpCode32Exception
|
||||
{
|
||||
int Id { get; }
|
||||
}
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
enum Multiplier
|
||||
{
|
||||
x1 = 0,
|
||||
x2 = 1,
|
||||
x4 = 2,
|
||||
x8 = 3
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace ARMeilleure.Memory
|
||||
{
|
||||
public interface IJitMemoryAllocator
|
||||
{
|
||||
IJitMemoryBlock Allocate(ulong size);
|
||||
IJitMemoryBlock Reserve(ulong size);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
53
Directory.Packages.props
Normal 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.1" />
|
||||
<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>
|
||||
10
README.md
10
README.md
@@ -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,7 +40,7 @@
|
||||
|
||||
## Compatibility
|
||||
|
||||
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 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
|
||||
@@ -48,6 +52,8 @@ See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ry
|
||||
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.**
|
||||
@@ -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), Vulkan, or Metal (via MoltenVK) 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**
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,176 +0,0 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Logging;
|
||||
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)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels;
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "SDL2 open audio device is not valid");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
class SDL2HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private SDL2HardwareDeviceDriver _driver;
|
||||
private ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers;
|
||||
private DynamicRingBuffer _ringBuffer;
|
||||
private ulong _playedSampleCount;
|
||||
private ManualResetEvent _updateRequiredEvent;
|
||||
private uint _outputStream;
|
||||
private SDL_AudioCallback _callbackDelegate;
|
||||
private int _bytesPerFrame;
|
||||
private uint _sampleCount;
|
||||
private bool _started;
|
||||
private float _volume;
|
||||
private ushort _nativeSampleFormat;
|
||||
|
||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||
_queuedBuffers = new ConcurrentQueue<SDL2AudioBuffer>();
|
||||
_ringBuffer = new DynamicRingBuffer();
|
||||
_callbackDelegate = Update;
|
||||
_bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount;
|
||||
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
||||
_sampleCount = uint.MaxValue;
|
||||
_started = false;
|
||||
_volume = requestedVolume;
|
||||
}
|
||||
|
||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||
{
|
||||
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
||||
bool needAudioSetup = _outputStream == 0 ||
|
||||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
||||
|
||||
if (needAudioSetup)
|
||||
{
|
||||
_sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
|
||||
|
||||
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
||||
|
||||
if (newOutputStream == 0)
|
||||
{
|
||||
// No stream in place, this is unexpected.
|
||||
throw new InvalidOperationException($"OpenStream failed with error: \"{SDL_GetError()}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_CloseAudioDevice(_outputStream);
|
||||
}
|
||||
|
||||
_outputStream = newOutputStream;
|
||||
|
||||
SDL_PauseAudioDevice(_outputStream, _started ? 0 : 1);
|
||||
|
||||
Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength)
|
||||
{
|
||||
Span<byte> streamSpan = new Span<byte>((void*)stream, streamLength);
|
||||
|
||||
int maxFrameCount = (int)GetSampleCount(streamLength);
|
||||
int bufferedFrames = _ringBuffer.Length / _bytesPerFrame;
|
||||
|
||||
int frameCount = Math.Min(bufferedFrames, maxFrameCount);
|
||||
|
||||
if (frameCount == 0)
|
||||
{
|
||||
// SDL2 left the responsibility to the user to clear the buffer.
|
||||
streamSpan.Fill(0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] samples = new byte[frameCount * _bytesPerFrame];
|
||||
|
||||
_ringBuffer.Read(samples, 0, samples.Length);
|
||||
|
||||
fixed (byte* p = samples)
|
||||
{
|
||||
IntPtr pStreamSrc = (IntPtr)p;
|
||||
|
||||
// Zero the dest buffer
|
||||
streamSpan.Fill(0);
|
||||
|
||||
// Apply volume to written data
|
||||
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
|
||||
}
|
||||
|
||||
ulong sampleCount = GetSampleCount(samples.Length);
|
||||
|
||||
ulong availaibleSampleCount = sampleCount;
|
||||
|
||||
bool needUpdate = false;
|
||||
|
||||
while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
|
||||
{
|
||||
ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
|
||||
ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
|
||||
|
||||
ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
|
||||
availaibleSampleCount -= playedAudioBufferSampleCount;
|
||||
|
||||
if (currentSamplePlayed == driverBuffer.SampleCount)
|
||||
{
|
||||
_queuedBuffers.TryDequeue(out _);
|
||||
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount);
|
||||
}
|
||||
|
||||
// Notify the output if needed.
|
||||
if (needUpdate)
|
||||
{
|
||||
_updateRequiredEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public override ulong GetPlayedSampleCount()
|
||||
{
|
||||
return Interlocked.Read(ref _playedSampleCount);
|
||||
}
|
||||
|
||||
public override float GetVolume()
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
|
||||
public override void PrepareToClose() { }
|
||||
|
||||
public override void QueueBuffer(AudioBuffer buffer)
|
||||
{
|
||||
EnsureAudioStreamSetup(buffer);
|
||||
|
||||
SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer));
|
||||
|
||||
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
||||
|
||||
_queuedBuffers.Enqueue(driverBuffer);
|
||||
}
|
||||
|
||||
public override void SetVolume(float volume)
|
||||
{
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
if (!_started)
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_PauseAudioDevice(_outputStream, 0);
|
||||
}
|
||||
|
||||
_started = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_PauseAudioDevice(_outputStream, 1);
|
||||
}
|
||||
|
||||
_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnregisterBuffer(AudioBuffer buffer) { }
|
||||
|
||||
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
|
||||
{
|
||||
if (!_queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return driverBuffer.DriverIdentifier != buffer.DataPointer;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _driver.Unregister(this))
|
||||
{
|
||||
PrepareToClose();
|
||||
Stop();
|
||||
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_CloseAudioDevice(_outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public static class MarshalEx
|
||||
{
|
||||
public static double ReadDouble(IntPtr handle, int offset = 0)
|
||||
{
|
||||
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(handle, offset));
|
||||
}
|
||||
|
||||
public static void WriteDouble(IntPtr handle, double value)
|
||||
{
|
||||
WriteDouble(handle, 0, value);
|
||||
}
|
||||
|
||||
public static void WriteDouble(IntPtr handle, int offset, double value)
|
||||
{
|
||||
Marshal.WriteInt64(handle, offset, BitConverter.DoubleToInt64Bits(value));
|
||||
}
|
||||
|
||||
public static float ReadFloat(IntPtr handle, int offset = 0)
|
||||
{
|
||||
return BitConverter.Int32BitsToSingle(Marshal.ReadInt32(handle, offset));
|
||||
}
|
||||
|
||||
public static void WriteFloat(IntPtr handle, float value)
|
||||
{
|
||||
WriteFloat(handle, 0, value);
|
||||
}
|
||||
|
||||
public static void WriteFloat(IntPtr handle, int offset, float value)
|
||||
{
|
||||
Marshal.WriteInt32(handle, offset, BitConverter.SingleToInt32Bits(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user