mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2026-04-21 11:32:23 +00:00
Compare commits
554 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
c250e3392c | ||
|
|
e56b069081 | ||
|
|
204c031fef | ||
|
|
d9053bbe37 | ||
|
|
c25e8427aa | ||
|
|
21a081b185 | ||
|
|
b540ea80d1 | ||
|
|
d692a9b83e | ||
|
|
9677ddaa5d | ||
|
|
ce92e8cd04 | ||
|
|
456fc04007 | ||
|
|
458452279c | ||
|
|
817b89767a | ||
|
|
3fb583c98c | ||
|
|
d2686e0a5b | ||
|
|
4905101df1 | ||
|
|
8750b90a7f | ||
|
|
af01100050 | ||
|
|
c0821fee1f | ||
|
|
a5c2aead67 | ||
|
|
d41c95dcff | ||
|
|
fbf2b09706 | ||
|
|
1fc0f569de | ||
|
|
dff138229c | ||
|
|
472119c8da | ||
|
|
1865ea87e5 | ||
|
|
18b61aff59 | ||
|
|
cb22629ac1 | ||
|
|
6f0f99ee2b | ||
|
|
70f2da8fdf | ||
|
|
5d3ef7761b | ||
|
|
476b4683cf | ||
|
|
5fb5079730 | ||
|
|
3fbacd0f49 | ||
|
|
7aa6abc120 | ||
|
|
548bfd60a2 | ||
|
|
65778a6b78 | ||
|
|
f4e879a1e6 | ||
|
|
a1ddaa2736 | ||
|
|
008286b79f | ||
|
|
a0c77f8d11 | ||
|
|
ece36b274d | ||
|
|
f3cc2e5703 | ||
|
|
5a39d3c4a1 | ||
|
|
cc51a03af9 | ||
|
|
567c64e149 | ||
|
|
36f00985d3 | ||
|
|
748d87adcc | ||
|
|
0fd47ff490 | ||
|
|
f088c3d344 | ||
|
|
905a191e28 | ||
|
|
ab0491817e | ||
|
|
5de6ae426e | ||
|
|
69ced3a6e8 | ||
|
|
2e43d01d36 | ||
|
|
7373ec5792 | ||
|
|
de162a648b | ||
|
|
131baebe2a | ||
|
|
187372cbde | ||
|
|
022d495335 | ||
|
|
c1372ed775 | ||
|
|
a16682cfd3 | ||
|
|
7c53b69c30 | ||
|
|
33a4d7d1ba | ||
|
|
391e08dd27 | ||
|
|
b5cf8b8af9 | ||
|
|
55043c8afc | ||
|
|
5d73a9f5fc | ||
|
|
2c9ab5e45f | ||
|
|
d536cc8ae6 | ||
|
|
d751da84f9 | ||
|
|
11aae9cfbc | ||
|
|
b96794e72b | ||
|
|
f1d1670b0b | ||
|
|
b8de72de8f | ||
|
|
eebc39228d | ||
|
|
9daf029f35 | ||
|
|
51a27032f0 | ||
|
|
a6a67a2b7a | ||
|
|
c6d05301aa | ||
|
|
647de4cd31 | ||
|
|
f82309fa2d | ||
|
|
7d8e198c33 | ||
|
|
3d98e1361b | ||
|
|
141cf61ff7 | ||
|
|
3fe3598d41 | ||
|
|
59cdf310bd | ||
|
|
4e34170a84 | ||
|
|
d540af5dc0 | ||
|
|
f7c7b66fc0 | ||
|
|
28ba55598d | ||
|
|
9719b6a112 | ||
|
|
f70236f947 | ||
|
|
eafadf10c7 | ||
|
|
9b06ee7736 | ||
|
|
baba2c2467 | ||
|
|
286e5d39b2 | ||
|
|
dc529c1181 | ||
|
|
c7cf1cbc35 | ||
|
|
d8e487d018 | ||
|
|
5fdc46ac7f | ||
|
|
1e5b45f580 | ||
|
|
62585755fd | ||
|
|
56621615b1 | ||
|
|
2099a3e84b | ||
|
|
7d26e4ac7b | ||
|
|
8d41402fa6 | ||
|
|
5af8ce7c38 | ||
|
|
77c4291c34 | ||
|
|
6e92b7a378 | ||
|
|
9b852c7481 | ||
|
|
c40c3905e2 | ||
|
|
a6cd044f0f | ||
|
|
f5a1de6ac5 | ||
|
|
2aeb5b00e3 | ||
|
|
60ba7b71f2 | ||
|
|
7c1d2bbb98 | ||
|
|
beacf8c1c8 | ||
|
|
0dbe45ae37 | ||
|
|
2b50e52e48 | ||
|
|
49eadbc209 | ||
|
|
2df16ded9b | ||
|
|
e43390c723 | ||
|
|
5af1327068 | ||
|
|
88a8d1e567 | ||
|
|
bf77d1cab9 | ||
|
|
1ca0517c99 | ||
|
|
599d485bff | ||
|
|
60e16c15b6 | ||
|
|
2068445939 | ||
|
|
a4fc9f8050 | ||
|
|
5437d6cb13 | ||
|
|
7539e26144 | ||
|
|
1c3697b6a4 | ||
|
|
81f848e54f | ||
|
|
358a781639 | ||
|
|
45ce540b9b | ||
|
|
96bf7f8522 | ||
|
|
33e673ceb8 | ||
|
|
9c2500de5f | ||
|
|
dbe43c1719 | ||
|
|
f502cfaf62 | ||
|
|
1fd5cf2b4a | ||
|
|
814f75142e | ||
|
|
4c0eb91d7e | ||
|
|
da75a9a6ea | ||
|
|
41790aa743 | ||
|
|
0cb1e926b5 | ||
|
|
6f0395538b | ||
|
|
b9f1ff3c77 | ||
|
|
a77af4c5e9 | ||
|
|
fbcf802fbc | ||
|
|
c3c41fa4bb | ||
|
|
356e480bf5 | ||
|
|
8e119a1e96 | ||
|
|
e05bf90af6 | ||
|
|
66f16f4392 | ||
|
|
729ff5337c | ||
|
|
2492e7e808 | ||
|
|
36172ab43b | ||
|
|
4d69286a9c | ||
|
|
1529e6cf0d | ||
|
|
f468db7602 | ||
|
|
c5f1d1749a | ||
|
|
7dd69f2d0e | ||
|
|
c646638680 | ||
|
|
65f2a82b97 | ||
|
|
93dd6d525a | ||
|
|
96d4ad952c | ||
|
|
6a07f80b76 | ||
|
|
22214ac664 | ||
|
|
45e520a27c | ||
|
|
5b5810a46a | ||
|
|
619ac86bd0 | ||
|
|
7a1ab71c73 | ||
|
|
dc4ba3993b | ||
|
|
81f1a4dc31 | ||
|
|
c64524a240 | ||
|
|
db45688aa8 | ||
|
|
c6d82209ab | ||
|
|
ee1825219b | ||
|
|
7baa08dcb4 | ||
|
|
408bd63b08 | ||
|
|
df99257d7f | ||
|
|
f3835dc78b | ||
|
|
51bb8707ef | ||
|
|
5ff5fe47ba |
@@ -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.
|
||||
#assignees:
|
||||
---
|
||||
|
||||
## Bug Report
|
||||
|
||||
[ If any section does not apply, replace its contents with "N/A". ]</br>
|
||||
[ Lines between [ ] (square brackets) should be removed before posting. ]
|
||||
|
||||
### What's the issue you encountered?
|
||||
|
||||
[ Describe the issue in detail and what you were doing beforehand. ]</br>
|
||||
[ Did you make any changes related to Ryujinx itself? ]</br>
|
||||
[ If so, make sure to include details relating to what exactly you changed. ]
|
||||
|
||||
### How can the issue be reproduced?
|
||||
|
||||
[ Include a detailed step by step process for recreating your issue. ]
|
||||
|
||||
### Log file
|
||||
|
||||
[ Logs files can be found under ``Logs`` folder in Ryujinx program folder. ]</br>
|
||||
[ If you don't include a crash report in instances of crash related issues, we will ask you one to provide one. ]
|
||||
|
||||
### Environment?
|
||||
|
||||
- Ryujinx version: 1.0.X</br>
|
||||
[ Replace X's with the Ryujinx version at time of crash. ]
|
||||
- Game version: X.X.X</br>
|
||||
[ Replace X's with the game version at time of crash. ]
|
||||
- System Specs:
|
||||
- OS: *(e.g. Windows 10)*
|
||||
- CPU: *(e.g. i7-6700)*
|
||||
- GPU: *(e.g. NVIDIA RTX 2070)*
|
||||
- RAM: *(e.g. 16GB)*
|
||||
- Applied Mods : [ Yes (Which ones) / No ]
|
||||
|
||||
### Additional context?
|
||||
|
||||
Additional info about your environment:</br>
|
||||
[ Any other information relevant to your issue. ]
|
||||
86
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
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
|
||||
24
.github/dependabot.yml
vendored
Normal file
24
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- "infra"
|
||||
reviewers:
|
||||
- marysaka
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
|
||||
- package-ecosystem: nuget
|
||||
directory: /
|
||||
open-pull-requests-limit: 5
|
||||
schedule:
|
||||
interval: daily
|
||||
labels:
|
||||
- "infra"
|
||||
reviewers:
|
||||
- marysaka
|
||||
commit-message:
|
||||
prefix: nuget
|
||||
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -48,45 +48,42 @@ jobs:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Ensure NuGet Source
|
||||
uses: fabriciomurta/ensure-nuget-source@v1
|
||||
dotnet-version: 7.0.x
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "::set-output name=result::$(git rev-parse --short "${{ github.sha }}")"
|
||||
- name: Clear
|
||||
run: dotnet clean && dotnet nuget locals all --clear
|
||||
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
|
||||
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 -c "${{ matrix.configuration }}"
|
||||
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||
- name: Publish Ryujinx
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Publish Ryujinx.Headless.SDL2
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Publish Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
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'
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
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'
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
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'
|
||||
171
.github/workflows/flatpak.yml
vendored
Normal file
171
.github/workflows/flatpak.yml
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
name: Flatpak release job
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ryujinx_version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
|
||||
concurrency: flatpak-release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||
GIT_COMMITTER_NAME: "RyujinxBot"
|
||||
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
|
||||
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
|
||||
NUGET_SOURCES_DESTDIR: "nuget-sources"
|
||||
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: Ryujinx
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: Ryujinx/global.json
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
working-directory: Ryujinx
|
||||
run: |
|
||||
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: flathub/org.ryujinx.Ryujinx
|
||||
token: ${{ secrets.RYUJINX_BOT_PAT }}
|
||||
submodules: recursive
|
||||
path: flathub
|
||||
|
||||
- name: Install dependencies
|
||||
run: python -m pip install PyYAML lxml
|
||||
|
||||
- name: Restore Nuget packages
|
||||
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
|
||||
|
||||
- name: Generate nuget_sources.json
|
||||
shell: python
|
||||
run: |
|
||||
from pathlib import Path
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
|
||||
sources = []
|
||||
|
||||
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = '{}.{}.nupkg'.format(name, version)
|
||||
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||
|
||||
sources.append({
|
||||
'type': 'file',
|
||||
'url': url,
|
||||
'sha512': sha512,
|
||||
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||
'dest-filename': filename,
|
||||
})
|
||||
|
||||
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
|
||||
- name: Update flatpak metadata
|
||||
id: metadata
|
||||
env:
|
||||
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
|
||||
shell: python
|
||||
run: |
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
from lxml import etree
|
||||
|
||||
|
||||
# Ensure we don't destroy multiline strings
|
||||
def str_presenter(dumper, data):
|
||||
if len(data.splitlines()) > 1:
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||
|
||||
|
||||
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
|
||||
|
||||
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
|
||||
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
|
||||
|
||||
with open(yaml_file, "r") as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
for source in data["modules"][0]["sources"]:
|
||||
if type(source) is str:
|
||||
continue
|
||||
if (
|
||||
source["type"] == "git"
|
||||
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
|
||||
):
|
||||
source["commit"] = os.environ['RYUJINX_GIT_HASH']
|
||||
|
||||
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
|
||||
|
||||
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
|
||||
if is_same_version:
|
||||
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
|
||||
else:
|
||||
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
|
||||
|
||||
if not is_same_version:
|
||||
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
|
||||
|
||||
with open(yaml_file, "w") as f:
|
||||
yaml.safe_dump(data, f, sort_keys=False)
|
||||
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(xml_file, parser)
|
||||
|
||||
root = tree.getroot()
|
||||
|
||||
releases = root.find("releases")
|
||||
|
||||
element = etree.Element("release")
|
||||
element.set("version", os.environ['RYUJINX_VERSION'])
|
||||
element.set("date", datetime.now().date().isoformat())
|
||||
releases.insert(0, element)
|
||||
|
||||
# Ensure 4 spaces
|
||||
etree.indent(root, space=" ")
|
||||
|
||||
with open(xml_file, "wb") as f:
|
||||
f.write(
|
||||
etree.tostring(
|
||||
tree,
|
||||
pretty_print=True,
|
||||
encoding="UTF-8",
|
||||
doctype='<?xml version="1.0" encoding="UTF-8"?>',
|
||||
)
|
||||
)
|
||||
|
||||
- name: Push flatpak update
|
||||
working-directory: flathub
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
|
||||
run: |
|
||||
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
|
||||
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
|
||||
git add .
|
||||
git commit -m "$COMMIT_MESSAGE"
|
||||
git push origin master
|
||||
12
.github/workflows/nightly_pr_comment.yml
vendored
12
.github/workflows/nightly_pr_comment.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v3
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const {owner, repo} = context.repo;
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
||||
|
||||
const issue_number = await (async () => {
|
||||
const pulls = await github.pulls.list({owner, repo});
|
||||
const pulls = await github.rest.pulls.list({owner, repo});
|
||||
for await (const {data} of github.paginate.iterator(pulls)) {
|
||||
for (const pull of data) {
|
||||
if (pull.head.sha === pull_head_sha) {
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
return core.error(`No matching pull request found`);
|
||||
}
|
||||
|
||||
const {data: {artifacts}} = await github.actions.listWorkflowRunArtifacts({owner, repo, run_id});
|
||||
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
|
||||
if (!artifacts.length) {
|
||||
return core.error(`No artifacts found`);
|
||||
}
|
||||
@@ -57,12 +57,12 @@ jobs:
|
||||
body += hidden_headless_artifacts;
|
||||
body += hidden_debug_artifacts;
|
||||
|
||||
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});
|
||||
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
||||
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
|
||||
if (existing_comment) {
|
||||
core.info(`Updating comment ${existing_comment.id}`);
|
||||
await github.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
|
||||
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
|
||||
} else {
|
||||
core.info(`Creating a comment`);
|
||||
await github.issues.createComment({repo, owner, issue_number, body});
|
||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||
}
|
||||
|
||||
92
.github/workflows/release.yml
vendored
92
.github/workflows/release.yml
vendored
@@ -11,49 +11,45 @@ on:
|
||||
- '*.yml'
|
||||
- 'README.md'
|
||||
|
||||
concurrency: release
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Ensure NuGet Source
|
||||
uses: fabriciomurta/ensure-nuget-source@v1
|
||||
- name: Clear
|
||||
run: dotnet clean && dotnet nuget locals all --clear
|
||||
global-json-file: global.json
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}"
|
||||
echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")"
|
||||
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
|
||||
run: |
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||
- name: Packing Windows builds
|
||||
run: |
|
||||
pushd publish_windows
|
||||
@@ -71,22 +67,34 @@ jobs:
|
||||
|
||||
- name: Publish Linux
|
||||
run: |
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||
|
||||
- name: Packing Linux builds
|
||||
run: |
|
||||
pushd publish_linux
|
||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
popd
|
||||
|
||||
pushd publish_linux_sdl2_headless
|
||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
popd
|
||||
|
||||
pushd publish_linux_ava
|
||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
@@ -96,10 +104,28 @@ jobs:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
allowUpdates: true
|
||||
removeArtifacts: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
- name: Create tag
|
||||
uses: actions/github-script@v5
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
flatpak_release:
|
||||
uses: ./.github/workflows/flatpak.yml
|
||||
needs: release
|
||||
with:
|
||||
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||
secrets: inherit
|
||||
6
.gitignore
vendored
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>net6.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,290 +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.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,80 +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, _, _) = X86Base.CpuId(0x00000007, 0x00000000);
|
||||
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
|
||||
}
|
||||
}
|
||||
|
||||
[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
|
||||
}
|
||||
|
||||
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
|
||||
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
|
||||
public static FeatureFlags7Ebx FeatureInfo7Ebx { 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 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,198 +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.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,227 +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,
|
||||
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.EventSources;
|
||||
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);
|
||||
|
||||
AddressTableEventSource.Log.Allocated(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,61 +0,0 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
static class BitUtils
|
||||
{
|
||||
private static readonly sbyte[] HbsNibbleLut;
|
||||
|
||||
static BitUtils()
|
||||
{
|
||||
HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
||||
}
|
||||
|
||||
public static long FillWithOnes(int bits)
|
||||
{
|
||||
return bits == 64 ? -1L : (1L << bits) - 1;
|
||||
}
|
||||
|
||||
public static int HighestBitSet(int value)
|
||||
{
|
||||
return 31 - BitOperations.LeadingZeroCount((uint)value);
|
||||
}
|
||||
|
||||
public static int HighestBitSetNibble(int value)
|
||||
{
|
||||
return HbsNibbleLut[value];
|
||||
}
|
||||
|
||||
public static long Replicate(long bits, int size)
|
||||
{
|
||||
long output = 0;
|
||||
|
||||
for (int bit = 0; bit < 64; bit += size)
|
||||
{
|
||||
output |= bits << bit;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static int RotateRight(int bits, int shift, int size)
|
||||
{
|
||||
return (int)RotateRight((uint)bits, shift, size);
|
||||
}
|
||||
|
||||
public static uint RotateRight(uint bits, int shift, int size)
|
||||
{
|
||||
return (bits >> shift) | (bits << (size - shift));
|
||||
}
|
||||
|
||||
public static long RotateRight(long bits, int shift, int size)
|
||||
{
|
||||
return (long)RotateRight((ulong)bits, shift, size);
|
||||
}
|
||||
|
||||
public static ulong RotateRight(ulong bits, int shift, int size)
|
||||
{
|
||||
return (bits >> shift) | (bits << (size - shift));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,384 +0,0 @@
|
||||
using ARMeilleure.Decoders.Optimizations;
|
||||
using ARMeilleure.Instructions;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
static class Decoder
|
||||
{
|
||||
// We define a limit on the number of instructions that a function may have,
|
||||
// this prevents functions being potentially too large, which would
|
||||
// take too long to compile and use too much memory.
|
||||
private const int MaxInstsPerFunction = 2500;
|
||||
|
||||
// For lower code quality translation, we set a lower limit since we're blocking execution.
|
||||
private const int MaxInstsPerFunctionLowCq = 500;
|
||||
|
||||
public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, DecoderMode dMode)
|
||||
{
|
||||
List<Block> blocks = new List<Block>();
|
||||
|
||||
Queue<Block> workQueue = new Queue<Block>();
|
||||
|
||||
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
||||
|
||||
Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction);
|
||||
|
||||
int opsCount = 0;
|
||||
|
||||
int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
|
||||
|
||||
Block GetBlock(ulong blkAddress)
|
||||
{
|
||||
if (!visited.TryGetValue(blkAddress, out Block block))
|
||||
{
|
||||
block = new Block(blkAddress);
|
||||
|
||||
if ((dMode != DecoderMode.MultipleBlocks && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress))
|
||||
{
|
||||
block.Exit = true;
|
||||
block.EndAddress = blkAddress;
|
||||
}
|
||||
|
||||
workQueue.Enqueue(block);
|
||||
|
||||
visited.Add(blkAddress, block);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
GetBlock(address);
|
||||
|
||||
while (workQueue.TryDequeue(out Block currBlock))
|
||||
{
|
||||
// Check if the current block is inside another block.
|
||||
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
if (nBlock.Address == currBlock.Address)
|
||||
{
|
||||
throw new InvalidOperationException("Found duplicate block address on the list.");
|
||||
}
|
||||
|
||||
currBlock.Exit = false;
|
||||
|
||||
nBlock.Split(currBlock);
|
||||
|
||||
blocks.Insert(nBlkIndex + 1, currBlock);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!currBlock.Exit)
|
||||
{
|
||||
// If we have a block after the current one, set the limit address.
|
||||
ulong limitAddress = ulong.MaxValue;
|
||||
|
||||
if (nBlkIndex != blocks.Count)
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
int nextIndex = nBlkIndex + 1;
|
||||
|
||||
if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
|
||||
{
|
||||
limitAddress = blocks[nextIndex].Address;
|
||||
}
|
||||
else if (nBlock.Address > currBlock.Address)
|
||||
{
|
||||
limitAddress = blocks[nBlkIndex].Address;
|
||||
}
|
||||
}
|
||||
|
||||
if (dMode == DecoderMode.SingleInstruction)
|
||||
{
|
||||
// Only read at most one instruction
|
||||
limitAddress = currBlock.Address + 1;
|
||||
}
|
||||
|
||||
FillBlock(memory, mode, currBlock, limitAddress);
|
||||
|
||||
opsCount += currBlock.OpCodes.Count;
|
||||
|
||||
if (currBlock.OpCodes.Count != 0)
|
||||
{
|
||||
// Set child blocks. "Branch" is the block the branch instruction
|
||||
// points to (when taken), "Next" is the block at the next address,
|
||||
// executed when the branch is not taken. For Unconditional Branches
|
||||
// (except BL/BLR that are sub calls) or end of executable, Next is null.
|
||||
OpCode lastOp = currBlock.GetLastOp();
|
||||
|
||||
bool isCall = IsCall(lastOp);
|
||||
|
||||
if (lastOp is IOpCodeBImm op && !isCall)
|
||||
{
|
||||
currBlock.Branch = GetBlock((ulong)op.Immediate);
|
||||
}
|
||||
|
||||
if (isCall || !(IsUnconditionalBranch(lastOp) || IsTrap(lastOp)))
|
||||
{
|
||||
currBlock.Next = GetBlock(currBlock.EndAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the new block on the list (sorted by address).
|
||||
if (blocks.Count != 0)
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks.Add(currBlock);
|
||||
}
|
||||
}
|
||||
|
||||
if (blocks.Count == 1 && blocks[0].OpCodes.Count == 0)
|
||||
{
|
||||
Debug.Assert(blocks[0].Exit);
|
||||
Debug.Assert(blocks[0].Address == blocks[0].EndAddress);
|
||||
|
||||
throw new InvalidOperationException($"Decoded a single empty exit block. Entry point = 0x{address:X}.");
|
||||
}
|
||||
|
||||
if (dMode == DecoderMode.MultipleBlocks)
|
||||
{
|
||||
return TailCallRemover.RunPass(address, blocks);
|
||||
}
|
||||
else
|
||||
{
|
||||
return blocks.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool BinarySearch(List<Block> blocks, ulong address, out int index)
|
||||
{
|
||||
index = 0;
|
||||
|
||||
int left = 0;
|
||||
int right = blocks.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
int middle = left + (size >> 1);
|
||||
|
||||
Block block = blocks[middle];
|
||||
|
||||
index = middle;
|
||||
|
||||
if (address >= block.Address && address < block.EndAddress)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (address < block.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void FillBlock(
|
||||
IMemoryManager memory,
|
||||
ExecutionMode mode,
|
||||
Block block,
|
||||
ulong limitAddress)
|
||||
{
|
||||
ulong address = block.Address;
|
||||
int itBlockSize = 0;
|
||||
|
||||
OpCode opCode;
|
||||
|
||||
do
|
||||
{
|
||||
if (address >= limitAddress && itBlockSize == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
opCode = DecodeOpCode(memory, address, mode);
|
||||
|
||||
block.OpCodes.Add(opCode);
|
||||
|
||||
address += (ulong)opCode.OpCodeSizeInBytes;
|
||||
|
||||
if (opCode is OpCodeT16IfThen it)
|
||||
{
|
||||
itBlockSize = it.IfThenBlockSize;
|
||||
}
|
||||
else if (itBlockSize > 0)
|
||||
{
|
||||
itBlockSize--;
|
||||
}
|
||||
}
|
||||
while (!(IsBranch(opCode) || IsException(opCode)));
|
||||
|
||||
block.EndAddress = address;
|
||||
}
|
||||
|
||||
private static bool IsBranch(OpCode opCode)
|
||||
{
|
||||
return opCode is OpCodeBImm ||
|
||||
opCode is OpCodeBReg || IsAarch32Branch(opCode);
|
||||
}
|
||||
|
||||
private static bool IsUnconditionalBranch(OpCode opCode)
|
||||
{
|
||||
return opCode is OpCodeBImmAl ||
|
||||
opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode);
|
||||
}
|
||||
|
||||
private static bool IsAarch32UnconditionalBranch(OpCode opCode)
|
||||
{
|
||||
if (!(opCode is OpCode32 op))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: On ARM32, most instructions have conditional execution,
|
||||
// so there's no "Always" (unconditional) branch like on ARM64.
|
||||
// We need to check if the condition is "Always" instead.
|
||||
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
|
||||
}
|
||||
|
||||
private static bool IsAarch32Branch(OpCode opCode)
|
||||
{
|
||||
// Note: On ARM32, most ALU operations can write to R15 (PC),
|
||||
// so we must consider such operations as a branch in potential aswell.
|
||||
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
if (opCode is OpCodeT32)
|
||||
{
|
||||
return opCode.Instruction.Name != InstName.Tst && opCode.Instruction.Name != InstName.Teq &&
|
||||
opCode.Instruction.Name != InstName.Cmp && opCode.Instruction.Name != InstName.Cmn;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same thing for memory operations. We have the cases where PC is a target
|
||||
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
||||
// a write back to PC (wback == true && Rn == 15), however the later may
|
||||
// be "undefined" depending on the CPU, so compilers should not produce that.
|
||||
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
||||
{
|
||||
int rt, rn;
|
||||
|
||||
bool wBack, isLoad;
|
||||
|
||||
if (opCode is IOpCode32Mem opMem)
|
||||
{
|
||||
rt = opMem.Rt;
|
||||
rn = opMem.Rn;
|
||||
wBack = opMem.WBack;
|
||||
isLoad = opMem.IsLoad;
|
||||
|
||||
// For the dual load, we also need to take into account the
|
||||
// case were Rt2 == 15 (PC).
|
||||
if (rt == 14 && opMem.Instruction.Name == InstName.Ldrd)
|
||||
{
|
||||
rt = RegisterAlias.Aarch32Pc;
|
||||
}
|
||||
}
|
||||
else if (opCode is IOpCode32MemMult opMemMult)
|
||||
{
|
||||
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
|
||||
|
||||
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
|
||||
rn = opMemMult.Rn;
|
||||
wBack = opMemMult.PostOffset != 0;
|
||||
isLoad = opMemMult.IsLoad;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
|
||||
}
|
||||
|
||||
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
|
||||
(rn == RegisterAlias.Aarch32Pc && wBack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit branch instructions.
|
||||
return opCode is IOpCode32BImm ||
|
||||
opCode is IOpCode32BReg;
|
||||
}
|
||||
|
||||
private static bool IsCall(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Bl ||
|
||||
opCode.Instruction.Name == InstName.Blr ||
|
||||
opCode.Instruction.Name == InstName.Blx;
|
||||
}
|
||||
|
||||
private static bool IsException(OpCode opCode)
|
||||
{
|
||||
return IsTrap(opCode) || opCode.Instruction.Name == InstName.Svc;
|
||||
}
|
||||
|
||||
private static bool IsTrap(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Brk ||
|
||||
opCode.Instruction.Name == InstName.Trap ||
|
||||
opCode.Instruction.Name == InstName.Und;
|
||||
}
|
||||
|
||||
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
||||
{
|
||||
int opCode = memory.Read<int>(address);
|
||||
|
||||
InstDescriptor inst;
|
||||
|
||||
OpCodeTable.MakeOp makeOp;
|
||||
|
||||
if (mode == ExecutionMode.Aarch64)
|
||||
{
|
||||
(inst, makeOp) = OpCodeTable.GetInstA64(opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == ExecutionMode.Aarch32Arm)
|
||||
{
|
||||
(inst, makeOp) = OpCodeTable.GetInstA32(opCode);
|
||||
}
|
||||
else /* if (mode == ExecutionMode.Aarch32Thumb) */
|
||||
{
|
||||
(inst, makeOp) = OpCodeTable.GetInstT32(opCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (makeOp != null)
|
||||
{
|
||||
return makeOp(inst, address, opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == ExecutionMode.Aarch32Thumb)
|
||||
{
|
||||
return new OpCodeT16(inst, address, opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OpCode(inst, address, opCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,11 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32AluBf
|
||||
{
|
||||
int Rd { get; }
|
||||
int Rn { get; }
|
||||
|
||||
int Msb { get; }
|
||||
int Lsb { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace ARMeilleure.Decoders;
|
||||
|
||||
interface IOpCode32Exception
|
||||
{
|
||||
int Id { get; }
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32Mem : IOpCode32
|
||||
{
|
||||
int Rt { get; }
|
||||
int Rn { get; }
|
||||
|
||||
bool WBack { get; }
|
||||
bool IsLoad { get; }
|
||||
bool Index { get; }
|
||||
bool Add { get; }
|
||||
|
||||
int Immediate { 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32 : OpCode
|
||||
{
|
||||
public Condition Cond { get; protected set; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32(inst, address, opCode);
|
||||
|
||||
public OpCode32(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
RegisterSize = RegisterSize.Int32;
|
||||
|
||||
Cond = (Condition)((uint)opCode >> 28);
|
||||
}
|
||||
|
||||
public bool IsThumb()
|
||||
{
|
||||
return this is OpCodeT16 || this is OpCodeT32;
|
||||
}
|
||||
|
||||
public uint GetPc()
|
||||
{
|
||||
// Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
||||
// the PC actually points 2 instructions ahead.
|
||||
if (IsThumb())
|
||||
{
|
||||
// PC is ahead by 4 in thumb mode whether or not the current instruction
|
||||
// is 16 or 32 bit.
|
||||
return (uint)Address + 4u;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (uint)Address + 8u;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluBf : OpCode32, IOpCode32AluBf
|
||||
{
|
||||
public int Rd { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public int Msb { get; }
|
||||
|
||||
public int Lsb { get; }
|
||||
|
||||
public int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb));
|
||||
public int DestMask => SourceMask & (int)(0xFFFFFFFF << Lsb);
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluBf(inst, address, opCode);
|
||||
|
||||
public OpCode32AluBf(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 0) & 0xf;
|
||||
|
||||
Msb = (opCode >> 16) & 0x1f;
|
||||
Lsb = (opCode >> 7) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluImm16 : OpCode32Alu
|
||||
{
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluImm16(inst, address, opCode);
|
||||
|
||||
public OpCode32AluImm16(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm12 = opCode & 0xfff;
|
||||
int imm4 = (opCode >> 16) & 0xf;
|
||||
|
||||
Immediate = (imm4 << 12) | imm12;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluMla : OpCode32, IOpCode32AluReg
|
||||
{
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
public int Ra { get; }
|
||||
public int Rd { get; }
|
||||
|
||||
public bool NHigh { get; }
|
||||
public bool MHigh { get; }
|
||||
public bool R { get; }
|
||||
public bool? SetFlags { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluMla(inst, address, opCode);
|
||||
|
||||
public OpCode32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rn = (opCode >> 0) & 0xf;
|
||||
Rm = (opCode >> 8) & 0xf;
|
||||
Ra = (opCode >> 12) & 0xf;
|
||||
Rd = (opCode >> 16) & 0xf;
|
||||
R = (opCode & (1 << 5)) != 0;
|
||||
|
||||
NHigh = ((opCode >> 5) & 0x1) == 1;
|
||||
MHigh = ((opCode >> 6) & 0x1) == 1;
|
||||
SetFlags = ((opCode >> 20) & 1) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluUmull : OpCode32, IOpCode32HasSetFlags
|
||||
{
|
||||
public int RdLo { get; }
|
||||
public int RdHi { get; }
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
|
||||
public bool NHigh { get; }
|
||||
public bool MHigh { get; }
|
||||
|
||||
public bool? SetFlags { get; }
|
||||
public DataOp DataOp { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32AluUmull(inst, address, opCode);
|
||||
|
||||
public OpCode32AluUmull(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
RdLo = (opCode >> 12) & 0xf;
|
||||
RdHi = (opCode >> 16) & 0xf;
|
||||
Rm = (opCode >> 8) & 0xf;
|
||||
Rn = (opCode >> 0) & 0xf;
|
||||
|
||||
NHigh = ((opCode >> 5) & 0x1) == 1;
|
||||
MHigh = ((opCode >> 6) & 0x1) == 1;
|
||||
|
||||
SetFlags = ((opCode >> 20) & 0x1) != 0;
|
||||
DataOp = DataOp.Arithmetic;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32Simd : OpCode32SimdBase
|
||||
{
|
||||
public int Opc { get; protected set; }
|
||||
public bool Q { get; protected set; }
|
||||
public bool F { get; protected set; }
|
||||
public bool U { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32Simd(inst, address, opCode);
|
||||
|
||||
public OpCode32Simd(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = (opCode >> 20) & 0x3;
|
||||
Q = ((opCode >> 6) & 0x1) != 0;
|
||||
F = ((opCode >> 10) & 0x1) != 0;
|
||||
U = ((opCode >> 24) & 0x1) != 0;
|
||||
Opc = (opCode >> 7) & 0x3;
|
||||
|
||||
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
|
||||
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||
|
||||
// Subclasses have their own handling of Vx to account for before checking.
|
||||
if (GetType() == typeof(OpCode32Simd) && DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
abstract class OpCode32SimdBase : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Vd { get; protected set; }
|
||||
public int Vm { get; protected set; }
|
||||
public int Size { get; protected set; }
|
||||
|
||||
// Helpers to index doublewords within quad words. Essentially, looping over the vector starts at quadword Q and index Fx or Ix within it,
|
||||
// depending on instruction type.
|
||||
//
|
||||
// Qx: The quadword register that the target vector is contained in.
|
||||
// Ix: The starting index of the target vector within the quadword, with size treated as integer.
|
||||
// Fx: The starting index of the target vector within the quadword, with size treated as floating point. (16 or 32)
|
||||
public int Qd => GetQuadwordIndex(Vd);
|
||||
public int Id => GetQuadwordSubindex(Vd) << (3 - Size);
|
||||
public int Fd => GetQuadwordSubindex(Vd) << (1 - (Size & 1)); // When the top bit is truncated, 1 is fp16 which is an optional extension in ARMv8.2. We always assume 64.
|
||||
|
||||
public int Qm => GetQuadwordIndex(Vm);
|
||||
public int Im => GetQuadwordSubindex(Vm) << (3 - Size);
|
||||
public int Fm => GetQuadwordSubindex(Vm) << (1 - (Size & 1));
|
||||
|
||||
protected int GetQuadwordIndex(int index)
|
||||
{
|
||||
switch (RegisterSize)
|
||||
{
|
||||
case RegisterSize.Simd128:
|
||||
case RegisterSize.Simd64:
|
||||
return index >> 1;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
protected int GetQuadwordSubindex(int index)
|
||||
{
|
||||
switch (RegisterSize)
|
||||
{
|
||||
case RegisterSize.Simd128:
|
||||
return 0;
|
||||
case RegisterSize.Simd64:
|
||||
return index & 1;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public OpCode32SimdBase(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { }
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
/// <summary>
|
||||
/// A special alias that always runs in 64 bit int, to speed up binary ops a little.
|
||||
/// </summary>
|
||||
class OpCode32SimdBinary : OpCode32SimdReg
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdBinary(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdBinary(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = 3;
|
||||
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm, Vn))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdCmpZ : OpCode32Simd
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCmpZ(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdCmpZ(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = (opCode >> 18) & 0x3;
|
||||
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdCvtFI : OpCode32SimdS
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtFI(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdCvtFI(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Opc = (opCode >> 7) & 0x1;
|
||||
|
||||
bool toInteger = (Opc2 & 0b100) != 0;
|
||||
|
||||
if (toInteger)
|
||||
{
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdDupElem : OpCode32Simd
|
||||
{
|
||||
public int Index { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdDupElem(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdDupElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
var opc = (opCode >> 16) & 0xf;
|
||||
|
||||
if ((opc & 0b1) == 1)
|
||||
{
|
||||
Size = 0;
|
||||
Index = (opc >> 1) & 0x7;
|
||||
}
|
||||
else if ((opc & 0b11) == 0b10)
|
||||
{
|
||||
Size = 1;
|
||||
Index = (opc >> 2) & 0x3;
|
||||
}
|
||||
else if ((opc & 0b111) == 0b100)
|
||||
{
|
||||
Size = 2;
|
||||
Index = (opc >> 3) & 0x1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdDupGP : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Size { get; }
|
||||
public int Vd { get; }
|
||||
public int Rt { get; }
|
||||
public bool Q { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdDupGP(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdDupGP(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = 2 - (((opCode >> 21) & 0x2) | ((opCode >> 5) & 0x1)); // B:E - 0 for 32, 16 then 8.
|
||||
if (Size == -1)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
return;
|
||||
}
|
||||
Q = ((opCode >> 21) & 0x1) != 0;
|
||||
|
||||
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
|
||||
|
||||
Vd = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
|
||||
Rt = ((opCode >> 12) & 0xf);
|
||||
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdExt : OpCode32SimdReg
|
||||
{
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdExt(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdExt(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Immediate = (opCode >> 8) & 0xf;
|
||||
Size = 0;
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm, Vn) || (!Q && Immediate > 7))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdImm : OpCode32SimdBase, IOpCode32SimdImm
|
||||
{
|
||||
public bool Q { get; }
|
||||
public long Immediate { get; }
|
||||
public int Elems => GetBytesCount() >> Size;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdImm(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Vd = (opCode >> 12) & 0xf;
|
||||
Vd |= (opCode >> 18) & 0x10;
|
||||
|
||||
Q = ((opCode >> 6) & 0x1) > 0;
|
||||
|
||||
int cMode = (opCode >> 8) & 0xf;
|
||||
int op = (opCode >> 5) & 0x1;
|
||||
|
||||
long imm;
|
||||
|
||||
imm = ((uint)opCode >> 0) & 0xf;
|
||||
imm |= ((uint)opCode >> 12) & 0x70;
|
||||
imm |= ((uint)opCode >> 17) & 0x80;
|
||||
|
||||
(Immediate, Size) = OpCodeSimdHelper.GetSimdImmediateAndSize(cMode, op, imm);
|
||||
|
||||
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
|
||||
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdImm44 : OpCode32, IOpCode32SimdImm
|
||||
{
|
||||
public int Vd { get; }
|
||||
public long Immediate { get; }
|
||||
public int Size { get; }
|
||||
public int Elems { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdImm44(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdImm44(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = (opCode >> 8) & 0x3;
|
||||
|
||||
bool single = Size != 3;
|
||||
|
||||
if (single)
|
||||
{
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
}
|
||||
|
||||
long imm;
|
||||
|
||||
imm = ((uint)opCode >> 0) & 0xf;
|
||||
imm |= ((uint)opCode >> 12) & 0xf0;
|
||||
|
||||
Immediate = (Size == 3) ? (long)DecoderHelper.Imm8ToFP64Table[(int)imm] : DecoderHelper.Imm8ToFP32Table[(int)imm];
|
||||
|
||||
RegisterSize = (!single) ? RegisterSize.Int64 : RegisterSize.Int32;
|
||||
Elems = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdLong : OpCode32SimdBase
|
||||
{
|
||||
public bool U { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdLong(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdLong(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm3h = (opCode >> 19) & 0x7;
|
||||
|
||||
// The value must be a power of 2, otherwise it is the encoding of another instruction.
|
||||
switch (imm3h)
|
||||
{
|
||||
case 1: Size = 0; break;
|
||||
case 2: Size = 1; break;
|
||||
case 4: Size = 2; break;
|
||||
}
|
||||
|
||||
U = ((opCode >> 24) & 0x1) != 0;
|
||||
|
||||
RegisterSize = RegisterSize.Simd64;
|
||||
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMemImm : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Vd { get; }
|
||||
public int Rn { get; }
|
||||
public int Size { get; }
|
||||
public bool Add { get; }
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemImm(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Immediate = opCode & 0xff;
|
||||
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
Size = (opCode >> 8) & 0x3;
|
||||
|
||||
Immediate <<= (Size == 1) ? 1 : 2;
|
||||
|
||||
bool u = (opCode & (1 << 23)) != 0;
|
||||
Add = u;
|
||||
|
||||
bool single = Size != 3;
|
||||
|
||||
if (single)
|
||||
{
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMemMult : OpCode32
|
||||
{
|
||||
public int Rn { get; }
|
||||
public int Vd { get; }
|
||||
|
||||
public int RegisterRange { get; }
|
||||
public int Offset { get; }
|
||||
public int PostOffset { get; }
|
||||
public bool IsLoad { get; }
|
||||
public bool DoubleWidth { get; }
|
||||
public bool Add { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemMult(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMemMult(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
bool isLoad = (opCode & (1 << 20)) != 0;
|
||||
bool w = (opCode & (1 << 21)) != 0;
|
||||
bool u = (opCode & (1 << 23)) != 0;
|
||||
bool p = (opCode & (1 << 24)) != 0;
|
||||
|
||||
if (p == u && w)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
DoubleWidth = (opCode & (1 << 8)) != 0;
|
||||
|
||||
if (!DoubleWidth)
|
||||
{
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
}
|
||||
|
||||
Add = u;
|
||||
|
||||
RegisterRange = opCode & 0xff;
|
||||
|
||||
int regsSize = RegisterRange * 4; // Double mode is still measured in single register size.
|
||||
|
||||
if (!u)
|
||||
{
|
||||
Offset -= regsSize;
|
||||
}
|
||||
|
||||
if (w)
|
||||
{
|
||||
PostOffset = u ? regsSize : -regsSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
PostOffset = 0;
|
||||
}
|
||||
|
||||
IsLoad = isLoad;
|
||||
|
||||
int regs = DoubleWidth ? RegisterRange / 2 : RegisterRange;
|
||||
|
||||
if (RegisterRange == 0 || RegisterRange > 32 || Vd + regs > 32)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMemPair : OpCode32, IOpCode32Simd
|
||||
{
|
||||
private static int[] RegsMap =
|
||||
{
|
||||
1, 1, 4, 2,
|
||||
1, 1, 3, 1,
|
||||
1, 1, 2, 1,
|
||||
1, 1, 1, 1
|
||||
};
|
||||
|
||||
public int Vd { get; }
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
public int Align { get; }
|
||||
public bool WBack { get; }
|
||||
public bool RegisterIndex { get; }
|
||||
public int Size { get; }
|
||||
public int Elems => 8 >> Size;
|
||||
public int Regs { get; }
|
||||
public int Increment { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemPair(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Vd = (opCode >> 12) & 0xf;
|
||||
Vd |= (opCode >> 18) & 0x10;
|
||||
|
||||
Size = (opCode >> 6) & 0x3;
|
||||
|
||||
Align = (opCode >> 4) & 0x3;
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
WBack = Rm != RegisterAlias.Aarch32Pc;
|
||||
RegisterIndex = Rm != RegisterAlias.Aarch32Pc && Rm != RegisterAlias.Aarch32Sp;
|
||||
|
||||
Regs = RegsMap[(opCode >> 8) & 0xf];
|
||||
|
||||
Increment = Math.Min(Regs, ((opCode >> 8) & 0x1) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using ARMeilleure.State;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMemSingle : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Vd { get; }
|
||||
public int Rn { get; }
|
||||
public int Rm { get; }
|
||||
public int IndexAlign { get; }
|
||||
public int Index { get; }
|
||||
public bool WBack { get; }
|
||||
public bool RegisterIndex { get; }
|
||||
public int Size { get; }
|
||||
public bool Replicate { get; }
|
||||
public int Increment { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMemSingle(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMemSingle(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Vd = (opCode >> 12) & 0xf;
|
||||
Vd |= (opCode >> 18) & 0x10;
|
||||
|
||||
IndexAlign = (opCode >> 4) & 0xf;
|
||||
|
||||
Size = (opCode >> 10) & 0x3;
|
||||
Replicate = Size == 3;
|
||||
if (Replicate)
|
||||
{
|
||||
Size = (opCode >> 6) & 0x3;
|
||||
Increment = ((opCode >> 5) & 1) + 1;
|
||||
Index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Increment = (((IndexAlign >> Size) & 1) == 0) ? 1 : 2;
|
||||
Index = IndexAlign >> (1 + Size);
|
||||
}
|
||||
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
WBack = Rm != RegisterAlias.Aarch32Pc;
|
||||
RegisterIndex = Rm != RegisterAlias.Aarch32Pc && Rm != RegisterAlias.Aarch32Sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMovGp : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Size => 2;
|
||||
|
||||
public int Vn { get; }
|
||||
public int Rt { get; }
|
||||
public int Op { get; }
|
||||
|
||||
public int Opc1 { get; }
|
||||
public int Opc2 { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGp(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMovGp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
// Which one is used is instruction dependant.
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
|
||||
Opc1 = (opCode >> 21) & 0x3;
|
||||
Opc2 = (opCode >> 5) & 0x3;
|
||||
|
||||
Vn = ((opCode >> 7) & 0x1) | ((opCode >> 15) & 0x1e);
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMovGpDouble : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Size => 3;
|
||||
|
||||
public int Vm { get; }
|
||||
public int Rt { get; }
|
||||
public int Rt2 { get; }
|
||||
public int Op { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpDouble(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMovGpDouble(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
// Which one is used is instruction dependant.
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rt2 = (opCode >> 16) & 0xf;
|
||||
|
||||
bool single = (opCode & (1 << 8)) == 0;
|
||||
if (single)
|
||||
{
|
||||
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdMovGpElem : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Size { get; }
|
||||
|
||||
public int Vd { get; }
|
||||
public int Rt { get; }
|
||||
public int Op { get; }
|
||||
public bool U { get; }
|
||||
|
||||
public int Index { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdMovGpElem(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdMovGpElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Op = (opCode >> 20) & 0x1;
|
||||
U = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
|
||||
|
||||
if ((opc & 0b01000) == 0b01000)
|
||||
{
|
||||
Size = 0;
|
||||
Index = opc & 0x7;
|
||||
}
|
||||
else if ((opc & 0b01001) == 0b00001)
|
||||
{
|
||||
Size = 1;
|
||||
Index = (opc >> 1) & 0x3;
|
||||
}
|
||||
else if ((opc & 0b11011) == 0)
|
||||
{
|
||||
Size = 2;
|
||||
Index = (opc >> 2) & 0x1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
Vd = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdReg : OpCode32Simd
|
||||
{
|
||||
public int Vn { get; }
|
||||
|
||||
public int Qn => GetQuadwordIndex(Vn);
|
||||
public int In => GetQuadwordSubindex(Vn) << (3 - Size);
|
||||
public int Fn => GetQuadwordSubindex(Vn) << (1 - (Size & 1));
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdReg(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
|
||||
|
||||
// Subclasses have their own handling of Vx to account for before checking.
|
||||
if (GetType() == typeof(OpCode32SimdReg) && DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm, Vn))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdRegElem : OpCode32SimdReg
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegElem(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdRegElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Q = ((opCode >> 24) & 0x1) != 0;
|
||||
F = ((opCode >> 8) & 0x1) != 0;
|
||||
Size = (opCode >> 20) & 0x3;
|
||||
|
||||
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
|
||||
|
||||
if (Size == 1)
|
||||
{
|
||||
Vm = ((opCode >> 3) & 0x1) | ((opCode >> 4) & 0x2) | ((opCode << 2) & 0x1c);
|
||||
}
|
||||
else /* if (Size == 2) */
|
||||
{
|
||||
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||
}
|
||||
|
||||
if (GetType() == typeof(OpCode32SimdRegElem) && DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vn) || Size == 0 || (Size == 1 && F))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdRegElemLong : OpCode32SimdRegElem
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegElemLong(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdRegElemLong(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Q = false;
|
||||
F = false;
|
||||
|
||||
RegisterSize = RegisterSize.Simd64;
|
||||
|
||||
// (Vd & 1) != 0 || Size == 3 are also invalid, but they are checked on encoding.
|
||||
if (Size == 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdRegLong : OpCode32SimdReg
|
||||
{
|
||||
public bool Polynomial { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegLong(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdRegLong(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Q = false;
|
||||
RegisterSize = RegisterSize.Simd64;
|
||||
|
||||
Polynomial = ((opCode >> 9) & 0x1) != 0;
|
||||
|
||||
// Subclasses have their own handling of Vx to account for before checking.
|
||||
if (GetType() == typeof(OpCode32SimdRegLong) && DecoderHelper.VectorArgumentsInvalid(true, Vd))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdRegS : OpCode32SimdS
|
||||
{
|
||||
public int Vn { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegS(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdRegS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
bool single = Size != 3;
|
||||
if (single)
|
||||
{
|
||||
Vn = ((opCode >> 7) & 0x1) | ((opCode >> 15) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdRegWide : OpCode32SimdReg
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRegWide(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdRegWide(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Q = false;
|
||||
RegisterSize = RegisterSize.Simd64;
|
||||
|
||||
// Subclasses have their own handling of Vx to account for before checking.
|
||||
if (GetType() == typeof(OpCode32SimdRegWide) && DecoderHelper.VectorArgumentsInvalid(true, Vd, Vn))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdRev : OpCode32SimdCmpZ
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdRev(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdRev(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
if (Opc + Size >= 3)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// Currently, this instruction is treated as though it's OPCODE is the true size,
|
||||
// which lets us deal with reversing vectors on a single element basis (eg. math magic an I64 rather than insert lots of I8s).
|
||||
int tempSize = Size;
|
||||
Size = 3 - Opc; // Op 0 is 64 bit, 1 is 32 and so on.
|
||||
Opc = tempSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdS : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Vd { get; protected set; }
|
||||
public int Vm { get; protected set; }
|
||||
public int Opc { get; protected set; } // "with_zero" (Opc<1>) [Vcmp, Vcmpe].
|
||||
public int Opc2 { get; } // opc2 or RM (opc2<1:0>) [Vcvt, Vrint].
|
||||
public int Size { get; protected set; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdS(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Opc = (opCode >> 15) & 0x3;
|
||||
Opc2 = (opCode >> 16) & 0x7;
|
||||
|
||||
Size = (opCode >> 8) & 0x3;
|
||||
|
||||
bool single = Size != 3;
|
||||
|
||||
RegisterSize = single ? RegisterSize.Int32 : RegisterSize.Int64;
|
||||
|
||||
if (single)
|
||||
{
|
||||
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdSel : OpCode32SimdRegS
|
||||
{
|
||||
public OpCode32SimdSelMode Cc { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSel(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdSel(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Cc = (OpCode32SimdSelMode)((opCode >> 20) & 3);
|
||||
}
|
||||
}
|
||||
|
||||
enum OpCode32SimdSelMode : int
|
||||
{
|
||||
Eq = 0,
|
||||
Vs,
|
||||
Ge,
|
||||
Gt
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdShImm : OpCode32Simd
|
||||
{
|
||||
public int Shift { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdShImm(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdShImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm6 = (opCode >> 16) & 0x3f;
|
||||
int limm6 = ((opCode >> 1) & 0x40) | imm6;
|
||||
|
||||
if ((limm6 & 0x40) == 0b1000000)
|
||||
{
|
||||
Size = 3;
|
||||
Shift = imm6;
|
||||
}
|
||||
else if ((limm6 & 0x60) == 0b0100000)
|
||||
{
|
||||
Size = 2;
|
||||
Shift = imm6 - 32;
|
||||
}
|
||||
else if ((limm6 & 0x70) == 0b0010000)
|
||||
{
|
||||
Size = 1;
|
||||
Shift = imm6 - 16;
|
||||
}
|
||||
else if ((limm6 & 0x78) == 0b0001000)
|
||||
{
|
||||
Size = 0;
|
||||
Shift = imm6 - 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
|
||||
if (GetType() == typeof(OpCode32SimdShImm) && DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdShImmLong : OpCode32Simd
|
||||
{
|
||||
public int Shift { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdShImmLong(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdShImmLong(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Q = false;
|
||||
RegisterSize = RegisterSize.Simd64;
|
||||
|
||||
int imm6 = (opCode >> 16) & 0x3f;
|
||||
|
||||
if ((imm6 & 0x20) == 0b100000)
|
||||
{
|
||||
Size = 2;
|
||||
Shift = imm6 - 32;
|
||||
}
|
||||
else if ((imm6 & 0x30) == 0b010000)
|
||||
{
|
||||
Size = 1;
|
||||
Shift = imm6 - 16;
|
||||
}
|
||||
else if ((imm6 & 0x38) == 0b001000)
|
||||
{
|
||||
Size = 0;
|
||||
Shift = imm6 - 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
|
||||
if (GetType() == typeof(OpCode32SimdShImmLong) && DecoderHelper.VectorArgumentsInvalid(true, Vd))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdShImmNarrow : OpCode32SimdShImm
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdShImmNarrow(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdShImmNarrow(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdSpecial : OpCode32
|
||||
{
|
||||
public int Rt { get; }
|
||||
public int Sreg { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSpecial(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdSpecial(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Sreg = (opCode >> 16) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdSqrte : OpCode32Simd
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdSqrte(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdSqrte(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = (opCode >> 18) & 0x1;
|
||||
F = ((opCode >> 8) & 0x1) != 0;
|
||||
|
||||
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdTbl : OpCode32SimdReg
|
||||
{
|
||||
public int Length { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdTbl(inst, address, opCode);
|
||||
|
||||
public OpCode32SimdTbl(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Length = (opCode >> 8) & 3;
|
||||
Size = 0;
|
||||
Opc = Q ? 1 : 0;
|
||||
Q = false;
|
||||
RegisterSize = RegisterSize.Simd64;
|
||||
|
||||
if (Vn + Length + 1 > 32)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT16 : OpCode32
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16(inst, address, opCode);
|
||||
|
||||
public OpCodeT16(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Cond = Condition.Al;
|
||||
|
||||
OpCodeSizeInBytes = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT16Adr : OpCodeT16, IOpCode32Adr
|
||||
{
|
||||
public int Rd { get; }
|
||||
|
||||
public bool Add => true;
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16Adr(inst, address, opCode);
|
||||
|
||||
public OpCodeT16Adr(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 8) & 7;
|
||||
|
||||
int imm = (opCode & 0xff) << 2;
|
||||
Immediate = (int)(GetPc() & 0xfffffffc) + imm;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32 : OpCode32
|
||||
{
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32(inst, address, opCode);
|
||||
|
||||
public OpCodeT32(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Cond = Condition.Al;
|
||||
|
||||
OpCodeSizeInBytes = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using ARMeilleure.Instructions;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32BImm24 : OpCodeT32, IOpCode32BImm
|
||||
{
|
||||
public long Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32BImm24(inst, address, opCode);
|
||||
|
||||
public OpCodeT32BImm24(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
uint pc = GetPc();
|
||||
|
||||
if (inst.Name == InstName.Blx)
|
||||
{
|
||||
pc &= ~3u;
|
||||
}
|
||||
|
||||
int imm11 = (opCode >> 0) & 0x7ff;
|
||||
int j2 = (opCode >> 11) & 1;
|
||||
int j1 = (opCode >> 13) & 1;
|
||||
int imm10 = (opCode >> 16) & 0x3ff;
|
||||
int s = (opCode >> 26) & 1;
|
||||
|
||||
int i1 = j1 ^ s ^ 1;
|
||||
int i2 = j2 ^ s ^ 1;
|
||||
|
||||
int imm32 = imm11 | (imm10 << 11) | (i2 << 21) | (i1 << 22) | (s << 23);
|
||||
imm32 = (imm32 << 9) >> 8;
|
||||
|
||||
Immediate = pc + imm32;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32MemImm8D : OpCodeT32, IOpCode32Mem
|
||||
{
|
||||
public int Rt { get; }
|
||||
public int Rt2 { get; }
|
||||
public int Rn { get; }
|
||||
public bool WBack { get; }
|
||||
public bool IsLoad { get; }
|
||||
public bool Index { get; }
|
||||
public bool Add { get; }
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MemImm8D(inst, address, opCode);
|
||||
|
||||
public OpCodeT32MemImm8D(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt2 = (opCode >> 8) & 0xf;
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
Index = ((opCode >> 24) & 1) != 0;
|
||||
Add = ((opCode >> 23) & 1) != 0;
|
||||
WBack = ((opCode >> 21) & 1) != 0;
|
||||
|
||||
Immediate = opCode & 0xff;
|
||||
|
||||
IsLoad = ((opCode >> 20) & 1) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32MemLdEx : OpCodeT32, IOpCode32MemEx
|
||||
{
|
||||
public int Rd => 0;
|
||||
public int Rt { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public bool WBack => false;
|
||||
public bool IsLoad => true;
|
||||
public bool Index => false;
|
||||
public bool Add => false;
|
||||
|
||||
public int Immediate => 0;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MemLdEx(inst, address, opCode);
|
||||
|
||||
public OpCodeT32MemLdEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT32MemStEx : OpCodeT32, IOpCode32MemEx
|
||||
{
|
||||
public int Rd { get; }
|
||||
public int Rt { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public bool WBack => false;
|
||||
public bool IsLoad => false;
|
||||
public bool Index => false;
|
||||
public bool Add => false;
|
||||
|
||||
public int Immediate => 0;
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32MemStEx(inst, address, opCode);
|
||||
|
||||
public OpCodeT32MemStEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 0) & 0xf;
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
||||
using System.Diagnostics.Tracing;
|
||||
|
||||
namespace ARMeilleure.Diagnostics.EventSources
|
||||
{
|
||||
[EventSource(Name = "ARMeilleure")]
|
||||
class AddressTableEventSource : EventSource
|
||||
{
|
||||
public static readonly AddressTableEventSource Log = new();
|
||||
|
||||
private ulong _size;
|
||||
private ulong _leafSize;
|
||||
private PollingCounter _sizeCounter;
|
||||
private PollingCounter _leafSizeCounter;
|
||||
|
||||
public AddressTableEventSource()
|
||||
{
|
||||
_sizeCounter = new PollingCounter("addr-tab-alloc", this, () => _size / 1024d / 1024d)
|
||||
{
|
||||
DisplayName = "AddressTable Total Bytes Allocated",
|
||||
DisplayUnits = "MB"
|
||||
};
|
||||
|
||||
_leafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _leafSize / 1024d / 1024d)
|
||||
{
|
||||
DisplayName = "AddressTable Total Leaf Bytes Allocated",
|
||||
DisplayUnits = "MB"
|
||||
};
|
||||
}
|
||||
|
||||
public void Allocated(int bytes, bool leaf)
|
||||
{
|
||||
_size += (uint)bytes;
|
||||
|
||||
if (leaf)
|
||||
{
|
||||
_leafSize += (uint)bytes;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_leafSizeCounter.Dispose();
|
||||
_leafSizeCounter = null;
|
||||
|
||||
_sizeCounter.Dispose();
|
||||
_sizeCounter = null;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,859 +0,0 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Add(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAddsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Adc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
Operand carry = GetFlag(PState.CFlag);
|
||||
|
||||
res = context.Add(res, carry);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAdcsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void And(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseAnd(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Bfc(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Bfi(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand part = context.BitwiseAnd(n, Const(op.SourceMask));
|
||||
|
||||
if (op.Lsb != 0)
|
||||
{
|
||||
part = context.ShiftLeft(part, Const(op.Lsb));
|
||||
}
|
||||
|
||||
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
|
||||
res = context.BitwiseOr(res, context.BitwiseAnd(part, Const(op.DestMask)));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Bic(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseAnd(n, context.BitwiseNot(m));
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Clz(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.CountLeadingZeros(m);
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Cmp(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, n, res);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
public static void Cmn(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAddsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
public static void Eor(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseExclusiveOr(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mov(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, m);
|
||||
}
|
||||
|
||||
EmitAluStore(context, m);
|
||||
}
|
||||
|
||||
public static void Movt(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluImm16 op = (OpCode32AluImm16)context.CurrOp;
|
||||
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand imm = Const(op.Immediate << 16); // Immeditate value as top halfword.
|
||||
Operand res = context.BitwiseAnd(d, Const(0x0000ffff));
|
||||
res = context.BitwiseOr(res, imm);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mul(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mvn(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseNot(m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Orr(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseOr(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Orn(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseOr(n, context.BitwiseNot(m));
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Pkh(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluRsImm op = (OpCode32AluRsImm)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res;
|
||||
|
||||
bool tbform = op.ShiftType == ShiftType.Asr;
|
||||
if (tbform)
|
||||
{
|
||||
res = context.BitwiseOr(context.BitwiseAnd(n, Const(0xFFFF0000)), context.BitwiseAnd(m, Const(0xFFFF)));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.BitwiseOr(context.BitwiseAnd(m, Const(0xFFFF0000)), context.BitwiseAnd(n, Const(0xFFFF)));
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rbit(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = EmitReverseBits32Op(context, m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rev(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.ByteSwap(m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rev16(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = EmitReverseBytes16_32Op(context, m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Revsh(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = EmitReverseBytes16_32Op(context, m);
|
||||
|
||||
EmitAluStore(context, context.SignExtend16(OperandType.I32, res));
|
||||
}
|
||||
|
||||
public static void Rsc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(m, n);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
res = context.Subtract(res, borrow);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSbcsCCheck(context, m, n);
|
||||
EmitSubsVCheck(context, m, n, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rsb(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(m, n);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, m, res);
|
||||
EmitSubsVCheck(context, m, n, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sbc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
res = context.Subtract(res, borrow);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSbcsCCheck(context, n, m);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sbfx(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand res = context.ShiftRightSI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Sdiv(ArmEmitterContext context)
|
||||
{
|
||||
EmitDiv(context, false);
|
||||
}
|
||||
|
||||
public static void Shadd8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHadd8(context, false);
|
||||
}
|
||||
|
||||
public static void Shsub8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHsub8(context, false);
|
||||
}
|
||||
|
||||
public static void Ssat(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
|
||||
EmitSat(context, -(1 << op.SatImm), (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Ssat16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||
|
||||
EmitSat16(context, -(1 << op.SatImm), (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Sub(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, n, res);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sxtb(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, true, 8);
|
||||
}
|
||||
|
||||
public static void Sxtb16(ArmEmitterContext context)
|
||||
{
|
||||
EmitExtend16(context, true);
|
||||
}
|
||||
|
||||
public static void Sxth(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, true, 16);
|
||||
}
|
||||
|
||||
public static void Teq(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseExclusiveOr(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
public static void Tst(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseAnd(n, m);
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
public static void Ubfx(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Udiv(ArmEmitterContext context)
|
||||
{
|
||||
EmitDiv(context, true);
|
||||
}
|
||||
|
||||
public static void Uhadd8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHadd8(context, true);
|
||||
}
|
||||
|
||||
public static void Uhsub8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHsub8(context, true);
|
||||
}
|
||||
|
||||
public static void Usat(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
|
||||
EmitSat(context, 0, op.SatImm == 32 ? (int)(~0) : (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Usat16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||
|
||||
EmitSat16(context, 0, (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Uxtb(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, false, 8);
|
||||
}
|
||||
|
||||
public static void Uxtb16(ArmEmitterContext context)
|
||||
{
|
||||
EmitExtend16(context, false);
|
||||
}
|
||||
|
||||
public static void Uxth(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, false, 16);
|
||||
}
|
||||
|
||||
private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits)
|
||||
{
|
||||
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
Operand res;
|
||||
|
||||
if (op.RotateBits == 0)
|
||||
{
|
||||
res = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand rotate = Const(op.RotateBits);
|
||||
res = context.RotateRight(m, rotate);
|
||||
}
|
||||
|
||||
switch (bits)
|
||||
{
|
||||
case 8:
|
||||
res = (signed) ? context.SignExtend8(OperandType.I32, res) : context.ZeroExtend8(OperandType.I32, res);
|
||||
break;
|
||||
case 16:
|
||||
res = (signed) ? context.SignExtend16(OperandType.I32, res) : context.ZeroExtend16(OperandType.I32, res);
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.Add)
|
||||
{
|
||||
res = context.Add(res, GetAluN(context));
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
private static void EmitExtend16(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
Operand res;
|
||||
|
||||
if (op.RotateBits == 0)
|
||||
{
|
||||
res = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand rotate = Const(op.RotateBits);
|
||||
res = context.RotateRight(m, rotate);
|
||||
}
|
||||
|
||||
Operand low16, high16;
|
||||
if (signed)
|
||||
{
|
||||
low16 = context.SignExtend8(OperandType.I32, res);
|
||||
high16 = context.SignExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
low16 = context.ZeroExtend8(OperandType.I32, res);
|
||||
high16 = context.ZeroExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
|
||||
}
|
||||
|
||||
if (op.Add)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand lowAdd, highAdd;
|
||||
if (signed)
|
||||
{
|
||||
lowAdd = context.SignExtend16(OperandType.I32, n);
|
||||
highAdd = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
lowAdd = context.ZeroExtend16(OperandType.I32, n);
|
||||
highAdd = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
|
||||
low16 = context.Add(low16, lowAdd);
|
||||
high16 = context.Add(high16, highAdd);
|
||||
}
|
||||
|
||||
res = context.BitwiseOr(
|
||||
context.ZeroExtend16(OperandType.I32, low16),
|
||||
context.ShiftLeft(context.ZeroExtend16(OperandType.I32, high16), Const(16)));
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
private static void EmitDiv(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
Operand zero = Const(m.Type, 0);
|
||||
|
||||
Operand divisorIsZero = context.ICompareEqual(m, zero);
|
||||
|
||||
Operand lblBadDiv = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBadDiv, divisorIsZero);
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// ARM64 behaviour: If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
||||
// TODO: tests to ensure A32 works the same
|
||||
|
||||
Operand intMin = Const(int.MinValue);
|
||||
Operand minus1 = Const(-1);
|
||||
|
||||
Operand nIsIntMin = context.ICompareEqual(n, intMin);
|
||||
Operand mIsMinus1 = context.ICompareEqual(m, minus1);
|
||||
|
||||
Operand lblGoodDiv = Label();
|
||||
|
||||
context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1));
|
||||
|
||||
EmitAluStore(context, intMin);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblGoodDiv);
|
||||
}
|
||||
|
||||
Operand res = unsigned
|
||||
? context.DivideUI(n, m)
|
||||
: context.Divide(n, m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBadDiv);
|
||||
|
||||
EmitAluStore(context, zero);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitHadd8(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand xor, res, carry;
|
||||
|
||||
// This relies on the equality x+y == ((x&y) << 1) + (x^y).
|
||||
// Note that x^y always contains the LSB of the result.
|
||||
// Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>1).
|
||||
// We mask by 0x7F to remove the LSB so that it doesn't leak into the field below.
|
||||
|
||||
res = context.BitwiseAnd(m, n);
|
||||
carry = context.BitwiseExclusiveOr(m, n);
|
||||
xor = context.ShiftRightUI(carry, Const(1));
|
||||
xor = context.BitwiseAnd(xor, Const(0x7F7F7F7Fu));
|
||||
res = context.Add(res, xor);
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// Propagates the sign bit from (x^y)>>1 upwards by one.
|
||||
carry = context.BitwiseAnd(carry, Const(0x80808080u));
|
||||
res = context.BitwiseExclusiveOr(res, carry);
|
||||
}
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitHsub8(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand left, right, carry, res;
|
||||
|
||||
// This relies on the equality x-y == (x^y) - (((x^y)&y) << 1).
|
||||
// Note that x^y always contains the LSB of the result.
|
||||
// Since we want to calculate (x+y)/2, we can instead calculate ((x^y)>>1) - ((x^y)&y).
|
||||
|
||||
carry = context.BitwiseExclusiveOr(m, n);
|
||||
left = context.ShiftRightUI(carry, Const(1));
|
||||
right = context.BitwiseAnd(carry, m);
|
||||
|
||||
// We must now perform a partitioned subtraction.
|
||||
// We can do this because minuend contains 7 bit fields.
|
||||
// We use the extra bit in minuend as a bit to borrow from; we set this bit.
|
||||
// We invert this bit at the end as this tells us if that bit was borrowed from.
|
||||
|
||||
res = context.BitwiseOr(left, Const(0x80808080));
|
||||
res = context.Subtract(res, right);
|
||||
res = context.BitwiseExclusiveOr(res, Const(0x80808080));
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// We then sign extend the result into this bit.
|
||||
carry = context.BitwiseAnd(carry, Const(0x80808080));
|
||||
res = context.BitwiseExclusiveOr(res, carry);
|
||||
}
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitSat(ArmEmitterContext context, int intMin, int intMax)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
int shift = DecodeImmShift(op.ShiftType, op.Imm5);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl:
|
||||
if (shift == 32)
|
||||
{
|
||||
n = Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ShiftLeft(n, Const(shift));
|
||||
}
|
||||
break;
|
||||
case ShiftType.Asr:
|
||||
if (shift == 32)
|
||||
{
|
||||
n = context.ShiftRightSI(n, Const(31));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ShiftRightSI(n, Const(shift));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Operand lblCheckLtIntMin = Label();
|
||||
Operand lblNoSat = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblCheckLtIntMin, context.ICompareGreater(n, Const(intMax)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetIntA32(context, op.Rd, Const(intMax));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblCheckLtIntMin);
|
||||
context.BranchIfFalse(lblNoSat, context.ICompareLess(n, Const(intMin)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetIntA32(context, op.Rd, Const(intMin));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
SetIntA32(context, op.Rd, n);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitSat16(ArmEmitterContext context, int intMin, int intMax)
|
||||
{
|
||||
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||
|
||||
void SetD(int part, Operand value)
|
||||
{
|
||||
if (part == 0)
|
||||
{
|
||||
SetIntA32(context, op.Rd, context.ZeroExtend16(OperandType.I32, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rd, context.BitwiseOr(GetIntA32(context, op.Rd), context.ShiftLeft(value, Const(16))));
|
||||
}
|
||||
}
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand nLow = context.SignExtend16(OperandType.I32, n);
|
||||
Operand nHigh = context.ShiftRightSI(n, Const(16));
|
||||
|
||||
for (int part = 0; part < 2; part++)
|
||||
{
|
||||
Operand nPart = part == 0 ? nLow : nHigh;
|
||||
|
||||
Operand lblCheckLtIntMin = Label();
|
||||
Operand lblNoSat = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblCheckLtIntMin, context.ICompareGreater(nPart, Const(intMax)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetD(part, Const(intMax));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblCheckLtIntMin);
|
||||
context.BranchIfFalse(lblNoSat, context.ICompareLess(nPart, Const(intMin)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetD(part, Const(intMin));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
SetD(part, nPart);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, ShouldSetFlags(context), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,613 +0,0 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitAluHelper
|
||||
{
|
||||
public static bool ShouldSetFlags(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32HasSetFlags op = (IOpCode32HasSetFlags)context.CurrOp;
|
||||
|
||||
if (op.SetFlags == null)
|
||||
{
|
||||
return !context.IsInIfThenBlock;
|
||||
}
|
||||
|
||||
return op.SetFlags.Value;
|
||||
}
|
||||
|
||||
public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d)
|
||||
{
|
||||
SetFlag(context, PState.NFlag, context.ICompareLess (d, Const(d.Type, 0)));
|
||||
SetFlag(context, PState.ZFlag, context.ICompareEqual(d, Const(d.Type, 0)));
|
||||
}
|
||||
|
||||
public static void EmitAdcsCCheck(ArmEmitterContext context, Operand n, Operand d)
|
||||
{
|
||||
// C = (Rd == Rn && CIn) || Rd < Rn
|
||||
Operand cIn = GetFlag(PState.CFlag);
|
||||
|
||||
Operand cOut = context.BitwiseAnd(context.ICompareEqual(d, n), cIn);
|
||||
|
||||
cOut = context.BitwiseOr(cOut, context.ICompareLessUI(d, n));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
public static void EmitAddsCCheck(ArmEmitterContext context, Operand n, Operand d)
|
||||
{
|
||||
// C = Rd < Rn
|
||||
SetFlag(context, PState.CFlag, context.ICompareLessUI(d, n));
|
||||
}
|
||||
|
||||
public static void EmitAddsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d)
|
||||
{
|
||||
// V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
||||
Operand vOut = context.BitwiseExclusiveOr(d, n);
|
||||
|
||||
vOut = context.BitwiseAnd(vOut, context.BitwiseNot(context.BitwiseExclusiveOr(n, m)));
|
||||
|
||||
vOut = context.ICompareLess(vOut, Const(vOut.Type, 0));
|
||||
|
||||
SetFlag(context, PState.VFlag, vOut);
|
||||
}
|
||||
|
||||
public static void EmitSbcsCCheck(ArmEmitterContext context, Operand n, Operand m)
|
||||
{
|
||||
// C = (Rn == Rm && CIn) || Rn > Rm
|
||||
Operand cIn = GetFlag(PState.CFlag);
|
||||
|
||||
Operand cOut = context.BitwiseAnd(context.ICompareEqual(n, m), cIn);
|
||||
|
||||
cOut = context.BitwiseOr(cOut, context.ICompareGreaterUI(n, m));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
public static void EmitSubsCCheck(ArmEmitterContext context, Operand n, Operand m)
|
||||
{
|
||||
// C = Rn >= Rm
|
||||
SetFlag(context, PState.CFlag, context.ICompareGreaterOrEqualUI(n, m));
|
||||
}
|
||||
|
||||
public static void EmitSubsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d)
|
||||
{
|
||||
// V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
||||
Operand vOut = context.BitwiseExclusiveOr(d, n);
|
||||
|
||||
vOut = context.BitwiseAnd(vOut, context.BitwiseExclusiveOr(n, m));
|
||||
|
||||
vOut = context.ICompareLess(vOut, Const(vOut.Type, 0));
|
||||
|
||||
SetFlag(context, PState.VFlag, vOut);
|
||||
}
|
||||
|
||||
public static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I32);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
|
||||
context.ShiftLeft(context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
|
||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
|
||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
|
||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
|
||||
}
|
||||
|
||||
public static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
|
||||
context.ShiftLeft(context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
|
||||
}
|
||||
|
||||
public static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I32);
|
||||
|
||||
Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
|
||||
|
||||
return context.ConvertI64ToI32(val);
|
||||
}
|
||||
|
||||
private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32);
|
||||
|
||||
if (((OpCode32)context.CurrOp).IsThumb())
|
||||
{
|
||||
bool isReturn = IsA32Return(context);
|
||||
if (!isReturn)
|
||||
{
|
||||
context.StoreToContext();
|
||||
}
|
||||
|
||||
InstEmitFlowHelper.EmitVirtualJump(context, value, isReturn);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32);
|
||||
|
||||
if (rd == RegisterAlias.Aarch32Pc && setFlags)
|
||||
{
|
||||
if (setFlags)
|
||||
{
|
||||
// TODO: Load SPSR etc.
|
||||
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitAluWritePc(context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, rd, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand GetAluN(ArmEmitterContext context)
|
||||
{
|
||||
if (context.CurrOp is IOpCodeAlu op)
|
||||
{
|
||||
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs)
|
||||
{
|
||||
return GetIntOrZR(context, op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetIntOrSP(context, op.Rn);
|
||||
}
|
||||
}
|
||||
else if (context.CurrOp is IOpCode32Alu op32)
|
||||
{
|
||||
return GetIntA32(context, op32.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw InvalidOpCodeType(context.CurrOp);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand GetAluM(ArmEmitterContext context, bool setCarry = true)
|
||||
{
|
||||
switch (context.CurrOp)
|
||||
{
|
||||
// ARM32.
|
||||
case IOpCode32AluImm op:
|
||||
{
|
||||
if (ShouldSetFlags(context) && op.IsRotated && setCarry)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
||||
}
|
||||
|
||||
return Const(op.Immediate);
|
||||
}
|
||||
|
||||
case OpCode32AluImm16 op: return Const(op.Immediate);
|
||||
|
||||
case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
|
||||
|
||||
case IOpCode32AluReg op: return GetIntA32(context, op.Rm);
|
||||
|
||||
// ARM64.
|
||||
case IOpCodeAluImm op:
|
||||
{
|
||||
if (op.GetOperandType() == OperandType.I32)
|
||||
{
|
||||
return Const((int)op.Immediate);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Const(op.Immediate);
|
||||
}
|
||||
}
|
||||
|
||||
case IOpCodeAluRs op:
|
||||
{
|
||||
Operand value = GetIntOrZR(context, op.Rm);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: value = context.ShiftLeft (value, Const(op.Shift)); break;
|
||||
case ShiftType.Lsr: value = context.ShiftRightUI(value, Const(op.Shift)); break;
|
||||
case ShiftType.Asr: value = context.ShiftRightSI(value, Const(op.Shift)); break;
|
||||
case ShiftType.Ror: value = context.RotateRight (value, Const(op.Shift)); break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
case IOpCodeAluRx op:
|
||||
{
|
||||
Operand value = GetExtendedM(context, op.Rm, op.IntType);
|
||||
|
||||
value = context.ShiftLeft(value, Const(op.Shift));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
default: throw InvalidOpCodeType(context.CurrOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception InvalidOpCodeType(OpCode opCode)
|
||||
{
|
||||
return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\".");
|
||||
}
|
||||
|
||||
// ARM32 helpers.
|
||||
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, IOpCode32AluRsImm op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
int shift = op.Immediate;
|
||||
|
||||
if (shift == 0)
|
||||
{
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsr: shift = 32; break;
|
||||
case ShiftType.Asr: shift = 32; break;
|
||||
case ShiftType.Ror: shift = 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shift != 0)
|
||||
{
|
||||
setCarry &= ShouldSetFlags(context);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: m = GetLslC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Lsr: m = GetLsrC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Asr: m = GetAsrC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Ror:
|
||||
if (op.Immediate != 0)
|
||||
{
|
||||
m = GetRorC(context, m, setCarry, shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = GetRrxC(context, m, setCarry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static int DecodeImmShift(ShiftType shiftType, int shift)
|
||||
{
|
||||
if (shift == 0)
|
||||
{
|
||||
switch (shiftType)
|
||||
{
|
||||
case ShiftType.Lsr: shift = 32; break;
|
||||
case ShiftType.Asr: shift = 32; break;
|
||||
case ShiftType.Ror: shift = 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
|
||||
Operand shiftIsZero = context.ICompareEqual(s, Const(0));
|
||||
|
||||
Operand zeroResult = m;
|
||||
Operand shiftResult = m;
|
||||
|
||||
setCarry &= ShouldSetFlags(context);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break;
|
||||
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
|
||||
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
|
||||
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
|
||||
}
|
||||
|
||||
public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true)
|
||||
{
|
||||
Debug.Assert(boolValue.Type == OperandType.I32);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
if (expected)
|
||||
{
|
||||
context.BranchIfFalse(endLabel, boolValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfTrue(endLabel, boolValue);
|
||||
}
|
||||
|
||||
action();
|
||||
|
||||
context.MarkLabel(endLabel);
|
||||
}
|
||||
|
||||
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||
Operand result = context.ShiftLeft(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, shiftIsZero, () =>
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}, false);
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
if ((uint)shift > 32)
|
||||
{
|
||||
return GetShiftByMoreThan32(context, setCarry);
|
||||
}
|
||||
else if (shift == 32)
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, m);
|
||||
}
|
||||
|
||||
return Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, Const(32 - shift));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
return context.ShiftLeft(m, Const(shift));
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||
Operand result = context.ShiftRightUI(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, shiftIsZero, () =>
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}, false);
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
if ((uint)shift > 32)
|
||||
{
|
||||
return GetShiftByMoreThan32(context, setCarry);
|
||||
}
|
||||
else if (shift == 32)
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}
|
||||
|
||||
return Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMShrOut(context, m, shift);
|
||||
}
|
||||
|
||||
return context.ShiftRightUI(m, Const(shift));
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetShiftByMoreThan32(ArmEmitterContext context, bool setCarry)
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, Const(0));
|
||||
}
|
||||
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
Operand l32Result;
|
||||
Operand ge32Result;
|
||||
|
||||
Operand less32 = context.ICompareLess(shift, Const(32));
|
||||
|
||||
ge32Result = context.ShiftRightSI(m, Const(31));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
|
||||
{
|
||||
SetCarryMLsb(context, ge32Result);
|
||||
}, false);
|
||||
}
|
||||
|
||||
l32Result = context.ShiftRightSI(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
});
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(less32, l32Result, ge32Result);
|
||||
}
|
||||
|
||||
public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
if ((uint)shift >= 32)
|
||||
{
|
||||
m = context.ShiftRightSI(m, Const(31));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, m);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMShrOut(context, m, shift);
|
||||
}
|
||||
|
||||
return context.ShiftRightSI(m, Const(shift));
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
shift = context.BitwiseAnd(shift, Const(0x1f));
|
||||
m = context.RotateRight(m, shift);
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, shiftIsZero, () =>
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}, false);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
shift &= 0x1f;
|
||||
|
||||
m = context.RotateRight(m, Const(shift));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
// Rotate right by 1 with carry.
|
||||
Operand cIn = context.Copy(GetFlag(PState.CFlag));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, m);
|
||||
}
|
||||
|
||||
m = context.ShiftRightUI(m, Const(1));
|
||||
|
||||
m = context.BitwiseOr(m, context.ShiftLeft(cIn, Const(31)));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private static void SetCarryMLsb(ArmEmitterContext context, Operand m)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1)));
|
||||
}
|
||||
|
||||
private static void SetCarryMMsb(ArmEmitterContext context, Operand m)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31)));
|
||||
}
|
||||
|
||||
private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
Operand cOut = context.ShiftRightUI(m, Const(shift - 1));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitFlowHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void B(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
context.Branch(context.GetLabel((ulong)op.Immediate));
|
||||
}
|
||||
|
||||
public static void Bl(ArmEmitterContext context)
|
||||
{
|
||||
Blx(context, x: false);
|
||||
}
|
||||
|
||||
public static void Blx(ArmEmitterContext context)
|
||||
{
|
||||
Blx(context, x: true);
|
||||
}
|
||||
|
||||
private static void Blx(ArmEmitterContext context, bool x)
|
||||
{
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
||||
|
||||
uint currentPc = isThumb
|
||||
? pc | 1
|
||||
: pc - 4;
|
||||
|
||||
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
||||
|
||||
// If x is true, then this is a branch with link and exchange.
|
||||
// In this case we need to swap the mode between Arm <-> Thumb.
|
||||
if (x)
|
||||
{
|
||||
SetFlag(context, PState.TFlag, Const(isThumb ? 0 : 1));
|
||||
}
|
||||
|
||||
EmitCall(context, (ulong)op.Immediate);
|
||||
}
|
||||
|
||||
public static void Blxr(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
Operand addr = context.Copy(GetIntA32(context, op.Rm));
|
||||
Operand bitOne = context.BitwiseAnd(addr, Const(1));
|
||||
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
||||
|
||||
uint currentPc = isThumb
|
||||
? (pc - 2) | 1
|
||||
: pc - 4;
|
||||
|
||||
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
||||
|
||||
SetFlag(context, PState.TFlag, bitOne);
|
||||
|
||||
EmitBxWritePc(context, addr);
|
||||
}
|
||||
|
||||
public static void Bx(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
||||
|
||||
EmitBxWritePc(context, GetIntA32(context, op.Rm), op.Rm);
|
||||
}
|
||||
|
||||
public static void Cbnz(ArmEmitterContext context) => EmitCb(context, onNotZero: true);
|
||||
public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false);
|
||||
|
||||
private static void EmitCb(ArmEmitterContext context, bool onNotZero)
|
||||
{
|
||||
OpCodeT16BImmCmp op = (OpCodeT16BImmCmp)context.CurrOp;
|
||||
|
||||
Operand value = GetIntA32(context, op.Rn);
|
||||
Operand lblTarget = context.GetLabel((ulong)op.Immediate);
|
||||
|
||||
if (onNotZero)
|
||||
{
|
||||
context.BranchIfTrue(lblTarget, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfFalse(lblTarget, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void It(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeT16IfThen op = (OpCodeT16IfThen)context.CurrOp;
|
||||
|
||||
context.SetIfThenBlockState(op.IfThenBlockConds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,265 +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.InstEmitMemoryHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
private const int ByteSizeLog2 = 0;
|
||||
private const int HWordSizeLog2 = 1;
|
||||
private const int WordSizeLog2 = 2;
|
||||
private const int DWordSizeLog2 = 3;
|
||||
|
||||
[Flags]
|
||||
enum AccessType
|
||||
{
|
||||
Store = 0,
|
||||
Signed = 1,
|
||||
Load = 2,
|
||||
Ordered = 4,
|
||||
Exclusive = 8,
|
||||
|
||||
LoadZx = Load,
|
||||
LoadSx = Load | Signed,
|
||||
}
|
||||
|
||||
public static void Ldm(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand baseAddress = context.Add(n, Const(op.Offset));
|
||||
|
||||
bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0;
|
||||
|
||||
bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc);
|
||||
|
||||
if (writeBack)
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
|
||||
}
|
||||
|
||||
int mask = op.RegisterMask;
|
||||
int offset = 0;
|
||||
|
||||
for (int register = 0; mask != 0; mask >>= 1, register++)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
Operand address = context.Add(baseAddress, Const(offset));
|
||||
|
||||
EmitLoadZx(context, address, register, WordSizeLog2);
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ldr(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrb(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrd(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrh(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrsb(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx);
|
||||
}
|
||||
|
||||
public static void Ldrsh(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx);
|
||||
}
|
||||
|
||||
public static void Stm(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
|
||||
Operand baseAddress = context.Add(n, Const(op.Offset));
|
||||
|
||||
int mask = op.RegisterMask;
|
||||
int offset = 0;
|
||||
|
||||
for (int register = 0; mask != 0; mask >>= 1, register++)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
Operand address = context.Add(baseAddress, Const(offset));
|
||||
|
||||
EmitStore(context, address, register, WordSizeLog2);
|
||||
|
||||
// Note: If Rn is also specified on the register list,
|
||||
// and Rn is the first register on this list, then the
|
||||
// value that is written to memory is the unmodified value,
|
||||
// before the write back. If it is on the list, but it's
|
||||
// not the first one, then the value written to memory
|
||||
// varies between CPUs.
|
||||
if (offset == 0 && op.PostOffset != 0)
|
||||
{
|
||||
// Emit write back after the first write.
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Str(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, WordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strb(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strd(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strh(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
private static void EmitLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
|
||||
{
|
||||
IOpCode32Mem op = (IOpCode32Mem)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32AlignedPC(context, op.Rn));
|
||||
Operand m = GetMemM(context, setCarry: false);
|
||||
|
||||
Operand temp = default;
|
||||
|
||||
if (op.Index || op.WBack)
|
||||
{
|
||||
temp = op.Add
|
||||
? context.Add (n, m)
|
||||
: context.Subtract(n, m);
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
SetIntA32(context, op.Rn, temp);
|
||||
}
|
||||
|
||||
Operand address;
|
||||
|
||||
if (op.Index)
|
||||
{
|
||||
address = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
address = n;
|
||||
}
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
void Load(int rt, int offs, int loadSize)
|
||||
{
|
||||
Operand addr = context.Add(address, Const(offs));
|
||||
|
||||
if ((accType & AccessType.Signed) != 0)
|
||||
{
|
||||
EmitLoadSx32(context, addr, rt, loadSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadZx(context, addr, rt, loadSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Load(op.Rt, 0, WordSizeLog2);
|
||||
Load(op.Rt | 1, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Load(op.Rt | 1, 0, WordSizeLog2);
|
||||
Load(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Load(op.Rt, 0, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void Store(int rt, int offs, int storeSize)
|
||||
{
|
||||
Operand addr = context.Add(address, Const(offs));
|
||||
|
||||
EmitStore(context, addr, rt, storeSize);
|
||||
}
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Store(op.Rt, 0, WordSizeLog2);
|
||||
Store(op.Rt | 1, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Store(op.Rt | 1, 0, WordSizeLog2);
|
||||
Store(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Store(op.Rt, 0, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Adr(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Adr op = (IOpCode32Adr)context.CurrOp;
|
||||
SetIntA32(context, op.Rd, Const(op.Immediate));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,173 +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.InstEmitMemoryExHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
[Flags]
|
||||
private enum AccessType
|
||||
{
|
||||
None = 0,
|
||||
Ordered = 1,
|
||||
Exclusive = 2,
|
||||
OrderedEx = Ordered | Exclusive
|
||||
}
|
||||
|
||||
public static void Clrex(ArmEmitterContext context)
|
||||
{
|
||||
EmitClearExclusive(context);
|
||||
}
|
||||
|
||||
public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
|
||||
public static void Ldar(ArmEmitterContext context) => EmitLdr(context, AccessType.Ordered);
|
||||
public static void Ldaxr(ArmEmitterContext context) => EmitLdr(context, AccessType.OrderedEx);
|
||||
public static void Ldxr(ArmEmitterContext context) => EmitLdr(context, AccessType.Exclusive);
|
||||
public static void Ldxp(ArmEmitterContext context) => EmitLdp(context, AccessType.Exclusive);
|
||||
public static void Ldaxp(ArmEmitterContext context) => EmitLdp(context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitLdr(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitLoadEx(context, accType, pair: false);
|
||||
}
|
||||
|
||||
private static void EmitLdp(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitLoadEx(context, accType, pair: true);
|
||||
}
|
||||
|
||||
private static void EmitLoadEx(ArmEmitterContext context, AccessType accType, bool pair)
|
||||
{
|
||||
OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
|
||||
|
||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
|
||||
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||
|
||||
if (pair)
|
||||
{
|
||||
// Exclusive loads should be atomic. For pairwise loads, we need to
|
||||
// read all the data at once. For a 32-bits pairwise load, we do a
|
||||
// simple 64-bits load, for a 128-bits load, we need to call a special
|
||||
// method to read 128-bits atomically.
|
||||
if (op.Size == 2)
|
||||
{
|
||||
Operand value = EmitLoadExclusive(context, address, exclusive, 3);
|
||||
|
||||
Operand valueLow = context.ConvertI64ToI32(value);
|
||||
|
||||
valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
|
||||
|
||||
Operand valueHigh = context.ShiftRightUI(value, Const(32));
|
||||
|
||||
SetIntOrZR(context, op.Rt, valueLow);
|
||||
SetIntOrZR(context, op.Rt2, valueHigh);
|
||||
}
|
||||
else if (op.Size == 3)
|
||||
{
|
||||
Operand value = EmitLoadExclusive(context, address, exclusive, 4);
|
||||
|
||||
Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
|
||||
Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
|
||||
|
||||
SetIntOrZR(context, op.Rt, valueLow);
|
||||
SetIntOrZR(context, op.Rt2, valueHigh);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid load size of {1 << op.Size} bytes.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 8, 16, 32 or 64-bits (non-pairwise) load.
|
||||
Operand value = EmitLoadExclusive(context, address, exclusive, op.Size);
|
||||
|
||||
SetIntOrZR(context, op.Rt, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Prfm(ArmEmitterContext context)
|
||||
{
|
||||
// Memory Prefetch, execute as no-op.
|
||||
}
|
||||
|
||||
public static void Stlr(ArmEmitterContext context) => EmitStr(context, AccessType.Ordered);
|
||||
public static void Stlxr(ArmEmitterContext context) => EmitStr(context, AccessType.OrderedEx);
|
||||
public static void Stxr(ArmEmitterContext context) => EmitStr(context, AccessType.Exclusive);
|
||||
public static void Stxp(ArmEmitterContext context) => EmitStp(context, AccessType.Exclusive);
|
||||
public static void Stlxp(ArmEmitterContext context) => EmitStp(context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitStr(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitStoreEx(context, accType, pair: false);
|
||||
}
|
||||
|
||||
private static void EmitStp(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitStoreEx(context, accType, pair: true);
|
||||
}
|
||||
|
||||
private static void EmitStoreEx(ArmEmitterContext context, AccessType accType, bool pair)
|
||||
{
|
||||
OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
|
||||
|
||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
|
||||
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||
|
||||
Operand t = GetIntOrZR(context, op.Rt);
|
||||
|
||||
if (pair)
|
||||
{
|
||||
Debug.Assert(op.Size == 2 || op.Size == 3, "Invalid size for pairwise store.");
|
||||
|
||||
Operand t2 = GetIntOrZR(context, op.Rt2);
|
||||
|
||||
Operand value;
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
value = context.BitwiseOr(t, context.ShiftLeft(t2, Const(32)));
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
value = context.VectorInsert(context.VectorZero(), t, 0);
|
||||
value = context.VectorInsert(value, t2, 1);
|
||||
}
|
||||
|
||||
EmitStoreExclusive(context, address, value, exclusive, op.Size + 1, op.Rs, a32: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreExclusive(context, address, t, exclusive, op.Size, op.Rs, a32: false);
|
||||
}
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBarrier(ArmEmitterContext context)
|
||||
{
|
||||
context.MemoryBarrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user