Compare commits
842 Commits
Author | SHA1 | Date | |
---|---|---|---|
c2e4c8f98e | |||
b53e7ffd46 | |||
ac66643346 | |||
21e88f17f6 | |||
5626f2ca1c | |||
402f05b8ef | |||
fb27042e01 | |||
69a9de33d3 | |||
bba51c2eeb | |||
fc26189fe1 | |||
a40c90e7dd | |||
f864a49014 | |||
ecbf303266 | |||
b3bf05356b | |||
cb4b58052f | |||
f8cdd5f484 | |||
22202be394 | |||
17ba217940 | |||
aae4595bdb | |||
880fd3cfcb | |||
f679f25e08 | |||
c2709b3bdd | |||
2b6e81deea | |||
7271f1b18e | |||
5fda543f84 | |||
95c06de4c1 | |||
49c63ea077 | |||
531da8a1c0 | |||
5cbdfbc7a4 | |||
e0544dd9c7 | |||
aa784c3e5e | |||
9205077590 | |||
0ed40c7175 | |||
40d47b7aa2 | |||
ec0bb74968 | |||
42f7f98666 | |||
95bad6995c | |||
3d42995822 | |||
9095941fd1 | |||
ba71141bdc | |||
0a0675a7f6 | |||
a7c6e6a8cf | |||
0bc8151c7e | |||
40c17673f5 | |||
a8950d6ac4 | |||
162798b026 | |||
1b28ecd63e | |||
895d9b53bc | |||
0e06aace45 | |||
adf4ebcd60 | |||
470a8031a4 | |||
5440d4ad5c | |||
dde208b480 | |||
4c3d2d5d75 | |||
fab11ba3f1 | |||
332891b5ff | |||
7df4fcada7 | |||
d6698680be | |||
e5c9838b0b | |||
f8ec878796 | |||
9ff21f9ab6 | |||
aa021085cf | |||
1f5d881860 | |||
1f664100bd | |||
1f5e1ffa80 | |||
264438ff19 | |||
3b8ac1641a | |||
4250732353 | |||
4d1579acbf | |||
6279f5e430 | |||
b7d2bff6aa | |||
7c327fecb3 | |||
cc1a933a2f | |||
dd574146fb | |||
2c94ac455e | |||
e18d258fa0 | |||
36f10df775 | |||
680e548022 | |||
21c4176157 | |||
3b4ff2d6d9 | |||
12504f280c | |||
250fc51374 | |||
206e0882c2 | |||
609abc8b9b | |||
cee7121058 | |||
cd124bda58 | |||
9f12e50a54 | |||
097562bc6c | |||
db4242c5dc | |||
4dd77316f7 | |||
3f98369a17 | |||
c26aeefe03 | |||
666e05f5cb | |||
8d9d508dc7 | |||
e27f5522e2 | |||
add2a9d151 | |||
9e50dd99d7 | |||
0dec91bb42 | |||
d9b63353b0 | |||
eabd0ec93f | |||
138d5dc64a | |||
3e68a87d63 | |||
69b6ef7a4a | |||
40e87c634e | |||
79d1c190db | |||
2bc88467eb | |||
baf8752e74 | |||
d5e4378aea | |||
6dbcdfea47 | |||
c5258cf082 | |||
5c89e22bb9 | |||
11ecff2ff0 | |||
4c3f09644a | |||
e187a8870a | |||
a64fee29dc | |||
9ef94c8292 | |||
915d6d044c | |||
a4780ab33b | |||
a947a45d81 | |||
9db73f74cf | |||
a1efd87c45 | |||
49be977588 | |||
c95be55091 | |||
63dedbda86 | |||
c532118d94 | |||
52d6f2e656 | |||
c9bc4eaf58 | |||
3249f8ff41 | |||
1b41b285ac | |||
f5a6f45b27 | |||
210557951b | |||
4c2d9ff3ff | |||
8198b99935 | |||
460f96967d | |||
7ca779a26d | |||
b5032b3c91 | |||
f0a3dff136 | |||
f659dcb9d8 | |||
a34fb0e939 | |||
21ce8a9b80 | |||
9ecbee8032 | |||
80519af67d | |||
26e30faff3 | |||
0992310b76 | |||
009c1101d2 | |||
ba95ee54ab | |||
4ce4299ca2 | |||
17620d18db | |||
9f1cf6458c | |||
67b4e63cff | |||
c05c688ee8 | |||
b2623dc27d | |||
5131b71437 | |||
7870423671 | |||
b72916fbc1 | |||
da073fce61 | |||
1fc90e57d2 | |||
eafcc314a9 | |||
6e9bd4de13 | |||
05a41b31bc | |||
eed17f963e | |||
c09c0c002d | |||
d56d335c0b | |||
23c844b2aa | |||
81691b9e37 | |||
2dc422bc14 | |||
a80fa5e33f | |||
954e995321 | |||
dad9ab6bb6 | |||
f0562b9c75 | |||
b8556530f2 | |||
4f3af839be | |||
155736c986 | |||
dba908dc78 | |||
ecee34a50c | |||
9b5a0c3889 | |||
80b4972139 | |||
5d85468302 | |||
9b1cc2cec6 | |||
e691622f0a | |||
f663a5cd38 | |||
f7c2e867f4 | |||
cedd200745 | |||
58207685c0 | |||
095ad923ad | |||
f07ae7d53f | |||
c308f09722 | |||
f1eef29409 | |||
1f8d66db7c | |||
c3a5716a95 | |||
1f1e2a7f03 | |||
e54f9dc4b4 | |||
edfd4d70c0 | |||
fc43aecbbd | |||
58d7a1fe97 | |||
7aa430f1a5 | |||
6bf460e104 | |||
efb135b74c | |||
a707842e14 | |||
a5a9b9bc8b | |||
17078ad929 | |||
32450d45de | |||
ed7a0474c6 | |||
fe9c49949a | |||
052b23c83c | |||
e4f68592c3 | |||
1dcd44b94f | |||
61b1ce252f | |||
5f38086f94 | |||
7bae440d3a | |||
f1943fd0b6 | |||
ec8d4f3af5 | |||
b3f0978869 | |||
f614d2c435 | |||
40c9416097 | |||
618c8edc79 | |||
99fc4fa61b | |||
f6d5499a16 | |||
26bf13a65d | |||
96cf242bcf | |||
59755818ef | |||
f8beeeb7d3 | |||
cb250162cb | |||
7528f94536 | |||
43081c16c4 | |||
780627e7b0 | |||
9044cb38d1 | |||
a53cfdab78 | |||
c7f9962dde | |||
296c4a3d01 | |||
e7cf4e6eaf | |||
a1a4771ac1 | |||
2fd819613f | |||
ad6ff6ce99 | |||
dc30d94852 | |||
4f293f8cbe | |||
32a1cd83fd | |||
e3d0ccf8d5 | |||
c14844d12c | |||
7fea26e97e | |||
7b7f62c776 | |||
423dbc8888 | |||
6adf15e479 | |||
2747f12591 | |||
a47824f961 | |||
8474d52778 | |||
dd7a924596 | |||
a76eaf9a9a | |||
009e6bcd1b | |||
eb2cc159fa | |||
bb89e36fd8 | |||
de3134adbe | |||
36d53819a4 | |||
ae4324032a | |||
f449895e6d | |||
410be95ab6 | |||
cff9046fc7 | |||
86fd0643c2 | |||
43a83a401e | |||
f0e27a23a5 | |||
e68650237d | |||
1faff14e73 | |||
784cf9d594 | |||
64263c5218 | |||
065c4e520d | |||
139a930407 | |||
719dc97bbd | |||
41bba5310a | |||
8071c8c8c0 | |||
b402b4e7f6 | |||
93df366b2c | |||
cd3a15aea5 | |||
070136b3f7 | |||
08ab47c6c0 | |||
85faa9d8fa | |||
dca5b14493 | |||
4d2c8e2a44 | |||
8fa248ceb4 | |||
30862b5ffd | |||
9f57747c57 | |||
fe29a2ff6e | |||
e9a173e00c | |||
a11784fcbf | |||
fd36c8deca | |||
70638340b3 | |||
4b495f3333 | |||
934b5a64e5 | |||
cee667b491 | |||
94a64f2aea | |||
2355c2af62 | |||
5e0f8e8738 | |||
d16288a2a8 | |||
600f86dc7b | |||
7210c17c5e | |||
a16854e55a | |||
3e455a90a1 | |||
e4413542b2 | |||
8c720783f5 | |||
8734ea9dd4 | |||
c586e6d2b7 | |||
3a4eeb77fe | |||
51b3953cfc | |||
610eecc1c1 | |||
492056abf6 | |||
ee6e682ab4 | |||
6f60e102a2 | |||
eeb2af9953 | |||
550747eac6 | |||
3ffceab1fb | |||
b9f2a96595 | |||
cbaa845f5d | |||
9e2681f2d7 | |||
81fae0d1a6 | |||
38519f3b9a | |||
7f27aabbd0 | |||
e876c43ce9 | |||
8639245533 | |||
d6b86a6629 | |||
8f2b7b5b8e | |||
fc4b7cba2c | |||
08831eecf7 | |||
c6a139a6e7 | |||
02714a1291 | |||
09c9686498 | |||
b6614c6ad5 | |||
b1d4b174a6 | |||
2b23463daa | |||
9dfe81770a | |||
52c115a1f8 | |||
e20abbf9cc | |||
76671d63d4 | |||
3d1a0bf374 | |||
c20f3fbebd | |||
0d3b82477e | |||
470be03c2f | |||
c963b3c804 | |||
a4fdfb5f94 | |||
37d27c4c99 | |||
f906eb06c2 | |||
219f63ff4e | |||
1cca3e99ab | |||
55a23e5ec8 | |||
479d1fd8b0 | |||
cb70e7bb30 | |||
c200a7b7c6 | |||
6268170a10 | |||
ee0f9b03a4 | |||
f93c5f006a | |||
295fbd0542 | |||
d7310d7a1c | |||
8c50943a2e | |||
ec4cd57ccf | |||
5a085cba0f | |||
1a1d33a018 | |||
0fbcd630bc | |||
f4d731ae20 | |||
8ac53c66b4 | |||
0f50de72be | |||
df758eddd1 | |||
5f32a8ed94 | |||
535fbec675 | |||
6fe88115a3 | |||
475fa4d390 | |||
edf7e628ca | |||
ba5c0cf5d8 | |||
403e67d983 | |||
c6f1908e0f | |||
851d81d24a | |||
459c4caeba | |||
539b22ef7b | |||
872f036d64 | |||
dca96122bf | |||
e752959109 | |||
cf01664698 | |||
b283a4adcd | |||
8428bb6541 | |||
9a0330f7f8 | |||
57fc996337 | |||
1f3b860f06 | |||
abe3c02ab4 | |||
45b417b2b4 | |||
d076339e3e | |||
837836431d | |||
9f555db5cd | |||
bf7fa60dfc | |||
752b93d3b7 | |||
f23b2878cc | |||
e211c3f00a | |||
d3709a753f | |||
ab676d58ea | |||
2372c194f1 | |||
40311310d1 | |||
dde9bb5c69 | |||
266338a7c9 | |||
90156eea4c | |||
071c01c235 | |||
de06ffb0f7 | |||
8a7de35e3f | |||
121296834a | |||
bbb24d8c7e | |||
4da44e09cb | |||
ae13f0ab4d | |||
a2a35f1be6 | |||
aedfadaaf7 | |||
5c0fb0cec3 | |||
17a1cab5d2 | |||
73aed239c3 | |||
9ac66336a2 | |||
4965681e06 | |||
3868a00206 | |||
933e5144a9 | |||
73a42c85c4 | |||
39ba11054b | |||
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 | |||
38275f9056 | |||
67cbdc3a6a | |||
131b43170e | |||
730d2f4b9b | |||
f6a7309b14 | |||
472a621589 | |||
311c2661b8 | |||
a92e2028cb | |||
6922862db8 | |||
6592d64751 | |||
8001c832d9 | |||
87919b193c | |||
8de033e60e | |||
90432946ac | |||
9bad71afbf | |||
923089a298 | |||
d9aa15eb24 | |||
12c89a61f9 | |||
f5235fff29 | |||
eba682b767 | |||
b994dafe7a | |||
54421760c3 | |||
88a0e720cb | |||
53cc9e0561 | |||
7defc59b9d | |||
951700fdd8 | |||
eb6430f103 | |||
80a879cb44 | |||
2197f41506 | |||
c8f9292bab | |||
0ec933a615 | |||
2135b6a51a | |||
00e35d9bf6 | |||
6dfb6ccf8c | |||
e87e8b012c | |||
e8f1ca8427 | |||
ad47bd2d4e | |||
a5ff0024fb | |||
f9661a54d2 | |||
66e7fdb871 | |||
2bb9b33da1 | |||
1080f64df9 | |||
c48a75979f | |||
842cb26ba5 | |||
e235d5e7bb | |||
ed0b10c81f | |||
f92650fcff | |||
712361f6e1 | |||
2232e4ae87 | |||
14ce9e1567 | |||
952d013c67 | |||
46c8129bf5 | |||
8cfec5de4b | |||
37b6e081da | |||
3c3bcd82fe | |||
a00c59a46c | |||
1825bd87b4 | |||
62f8ceb60b | |||
1a888ae087 | |||
84d0ca5645 | |||
31b8d413d5 | |||
6e02cac952 | |||
3a3380fa25 | |||
2d252db0a7 | |||
7f8a3541eb | |||
b34de74f81 | |||
5811d121df | |||
6eb85e846f | |||
c5bddfeab8 | |||
70ec5def9c | |||
7853faa334 | |||
b7fb474bfe | |||
2fa6413ed8 | |||
4523a73f75 | |||
f4c47f3c9a | |||
7d9a5feccb | |||
14ae4e276f | |||
3af42d6c7e | |||
bccf5e8b5a | |||
d86a116e1e | |||
4c2ab880ef | |||
bc5bb4459e | |||
55e97959b9 | |||
f7ef6364b7 | |||
b46b63e06a | |||
594246ea47 | |||
d21b403886 | |||
5afd521c5a | |||
0c66d71fe8 | |||
bdc4fa81f2 | |||
625f5fb88a | |||
2382717600 | |||
30ee70a9bc | |||
232b1012b0 | |||
e747f5cd83 | |||
8aff17a93c | |||
f2a41b7a1c | |||
c881cd2d14 | |||
68f9091870 | |||
99ffc061d3 | |||
d987cacfb7 | |||
851f56b08a | |||
b1bd6a50b5 | |||
70895bdb04 | |||
830cbf91bb | |||
9a9349f0f4 | |||
46cc7b55f0 | |||
dd8f97ab9e | |||
633c5ec330 | |||
a3e7bb8eb4 | |||
2073ba2919 | |||
d03124a992 | |||
59490d54b5 | |||
e546e5933f | |||
0c87bf9ea4 | |||
9827dc35e1 | |||
448723d3b3 | |||
89294b7772 | |||
7b9c4757dd | |||
b8fc97adf2 | |||
c1a7b5bcdb | |||
be1c375589 | |||
378d19f87a | |||
f59f65ec4f | |||
7bc4971cf9 | |||
3551c18902 | |||
deb99d2cae | |||
9ba73ffbe5 | |||
43b4b34376 | |||
92ca1cb0cb | |||
50d7ecf76d | |||
42a2a80b87 | |||
54deded929 | |||
39bdf6d41e | |||
074190e03c | |||
256514c7c9 | |||
556be08c4e | |||
1cbca5eecb | |||
95017b8c66 | |||
4a892fbdc9 | |||
9eb5b7a10d | |||
d64594ec74 | |||
6a1a03566a | |||
13f5294aa3 | |||
9444b4a647 | |||
610fc84f3e | |||
247d26b4b5 | |||
43ebd7a9bb | |||
26a881176e | |||
e44a43c7e1 | |||
3139a85a2b | |||
a4e8bea866 | |||
6a9e9b5360 | |||
952f6f8a65 | |||
d04ba51bb0 | |||
55ee261363 | |||
4e3a34412e | |||
3f4fb8f73a | |||
56c56aa34d | |||
d4b960d348 | |||
b2a225558d | |||
0ef0fc044a | |||
04bd87ed5a | |||
5158cdb308 | |||
1402d8391d | |||
e3b36db71c | |||
ba0171d054 | |||
d1146a5af2 | |||
79408b68c3 | |||
d461d4f68b | |||
b45d30acf8 | |||
df70442c46 | |||
e2ffa5a125 | |||
73feac5819 | |||
e5ad1dfa48 | |||
79becc4b78 | |||
223172ac0b | |||
8c9633d72f | |||
1f93fd52d9 | |||
aac7bbd378 | |||
bed516bfda | |||
69b05f9918 | |||
fb7c80e928 | |||
bb2f9df0a1 | |||
54bfaa125d | |||
7af9fcbc06 | |||
ee174be57c | |||
0bcbe32367 | |||
b97ff4da5e | |||
747081d2c7 | |||
497199bb50 | |||
bd9ac0fdaa | |||
ac21abbb9d | |||
a3dd04deef | |||
3705c20668 | |||
7b35ebc64a | |||
0a24aa6af2 | |||
c9c65af59e | |||
dc063eac83 | |||
ccf23fc629 | |||
f1460d5494 | |||
644b497df1 | |||
fb935fd201 | |||
f2087ca29e | |||
92d166ecb7 | |||
72e543e946 | |||
98c838b24c | |||
63c9c64196 | |||
a113ed0811 | |||
747876dc67 | |||
95cc18a7b4 | |||
c017c77365 | |||
98e05ee4b7 | |||
868919e101 | |||
9ca040c0ff | |||
7e9011673b | |||
741db8e43d | |||
3bd357045f | |||
ab5d77c0c4 | |||
7bfb5f79b8 | |||
8cc2479825 | |||
8f35345729 | |||
ce71f9144e | |||
f861f0bca2 | |||
571496d243 | |||
c3c3914ed3 | |||
6dffe0fad4 | |||
86b37d0ff7 | |||
863c581190 | |||
5c3112aaeb | |||
88d3ffb97c | |||
222b1ad7da | |||
d317cfd639 | |||
76b9041adf | |||
b944941733 | |||
0dddcd012c | |||
bd412afb9f | |||
20ce37dee6 | |||
c52158b733 | |||
fd6d3ec88f | |||
0a0a95fd81 | |||
26019c7d06 |
@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
|
|||||||
|
|
||||||
#### C# Coding Conventions ####
|
#### C# Coding Conventions ####
|
||||||
|
|
||||||
|
# Namespace preferences
|
||||||
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
|
resharper_csharp_namespace_body = block_scoped
|
||||||
|
|
||||||
# var preferences
|
# var preferences
|
||||||
csharp_style_var_elsewhere = false:silent
|
csharp_style_var_elsewhere = false:silent
|
||||||
csharp_style_var_for_built_in_types = false:silent
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
@ -89,6 +93,7 @@ csharp_style_conditional_delegate_call = true:suggestion
|
|||||||
# Modifier preferences
|
# Modifier preferences
|
||||||
csharp_prefer_static_local_function = true:suggestion
|
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_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
|
# Code-block preferences
|
||||||
csharp_prefer_braces = true:silent
|
csharp_prefer_braces = true:silent
|
||||||
|
61
.gitattributes
vendored
61
.gitattributes
vendored
@ -1,63 +1,4 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# Set default behavior to automatically normalize line endings.
|
# Set default behavior to automatically normalize line endings.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
* text=auto
|
* text=auto eol=lf
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# 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
|
|
||||||
|
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
|
125
.github/workflows/build.yml
vendored
125
.github/workflows/build.yml
vendored
@ -18,10 +18,16 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
|
env:
|
||||||
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.1.0"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.os }} (${{ matrix.configuration }})
|
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 35
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||||
@ -33,50 +39,117 @@ jobs:
|
|||||||
RELEASE_ZIP_OS_NAME: linux_x64
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
- os: macOS-latest
|
- os: macOS-latest
|
||||||
OS_NAME: MacOS x64
|
OS_NAME: macOS x64
|
||||||
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||||
RELEASE_ZIP_OS_NAME: osx_x64
|
RELEASE_ZIP_OS_NAME: osx_x64
|
||||||
|
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
OS_NAME: Windows x64
|
OS_NAME: Windows x64
|
||||||
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||||
RELEASE_ZIP_OS_NAME: win_x64
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
env:
|
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-dotnet@v1
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 6.0.x
|
global-json-file: global.json
|
||||||
- name: Ensure NuGet Source
|
|
||||||
uses: fabriciomurta/ensure-nuget-source@v1
|
|
||||||
- name: Get git short hash
|
- name: Get git short hash
|
||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "::set-output name=result::$(git rev-parse --short "${{ github.sha }}")"
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
- name: Clear
|
shell: bash
|
||||||
run: dotnet clean && dotnet nuget locals all --clear
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.1.0" /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
|
- name: Test
|
||||||
run: dotnet test -c "${{ matrix.configuration }}"
|
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||||
|
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.1.0" /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'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- name: Publish Ryujinx.Headless.SDL2
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.1.0" /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'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
- name: Publish Ryujinx.Ava
|
||||||
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||||
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
- name: Set executable bit
|
||||||
|
run: |
|
||||||
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
|
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||||
|
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||||
|
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-1.0.0+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish
|
path: publish
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ryujinx-headless-sdl2-${{ matrix.configuration }}-1.0.0+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
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
|
path: publish_sdl2_headless
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
- name: Upload Ryujinx.Ava artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
|
path: publish_ava
|
||||||
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
build_macos:
|
||||||
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 35
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
configuration: [ Debug, Release ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 14
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 14
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get git short hash
|
||||||
|
id: git_short_hash
|
||||||
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Publish macOS
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
|
||||||
|
- name: Upload Ryujinx.Ava artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||||
|
path: "publish_ava/*.tar.gz"
|
||||||
|
if: github.event_name == 'pull_request'
|
172
.github/workflows/flatpak.yml
vendored
Normal file
172
.github/workflows/flatpak.yml
vendored
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
name: Flatpak release job
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
ryujinx_version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
|
||||||
|
concurrency: flatpak-release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
timeout-minutes: 35
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||||
|
GIT_COMMITTER_NAME: "RyujinxBot"
|
||||||
|
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
|
||||||
|
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
|
||||||
|
NUGET_SOURCES_DESTDIR: "nuget-sources"
|
||||||
|
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: Ryujinx
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: Ryujinx/global.json
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
working-directory: Ryujinx
|
||||||
|
run: |
|
||||||
|
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: flathub/org.ryujinx.Ryujinx
|
||||||
|
token: ${{ secrets.RYUJINX_BOT_PAT }}
|
||||||
|
submodules: recursive
|
||||||
|
path: flathub
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: python -m pip install PyYAML lxml
|
||||||
|
|
||||||
|
- name: Restore Nuget packages
|
||||||
|
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
|
||||||
|
|
||||||
|
- name: Generate nuget_sources.json
|
||||||
|
shell: python
|
||||||
|
run: |
|
||||||
|
from pathlib import Path
|
||||||
|
import base64
|
||||||
|
import binascii
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
|
||||||
|
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||||
|
name = path.parent.parent.name
|
||||||
|
version = path.parent.name
|
||||||
|
filename = '{}.{}.nupkg'.format(name, version)
|
||||||
|
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||||
|
|
||||||
|
with path.open() as fp:
|
||||||
|
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||||
|
|
||||||
|
sources.append({
|
||||||
|
'type': 'file',
|
||||||
|
'url': url,
|
||||||
|
'sha512': sha512,
|
||||||
|
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||||
|
'dest-filename': filename,
|
||||||
|
})
|
||||||
|
|
||||||
|
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||||
|
json.dump(sources, fp, indent=4)
|
||||||
|
|
||||||
|
- name: Update flatpak metadata
|
||||||
|
id: metadata
|
||||||
|
env:
|
||||||
|
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
|
||||||
|
shell: python
|
||||||
|
run: |
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
from datetime import datetime
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure we don't destroy multiline strings
|
||||||
|
def str_presenter(dumper, data):
|
||||||
|
if len(data.splitlines()) > 1:
|
||||||
|
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||||
|
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||||
|
|
||||||
|
|
||||||
|
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
|
||||||
|
|
||||||
|
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
|
||||||
|
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
|
||||||
|
|
||||||
|
with open(yaml_file, "r") as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
for source in data["modules"][0]["sources"]:
|
||||||
|
if type(source) is str:
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
source["type"] == "git"
|
||||||
|
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
|
||||||
|
):
|
||||||
|
source["commit"] = os.environ['RYUJINX_GIT_HASH']
|
||||||
|
|
||||||
|
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
|
||||||
|
|
||||||
|
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
|
||||||
|
if is_same_version:
|
||||||
|
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
|
||||||
|
else:
|
||||||
|
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
|
||||||
|
|
||||||
|
if not is_same_version:
|
||||||
|
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
|
||||||
|
|
||||||
|
with open(yaml_file, "w") as f:
|
||||||
|
yaml.safe_dump(data, f, sort_keys=False)
|
||||||
|
|
||||||
|
parser = etree.XMLParser(remove_blank_text=True)
|
||||||
|
tree = etree.parse(xml_file, parser)
|
||||||
|
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
releases = root.find("releases")
|
||||||
|
|
||||||
|
element = etree.Element("release")
|
||||||
|
element.set("version", os.environ['RYUJINX_VERSION'])
|
||||||
|
element.set("date", datetime.now().date().isoformat())
|
||||||
|
releases.insert(0, element)
|
||||||
|
|
||||||
|
# Ensure 4 spaces
|
||||||
|
etree.indent(root, space=" ")
|
||||||
|
|
||||||
|
with open(xml_file, "wb") as f:
|
||||||
|
f.write(
|
||||||
|
etree.tostring(
|
||||||
|
tree,
|
||||||
|
pretty_print=True,
|
||||||
|
encoding="UTF-8",
|
||||||
|
doctype='<?xml version="1.0" encoding="UTF-8"?>',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
- name: Push flatpak update
|
||||||
|
working-directory: flathub
|
||||||
|
env:
|
||||||
|
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
|
||||||
|
run: |
|
||||||
|
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
|
||||||
|
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
|
||||||
|
git add .
|
||||||
|
git commit -m "$COMMIT_MESSAGE"
|
||||||
|
git push origin master
|
29
.github/workflows/nightly_pr_comment.yml
vendored
29
.github/workflows/nightly_pr_comment.yml
vendored
@ -7,8 +7,9 @@ jobs:
|
|||||||
pr_comment:
|
pr_comment:
|
||||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 35
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v3
|
- uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const {owner, repo} = context.repo;
|
const {owner, repo} = context.repo;
|
||||||
@ -16,7 +17,7 @@ jobs:
|
|||||||
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
||||||
|
|
||||||
const issue_number = await (async () => {
|
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 await (const {data} of github.paginate.iterator(pulls)) {
|
||||||
for (const pull of data) {
|
for (const pull of data) {
|
||||||
if (pull.head.sha === pull_head_sha) {
|
if (pull.head.sha === pull_head_sha) {
|
||||||
@ -31,28 +32,38 @@ jobs:
|
|||||||
return core.error(`No matching pull request found`);
|
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) {
|
if (!artifacts.length) {
|
||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
let body = `Download the artifacts for this pull request:\n`;
|
||||||
|
let hidden_avalonia_artifacts = `\n\n <details><summary>Experimental GUI (Avalonia)</summary>\n`;
|
||||||
|
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
if(art.name.includes('Debug')){
|
if(art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}else{
|
} else if(art.name.includes('ava-ryujinx')) {
|
||||||
|
hidden_avalonia_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
|
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
||||||
|
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
|
} else {
|
||||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hidden_avalonia_artifacts += `\n</details>`;
|
||||||
|
hidden_headless_artifacts += `\n</details>`;
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
hidden_debug_artifacts += `\n</details>`;
|
||||||
|
body += hidden_avalonia_artifacts;
|
||||||
|
body += hidden_headless_artifacts;
|
||||||
body += hidden_debug_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]');
|
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
|
||||||
if (existing_comment) {
|
if (existing_comment) {
|
||||||
core.info(`Updating comment ${existing_comment.id}`);
|
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 {
|
} else {
|
||||||
core.info(`Creating a comment`);
|
core.info(`Creating a comment`);
|
||||||
await github.issues.createComment({repo, owner, issue_number, body});
|
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||||
}
|
}
|
199
.github/workflows/release.yml
vendored
199
.github/workflows/release.yml
vendored
@ -11,72 +11,119 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- 'README.md'
|
- '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:
|
jobs:
|
||||||
release:
|
tag:
|
||||||
runs-on: windows-latest
|
name: Create tag
|
||||||
|
runs-on: ubuntu-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:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-dotnet@v1
|
|
||||||
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
|
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}"
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")"
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create tag
|
||||||
|
uses: actions/github-script@v5
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||||
|
sha: context.sha
|
||||||
|
})
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Release ${{ matrix.OS_NAME }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 35
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ ubuntu-latest, windows-latest ]
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
OS_NAME: Linux x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
|
- os: windows-latest
|
||||||
|
OS_NAME: Windows x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
run: |
|
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_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;' Ryujinx.Common/ReleaseInformations.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;' Ryujinx.Common/ReleaseInformations.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;' Ryujinx.Common/ReleaseInformations.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;' Ryujinx.Common/ReleaseInformations.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
|
shell: bash
|
||||||
|
|
||||||
- name: Create output dir
|
- name: Create output dir
|
||||||
run: "mkdir release_output"
|
run: "mkdir release_output"
|
||||||
- name: Publish Windows
|
|
||||||
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r win-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 "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||||
dotnet publish -c Release -r win-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 "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||||
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_windows
|
pushd publish_gtk
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_windows_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
7z a ../release_output/ryujinx-headless-sdl2-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd publish_ava
|
||||||
|
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Publish Linux
|
|
||||||
run: |
|
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
|
||||||
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
|
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_linux
|
pushd publish_gtk
|
||||||
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_linux_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
tar -czvf ../release_output/ryujinx-headless-sdl2-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||||
|
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd publish_ava
|
||||||
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||||
|
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -86,10 +133,78 @@ jobs:
|
|||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
removeArtifacts: true
|
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
macos_release:
|
||||||
|
name: Release MacOS universal
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 35
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 14
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 14
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Publish macOS
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
artifacts: "publish_ava/*.tar.gz"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
flatpak_release:
|
||||||
|
uses: ./.github/workflows/flatpak.yml
|
||||||
|
needs: release
|
||||||
|
with:
|
||||||
|
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||||
|
secrets: inherit
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -74,6 +74,9 @@ _TeamCity*
|
|||||||
# DotCover is a Code Coverage Tool
|
# DotCover is a Code Coverage Tool
|
||||||
*.dotCover
|
*.dotCover
|
||||||
|
|
||||||
|
# Rider is a Visual Studio alternative
|
||||||
|
.idea/*
|
||||||
|
|
||||||
# NCrunch
|
# NCrunch
|
||||||
*.ncrunch*
|
*.ncrunch*
|
||||||
.*crunch*.local.xml
|
.*crunch*.local.xml
|
||||||
@ -122,6 +125,9 @@ ClientBin/
|
|||||||
packages/*
|
packages/*
|
||||||
*.config
|
*.config
|
||||||
|
|
||||||
|
# Include nuget.config
|
||||||
|
!nuget.config
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
# RIA/Silverlight projects
|
||||||
Generated_Code/
|
Generated_Code/
|
||||||
|
|
||||||
@ -164,3 +170,6 @@ launchSettings.json
|
|||||||
|
|
||||||
# NetCore Publishing Profiles
|
# NetCore Publishing Profiles
|
||||||
PublishProfiles/
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,281 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.X86
|
|
||||||
{
|
|
||||||
partial class Assembler
|
|
||||||
{
|
|
||||||
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.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.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,159 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.X86
|
|
||||||
{
|
|
||||||
static class CallingConvention
|
|
||||||
{
|
|
||||||
private const int RegistersMask = 0xffff;
|
|
||||||
|
|
||||||
public static int GetIntAvailableRegisters()
|
|
||||||
{
|
|
||||||
return RegistersMask & ~(1 << (int)X86Register.Rsp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetVecAvailableRegisters()
|
|
||||||
{
|
|
||||||
return RegistersMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetIntCallerSavedRegisters()
|
|
||||||
{
|
|
||||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
|
||||||
{
|
|
||||||
return (1 << (int)X86Register.Rax) |
|
|
||||||
(1 << (int)X86Register.Rcx) |
|
|
||||||
(1 << (int)X86Register.Rdx) |
|
|
||||||
(1 << (int)X86Register.R8) |
|
|
||||||
(1 << (int)X86Register.R9) |
|
|
||||||
(1 << (int)X86Register.R10) |
|
|
||||||
(1 << (int)X86Register.R11);
|
|
||||||
}
|
|
||||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
|
||||||
{
|
|
||||||
return (1 << (int)X86Register.Rax) |
|
|
||||||
(1 << (int)X86Register.Rcx) |
|
|
||||||
(1 << (int)X86Register.Rdx) |
|
|
||||||
(1 << (int)X86Register.Rsi) |
|
|
||||||
(1 << (int)X86Register.Rdi) |
|
|
||||||
(1 << (int)X86Register.R8) |
|
|
||||||
(1 << (int)X86Register.R9) |
|
|
||||||
(1 << (int)X86Register.R10) |
|
|
||||||
(1 << (int)X86Register.R11);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetVecCallerSavedRegisters()
|
|
||||||
{
|
|
||||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
|
||||||
{
|
|
||||||
return (1 << (int)X86Register.Xmm0) |
|
|
||||||
(1 << (int)X86Register.Xmm1) |
|
|
||||||
(1 << (int)X86Register.Xmm2) |
|
|
||||||
(1 << (int)X86Register.Xmm3) |
|
|
||||||
(1 << (int)X86Register.Xmm4) |
|
|
||||||
(1 << (int)X86Register.Xmm5);
|
|
||||||
}
|
|
||||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
|
||||||
{
|
|
||||||
return RegistersMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetIntCalleeSavedRegisters()
|
|
||||||
{
|
|
||||||
return GetIntCallerSavedRegisters() ^ RegistersMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetVecCalleeSavedRegisters()
|
|
||||||
{
|
|
||||||
return GetVecCallerSavedRegisters() ^ RegistersMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetArgumentsOnRegsCount()
|
|
||||||
{
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetIntArgumentsOnRegsCount()
|
|
||||||
{
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetVecArgumentsOnRegsCount()
|
|
||||||
{
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X86Register GetIntArgumentRegister(int index)
|
|
||||||
{
|
|
||||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
|
||||||
{
|
|
||||||
switch (index)
|
|
||||||
{
|
|
||||||
case 0: return X86Register.Rcx;
|
|
||||||
case 1: return X86Register.Rdx;
|
|
||||||
case 2: return X86Register.R8;
|
|
||||||
case 3: return X86Register.R9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
|
||||||
{
|
|
||||||
switch (index)
|
|
||||||
{
|
|
||||||
case 0: return X86Register.Rdi;
|
|
||||||
case 1: return X86Register.Rsi;
|
|
||||||
case 2: return X86Register.Rdx;
|
|
||||||
case 3: return X86Register.Rcx;
|
|
||||||
case 4: return X86Register.R8;
|
|
||||||
case 5: return X86Register.R9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X86Register GetVecArgumentRegister(int index)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
|
|
||||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
|
||||||
{
|
|
||||||
count = 4;
|
|
||||||
}
|
|
||||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
|
||||||
{
|
|
||||||
count = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)index < count)
|
|
||||||
{
|
|
||||||
return X86Register.Xmm0 + index;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X86Register GetIntReturnRegister()
|
|
||||||
{
|
|
||||||
return X86Register.Rax;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X86Register GetIntReturnRegisterHigh()
|
|
||||||
{
|
|
||||||
return X86Register.Rdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X86Register GetVecReturnRegister()
|
|
||||||
{
|
|
||||||
return X86Register.Xmm0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CallConvName GetCurrentCallConv()
|
|
||||||
{
|
|
||||||
return OperatingSystem.IsWindows()
|
|
||||||
? CallConvName.Windows
|
|
||||||
: CallConvName.SystemV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.X86
|
|
||||||
{
|
|
||||||
static class HardwareCapabilities
|
|
||||||
{
|
|
||||||
static HardwareCapabilities()
|
|
||||||
{
|
|
||||||
if (!X86Base.IsSupported)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, _, int ecx, int edx) = X86Base.CpuId(0x00000001, 0x00000000);
|
|
||||||
|
|
||||||
FeatureInfoEdx = (FeatureFlagsEdx)edx;
|
|
||||||
FeatureInfoEcx = (FeatureFlagsEcx)ecx;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum FeatureFlagsEdx
|
|
||||||
{
|
|
||||||
Sse = 1 << 25,
|
|
||||||
Sse2 = 1 << 26
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum FeatureFlagsEcx
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FeatureFlagsEdx FeatureInfoEdx { get; }
|
|
||||||
public static FeatureFlagsEcx FeatureInfoEcx { get; }
|
|
||||||
|
|
||||||
public static bool SupportsSse => FeatureInfoEdx.HasFlag(FeatureFlagsEdx.Sse);
|
|
||||||
public static bool SupportsSse2 => FeatureInfoEdx.HasFlag(FeatureFlagsEdx.Sse2);
|
|
||||||
public static bool SupportsSse3 => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Sse3);
|
|
||||||
public static bool SupportsPclmulqdq => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Pclmulqdq);
|
|
||||||
public static bool SupportsSsse3 => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Ssse3);
|
|
||||||
public static bool SupportsFma => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Fma);
|
|
||||||
public static bool SupportsSse41 => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Sse41);
|
|
||||||
public static bool SupportsSse42 => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Sse42);
|
|
||||||
public static bool SupportsPopcnt => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Popcnt);
|
|
||||||
public static bool SupportsAesni => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Aes);
|
|
||||||
public static bool SupportsAvx => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.Avx);
|
|
||||||
public static bool SupportsF16c => FeatureInfoEcx.HasFlag(FeatureFlagsEcx.F16c);
|
|
||||||
|
|
||||||
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,194 +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.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.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,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,352 +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, bool singleBlock)
|
|
||||||
{
|
|
||||||
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 ((singleBlock && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!IsUnconditionalBranch(lastOp) || isCall)
|
|
||||||
{
|
|
||||||
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 (!singleBlock)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
OpCode opCode;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (address >= limitAddress)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
opCode = DecodeOpCode(memory, address, mode);
|
|
||||||
|
|
||||||
block.OpCodes.Add(opCode);
|
|
||||||
|
|
||||||
address += (ulong)opCode.OpCodeSizeInBytes;
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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 opCode.Instruction.Name == InstName.Brk ||
|
|
||||||
opCode.Instruction.Name == InstName.Svc ||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
return new OpCode(inst, address, opCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders
|
|
||||||
{
|
|
||||||
interface IOpCode32Alu : IOpCode32
|
|
||||||
{
|
|
||||||
int Rd { get; }
|
|
||||||
int Rn { get; }
|
|
||||||
|
|
||||||
bool SetFlags { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders
|
|
||||||
{
|
|
||||||
interface IOpCode32AluBf
|
|
||||||
{
|
|
||||||
int Rd { get; }
|
|
||||||
int Rn { get; }
|
|
||||||
|
|
||||||
int Msb { get; }
|
|
||||||
int Lsb { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders
|
|
||||||
{
|
|
||||||
interface IOpCode32Mem : IOpCode32
|
|
||||||
{
|
|
||||||
int Rt { get; }
|
|
||||||
int Rn { get; }
|
|
||||||
|
|
||||||
bool WBack { get; }
|
|
||||||
bool IsLoad { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +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 uint GetPc()
|
|
||||||
{
|
|
||||||
// Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
|
||||||
// the PC actually points 2 instructions ahead.
|
|
||||||
return (uint)Address + (uint)OpCodeSizeInBytes * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,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,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,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,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 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,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,22 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders
|
|
||||||
{
|
|
||||||
class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu
|
|
||||||
{
|
|
||||||
private int _rdn;
|
|
||||||
|
|
||||||
public int Rd => _rdn;
|
|
||||||
public int Rn => _rdn;
|
|
||||||
|
|
||||||
public bool SetFlags => false;
|
|
||||||
|
|
||||||
public int Immediate { get; }
|
|
||||||
|
|
||||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16AluImm8(inst, address, opCode);
|
|
||||||
|
|
||||||
public OpCodeT16AluImm8(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
|
||||||
{
|
|
||||||
Immediate = (opCode >> 0) & 0xff;
|
|
||||||
_rdn = (opCode >> 8) & 0x7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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,775 +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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
EmitNZFlagsCheck(context, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitAluStore(context, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Mov(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
|
||||||
|
|
||||||
Operand m = GetAluM(context);
|
|
||||||
|
|
||||||
if (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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 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 (op.SetFlags)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
|
|
||||||
|
|
||||||
Operand m = GetIntA32(context, op.Rm);
|
|
||||||
Operand n = GetIntA32(context, op.Rn);
|
|
||||||
|
|
||||||
Operand xor, res;
|
|
||||||
|
|
||||||
res = context.BitwiseAnd(m, n);
|
|
||||||
xor = context.BitwiseExclusiveOr(m, n);
|
|
||||||
xor = context.ShiftRightUI(xor, Const(1));
|
|
||||||
xor = context.BitwiseAnd(xor, Const(0x7F7F7F7Fu));
|
|
||||||
res = context.Add(res, xor);
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rd, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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, op.SetFlags, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,603 +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 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 (IsThumb(context.CurrOp))
|
|
||||||
{
|
|
||||||
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 OpCode32AluImm op:
|
|
||||||
{
|
|
||||||
if (op.SetFlags && op.IsRotated)
|
|
||||||
{
|
|
||||||
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Const(op.Immediate);
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode32AluImm16 op: return Const(op.Immediate);
|
|
||||||
|
|
||||||
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
|
||||||
case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
|
|
||||||
|
|
||||||
case OpCodeT16AluImm8 op: return Const(op.Immediate);
|
|
||||||
|
|
||||||
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, OpCode32AluRsImm 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 &= op.SetFlags;
|
|
||||||
|
|
||||||
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, OpCode32AluRsReg 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 &= op.SetFlags;
|
|
||||||
|
|
||||||
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,46 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit
|
|
||||||
{
|
|
||||||
public static void Brk(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.Break));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Svc(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitExceptionCall(ArmEmitterContext context, string name)
|
|
||||||
{
|
|
||||||
OpCodeException op = (OpCodeException)context.CurrOp;
|
|
||||||
|
|
||||||
context.StoreToContext();
|
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
|
||||||
|
|
||||||
context.LoadFromContext();
|
|
||||||
|
|
||||||
Translator.EmitSynchronization(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Und(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode op = context.CurrOp;
|
|
||||||
|
|
||||||
string name = nameof(NativeInterface.Undefined);
|
|
||||||
|
|
||||||
context.StoreToContext();
|
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
|
|
||||||
|
|
||||||
context.LoadFromContext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitFlowHelper;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit32
|
|
||||||
{
|
|
||||||
public static void Svc(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Trap(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.Break));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitExceptionCall(ArmEmitterContext context, string name)
|
|
||||||
{
|
|
||||||
OpCode32Exception op = (OpCode32Exception)context.CurrOp;
|
|
||||||
|
|
||||||
context.StoreToContext();
|
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
|
||||||
|
|
||||||
context.LoadFromContext();
|
|
||||||
|
|
||||||
Translator.EmitSynchronization(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +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 = IsThumb(context.CurrOp);
|
|
||||||
|
|
||||||
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 = IsThumb(context.CurrOp);
|
|
||||||
|
|
||||||
uint currentPc = isThumb
|
|
||||||
? pc | 1
|
|
||||||
: pc - 4;
|
|
||||||
|
|
||||||
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
|
||||||
|
|
||||||
SetFlag(context, PState.TFlag, bitOne);
|
|
||||||
|
|
||||||
EmitVirtualCall(context, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Bx(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
|
||||||
|
|
||||||
EmitBxWritePc(context, GetIntA32(context, op.Rm), op.Rm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,255 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static class InstEmitHelper
|
|
||||||
{
|
|
||||||
public static bool IsThumb(OpCode op)
|
|
||||||
{
|
|
||||||
return op is OpCodeT16;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetExtendedM(ArmEmitterContext context, int rm, IntType type)
|
|
||||||
{
|
|
||||||
Operand value = GetIntOrZR(context, rm);
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case IntType.UInt8: value = context.ZeroExtend8 (value.Type, value); break;
|
|
||||||
case IntType.UInt16: value = context.ZeroExtend16(value.Type, value); break;
|
|
||||||
case IntType.UInt32: value = context.ZeroExtend32(value.Type, value); break;
|
|
||||||
|
|
||||||
case IntType.Int8: value = context.SignExtend8 (value.Type, value); break;
|
|
||||||
case IntType.Int16: value = context.SignExtend16(value.Type, value); break;
|
|
||||||
case IntType.Int32: value = context.SignExtend32(value.Type, value); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetIntA32(ArmEmitterContext context, int regIndex)
|
|
||||||
{
|
|
||||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
|
||||||
{
|
|
||||||
OpCode32 op = (OpCode32)context.CurrOp;
|
|
||||||
|
|
||||||
return Const((int)op.GetPc());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetVecA32(int regIndex)
|
|
||||||
{
|
|
||||||
return Register(regIndex, RegisterType.Vector, OperandType.V128);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
|
|
||||||
{
|
|
||||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
|
||||||
{
|
|
||||||
if (!IsA32Return(context))
|
|
||||||
{
|
|
||||||
context.StoreToContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitBxWritePc(context, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (value.Type == OperandType.I64)
|
|
||||||
{
|
|
||||||
value = context.ConvertI64ToI32(value);
|
|
||||||
}
|
|
||||||
Operand reg = Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
|
|
||||||
|
|
||||||
context.Copy(reg, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetRegisterAlias(Aarch32Mode mode, int regIndex)
|
|
||||||
{
|
|
||||||
// Only registers >= 8 are banked,
|
|
||||||
// with registers in the range [8, 12] being
|
|
||||||
// banked for the FIQ mode, and registers
|
|
||||||
// 13 and 14 being banked for all modes.
|
|
||||||
if ((uint)regIndex < 8)
|
|
||||||
{
|
|
||||||
return regIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetBankedRegisterAlias(mode, regIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBankedRegisterAlias(Aarch32Mode mode, int regIndex)
|
|
||||||
{
|
|
||||||
switch (regIndex)
|
|
||||||
{
|
|
||||||
case 8: return mode == Aarch32Mode.Fiq
|
|
||||||
? RegisterAlias.R8Fiq
|
|
||||||
: RegisterAlias.R8Usr;
|
|
||||||
|
|
||||||
case 9: return mode == Aarch32Mode.Fiq
|
|
||||||
? RegisterAlias.R9Fiq
|
|
||||||
: RegisterAlias.R9Usr;
|
|
||||||
|
|
||||||
case 10: return mode == Aarch32Mode.Fiq
|
|
||||||
? RegisterAlias.R10Fiq
|
|
||||||
: RegisterAlias.R10Usr;
|
|
||||||
|
|
||||||
case 11: return mode == Aarch32Mode.Fiq
|
|
||||||
? RegisterAlias.R11Fiq
|
|
||||||
: RegisterAlias.R11Usr;
|
|
||||||
|
|
||||||
case 12: return mode == Aarch32Mode.Fiq
|
|
||||||
? RegisterAlias.R12Fiq
|
|
||||||
: RegisterAlias.R12Usr;
|
|
||||||
|
|
||||||
case 13:
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case Aarch32Mode.User:
|
|
||||||
case Aarch32Mode.System: return RegisterAlias.SpUsr;
|
|
||||||
case Aarch32Mode.Fiq: return RegisterAlias.SpFiq;
|
|
||||||
case Aarch32Mode.Irq: return RegisterAlias.SpIrq;
|
|
||||||
case Aarch32Mode.Supervisor: return RegisterAlias.SpSvc;
|
|
||||||
case Aarch32Mode.Abort: return RegisterAlias.SpAbt;
|
|
||||||
case Aarch32Mode.Hypervisor: return RegisterAlias.SpHyp;
|
|
||||||
case Aarch32Mode.Undefined: return RegisterAlias.SpUnd;
|
|
||||||
|
|
||||||
default: throw new ArgumentException(nameof(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
case 14:
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case Aarch32Mode.User:
|
|
||||||
case Aarch32Mode.Hypervisor:
|
|
||||||
case Aarch32Mode.System: return RegisterAlias.LrUsr;
|
|
||||||
case Aarch32Mode.Fiq: return RegisterAlias.LrFiq;
|
|
||||||
case Aarch32Mode.Irq: return RegisterAlias.LrIrq;
|
|
||||||
case Aarch32Mode.Supervisor: return RegisterAlias.LrSvc;
|
|
||||||
case Aarch32Mode.Abort: return RegisterAlias.LrAbt;
|
|
||||||
case Aarch32Mode.Undefined: return RegisterAlias.LrUnd;
|
|
||||||
|
|
||||||
default: throw new ArgumentException(nameof(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
default: throw new ArgumentOutOfRangeException(nameof(regIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsA32Return(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
switch (context.CurrOp)
|
|
||||||
{
|
|
||||||
case IOpCode32MemMult op:
|
|
||||||
return true; // Setting PC using LDM is nearly always a return.
|
|
||||||
case OpCode32AluRsImm op:
|
|
||||||
return op.Rm == RegisterAlias.Aarch32Lr;
|
|
||||||
case OpCode32AluRsReg op:
|
|
||||||
return op.Rm == RegisterAlias.Aarch32Lr;
|
|
||||||
case OpCode32AluReg op:
|
|
||||||
return op.Rm == RegisterAlias.Aarch32Lr;
|
|
||||||
case OpCode32Mem op:
|
|
||||||
return op.Rn == RegisterAlias.Aarch32Sp && op.WBack && !op.Index; // Setting PC to an address stored on the stack is nearly always a return.
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitBxWritePc(ArmEmitterContext context, Operand pc, int sourceRegister = 0)
|
|
||||||
{
|
|
||||||
bool isReturn = sourceRegister == RegisterAlias.Aarch32Lr || IsA32Return(context);
|
|
||||||
Operand mode = context.BitwiseAnd(pc, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.TFlag, mode);
|
|
||||||
|
|
||||||
Operand addr = context.ConditionalSelect(mode, pc, context.BitwiseAnd(pc, Const(~3)));
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitVirtualJump(context, addr, isReturn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetIntOrZR(ArmEmitterContext context, int regIndex)
|
|
||||||
{
|
|
||||||
if (regIndex == RegisterConsts.ZeroIndex)
|
|
||||||
{
|
|
||||||
OperandType type = context.CurrOp.GetOperandType();
|
|
||||||
|
|
||||||
return type == OperandType.I32 ? Const(0) : Const(0L);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return GetIntOrSP(context, regIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetIntOrZR(ArmEmitterContext context, int regIndex, Operand value)
|
|
||||||
{
|
|
||||||
if (regIndex == RegisterConsts.ZeroIndex)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetIntOrSP(context, regIndex, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetIntOrSP(ArmEmitterContext context, int regIndex)
|
|
||||||
{
|
|
||||||
Operand value = Register(regIndex, RegisterType.Integer, OperandType.I64);
|
|
||||||
|
|
||||||
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
|
|
||||||
{
|
|
||||||
value = context.ConvertI64ToI32(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetIntOrSP(ArmEmitterContext context, int regIndex, Operand value)
|
|
||||||
{
|
|
||||||
Operand reg = Register(regIndex, RegisterType.Integer, OperandType.I64);
|
|
||||||
|
|
||||||
if (value.Type == OperandType.I32)
|
|
||||||
{
|
|
||||||
value = context.ZeroExtend32(OperandType.I64, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(reg, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetVec(int regIndex)
|
|
||||||
{
|
|
||||||
return Register(regIndex, RegisterType.Vector, OperandType.V128);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetFlag(PState stateFlag)
|
|
||||||
{
|
|
||||||
return Register((int)stateFlag, RegisterType.Flag, OperandType.I32);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetFpFlag(FPState stateFlag)
|
|
||||||
{
|
|
||||||
return Register((int)stateFlag, RegisterType.FpFlag, OperandType.I32);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFlag(ArmEmitterContext context, PState stateFlag, Operand value)
|
|
||||||
{
|
|
||||||
context.Copy(GetFlag(stateFlag), value);
|
|
||||||
|
|
||||||
context.MarkFlagSet(stateFlag);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFpFlag(ArmEmitterContext context, FPState stateFlag, Operand value)
|
|
||||||
{
|
|
||||||
context.Copy(GetFpFlag(stateFlag), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,584 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit32
|
|
||||||
{
|
|
||||||
private static int FlipVdBits(int vd, bool lowBit)
|
|
||||||
{
|
|
||||||
if (lowBit)
|
|
||||||
{
|
|
||||||
// Move the low bit to the top.
|
|
||||||
return ((vd & 0x1) << 4) | (vd >> 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Move the high bit to the bottom.
|
|
||||||
return ((vd & 0xf) << 1) | (vd >> 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
|
|
||||||
{
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
if (op1.Type == OperandType.FP64)
|
|
||||||
{
|
|
||||||
info = unsigned
|
|
||||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))
|
|
||||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
info = unsigned
|
|
||||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))
|
|
||||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.Call(info, op1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vcvt_V(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
bool unsigned = (op.Opc & 1) != 0;
|
|
||||||
bool toInteger = (op.Opc & 2) != 0;
|
|
||||||
OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
|
|
||||||
|
|
||||||
if (toInteger)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse41)
|
|
||||||
{
|
|
||||||
EmitSse41ConvertVector32(context, FPRoundingMode.TowardsZero, !unsigned);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorUnaryOpF32(context, (op1) =>
|
|
||||||
{
|
|
||||||
return EmitSaturateFloatToInt(context, op1, unsigned);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorUnaryOpSimd32(context, (n) =>
|
|
||||||
{
|
|
||||||
if (unsigned)
|
|
||||||
{
|
|
||||||
Operand mask = X86GetAllElements(context, 0x47800000);
|
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Psrld, n, Const(16));
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res);
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, res, mask);
|
|
||||||
|
|
||||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pslld, n, Const(16));
|
|
||||||
res2 = context.AddIntrinsic(Intrinsic.X86Psrld, res2, Const(16));
|
|
||||||
res2 = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res2);
|
|
||||||
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Addps, res, res2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, n);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (unsigned)
|
|
||||||
{
|
|
||||||
EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vcvt_FD(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
|
||||||
|
|
||||||
int vm = op.Vm;
|
|
||||||
int vd;
|
|
||||||
if (op.Size == 3)
|
|
||||||
{
|
|
||||||
vd = FlipVdBits(op.Vd, false);
|
|
||||||
// Double to single.
|
|
||||||
Operand fp = ExtractScalar(context, OperandType.FP64, vm);
|
|
||||||
|
|
||||||
Operand res = context.ConvertToFP(OperandType.FP32, fp);
|
|
||||||
|
|
||||||
InsertScalar(context, vd, res);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vd = FlipVdBits(op.Vd, true);
|
|
||||||
// Single to double.
|
|
||||||
Operand fp = ExtractScalar(context, OperandType.FP32, vm);
|
|
||||||
|
|
||||||
Operand res = context.ConvertToFP(OperandType.FP64, fp);
|
|
||||||
|
|
||||||
InsertScalar(context, vd, res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VCVT (floating-point to integer, floating-point) | VCVT (integer to floating-point, floating-point).
|
|
||||||
public static void Vcvt_FI(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
|
|
||||||
|
|
||||||
bool toInteger = (op.Opc2 & 0b100) != 0;
|
|
||||||
|
|
||||||
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
|
|
||||||
|
|
||||||
if (toInteger)
|
|
||||||
{
|
|
||||||
bool unsigned = (op.Opc2 & 1) == 0;
|
|
||||||
bool roundWithFpscr = op.Opc != 1;
|
|
||||||
|
|
||||||
if (!roundWithFpscr && Optimizations.UseSse41)
|
|
||||||
{
|
|
||||||
EmitSse41ConvertInt32(context, FPRoundingMode.TowardsZero, !unsigned);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
|
||||||
|
|
||||||
Operand asInteger;
|
|
||||||
|
|
||||||
// TODO: Fast Path.
|
|
||||||
if (roundWithFpscr)
|
|
||||||
{
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
if (floatSize == OperandType.FP64)
|
|
||||||
{
|
|
||||||
info = unsigned
|
|
||||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))
|
|
||||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
info = unsigned
|
|
||||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))
|
|
||||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32));
|
|
||||||
}
|
|
||||||
|
|
||||||
asInteger = context.Call(info, toConvert);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Round towards zero.
|
|
||||||
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, asInteger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool unsigned = op.Opc == 0;
|
|
||||||
|
|
||||||
Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm);
|
|
||||||
|
|
||||||
Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned);
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, asFloat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
|
|
||||||
{
|
|
||||||
IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
string name = nameof(Math.Round);
|
|
||||||
|
|
||||||
MethodInfo info = (op.Size & 1) == 0
|
|
||||||
? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) })
|
|
||||||
: typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) });
|
|
||||||
|
|
||||||
return context.Call(info, n, Const((int)roundMode));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FPRoundingMode RMToRoundMode(int rm)
|
|
||||||
{
|
|
||||||
FPRoundingMode roundMode;
|
|
||||||
switch (rm)
|
|
||||||
{
|
|
||||||
case 0b01:
|
|
||||||
roundMode = FPRoundingMode.ToNearest;
|
|
||||||
break;
|
|
||||||
case 0b10:
|
|
||||||
roundMode = FPRoundingMode.TowardsPlusInfinity;
|
|
||||||
break;
|
|
||||||
case 0b11:
|
|
||||||
roundMode = FPRoundingMode.TowardsMinusInfinity;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(rm));
|
|
||||||
}
|
|
||||||
return roundMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VCVTA/M/N/P (floating-point).
|
|
||||||
public static void Vcvt_RM(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; // toInteger == true (opCode<18> == 1 => Opc2<2> == 1).
|
|
||||||
|
|
||||||
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
|
|
||||||
|
|
||||||
bool unsigned = op.Opc == 0;
|
|
||||||
int rm = op.Opc2 & 3;
|
|
||||||
|
|
||||||
if (Optimizations.UseSse41 && rm != 0b00)
|
|
||||||
{
|
|
||||||
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
|
||||||
|
|
||||||
switch (rm)
|
|
||||||
{
|
|
||||||
case 0b00: // Away
|
|
||||||
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
|
|
||||||
break;
|
|
||||||
case 0b01: // Nearest
|
|
||||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
|
||||||
break;
|
|
||||||
case 0b10: // Towards positive infinity
|
|
||||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
|
||||||
break;
|
|
||||||
case 0b11: // Towards negative infinity
|
|
||||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand asInteger;
|
|
||||||
|
|
||||||
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, asInteger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VRINTA/M/N/P (floating-point).
|
|
||||||
public static void Vrint_RM(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
|
||||||
|
|
||||||
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
|
|
||||||
|
|
||||||
int rm = op.Opc2 & 3;
|
|
||||||
|
|
||||||
if (Optimizations.UseSse2 && rm != 0b00)
|
|
||||||
{
|
|
||||||
EmitScalarUnaryOpSimd32(context, (m) =>
|
|
||||||
{
|
|
||||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
|
||||||
|
|
||||||
FPRoundingMode roundMode = RMToRoundMode(rm);
|
|
||||||
|
|
||||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
|
||||||
|
|
||||||
switch (rm)
|
|
||||||
{
|
|
||||||
case 0b00: // Away
|
|
||||||
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
|
|
||||||
break;
|
|
||||||
case 0b01: // Nearest
|
|
||||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
|
||||||
break;
|
|
||||||
case 0b10: // Towards positive infinity
|
|
||||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
|
||||||
break;
|
|
||||||
case 0b11: // Towards negative infinity
|
|
||||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, toConvert);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VRINTZ (floating-point).
|
|
||||||
public static void Vrint_Z(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
|
||||||
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitScalarUnaryOpSimd32(context, (m) =>
|
|
||||||
{
|
|
||||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
|
||||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(FPRoundingMode.TowardsZero)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VRINTX (floating-point).
|
|
||||||
public static void Vrintx_S(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
|
||||||
|
|
||||||
bool doubleSize = (op.Size & 1) == 1;
|
|
||||||
string methodName = doubleSize ? nameof(SoftFallback.Round) : nameof(SoftFallback.RoundF);
|
|
||||||
|
|
||||||
EmitScalarUnaryOpF32(context, (op1) =>
|
|
||||||
{
|
|
||||||
MethodInfo info = typeof(SoftFallback).GetMethod(methodName);
|
|
||||||
return context.Call(info, op1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
|
||||||
{
|
|
||||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
|
||||||
|
|
||||||
if (signed)
|
|
||||||
{
|
|
||||||
return context.ConvertToFP(type, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return context.ConvertToFPUI(type, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSse41ConvertInt32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed)
|
|
||||||
{
|
|
||||||
// A port of the similar round function in InstEmitSimdCvt.
|
|
||||||
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
|
|
||||||
|
|
||||||
bool doubleSize = (op.Size & 1) != 0;
|
|
||||||
int shift = doubleSize ? 1 : 2;
|
|
||||||
Operand n = GetVecA32(op.Vm >> shift);
|
|
||||||
n = EmitSwapScalar(context, n, op.Vm, doubleSize);
|
|
||||||
|
|
||||||
if (!doubleSize)
|
|
||||||
{
|
|
||||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
|
||||||
|
|
||||||
Operand nCmp;
|
|
||||||
Operand nIntOrLong2 = default;
|
|
||||||
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fpMaxVal = 0x4F000000; // 2.14748365E9f (2147483648)
|
|
||||||
|
|
||||||
Operand fpMaxValMask = X86GetScalar(context, fpMaxVal);
|
|
||||||
|
|
||||||
Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes);
|
|
||||||
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Subss, nRes, fpMaxValMask);
|
|
||||||
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
|
|
||||||
nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
|
||||||
|
|
||||||
Operand nInt = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, nRes);
|
|
||||||
|
|
||||||
Operand dRes;
|
|
||||||
if (signed)
|
|
||||||
{
|
|
||||||
dRes = context.BitwiseExclusiveOr(nIntOrLong, nInt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dRes = context.BitwiseExclusiveOr(nIntOrLong2, nInt);
|
|
||||||
dRes = context.Add(dRes, nIntOrLong);
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, dRes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
|
||||||
|
|
||||||
Operand nCmp;
|
|
||||||
Operand nIntOrLong2 = default;
|
|
||||||
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
long fpMaxVal = 0x41E0000000000000L; // 2147483648.0000000d (2147483648)
|
|
||||||
|
|
||||||
Operand fpMaxValMask = X86GetScalar(context, fpMaxVal);
|
|
||||||
|
|
||||||
Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes);
|
|
||||||
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Subsd, nRes, fpMaxValMask);
|
|
||||||
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
|
|
||||||
nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
|
||||||
|
|
||||||
Operand nLong = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, nRes);
|
|
||||||
nLong = context.ConvertI64ToI32(nLong);
|
|
||||||
|
|
||||||
Operand dRes;
|
|
||||||
if (signed)
|
|
||||||
{
|
|
||||||
dRes = context.BitwiseExclusiveOr(nIntOrLong, nLong);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dRes = context.BitwiseExclusiveOr(nIntOrLong2, nLong);
|
|
||||||
dRes = context.Add(dRes, nIntOrLong);
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, dRes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSse41ConvertVector32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed)
|
|
||||||
{
|
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
|
||||||
|
|
||||||
EmitVectorUnaryOpSimd32(context, (n) =>
|
|
||||||
{
|
|
||||||
int sizeF = op.Size & 1;
|
|
||||||
|
|
||||||
if (sizeF == 0)
|
|
||||||
{
|
|
||||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
|
||||||
Operand nCmp;
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand fpMaxValMask = X86GetAllElements(context, 0x4F000000); // 2.14748365E9f (2147483648)
|
|
||||||
|
|
||||||
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
|
||||||
Operand nInt2 = default;
|
|
||||||
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Subps, nRes, fpMaxValMask);
|
|
||||||
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
|
|
||||||
nInt2 = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
|
||||||
|
|
||||||
if (signed)
|
|
||||||
{
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Pxor, nInt, nRes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nInt2, nRes);
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Paddd, dRes, nInt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* if (sizeF == 1) */
|
|
||||||
{
|
|
||||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
|
||||||
|
|
||||||
Operand zero = context.VectorZero();
|
|
||||||
Operand nCmp;
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand fpMaxValMask = X86GetAllElements(context, 0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808)
|
|
||||||
|
|
||||||
Operand nLong = InstEmit.EmitSse2CvtDoubleToInt64OpF(context, nRes, false);
|
|
||||||
Operand nLong2 = default;
|
|
||||||
|
|
||||||
if (!signed)
|
|
||||||
{
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Subpd, nRes, fpMaxValMask);
|
|
||||||
|
|
||||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
|
||||||
|
|
||||||
nLong2 = InstEmit.EmitSse2CvtDoubleToInt64OpF(context, nRes, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
|
||||||
|
|
||||||
if (signed)
|
|
||||||
{
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Pxor, nLong, nRes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nLong2, nRes);
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Paddq, dRes, nLong);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,221 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit32
|
|
||||||
{
|
|
||||||
public static void Vand_I(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pand, n, m));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, op2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vbic_I(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pandn, m, n));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, context.BitwiseNot(op2)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vbic_II(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdImm op = (OpCode32SimdImm)context.CurrOp;
|
|
||||||
|
|
||||||
long immediate = op.Immediate;
|
|
||||||
|
|
||||||
// Replicate fields to fill the 64-bits, if size is < 64-bits.
|
|
||||||
switch (op.Size)
|
|
||||||
{
|
|
||||||
case 0: immediate *= 0x0101010101010101L; break;
|
|
||||||
case 1: immediate *= 0x0001000100010001L; break;
|
|
||||||
case 2: immediate *= 0x0000000100000001L; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand imm = Const(immediate);
|
|
||||||
Operand res = GetVecA32(op.Qd);
|
|
||||||
|
|
||||||
if (op.Q)
|
|
||||||
{
|
|
||||||
for (int elem = 0; elem < 2; elem++)
|
|
||||||
{
|
|
||||||
Operand de = EmitVectorExtractZx(context, op.Qd, elem, 3);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, context.BitwiseAnd(de, context.BitwiseNot(imm)), elem, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Vd & 1, 3);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, context.BitwiseAnd(de, context.BitwiseNot(imm)), op.Vd & 1, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vbif(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitBifBit(context, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vbit(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitBifBit(context, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vbsl(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorTernaryOpSimd32(context, (d, n, m) =>
|
|
||||||
{
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Pand, res, d);
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Pxor, res, m);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorTernaryOpZx32(context, (op1, op2, op3) =>
|
|
||||||
{
|
|
||||||
return context.BitwiseExclusiveOr(
|
|
||||||
context.BitwiseAnd(op1,
|
|
||||||
context.BitwiseExclusiveOr(op2, op3)), op3);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Veor_I(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pxor, n, m));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseExclusiveOr(op1, op2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vorn_I(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
Operand mask = context.VectorOne();
|
|
||||||
|
|
||||||
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
|
||||||
{
|
|
||||||
m = context.AddIntrinsic(Intrinsic.X86Pandn, m, mask);
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Por, n, m);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, context.BitwiseNot(op2)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vorr_I(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Por, n, m));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, op2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vorr_II(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdImm op = (OpCode32SimdImm)context.CurrOp;
|
|
||||||
|
|
||||||
long immediate = op.Immediate;
|
|
||||||
|
|
||||||
// Replicate fields to fill the 64-bits, if size is < 64-bits.
|
|
||||||
switch (op.Size)
|
|
||||||
{
|
|
||||||
case 0: immediate *= 0x0101010101010101L; break;
|
|
||||||
case 1: immediate *= 0x0001000100010001L; break;
|
|
||||||
case 2: immediate *= 0x0000000100000001L; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand imm = Const(immediate);
|
|
||||||
Operand res = GetVecA32(op.Qd);
|
|
||||||
|
|
||||||
if (op.Q)
|
|
||||||
{
|
|
||||||
for (int elem = 0; elem < 2; elem++)
|
|
||||||
{
|
|
||||||
Operand de = EmitVectorExtractZx(context, op.Qd, elem, 3);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, context.BitwiseOr(de, imm), elem, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Vd & 1, 3);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, context.BitwiseOr(de, imm), op.Vd & 1, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVecA32(op.Qd), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vtst(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitVectorBinaryOpZx32(context, (op1, op2) =>
|
|
||||||
{
|
|
||||||
Operand isZero = context.ICompareEqual(context.BitwiseAnd(op1, op2), Const(0));
|
|
||||||
return context.ConditionalSelect(isZero, Const(0), Const(-1));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
|
|
||||||
{
|
|
||||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
|
||||||
|
|
||||||
if (Optimizations.UseSse2)
|
|
||||||
{
|
|
||||||
EmitVectorTernaryOpSimd32(context, (d, n, m) =>
|
|
||||||
{
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, d);
|
|
||||||
res = context.AddIntrinsic((notRm) ? Intrinsic.X86Pandn : Intrinsic.X86Pand, m, res);
|
|
||||||
return context.AddIntrinsic(Intrinsic.X86Pxor, d, res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitVectorTernaryOpZx32(context, (d, n, m) =>
|
|
||||||
{
|
|
||||||
if (notRm)
|
|
||||||
{
|
|
||||||
m = context.BitwiseNot(m);
|
|
||||||
}
|
|
||||||
return context.BitwiseExclusiveOr(
|
|
||||||
context.BitwiseAnd(m,
|
|
||||||
context.BitwiseExclusiveOr(d, n)), d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,158 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit
|
|
||||||
{
|
|
||||||
private const int DczSizeLog2 = 4;
|
|
||||||
|
|
||||||
public static void Hint(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
// Execute as no-op.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Isb(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
// Execute as no-op.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Mrs(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (GetPackedId(op))
|
|
||||||
{
|
|
||||||
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
|
|
||||||
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
|
|
||||||
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
|
||||||
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
|
|
||||||
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
|
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
|
||||||
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)); break;
|
|
||||||
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
|
||||||
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
|
||||||
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SetIntOrZR(context, op.Rt, context.Call(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Msr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (GetPackedId(op))
|
|
||||||
{
|
|
||||||
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
|
||||||
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)); break;
|
|
||||||
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)); break;
|
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
|
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Call(info, GetIntOrZR(context, op.Rt));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Nop(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sys(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
// This instruction is used to do some operations on the CPU like cache invalidation,
|
|
||||||
// address translation and the like.
|
|
||||||
// We treat it as no-op here since we don't have any cache being emulated anyway.
|
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
|
||||||
|
|
||||||
switch (GetPackedId(op))
|
|
||||||
{
|
|
||||||
case 0b11_011_0111_0100_001:
|
|
||||||
{
|
|
||||||
// DC ZVA
|
|
||||||
Operand t = GetIntOrZR(context, op.Rt);
|
|
||||||
|
|
||||||
for (long offset = 0; offset < (4 << DczSizeLog2); offset += 8)
|
|
||||||
{
|
|
||||||
Operand address = context.Add(t, Const(offset));
|
|
||||||
|
|
||||||
InstEmitMemoryHelper.EmitStore(context, address, RegisterConsts.ZeroIndex, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No-op
|
|
||||||
case 0b11_011_0111_1110_001: //DC CIVAC
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetPackedId(OpCodeSystem op)
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
|
|
||||||
id = op.Op2 << 0;
|
|
||||||
id |= op.CRm << 3;
|
|
||||||
id |= op.CRn << 7;
|
|
||||||
id |= op.Op1 << 11;
|
|
||||||
id |= op.Op0 << 14;
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitGetNzcv(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
|
||||||
|
|
||||||
Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
|
||||||
Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag));
|
|
||||||
Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag));
|
|
||||||
Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag));
|
|
||||||
|
|
||||||
Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
|
|
||||||
|
|
||||||
SetIntOrZR(context, op.Rt, nzcvSh);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSetNzcv(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
|
||||||
|
|
||||||
Operand t = GetIntOrZR(context, op.Rt);
|
|
||||||
t = context.ConvertI64ToI32(t);
|
|
||||||
|
|
||||||
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
|
|
||||||
v = context.BitwiseAnd (v, Const(1));
|
|
||||||
|
|
||||||
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
|
|
||||||
c = context.BitwiseAnd (c, Const(1));
|
|
||||||
|
|
||||||
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
|
|
||||||
z = context.BitwiseAnd (z, Const(1));
|
|
||||||
|
|
||||||
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
|
|
||||||
n = context.BitwiseAnd (n, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.VFlag, v);
|
|
||||||
SetFlag(context, PState.CFlag, c);
|
|
||||||
SetFlag(context, PState.ZFlag, z);
|
|
||||||
SetFlag(context, PState.NFlag, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,332 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit32
|
|
||||||
{
|
|
||||||
public static void Mcr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.Coproc != 15)
|
|
||||||
{
|
|
||||||
InstEmit.Und(context);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op.Opc1 != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (op.CRn)
|
|
||||||
{
|
|
||||||
case 13: // Process and Thread Info.
|
|
||||||
if (op.CRm != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (op.Opc2)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
|
||||||
switch (op.CRm) // Cache and Memory barrier.
|
|
||||||
{
|
|
||||||
case 10:
|
|
||||||
switch (op.Opc2)
|
|
||||||
{
|
|
||||||
case 5: // Data Memory Barrier Register.
|
|
||||||
return; // No-op.
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Call(info, GetIntA32(context, op.Rt));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Mrc(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.Coproc != 15)
|
|
||||||
{
|
|
||||||
InstEmit.Und(context);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op.Opc1 != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (op.CRn)
|
|
||||||
{
|
|
||||||
case 13: // Process and Thread Info.
|
|
||||||
if (op.CRm != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (op.Opc2)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op.Rt == RegisterAlias.Aarch32Pc)
|
|
||||||
{
|
|
||||||
// Special behavior: copy NZCV flags into APSR.
|
|
||||||
EmitSetNzcv(context, context.Call(info));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetIntA32(context, op.Rt, context.Call(info));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Mrrc(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.Coproc != 15)
|
|
||||||
{
|
|
||||||
InstEmit.Und(context);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int opc = op.MrrcOp;
|
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (op.CRm)
|
|
||||||
{
|
|
||||||
case 14: // Timer.
|
|
||||||
switch (opc)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand result = context.Call(info);
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
|
|
||||||
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Msr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32MsrReg op = (OpCode32MsrReg)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.R)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("SPSR");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((op.Mask & 8) != 0)
|
|
||||||
{
|
|
||||||
Operand value = GetIntA32(context, op.Rn);
|
|
||||||
|
|
||||||
EmitSetNzcv(context, value);
|
|
||||||
|
|
||||||
Operand q = context.ShiftRightUI(value, Const((int)PState.QFlag));
|
|
||||||
q = context.BitwiseAnd(q, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.QFlag, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((op.Mask & 4) != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("APSR_g");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((op.Mask & 2) != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("CPSR_x");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((op.Mask & 1) != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("CPSR_c");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Nop(ArmEmitterContext context) { }
|
|
||||||
|
|
||||||
public static void Vmrs(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.Rt == RegisterAlias.Aarch32Pc && op.Sreg == 0b0001)
|
|
||||||
{
|
|
||||||
// Special behavior: copy NZCV flags into APSR.
|
|
||||||
SetFlag(context, PState.VFlag, GetFpFlag(FPState.VFlag));
|
|
||||||
SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag));
|
|
||||||
SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
|
|
||||||
SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (op.Sreg)
|
|
||||||
{
|
|
||||||
case 0b0000: // FPSID
|
|
||||||
throw new NotImplementedException("Supervisor Only");
|
|
||||||
case 0b0001: // FPSCR
|
|
||||||
EmitGetFpscr(context); return;
|
|
||||||
case 0b0101: // MVFR2
|
|
||||||
throw new NotImplementedException("MVFR2");
|
|
||||||
case 0b0110: // MVFR1
|
|
||||||
throw new NotImplementedException("MVFR1");
|
|
||||||
case 0b0111: // MVFR0
|
|
||||||
throw new NotImplementedException("MVFR0");
|
|
||||||
case 0b1000: // FPEXC
|
|
||||||
throw new NotImplementedException("Supervisor Only");
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Vmsr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
|
||||||
|
|
||||||
switch (op.Sreg)
|
|
||||||
{
|
|
||||||
case 0b0000: // FPSID
|
|
||||||
throw new NotImplementedException("Supervisor Only");
|
|
||||||
case 0b0001: // FPSCR
|
|
||||||
EmitSetFpscr(context); return;
|
|
||||||
case 0b0101: // MVFR2
|
|
||||||
throw new NotImplementedException("MVFR2");
|
|
||||||
case 0b0110: // MVFR1
|
|
||||||
throw new NotImplementedException("MVFR1");
|
|
||||||
case 0b0111: // MVFR0
|
|
||||||
throw new NotImplementedException("MVFR0");
|
|
||||||
case 0b1000: // FPEXC
|
|
||||||
throw new NotImplementedException("Supervisor Only");
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
|
|
||||||
{
|
|
||||||
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
|
|
||||||
v = context.BitwiseAnd(v, Const(1));
|
|
||||||
|
|
||||||
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
|
|
||||||
c = context.BitwiseAnd(c, Const(1));
|
|
||||||
|
|
||||||
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
|
|
||||||
z = context.BitwiseAnd(z, Const(1));
|
|
||||||
|
|
||||||
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
|
|
||||||
n = context.BitwiseAnd(n, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.VFlag, v);
|
|
||||||
SetFlag(context, PState.CFlag, c);
|
|
||||||
SetFlag(context, PState.ZFlag, z);
|
|
||||||
SetFlag(context, PState.NFlag, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitGetFpscr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
|
||||||
|
|
||||||
Operand vSh = context.ShiftLeft(GetFpFlag(FPState.VFlag), Const((int)FPState.VFlag));
|
|
||||||
Operand cSh = context.ShiftLeft(GetFpFlag(FPState.CFlag), Const((int)FPState.CFlag));
|
|
||||||
Operand zSh = context.ShiftLeft(GetFpFlag(FPState.ZFlag), Const((int)FPState.ZFlag));
|
|
||||||
Operand nSh = context.ShiftLeft(GetFpFlag(FPState.NFlag), Const((int)FPState.NFlag));
|
|
||||||
|
|
||||||
Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
|
|
||||||
|
|
||||||
Operand fpscr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr)));
|
|
||||||
|
|
||||||
SetIntA32(context, op.Rt, context.BitwiseOr(nzcvSh, fpscr));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitSetFpscr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
|
||||||
|
|
||||||
Operand t = GetIntA32(context, op.Rt);
|
|
||||||
|
|
||||||
Operand v = context.ShiftRightUI(t, Const((int)FPState.VFlag));
|
|
||||||
v = context.BitwiseAnd(v, Const(1));
|
|
||||||
|
|
||||||
Operand c = context.ShiftRightUI(t, Const((int)FPState.CFlag));
|
|
||||||
c = context.BitwiseAnd(c, Const(1));
|
|
||||||
|
|
||||||
Operand z = context.ShiftRightUI(t, Const((int)FPState.ZFlag));
|
|
||||||
z = context.BitwiseAnd(z, Const(1));
|
|
||||||
|
|
||||||
Operand n = context.ShiftRightUI(t, Const((int)FPState.NFlag));
|
|
||||||
n = context.BitwiseAnd(n, Const(1));
|
|
||||||
|
|
||||||
SetFpFlag(context, FPState.VFlag, v);
|
|
||||||
SetFpFlag(context, FPState.CFlag, c);
|
|
||||||
SetFpFlag(context, FPState.ZFlag, z);
|
|
||||||
SetFpFlag(context, FPState.NFlag, n);
|
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr)), t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,268 +0,0 @@
|
|||||||
using ARMeilleure.Memory;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static class NativeInterface
|
|
||||||
{
|
|
||||||
private class ThreadContext
|
|
||||||
{
|
|
||||||
public ExecutionContext Context { get; }
|
|
||||||
public IMemoryManager Memory { get; }
|
|
||||||
public Translator Translator { get; }
|
|
||||||
|
|
||||||
public ThreadContext(ExecutionContext context, IMemoryManager memory, Translator translator)
|
|
||||||
{
|
|
||||||
Context = context;
|
|
||||||
Memory = memory;
|
|
||||||
Translator = translator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static ThreadContext Context;
|
|
||||||
|
|
||||||
public static void RegisterThread(ExecutionContext context, IMemoryManager memory, Translator translator)
|
|
||||||
{
|
|
||||||
Context = new ThreadContext(context, memory, translator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void UnregisterThread()
|
|
||||||
{
|
|
||||||
Context = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Break(ulong address, int imm)
|
|
||||||
{
|
|
||||||
Statistics.PauseTimer();
|
|
||||||
|
|
||||||
GetContext().OnBreak(address, imm);
|
|
||||||
|
|
||||||
Statistics.ResumeTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SupervisorCall(ulong address, int imm)
|
|
||||||
{
|
|
||||||
Statistics.PauseTimer();
|
|
||||||
|
|
||||||
GetContext().OnSupervisorCall(address, imm);
|
|
||||||
|
|
||||||
Statistics.ResumeTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Undefined(ulong address, int opCode)
|
|
||||||
{
|
|
||||||
Statistics.PauseTimer();
|
|
||||||
|
|
||||||
GetContext().OnUndefined(address, opCode);
|
|
||||||
|
|
||||||
Statistics.ResumeTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region "System registers"
|
|
||||||
public static ulong GetCtrEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().CtrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetDczidEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().DczidEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetFpcr()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().Fpcr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GetFpcrFz()
|
|
||||||
{
|
|
||||||
return (GetContext().Fpcr & FPCR.Fz) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetFpsr()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().Fpsr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetFpscr()
|
|
||||||
{
|
|
||||||
ExecutionContext context = GetContext();
|
|
||||||
|
|
||||||
return (uint)(context.Fpsr & FPSR.A32Mask & ~FPSR.Nzcv) |
|
|
||||||
(uint)(context.Fpcr & FPCR.A32Mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetTpidrEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().TpidrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetTpidrEl032()
|
|
||||||
{
|
|
||||||
return (uint)GetContext().TpidrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetTpidr()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().Tpidr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetTpidr32()
|
|
||||||
{
|
|
||||||
return (uint)GetContext().Tpidr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetCntfrqEl0()
|
|
||||||
{
|
|
||||||
return GetContext().CntfrqEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetCntpctEl0()
|
|
||||||
{
|
|
||||||
return GetContext().CntpctEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetCntvctEl0()
|
|
||||||
{
|
|
||||||
return GetContext().CntvctEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFpcr(ulong value)
|
|
||||||
{
|
|
||||||
GetContext().Fpcr = (FPCR)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFpsr(ulong value)
|
|
||||||
{
|
|
||||||
GetContext().Fpsr = (FPSR)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFpsrQc()
|
|
||||||
{
|
|
||||||
GetContext().Fpsr |= FPSR.Qc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFpscr(uint fpscr)
|
|
||||||
{
|
|
||||||
ExecutionContext context = GetContext();
|
|
||||||
|
|
||||||
context.Fpsr = FPSR.A32Mask & (FPSR)fpscr;
|
|
||||||
context.Fpcr = FPCR.A32Mask & (FPCR)fpscr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetTpidrEl0(ulong value)
|
|
||||||
{
|
|
||||||
GetContext().TpidrEl0 = (long)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetTpidrEl032(uint value)
|
|
||||||
{
|
|
||||||
GetContext().TpidrEl0 = (long)value;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region "Read"
|
|
||||||
public static byte ReadByte(ulong address)
|
|
||||||
{
|
|
||||||
return GetMemoryManager().ReadTracked<byte>(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ushort ReadUInt16(ulong address)
|
|
||||||
{
|
|
||||||
return GetMemoryManager().ReadTracked<ushort>(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint ReadUInt32(ulong address)
|
|
||||||
{
|
|
||||||
return GetMemoryManager().ReadTracked<uint>(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong ReadUInt64(ulong address)
|
|
||||||
{
|
|
||||||
return GetMemoryManager().ReadTracked<ulong>(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static V128 ReadVector128(ulong address)
|
|
||||||
{
|
|
||||||
return GetMemoryManager().ReadTracked<V128>(address);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region "Write"
|
|
||||||
public static void WriteByte(ulong address, byte value)
|
|
||||||
{
|
|
||||||
GetMemoryManager().Write(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteUInt16(ulong address, ushort value)
|
|
||||||
{
|
|
||||||
GetMemoryManager().Write(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteUInt32(ulong address, uint value)
|
|
||||||
{
|
|
||||||
GetMemoryManager().Write(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteUInt64(ulong address, ulong value)
|
|
||||||
{
|
|
||||||
GetMemoryManager().Write(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteVector128(ulong address, V128 value)
|
|
||||||
{
|
|
||||||
GetMemoryManager().Write(address, value);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public static void EnqueueForRejit(ulong address)
|
|
||||||
{
|
|
||||||
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
|
|
||||||
{
|
|
||||||
GetMemoryManager().SignalMemoryTracking(address, size, write);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ThrowInvalidMemoryAccess(ulong address)
|
|
||||||
{
|
|
||||||
throw new InvalidAccessException(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetFunctionAddress(ulong address)
|
|
||||||
{
|
|
||||||
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
|
|
||||||
|
|
||||||
return (ulong)function.FuncPtr.ToInt64();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CheckSynchronization()
|
|
||||||
{
|
|
||||||
Statistics.PauseTimer();
|
|
||||||
|
|
||||||
ExecutionContext context = GetContext();
|
|
||||||
|
|
||||||
context.CheckInterrupt();
|
|
||||||
|
|
||||||
Statistics.ResumeTimer();
|
|
||||||
|
|
||||||
return context.Running;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ExecutionContext GetContext()
|
|
||||||
{
|
|
||||||
return Context.Context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMemoryManager GetMemoryManager()
|
|
||||||
{
|
|
||||||
return Context.Memory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,166 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.IntermediateRepresentation
|
|
||||||
{
|
|
||||||
class BasicBlock : IEquatable<BasicBlock>, IIntrusiveListNode<BasicBlock>
|
|
||||||
{
|
|
||||||
private const uint MaxSuccessors = 2;
|
|
||||||
|
|
||||||
private int _succCount;
|
|
||||||
private BasicBlock _succ0;
|
|
||||||
private BasicBlock _succ1;
|
|
||||||
private HashSet<BasicBlock> _domFrontiers;
|
|
||||||
|
|
||||||
public int Index { get; set; }
|
|
||||||
public BasicBlockFrequency Frequency { get; set; }
|
|
||||||
public BasicBlock ListPrevious { get; set; }
|
|
||||||
public BasicBlock ListNext { get; set; }
|
|
||||||
public IntrusiveList<Operation> Operations { get; }
|
|
||||||
public List<BasicBlock> Predecessors { get; }
|
|
||||||
public BasicBlock ImmediateDominator { get; set; }
|
|
||||||
|
|
||||||
public int SuccessorsCount => _succCount;
|
|
||||||
|
|
||||||
public HashSet<BasicBlock> DominanceFrontiers
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_domFrontiers == null)
|
|
||||||
{
|
|
||||||
_domFrontiers = new HashSet<BasicBlock>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _domFrontiers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicBlock() : this(index: -1) { }
|
|
||||||
|
|
||||||
public BasicBlock(int index)
|
|
||||||
{
|
|
||||||
Operations = new IntrusiveList<Operation>();
|
|
||||||
Predecessors = new List<BasicBlock>();
|
|
||||||
|
|
||||||
Index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddSuccessor(BasicBlock block)
|
|
||||||
{
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
ThrowNull(nameof(block));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)_succCount + 1 > MaxSuccessors)
|
|
||||||
{
|
|
||||||
ThrowSuccessorOverflow();
|
|
||||||
}
|
|
||||||
|
|
||||||
block.Predecessors.Add(this);
|
|
||||||
|
|
||||||
GetSuccessorUnsafe(_succCount++) = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveSuccessor(int index)
|
|
||||||
{
|
|
||||||
if ((uint)index >= (uint)_succCount)
|
|
||||||
{
|
|
||||||
ThrowOutOfRange(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
|
|
||||||
|
|
||||||
oldBlock.Predecessors.Remove(this);
|
|
||||||
oldBlock = null;
|
|
||||||
|
|
||||||
if (index == 0)
|
|
||||||
{
|
|
||||||
_succ0 = _succ1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_succCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicBlock GetSuccessor(int index)
|
|
||||||
{
|
|
||||||
if ((uint)index >= (uint)_succCount)
|
|
||||||
{
|
|
||||||
ThrowOutOfRange(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetSuccessorUnsafe(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ref BasicBlock GetSuccessorUnsafe(int index)
|
|
||||||
{
|
|
||||||
return ref Unsafe.Add(ref _succ0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSuccessor(int index, BasicBlock block)
|
|
||||||
{
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
ThrowNull(nameof(block));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)index >= (uint)_succCount)
|
|
||||||
{
|
|
||||||
ThrowOutOfRange(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
|
|
||||||
|
|
||||||
oldBlock.Predecessors.Remove(this);
|
|
||||||
block.Predecessors.Add(this);
|
|
||||||
|
|
||||||
oldBlock = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Append(Operation node)
|
|
||||||
{
|
|
||||||
Operation last = Operations.Last;
|
|
||||||
|
|
||||||
// Append node before terminal or to end if no terminal.
|
|
||||||
if (last == default)
|
|
||||||
{
|
|
||||||
Operations.AddLast(node);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (last.Instruction)
|
|
||||||
{
|
|
||||||
case Instruction.Return:
|
|
||||||
case Instruction.Tailcall:
|
|
||||||
case Instruction.BranchIf:
|
|
||||||
Operations.AddBefore(last, node);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Operations.AddLast(node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ThrowNull(string name) => throw new ArgumentNullException(name);
|
|
||||||
private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
|
|
||||||
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
|
|
||||||
|
|
||||||
public bool Equals(BasicBlock other)
|
|
||||||
{
|
|
||||||
return other == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return Equals(obj as BasicBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
namespace ARMeilleure.IntermediateRepresentation
|
|
||||||
{
|
|
||||||
enum Intrinsic : ushort
|
|
||||||
{
|
|
||||||
X86Addpd,
|
|
||||||
X86Addps,
|
|
||||||
X86Addsd,
|
|
||||||
X86Addss,
|
|
||||||
X86Aesdec,
|
|
||||||
X86Aesdeclast,
|
|
||||||
X86Aesenc,
|
|
||||||
X86Aesenclast,
|
|
||||||
X86Aesimc,
|
|
||||||
X86Andnpd,
|
|
||||||
X86Andnps,
|
|
||||||
X86Andpd,
|
|
||||||
X86Andps,
|
|
||||||
X86Blendvpd,
|
|
||||||
X86Blendvps,
|
|
||||||
X86Cmppd,
|
|
||||||
X86Cmpps,
|
|
||||||
X86Cmpsd,
|
|
||||||
X86Cmpss,
|
|
||||||
X86Comisdeq,
|
|
||||||
X86Comisdge,
|
|
||||||
X86Comisdlt,
|
|
||||||
X86Comisseq,
|
|
||||||
X86Comissge,
|
|
||||||
X86Comisslt,
|
|
||||||
X86Crc32,
|
|
||||||
X86Crc32_16,
|
|
||||||
X86Crc32_8,
|
|
||||||
X86Cvtdq2pd,
|
|
||||||
X86Cvtdq2ps,
|
|
||||||
X86Cvtpd2dq,
|
|
||||||
X86Cvtpd2ps,
|
|
||||||
X86Cvtps2dq,
|
|
||||||
X86Cvtps2pd,
|
|
||||||
X86Cvtsd2si,
|
|
||||||
X86Cvtsd2ss,
|
|
||||||
X86Cvtsi2sd,
|
|
||||||
X86Cvtsi2si,
|
|
||||||
X86Cvtsi2ss,
|
|
||||||
X86Cvtss2sd,
|
|
||||||
X86Cvtss2si,
|
|
||||||
X86Divpd,
|
|
||||||
X86Divps,
|
|
||||||
X86Divsd,
|
|
||||||
X86Divss,
|
|
||||||
X86Haddpd,
|
|
||||||
X86Haddps,
|
|
||||||
X86Insertps,
|
|
||||||
X86Maxpd,
|
|
||||||
X86Maxps,
|
|
||||||
X86Maxsd,
|
|
||||||
X86Maxss,
|
|
||||||
X86Minpd,
|
|
||||||
X86Minps,
|
|
||||||
X86Minsd,
|
|
||||||
X86Minss,
|
|
||||||
X86Movhlps,
|
|
||||||
X86Movlhps,
|
|
||||||
X86Movss,
|
|
||||||
X86Mulpd,
|
|
||||||
X86Mulps,
|
|
||||||
X86Mulsd,
|
|
||||||
X86Mulss,
|
|
||||||
X86Mxcsrmb,
|
|
||||||
X86Mxcsrub,
|
|
||||||
X86Paddb,
|
|
||||||
X86Paddd,
|
|
||||||
X86Paddq,
|
|
||||||
X86Paddw,
|
|
||||||
X86Pand,
|
|
||||||
X86Pandn,
|
|
||||||
X86Pavgb,
|
|
||||||
X86Pavgw,
|
|
||||||
X86Pblendvb,
|
|
||||||
X86Pclmulqdq,
|
|
||||||
X86Pcmpeqb,
|
|
||||||
X86Pcmpeqd,
|
|
||||||
X86Pcmpeqq,
|
|
||||||
X86Pcmpeqw,
|
|
||||||
X86Pcmpgtb,
|
|
||||||
X86Pcmpgtd,
|
|
||||||
X86Pcmpgtq,
|
|
||||||
X86Pcmpgtw,
|
|
||||||
X86Pmaxsb,
|
|
||||||
X86Pmaxsd,
|
|
||||||
X86Pmaxsw,
|
|
||||||
X86Pmaxub,
|
|
||||||
X86Pmaxud,
|
|
||||||
X86Pmaxuw,
|
|
||||||
X86Pminsb,
|
|
||||||
X86Pminsd,
|
|
||||||
X86Pminsw,
|
|
||||||
X86Pminub,
|
|
||||||
X86Pminud,
|
|
||||||
X86Pminuw,
|
|
||||||
X86Pmovsxbw,
|
|
||||||
X86Pmovsxdq,
|
|
||||||
X86Pmovsxwd,
|
|
||||||
X86Pmovzxbw,
|
|
||||||
X86Pmovzxdq,
|
|
||||||
X86Pmovzxwd,
|
|
||||||
X86Pmulld,
|
|
||||||
X86Pmullw,
|
|
||||||
X86Popcnt,
|
|
||||||
X86Por,
|
|
||||||
X86Pshufb,
|
|
||||||
X86Pshufd,
|
|
||||||
X86Pslld,
|
|
||||||
X86Pslldq,
|
|
||||||
X86Psllq,
|
|
||||||
X86Psllw,
|
|
||||||
X86Psrad,
|
|
||||||
X86Psraw,
|
|
||||||
X86Psrld,
|
|
||||||
X86Psrlq,
|
|
||||||
X86Psrldq,
|
|
||||||
X86Psrlw,
|
|
||||||
X86Psubb,
|
|
||||||
X86Psubd,
|
|
||||||
X86Psubq,
|
|
||||||
X86Psubw,
|
|
||||||
X86Punpckhbw,
|
|
||||||
X86Punpckhdq,
|
|
||||||
X86Punpckhqdq,
|
|
||||||
X86Punpckhwd,
|
|
||||||
X86Punpcklbw,
|
|
||||||
X86Punpckldq,
|
|
||||||
X86Punpcklqdq,
|
|
||||||
X86Punpcklwd,
|
|
||||||
X86Pxor,
|
|
||||||
X86Rcpps,
|
|
||||||
X86Rcpss,
|
|
||||||
X86Roundpd,
|
|
||||||
X86Roundps,
|
|
||||||
X86Roundsd,
|
|
||||||
X86Roundss,
|
|
||||||
X86Rsqrtps,
|
|
||||||
X86Rsqrtss,
|
|
||||||
X86Shufpd,
|
|
||||||
X86Shufps,
|
|
||||||
X86Sqrtpd,
|
|
||||||
X86Sqrtps,
|
|
||||||
X86Sqrtsd,
|
|
||||||
X86Sqrtss,
|
|
||||||
X86Subpd,
|
|
||||||
X86Subps,
|
|
||||||
X86Subsd,
|
|
||||||
X86Subss,
|
|
||||||
X86Unpckhpd,
|
|
||||||
X86Unpckhps,
|
|
||||||
X86Unpcklpd,
|
|
||||||
X86Unpcklps,
|
|
||||||
X86Vcvtph2ps,
|
|
||||||
X86Vcvtps2ph,
|
|
||||||
X86Vfmadd231ps,
|
|
||||||
X86Vfmadd231sd,
|
|
||||||
X86Vfmadd231ss,
|
|
||||||
X86Vfmsub231sd,
|
|
||||||
X86Vfmsub231ss,
|
|
||||||
X86Vfnmadd231ps,
|
|
||||||
X86Vfnmadd231sd,
|
|
||||||
X86Vfnmadd231ss,
|
|
||||||
X86Vfnmsub231sd,
|
|
||||||
X86Vfnmsub231ss,
|
|
||||||
X86Xorpd,
|
|
||||||
X86Xorps
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,525 +0,0 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
|
||||||
using ARMeilleure.Common;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.IntermediateRepresentation
|
|
||||||
{
|
|
||||||
unsafe struct Operand : IEquatable<Operand>
|
|
||||||
{
|
|
||||||
internal struct Data
|
|
||||||
{
|
|
||||||
public byte Kind;
|
|
||||||
public byte Type;
|
|
||||||
public byte SymbolType;
|
|
||||||
public ushort AssignmentsCount;
|
|
||||||
public ushort AssignmentsCapacity;
|
|
||||||
public ushort UsesCount;
|
|
||||||
public ushort UsesCapacity;
|
|
||||||
public Operation* Assignments;
|
|
||||||
public Operation* Uses;
|
|
||||||
public ulong Value;
|
|
||||||
public ulong SymbolValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Data* _data;
|
|
||||||
|
|
||||||
public OperandKind Kind
|
|
||||||
{
|
|
||||||
get => (OperandKind)_data->Kind;
|
|
||||||
private set => _data->Kind = (byte)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperandType Type
|
|
||||||
{
|
|
||||||
get => (OperandType)_data->Type;
|
|
||||||
private set => _data->Type = (byte)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Value
|
|
||||||
{
|
|
||||||
get => _data->Value;
|
|
||||||
private set => _data->Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Symbol Symbol
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind != OperandKind.Memory);
|
|
||||||
|
|
||||||
return new Symbol((SymbolType)_data->SymbolType, _data->SymbolValue);
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind != OperandKind.Memory);
|
|
||||||
|
|
||||||
if (value.Type == SymbolType.None)
|
|
||||||
{
|
|
||||||
_data->SymbolType = (byte)SymbolType.None;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_data->SymbolType = (byte)value.Type;
|
|
||||||
_data->SymbolValue = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<Operation> Assignments
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind != OperandKind.Memory);
|
|
||||||
|
|
||||||
return new ReadOnlySpan<Operation>(_data->Assignments, _data->AssignmentsCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<Operation> Uses
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind != OperandKind.Memory);
|
|
||||||
|
|
||||||
return new ReadOnlySpan<Operation>(_data->Uses, _data->UsesCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int UsesCount => _data->UsesCount;
|
|
||||||
public int AssignmentsCount => _data->AssignmentsCount;
|
|
||||||
|
|
||||||
public bool Relocatable => Symbol.Type != SymbolType.None;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Register GetRegister()
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind == OperandKind.Register);
|
|
||||||
|
|
||||||
return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public MemoryOperand GetMemory()
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind == OperandKind.Memory);
|
|
||||||
|
|
||||||
return new MemoryOperand(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLocalNumber()
|
|
||||||
{
|
|
||||||
Debug.Assert(Kind == OperandKind.LocalVariable);
|
|
||||||
|
|
||||||
return (int)Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte AsByte()
|
|
||||||
{
|
|
||||||
return (byte)Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short AsInt16()
|
|
||||||
{
|
|
||||||
return (short)Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int AsInt32()
|
|
||||||
{
|
|
||||||
return (int)Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long AsInt64()
|
|
||||||
{
|
|
||||||
return (long)Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float AsFloat()
|
|
||||||
{
|
|
||||||
return BitConverter.Int32BitsToSingle((int)Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double AsDouble()
|
|
||||||
{
|
|
||||||
return BitConverter.Int64BitsToDouble((long)Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal ref ulong GetValueUnsafe()
|
|
||||||
{
|
|
||||||
return ref _data->Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NumberLocal(int number)
|
|
||||||
{
|
|
||||||
if (Kind != OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The operand is not a local variable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Value = (ulong)number;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddAssignment(Operation operation)
|
|
||||||
{
|
|
||||||
if (Kind == OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
Add(operation, ref _data->Assignments, ref _data->AssignmentsCount, ref _data->AssignmentsCapacity);
|
|
||||||
}
|
|
||||||
else if (Kind == OperandKind.Memory)
|
|
||||||
{
|
|
||||||
MemoryOperand memOp = GetMemory();
|
|
||||||
Operand addr = memOp.BaseAddress;
|
|
||||||
Operand index = memOp.Index;
|
|
||||||
|
|
||||||
if (addr != default)
|
|
||||||
{
|
|
||||||
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != default)
|
|
||||||
{
|
|
||||||
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveAssignment(Operation operation)
|
|
||||||
{
|
|
||||||
if (Kind == OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
Remove(operation, ref _data->Assignments, ref _data->AssignmentsCount);
|
|
||||||
}
|
|
||||||
else if (Kind == OperandKind.Memory)
|
|
||||||
{
|
|
||||||
MemoryOperand memOp = GetMemory();
|
|
||||||
Operand addr = memOp.BaseAddress;
|
|
||||||
Operand index = memOp.Index;
|
|
||||||
|
|
||||||
if (addr != default)
|
|
||||||
{
|
|
||||||
Remove(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != default)
|
|
||||||
{
|
|
||||||
Remove(operation, ref index._data->Assignments, ref index._data->AssignmentsCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddUse(Operation operation)
|
|
||||||
{
|
|
||||||
if (Kind == OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
Add(operation, ref _data->Uses, ref _data->UsesCount, ref _data->UsesCapacity);
|
|
||||||
}
|
|
||||||
else if (Kind == OperandKind.Memory)
|
|
||||||
{
|
|
||||||
MemoryOperand memOp = GetMemory();
|
|
||||||
Operand addr = memOp.BaseAddress;
|
|
||||||
Operand index = memOp.Index;
|
|
||||||
|
|
||||||
if (addr != default)
|
|
||||||
{
|
|
||||||
Add(operation, ref addr._data->Uses, ref addr._data->UsesCount, ref addr._data->UsesCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != default)
|
|
||||||
{
|
|
||||||
Add(operation, ref index._data->Uses, ref index._data->UsesCount, ref index._data->UsesCapacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveUse(Operation operation)
|
|
||||||
{
|
|
||||||
if (Kind == OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
Remove(operation, ref _data->Uses, ref _data->UsesCount);
|
|
||||||
}
|
|
||||||
else if (Kind == OperandKind.Memory)
|
|
||||||
{
|
|
||||||
MemoryOperand memOp = GetMemory();
|
|
||||||
Operand addr = memOp.BaseAddress;
|
|
||||||
Operand index = memOp.Index;
|
|
||||||
|
|
||||||
if (addr != default)
|
|
||||||
{
|
|
||||||
Remove(operation, ref addr._data->Uses, ref addr._data->UsesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != default)
|
|
||||||
{
|
|
||||||
Remove(operation, ref index._data->Uses, ref index._data->UsesCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void New<T>(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
capacity = initialCapacity;
|
|
||||||
data = Allocators.References.Allocate<T>(initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
|
|
||||||
{
|
|
||||||
if (count < capacity)
|
|
||||||
{
|
|
||||||
data[(uint)count++] = item;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could not add item in the fast path, fallback onto the slow path.
|
|
||||||
ExpandAdd(item, ref data, ref count, ref capacity);
|
|
||||||
|
|
||||||
static void ExpandAdd(T item, ref T* data, ref ushort count, ref ushort capacity)
|
|
||||||
{
|
|
||||||
ushort newCount = checked((ushort)(count + 1));
|
|
||||||
ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
|
|
||||||
|
|
||||||
var oldSpan = new Span<T>(data, count);
|
|
||||||
|
|
||||||
capacity = newCapacity;
|
|
||||||
data = Allocators.References.Allocate<T>(capacity);
|
|
||||||
|
|
||||||
oldSpan.CopyTo(new Span<T>(data, count));
|
|
||||||
|
|
||||||
data[count] = item;
|
|
||||||
count = newCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
|
|
||||||
{
|
|
||||||
var span = new Span<T>(data, count);
|
|
||||||
|
|
||||||
for (int i = 0; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
if (EqualityComparer<T>.Default.Equals(span[i], item))
|
|
||||||
{
|
|
||||||
if (i + 1 < count)
|
|
||||||
{
|
|
||||||
span.Slice(i + 1).CopyTo(span.Slice(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
count--;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
if (Kind == OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (int)Value ^ ((int)Kind << 16) ^ ((int)Type << 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(Operand operand)
|
|
||||||
{
|
|
||||||
return operand._data == _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is Operand operand && Equals(operand);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Operand a, Operand b)
|
|
||||||
{
|
|
||||||
return a.Equals(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Operand a, Operand b)
|
|
||||||
{
|
|
||||||
return !a.Equals(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory
|
|
||||||
{
|
|
||||||
private const int InternTableSize = 256;
|
|
||||||
private const int InternTableProbeLength = 8;
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static Data* _internTable;
|
|
||||||
|
|
||||||
private static Data* InternTable
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_internTable == null)
|
|
||||||
{
|
|
||||||
_internTable = (Data*)NativeAllocator.Instance.Allocate((uint)sizeof(Data) * InternTableSize);
|
|
||||||
|
|
||||||
// Make sure the table is zeroed.
|
|
||||||
new Span<Data>(_internTable, InternTableSize).Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _internTable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand Make(OperandKind kind, OperandType type, ulong value, Symbol symbol = default)
|
|
||||||
{
|
|
||||||
Debug.Assert(kind != OperandKind.None);
|
|
||||||
|
|
||||||
Data* data = null;
|
|
||||||
|
|
||||||
// If constant or register, then try to look up in the intern table before allocating.
|
|
||||||
if (kind == OperandKind.Constant || kind == OperandKind.Register)
|
|
||||||
{
|
|
||||||
uint hash = (uint)HashCode.Combine(kind, type, value);
|
|
||||||
|
|
||||||
// Look in the next InternTableProbeLength slots for a match.
|
|
||||||
for (uint i = 0; i < InternTableProbeLength; i++)
|
|
||||||
{
|
|
||||||
Operand interned = new();
|
|
||||||
interned._data = &InternTable[(hash + i) % InternTableSize];
|
|
||||||
|
|
||||||
// If slot matches the allocation request then return that slot.
|
|
||||||
if (interned.Kind == kind && interned.Type == type && interned.Value == value && interned.Symbol == symbol)
|
|
||||||
{
|
|
||||||
return interned;
|
|
||||||
}
|
|
||||||
// Otherwise if the slot is not occupied, we store in that slot.
|
|
||||||
else if (interned.Kind == OperandKind.None)
|
|
||||||
{
|
|
||||||
data = interned._data;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we could not get a slot from the intern table, we allocate somewhere else and store there.
|
|
||||||
if (data == null)
|
|
||||||
{
|
|
||||||
data = Allocators.Operands.Allocate<Data>();
|
|
||||||
}
|
|
||||||
|
|
||||||
*data = default;
|
|
||||||
|
|
||||||
Operand result = new();
|
|
||||||
result._data = data;
|
|
||||||
result.Value = value;
|
|
||||||
result.Kind = kind;
|
|
||||||
result.Type = type;
|
|
||||||
|
|
||||||
if (kind != OperandKind.Memory)
|
|
||||||
{
|
|
||||||
result.Symbol = symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If local variable, then the use and def list is initialized with default sizes.
|
|
||||||
if (kind == OperandKind.LocalVariable)
|
|
||||||
{
|
|
||||||
New(ref result._data->Assignments, ref result._data->AssignmentsCount, ref result._data->AssignmentsCapacity, 1);
|
|
||||||
New(ref result._data->Uses, ref result._data->UsesCount, ref result._data->UsesCapacity, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(OperandType type, long value)
|
|
||||||
{
|
|
||||||
Debug.Assert(type is OperandType.I32 or OperandType.I64);
|
|
||||||
|
|
||||||
return type == OperandType.I32 ? Const((int)value) : Const(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(bool value)
|
|
||||||
{
|
|
||||||
return Const(value ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(int value)
|
|
||||||
{
|
|
||||||
return Const((uint)value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(uint value)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Constant, OperandType.I32, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(long value)
|
|
||||||
{
|
|
||||||
return Const(value, symbol: default);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const<T>(ref T reference, Symbol symbol = default)
|
|
||||||
{
|
|
||||||
return Const((long)Unsafe.AsPointer(ref reference), symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(long value, Symbol symbol)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Constant, OperandType.I64, (ulong)value, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Const(ulong value)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Constant, OperandType.I64, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand ConstF(float value)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Constant, OperandType.FP32, (ulong)BitConverter.SingleToInt32Bits(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand ConstF(double value)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Constant, OperandType.FP64, (ulong)BitConverter.DoubleToInt64Bits(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Label()
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Label, OperandType.None, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Local(OperandType type)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.LocalVariable, type, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Register(int index, RegisterType regType, OperandType type)
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Register, type, (ulong)((int)regType << 24 | index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand Undef()
|
|
||||||
{
|
|
||||||
return Make(OperandKind.Undefined, OperandType.None, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand MemoryOp(
|
|
||||||
OperandType type,
|
|
||||||
Operand baseAddress,
|
|
||||||
Operand index = default,
|
|
||||||
Multiplier scale = Multiplier.x1,
|
|
||||||
int displacement = 0)
|
|
||||||
{
|
|
||||||
Operand result = Make(OperandKind.Memory, type, 0);
|
|
||||||
|
|
||||||
MemoryOperand memory = result.GetMemory();
|
|
||||||
memory.BaseAddress = baseAddress;
|
|
||||||
memory.Index = index;
|
|
||||||
memory.Scale = scale;
|
|
||||||
memory.Displacement = displacement;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.IntermediateRepresentation
|
|
||||||
{
|
|
||||||
enum OperandType
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
I32,
|
|
||||||
I64,
|
|
||||||
FP32,
|
|
||||||
FP64,
|
|
||||||
V128
|
|
||||||
}
|
|
||||||
|
|
||||||
static class OperandTypeExtensions
|
|
||||||
{
|
|
||||||
public static bool IsInteger(this OperandType type)
|
|
||||||
{
|
|
||||||
return type == OperandType.I32 ||
|
|
||||||
type == OperandType.I64;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RegisterType ToRegisterType(this OperandType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case OperandType.FP32: return RegisterType.Vector;
|
|
||||||
case OperandType.FP64: return RegisterType.Vector;
|
|
||||||
case OperandType.I32: return RegisterType.Integer;
|
|
||||||
case OperandType.I64: return RegisterType.Integer;
|
|
||||||
case OperandType.V128: return RegisterType.Vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetSizeInBytes(this OperandType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case OperandType.FP32: return 4;
|
|
||||||
case OperandType.FP64: return 8;
|
|
||||||
case OperandType.I32: return 4;
|
|
||||||
case OperandType.I64: return 8;
|
|
||||||
case OperandType.V128: return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.IntermediateRepresentation
|
|
||||||
{
|
|
||||||
struct Register : IEquatable<Register>
|
|
||||||
{
|
|
||||||
public int Index { get; }
|
|
||||||
|
|
||||||
public RegisterType Type { get; }
|
|
||||||
|
|
||||||
public Register(int index, RegisterType type)
|
|
||||||
{
|
|
||||||
Index = index;
|
|
||||||
Type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (ushort)Index | ((int)Type << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Register x, Register y)
|
|
||||||
{
|
|
||||||
return x.Equals(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Register x, Register y)
|
|
||||||
{
|
|
||||||
return !x.Equals(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is Register reg && Equals(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(Register other)
|
|
||||||
{
|
|
||||||
return other.Index == Index &&
|
|
||||||
other.Type == Type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
using ARMeilleure.CodeGen.X86;
|
|
||||||
|
|
||||||
namespace ARMeilleure
|
|
||||||
{
|
|
||||||
public static class Optimizations
|
|
||||||
{
|
|
||||||
public static bool FastFP { get; set; } = true;
|
|
||||||
|
|
||||||
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
|
||||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
|
||||||
|
|
||||||
public static bool UseSseIfAvailable { get; set; } = true;
|
|
||||||
public static bool UseSse2IfAvailable { get; set; } = true;
|
|
||||||
public static bool UseSse3IfAvailable { get; set; } = true;
|
|
||||||
public static bool UseSsse3IfAvailable { get; set; } = true;
|
|
||||||
public static bool UseSse41IfAvailable { get; set; } = true;
|
|
||||||
public static bool UseSse42IfAvailable { get; set; } = true;
|
|
||||||
public static bool UsePopCntIfAvailable { get; set; } = true;
|
|
||||||
public static bool UseAvxIfAvailable { get; set; } = true;
|
|
||||||
public static bool UseF16cIfAvailable { get; set; } = true;
|
|
||||||
public static bool UseFmaIfAvailable { get; set; } = true;
|
|
||||||
public static bool UseAesniIfAvailable { get; set; } = true;
|
|
||||||
public static bool UsePclmulqdqIfAvailable { get; set; } = true;
|
|
||||||
|
|
||||||
public static bool ForceLegacySse
|
|
||||||
{
|
|
||||||
get => HardwareCapabilities.ForceLegacySse;
|
|
||||||
set => HardwareCapabilities.ForceLegacySse = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool UseSse => UseSseIfAvailable && HardwareCapabilities.SupportsSse;
|
|
||||||
internal static bool UseSse2 => UseSse2IfAvailable && HardwareCapabilities.SupportsSse2;
|
|
||||||
internal static bool UseSse3 => UseSse3IfAvailable && HardwareCapabilities.SupportsSse3;
|
|
||||||
internal static bool UseSsse3 => UseSsse3IfAvailable && HardwareCapabilities.SupportsSsse3;
|
|
||||||
internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41;
|
|
||||||
internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42;
|
|
||||||
internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt;
|
|
||||||
internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
|
||||||
internal static bool UseF16c => UseF16cIfAvailable && HardwareCapabilities.SupportsF16c;
|
|
||||||
internal static bool UseFma => UseFmaIfAvailable && HardwareCapabilities.SupportsFma;
|
|
||||||
internal static bool UseAesni => UseAesniIfAvailable && HardwareCapabilities.SupportsAesni;
|
|
||||||
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && HardwareCapabilities.SupportsPclmulqdq;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,321 +0,0 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
struct SignalHandlerRange
|
|
||||||
{
|
|
||||||
public int IsActive;
|
|
||||||
public nuint RangeAddress;
|
|
||||||
public nuint RangeEndAddress;
|
|
||||||
public IntPtr ActionPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
struct SignalHandlerConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
|
|
||||||
/// </summary>
|
|
||||||
public int StructAddressOffset;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
|
|
||||||
/// </summary>
|
|
||||||
public int StructWriteOffset;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The sigaction handler that was registered before this one. (unix only)
|
|
||||||
/// </summary>
|
|
||||||
public nuint UnixOldSigaction;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
|
|
||||||
/// </summary>
|
|
||||||
public int UnixOldSigaction3Arg;
|
|
||||||
|
|
||||||
public SignalHandlerRange Range0;
|
|
||||||
public SignalHandlerRange Range1;
|
|
||||||
public SignalHandlerRange Range2;
|
|
||||||
public SignalHandlerRange Range3;
|
|
||||||
public SignalHandlerRange Range4;
|
|
||||||
public SignalHandlerRange Range5;
|
|
||||||
public SignalHandlerRange Range6;
|
|
||||||
public SignalHandlerRange Range7;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NativeSignalHandler
|
|
||||||
{
|
|
||||||
private delegate void UnixExceptionHandler(int sig, IntPtr info, IntPtr ucontext);
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
|
||||||
private delegate int VectoredExceptionHandler(IntPtr exceptionInfo);
|
|
||||||
|
|
||||||
private const int MaxTrackedRanges = 8;
|
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
|
||||||
private const int StructWriteOffset = 4;
|
|
||||||
private const int UnixOldSigaction = 8;
|
|
||||||
private const int UnixOldSigaction3Arg = 16;
|
|
||||||
private const int RangeOffset = 20;
|
|
||||||
|
|
||||||
private const int EXCEPTION_CONTINUE_SEARCH = 0;
|
|
||||||
private const int EXCEPTION_CONTINUE_EXECUTION = -1;
|
|
||||||
|
|
||||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
|
||||||
|
|
||||||
private const ulong PageSize = 0x1000;
|
|
||||||
private const ulong PageMask = PageSize - 1;
|
|
||||||
|
|
||||||
private static IntPtr _handlerConfig;
|
|
||||||
private static IntPtr _signalHandlerPtr;
|
|
||||||
private static IntPtr _signalHandlerHandle;
|
|
||||||
|
|
||||||
private static readonly object _lock = new object();
|
|
||||||
private static bool _initialized;
|
|
||||||
|
|
||||||
static NativeSignalHandler()
|
|
||||||
{
|
|
||||||
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
|
|
||||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
|
||||||
|
|
||||||
config = new SignalHandlerConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InitializeSignalHandler()
|
|
||||||
{
|
|
||||||
if (_initialized) return;
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_initialized) return;
|
|
||||||
|
|
||||||
bool unix = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS();
|
|
||||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
|
||||||
|
|
||||||
if (unix)
|
|
||||||
{
|
|
||||||
// Unix siginfo struct locations.
|
|
||||||
// NOTE: These are incredibly likely to be different between kernel version and architectures.
|
|
||||||
|
|
||||||
config.StructAddressOffset = 16; // si_addr
|
|
||||||
config.StructWriteOffset = 8; // si_code
|
|
||||||
|
|
||||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
|
||||||
|
|
||||||
SigAction old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
|
||||||
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
|
|
||||||
config.UnixOldSigaction3Arg = old.sa_flags & 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
config.StructAddressOffset = 40; // ExceptionInformation1
|
|
||||||
config.StructWriteOffset = 32; // ExceptionInformation0
|
|
||||||
|
|
||||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig));
|
|
||||||
|
|
||||||
_signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
_initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
|
||||||
{
|
|
||||||
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
|
|
||||||
{
|
|
||||||
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
|
||||||
|
|
||||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
|
||||||
{
|
|
||||||
if (ranges[i].IsActive == 0)
|
|
||||||
{
|
|
||||||
ranges[i].RangeAddress = address;
|
|
||||||
ranges[i].RangeEndAddress = endAddress;
|
|
||||||
ranges[i].ActionPointer = action;
|
|
||||||
ranges[i].IsActive = 1;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe bool RemoveTrackedRegion(nuint address)
|
|
||||||
{
|
|
||||||
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
|
||||||
|
|
||||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
|
||||||
{
|
|
||||||
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
|
|
||||||
{
|
|
||||||
ranges[i].IsActive = 0;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite)
|
|
||||||
{
|
|
||||||
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
|
||||||
context.Copy(inRegionLocal, Const(0));
|
|
||||||
|
|
||||||
Operand endLabel = Label();
|
|
||||||
|
|
||||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
|
||||||
{
|
|
||||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
|
|
||||||
|
|
||||||
Operand nextLabel = Label();
|
|
||||||
|
|
||||||
Operand isActive = context.Load(OperandType.I32, Const((ulong)signalStructPtr + rangeBaseOffset));
|
|
||||||
|
|
||||||
context.BranchIfFalse(nextLabel, isActive);
|
|
||||||
|
|
||||||
Operand rangeAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 4));
|
|
||||||
Operand rangeEndAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 12));
|
|
||||||
|
|
||||||
// Is the fault address within this tracked region?
|
|
||||||
Operand inRange = context.BitwiseAnd(
|
|
||||||
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
|
||||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.Less)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Only call tracking if in range.
|
|
||||||
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
context.Copy(inRegionLocal, Const(1));
|
|
||||||
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~PageMask));
|
|
||||||
|
|
||||||
// Call the tracking action, with the pointer's relative offset to the base address.
|
|
||||||
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
|
|
||||||
context.Call(trackingActionPtr, OperandType.I32, offset, Const(PageSize), isWrite, Const(0));
|
|
||||||
|
|
||||||
context.Branch(endLabel);
|
|
||||||
|
|
||||||
context.MarkLabel(nextLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.MarkLabel(endLabel);
|
|
||||||
|
|
||||||
return context.Copy(inRegionLocal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
|
|
||||||
{
|
|
||||||
EmitterContext context = new EmitterContext();
|
|
||||||
|
|
||||||
// (int sig, SigInfo* sigInfo, void* ucontext)
|
|
||||||
Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1);
|
|
||||||
|
|
||||||
Operand structAddressOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructAddressOffset));
|
|
||||||
Operand structWriteOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructWriteOffset));
|
|
||||||
|
|
||||||
Operand faultAddress = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset)));
|
|
||||||
Operand writeFlag = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset)));
|
|
||||||
|
|
||||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
|
||||||
|
|
||||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
|
||||||
|
|
||||||
Operand endLabel = Label();
|
|
||||||
|
|
||||||
context.BranchIfTrue(endLabel, isInRegion);
|
|
||||||
|
|
||||||
Operand unixOldSigaction = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction));
|
|
||||||
Operand unixOldSigaction3Arg = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction3Arg));
|
|
||||||
Operand threeArgLabel = Label();
|
|
||||||
|
|
||||||
context.BranchIfTrue(threeArgLabel, unixOldSigaction3Arg);
|
|
||||||
|
|
||||||
context.Call(unixOldSigaction, OperandType.None, context.LoadArgument(OperandType.I32, 0));
|
|
||||||
context.Branch(endLabel);
|
|
||||||
|
|
||||||
context.MarkLabel(threeArgLabel);
|
|
||||||
|
|
||||||
context.Call(unixOldSigaction,
|
|
||||||
OperandType.None,
|
|
||||||
context.LoadArgument(OperandType.I32, 0),
|
|
||||||
sigInfoPtr,
|
|
||||||
context.LoadArgument(OperandType.I64, 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
context.MarkLabel(endLabel);
|
|
||||||
|
|
||||||
context.Return();
|
|
||||||
|
|
||||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
|
||||||
|
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq).Map<UnixExceptionHandler>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr)
|
|
||||||
{
|
|
||||||
EmitterContext context = new EmitterContext();
|
|
||||||
|
|
||||||
// (ExceptionPointers* exceptionInfo)
|
|
||||||
Operand exceptionInfoPtr = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
Operand exceptionRecordPtr = context.Load(OperandType.I64, exceptionInfoPtr);
|
|
||||||
|
|
||||||
// First thing's first - this catches a number of exceptions, but we only want access violations.
|
|
||||||
Operand validExceptionLabel = Label();
|
|
||||||
|
|
||||||
Operand exceptionCode = context.Load(OperandType.I32, exceptionRecordPtr);
|
|
||||||
|
|
||||||
context.BranchIf(validExceptionLabel, exceptionCode, Const(EXCEPTION_ACCESS_VIOLATION), Comparison.Equal);
|
|
||||||
|
|
||||||
context.Return(Const(EXCEPTION_CONTINUE_SEARCH)); // Don't handle this one.
|
|
||||||
|
|
||||||
context.MarkLabel(validExceptionLabel);
|
|
||||||
|
|
||||||
// Next, read the address of the invalid access, and whether it is a write or not.
|
|
||||||
|
|
||||||
Operand structAddressOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructAddressOffset));
|
|
||||||
Operand structWriteOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructWriteOffset));
|
|
||||||
|
|
||||||
Operand faultAddress = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset)));
|
|
||||||
Operand writeFlag = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset)));
|
|
||||||
|
|
||||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
|
||||||
|
|
||||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
|
||||||
|
|
||||||
Operand endLabel = Label();
|
|
||||||
|
|
||||||
// If the region check result is false, then run the next vectored exception handler.
|
|
||||||
|
|
||||||
context.BranchIfTrue(endLabel, isInRegion);
|
|
||||||
|
|
||||||
context.Return(Const(EXCEPTION_CONTINUE_SEARCH));
|
|
||||||
|
|
||||||
context.MarkLabel(endLabel);
|
|
||||||
|
|
||||||
// Otherwise, return to execution.
|
|
||||||
|
|
||||||
context.Return(Const(EXCEPTION_CONTINUE_EXECUTION));
|
|
||||||
|
|
||||||
// Compile and return the function.
|
|
||||||
|
|
||||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
|
||||||
|
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq).Map<VectoredExceptionHandler>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
unsafe struct SigSet
|
|
||||||
{
|
|
||||||
fixed long sa_mask[16];
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
struct SigAction
|
|
||||||
{
|
|
||||||
public IntPtr sa_handler;
|
|
||||||
public SigSet sa_mask;
|
|
||||||
public int sa_flags;
|
|
||||||
public IntPtr sa_restorer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class UnixSignalHandlerRegistration
|
|
||||||
{
|
|
||||||
private const int SIGSEGV = 11;
|
|
||||||
private const int SA_SIGINFO = 0x00000004;
|
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
|
||||||
private static extern int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
|
||||||
private static extern int sigemptyset(ref SigSet set);
|
|
||||||
|
|
||||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
|
||||||
{
|
|
||||||
SigAction sig = new SigAction
|
|
||||||
{
|
|
||||||
sa_handler = action,
|
|
||||||
sa_flags = SA_SIGINFO
|
|
||||||
};
|
|
||||||
|
|
||||||
sigemptyset(ref sig.sa_mask);
|
|
||||||
|
|
||||||
int result = sigaction(SIGSEGV, ref sig, out SigAction old);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Could not register sigaction. Error: {result}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool RestoreExceptionHandler(SigAction oldAction)
|
|
||||||
{
|
|
||||||
return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
|
||||||
{
|
|
||||||
class WindowsSignalHandlerRegistration
|
|
||||||
{
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
private static extern IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
private static extern ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
|
||||||
|
|
||||||
public static IntPtr RegisterExceptionHandler(IntPtr action)
|
|
||||||
{
|
|
||||||
return AddVectoredExceptionHandler(1, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool RemoveExceptionHandler(IntPtr handle)
|
|
||||||
{
|
|
||||||
return RemoveVectoredExceptionHandler(handle) != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
using ARMeilleure.Memory;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public class ExecutionContext
|
|
||||||
{
|
|
||||||
private const int MinCountForCheck = 4000;
|
|
||||||
|
|
||||||
private NativeContext _nativeContext;
|
|
||||||
|
|
||||||
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
|
|
||||||
|
|
||||||
private bool _interrupted;
|
|
||||||
|
|
||||||
private static Stopwatch _tickCounter;
|
|
||||||
|
|
||||||
private static double _hostTickFreq;
|
|
||||||
|
|
||||||
public uint CtrEl0 => 0x8444c004;
|
|
||||||
public uint DczidEl0 => 0x00000004;
|
|
||||||
|
|
||||||
public ulong CntfrqEl0 { get; set; }
|
|
||||||
public ulong CntpctEl0
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
double ticks = _tickCounter.ElapsedTicks * _hostTickFreq;
|
|
||||||
|
|
||||||
return (ulong)(ticks * CntfrqEl0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2
|
|
||||||
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
|
||||||
public ulong CntvctEl0 => CntpctEl0;
|
|
||||||
|
|
||||||
public static TimeSpan ElapsedTime => _tickCounter.Elapsed;
|
|
||||||
public static long ElapsedTicks => _tickCounter.ElapsedTicks;
|
|
||||||
public static double TickFrequency => _hostTickFreq;
|
|
||||||
|
|
||||||
public long TpidrEl0 { get; set; }
|
|
||||||
public long Tpidr { get; set; }
|
|
||||||
|
|
||||||
public FPCR Fpcr { get; set; }
|
|
||||||
public FPSR Fpsr { get; set; }
|
|
||||||
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
|
|
||||||
|
|
||||||
public bool IsAarch32 { get; set; }
|
|
||||||
|
|
||||||
internal ExecutionMode ExecutionMode
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsAarch32)
|
|
||||||
{
|
|
||||||
return GetPstateFlag(PState.TFlag)
|
|
||||||
? ExecutionMode.Aarch32Thumb
|
|
||||||
: ExecutionMode.Aarch32Arm;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ExecutionMode.Aarch64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Running
|
|
||||||
{
|
|
||||||
get => _nativeContext.GetRunning();
|
|
||||||
private set => _nativeContext.SetRunning(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<EventArgs> Interrupt;
|
|
||||||
public event EventHandler<InstExceptionEventArgs> Break;
|
|
||||||
public event EventHandler<InstExceptionEventArgs> SupervisorCall;
|
|
||||||
public event EventHandler<InstUndefinedEventArgs> Undefined;
|
|
||||||
|
|
||||||
static ExecutionContext()
|
|
||||||
{
|
|
||||||
_hostTickFreq = 1.0 / Stopwatch.Frequency;
|
|
||||||
|
|
||||||
_tickCounter = new Stopwatch();
|
|
||||||
_tickCounter.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExecutionContext(IJitMemoryAllocator allocator)
|
|
||||||
{
|
|
||||||
_nativeContext = new NativeContext(allocator);
|
|
||||||
|
|
||||||
Running = true;
|
|
||||||
|
|
||||||
_nativeContext.SetCounter(MinCountForCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong GetX(int index) => _nativeContext.GetX(index);
|
|
||||||
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
|
|
||||||
|
|
||||||
public V128 GetV(int index) => _nativeContext.GetV(index);
|
|
||||||
public void SetV(int index, V128 value) => _nativeContext.SetV(index, value);
|
|
||||||
|
|
||||||
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
|
|
||||||
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
|
|
||||||
|
|
||||||
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
|
||||||
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
|
||||||
|
|
||||||
internal void CheckInterrupt()
|
|
||||||
{
|
|
||||||
if (_interrupted)
|
|
||||||
{
|
|
||||||
_interrupted = false;
|
|
||||||
|
|
||||||
Interrupt?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
_nativeContext.SetCounter(MinCountForCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RequestInterrupt()
|
|
||||||
{
|
|
||||||
_interrupted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnBreak(ulong address, int imm)
|
|
||||||
{
|
|
||||||
Break?.Invoke(this, new InstExceptionEventArgs(address, imm));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnSupervisorCall(ulong address, int imm)
|
|
||||||
{
|
|
||||||
SupervisorCall?.Invoke(this, new InstExceptionEventArgs(address, imm));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnUndefined(ulong address, int opCode)
|
|
||||||
{
|
|
||||||
Undefined?.Invoke(this, new InstUndefinedEventArgs(address, opCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopRunning()
|
|
||||||
{
|
|
||||||
Running = false;
|
|
||||||
|
|
||||||
_nativeContext.SetCounter(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SuspendCounter()
|
|
||||||
{
|
|
||||||
_tickCounter.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ResumeCounter()
|
|
||||||
{
|
|
||||||
_tickCounter.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_nativeContext.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum FPCR : uint
|
|
||||||
{
|
|
||||||
Ufe = 1u << 11,
|
|
||||||
Fz = 1u << 24,
|
|
||||||
Dn = 1u << 25,
|
|
||||||
Ahp = 1u << 26,
|
|
||||||
|
|
||||||
A32Mask = 0x07FF9F00u
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FPCRExtensions
|
|
||||||
{
|
|
||||||
private const int RModeShift = 22;
|
|
||||||
|
|
||||||
public static FPRoundingMode GetRoundingMode(this FPCR fpcr)
|
|
||||||
{
|
|
||||||
return (FPRoundingMode)(((int)fpcr >> RModeShift) & 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public enum FPRoundingMode
|
|
||||||
{
|
|
||||||
ToNearest = 0,
|
|
||||||
TowardsPlusInfinity = 1,
|
|
||||||
TowardsMinusInfinity = 2,
|
|
||||||
TowardsZero = 3
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum FPSR : uint
|
|
||||||
{
|
|
||||||
Ufc = 1u << 3,
|
|
||||||
Qc = 1u << 27,
|
|
||||||
|
|
||||||
Nzcv = (1u << 31) | (1u << 30) | (1u << 29) | (1u << 28),
|
|
||||||
|
|
||||||
A32Mask = 0xF800009Fu
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public enum FPState
|
|
||||||
{
|
|
||||||
VFlag = 28,
|
|
||||||
CFlag = 29,
|
|
||||||
ZFlag = 30,
|
|
||||||
NFlag = 31
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public class InstExceptionEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public ulong Address { get; }
|
|
||||||
public int Id { get; }
|
|
||||||
|
|
||||||
public InstExceptionEventArgs(ulong address, int id)
|
|
||||||
{
|
|
||||||
Address = address;
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public class InstUndefinedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public ulong Address { get; }
|
|
||||||
public int OpCode { get; }
|
|
||||||
|
|
||||||
public InstUndefinedEventArgs(ulong address, int opCode)
|
|
||||||
{
|
|
||||||
Address = address;
|
|
||||||
OpCode = opCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,198 +0,0 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.Memory;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
class NativeContext : IDisposable
|
|
||||||
{
|
|
||||||
private unsafe struct NativeCtxStorage
|
|
||||||
{
|
|
||||||
public fixed ulong X[RegisterConsts.IntRegsCount];
|
|
||||||
public fixed ulong V[RegisterConsts.VecRegsCount * 2];
|
|
||||||
public fixed uint Flags[RegisterConsts.FlagsCount];
|
|
||||||
public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
|
|
||||||
public int Counter;
|
|
||||||
public ulong DispatchAddress;
|
|
||||||
public ulong ExclusiveAddress;
|
|
||||||
public ulong ExclusiveValueLow;
|
|
||||||
public ulong ExclusiveValueHigh;
|
|
||||||
public int Running;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static NativeCtxStorage _dummyStorage = new NativeCtxStorage();
|
|
||||||
|
|
||||||
private readonly IJitMemoryBlock _block;
|
|
||||||
|
|
||||||
public IntPtr BasePtr => _block.Pointer;
|
|
||||||
|
|
||||||
public NativeContext(IJitMemoryAllocator allocator)
|
|
||||||
{
|
|
||||||
_block = allocator.Allocate((ulong)Unsafe.SizeOf<NativeCtxStorage>());
|
|
||||||
|
|
||||||
GetStorage().ExclusiveAddress = ulong.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe ulong GetX(int index)
|
|
||||||
{
|
|
||||||
if ((uint)index >= RegisterConsts.IntRegsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetStorage().X[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetX(int index, ulong value)
|
|
||||||
{
|
|
||||||
if ((uint)index >= RegisterConsts.IntRegsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
GetStorage().X[index] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe V128 GetV(int index)
|
|
||||||
{
|
|
||||||
if ((uint)index >= RegisterConsts.VecRegsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new V128(GetStorage().V[index * 2 + 0], GetStorage().V[index * 2 + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetV(int index, V128 value)
|
|
||||||
{
|
|
||||||
if ((uint)index >= RegisterConsts.VecRegsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
GetStorage().V[index * 2 + 0] = value.Extract<ulong>(0);
|
|
||||||
GetStorage().V[index * 2 + 1] = value.Extract<ulong>(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe bool GetPstateFlag(PState flag)
|
|
||||||
{
|
|
||||||
if ((uint)flag >= RegisterConsts.FlagsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetStorage().Flags[(int)flag] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetPstateFlag(PState flag, bool value)
|
|
||||||
{
|
|
||||||
if ((uint)flag >= RegisterConsts.FlagsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
GetStorage().Flags[(int)flag] = value ? 1u : 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe bool GetFPStateFlag(FPState flag)
|
|
||||||
{
|
|
||||||
if ((uint)flag >= RegisterConsts.FpFlagsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetStorage().FpFlags[(int)flag] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetFPStateFlag(FPState flag, bool value)
|
|
||||||
{
|
|
||||||
if ((uint)flag >= RegisterConsts.FpFlagsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
GetStorage().FpFlags[(int)flag] = value ? 1u : 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCounter() => GetStorage().Counter;
|
|
||||||
public void SetCounter(int value) => GetStorage().Counter = value;
|
|
||||||
|
|
||||||
public bool GetRunning() => GetStorage().Running != 0;
|
|
||||||
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
|
||||||
|
|
||||||
public unsafe static int GetRegisterOffset(Register reg)
|
|
||||||
{
|
|
||||||
if (reg.Type == RegisterType.Integer)
|
|
||||||
{
|
|
||||||
if ((uint)reg.Index >= RegisterConsts.IntRegsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid register.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[reg.Index]);
|
|
||||||
}
|
|
||||||
else if (reg.Type == RegisterType.Vector)
|
|
||||||
{
|
|
||||||
if ((uint)reg.Index >= RegisterConsts.VecRegsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid register.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[reg.Index * 2]);
|
|
||||||
}
|
|
||||||
else if (reg.Type == RegisterType.Flag)
|
|
||||||
{
|
|
||||||
if ((uint)reg.Index >= RegisterConsts.FlagsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid register.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Flags[reg.Index]);
|
|
||||||
}
|
|
||||||
else /* if (reg.Type == RegisterType.FpFlag) */
|
|
||||||
{
|
|
||||||
if ((uint)reg.Index >= RegisterConsts.FpFlagsCount)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid register.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.FpFlags[reg.Index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetCounterOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetDispatchAddressOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DispatchAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetExclusiveAddressOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetExclusiveValueOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.ExclusiveValueLow);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetRunningOffset()
|
|
||||||
{
|
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
|
||||||
{
|
|
||||||
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer);
|
|
||||||
|
|
||||||
public void Dispose() => _block.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public enum PState
|
|
||||||
{
|
|
||||||
TFlag = 5,
|
|
||||||
EFlag = 9,
|
|
||||||
QFlag = 27,
|
|
||||||
VFlag = 28,
|
|
||||||
CFlag = 29,
|
|
||||||
ZFlag = 30,
|
|
||||||
NFlag = 31
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
using ARMeilleure.CodeGen.Linking;
|
|
||||||
using ARMeilleure.Common;
|
|
||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.Diagnostics;
|
|
||||||
using ARMeilleure.Instructions;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.Memory;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
class ArmEmitterContext : EmitterContext
|
|
||||||
{
|
|
||||||
private readonly Dictionary<ulong, Operand> _labels;
|
|
||||||
|
|
||||||
private OpCode _optOpLastCompare;
|
|
||||||
private OpCode _optOpLastFlagSet;
|
|
||||||
|
|
||||||
private Operand _optCmpTempN;
|
|
||||||
private Operand _optCmpTempM;
|
|
||||||
|
|
||||||
private Block _currBlock;
|
|
||||||
|
|
||||||
public Block CurrBlock
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _currBlock;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_currBlock = value;
|
|
||||||
|
|
||||||
ResetBlockState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpCode CurrOp { get; set; }
|
|
||||||
|
|
||||||
public IMemoryManager Memory { get; }
|
|
||||||
|
|
||||||
public bool HasPtc { get; }
|
|
||||||
|
|
||||||
public EntryTable<uint> CountTable { get; }
|
|
||||||
public AddressTable<ulong> FunctionTable { get; }
|
|
||||||
public TranslatorStubs Stubs { get; }
|
|
||||||
|
|
||||||
public ulong EntryAddress { get; }
|
|
||||||
public bool HighCq { get; }
|
|
||||||
public Aarch32Mode Mode { get; }
|
|
||||||
|
|
||||||
public ArmEmitterContext(
|
|
||||||
IMemoryManager memory,
|
|
||||||
EntryTable<uint> countTable,
|
|
||||||
AddressTable<ulong> funcTable,
|
|
||||||
TranslatorStubs stubs,
|
|
||||||
ulong entryAddress,
|
|
||||||
bool highCq,
|
|
||||||
Aarch32Mode mode)
|
|
||||||
{
|
|
||||||
HasPtc = Ptc.State != PtcState.Disabled;
|
|
||||||
Memory = memory;
|
|
||||||
CountTable = countTable;
|
|
||||||
FunctionTable = funcTable;
|
|
||||||
Stubs = stubs;
|
|
||||||
EntryAddress = entryAddress;
|
|
||||||
HighCq = highCq;
|
|
||||||
Mode = mode;
|
|
||||||
|
|
||||||
_labels = new Dictionary<ulong, Operand>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Operand Call(MethodInfo info, params Operand[] callArgs)
|
|
||||||
{
|
|
||||||
if (!HasPtc)
|
|
||||||
{
|
|
||||||
return base.Call(info, callArgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int index = Delegates.GetDelegateIndex(info);
|
|
||||||
IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
|
||||||
|
|
||||||
OperandType returnType = GetOperandType(info.ReturnType);
|
|
||||||
|
|
||||||
Symbol symbol = new Symbol(SymbolType.DelegateTable, (ulong)index);
|
|
||||||
|
|
||||||
Symbols.Add((ulong)funcPtr.ToInt64(), info.Name);
|
|
||||||
|
|
||||||
return Call(Const(funcPtr.ToInt64(), symbol), returnType, callArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operand GetLabel(ulong address)
|
|
||||||
{
|
|
||||||
if (!_labels.TryGetValue(address, out Operand label))
|
|
||||||
{
|
|
||||||
label = Label();
|
|
||||||
|
|
||||||
_labels.Add(address, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkComparison(Operand n, Operand m)
|
|
||||||
{
|
|
||||||
_optOpLastCompare = CurrOp;
|
|
||||||
|
|
||||||
_optCmpTempN = Copy(n);
|
|
||||||
_optCmpTempM = Copy(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkFlagSet(PState stateFlag)
|
|
||||||
{
|
|
||||||
// Set this only if any of the NZCV flag bits were modified.
|
|
||||||
// This is used to ensure that when emiting a direct IL branch
|
|
||||||
// instruction for compare + branch sequences, we're not expecting
|
|
||||||
// to use comparison values from an old instruction, when in fact
|
|
||||||
// the flags were already overwritten by another instruction further along.
|
|
||||||
if (stateFlag >= PState.VFlag)
|
|
||||||
{
|
|
||||||
_optOpLastFlagSet = CurrOp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResetBlockState()
|
|
||||||
{
|
|
||||||
_optOpLastCompare = null;
|
|
||||||
_optOpLastFlagSet = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operand TryGetComparisonResult(Condition condition)
|
|
||||||
{
|
|
||||||
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand n = _optCmpTempN;
|
|
||||||
Operand m = _optCmpTempM;
|
|
||||||
|
|
||||||
InstName cmpName = _optOpLastCompare.Instruction.Name;
|
|
||||||
|
|
||||||
if (cmpName == InstName.Subs)
|
|
||||||
{
|
|
||||||
switch (condition)
|
|
||||||
{
|
|
||||||
case Condition.Eq: return ICompareEqual (n, m);
|
|
||||||
case Condition.Ne: return ICompareNotEqual (n, m);
|
|
||||||
case Condition.GeUn: return ICompareGreaterOrEqualUI(n, m);
|
|
||||||
case Condition.LtUn: return ICompareLessUI (n, m);
|
|
||||||
case Condition.GtUn: return ICompareGreaterUI (n, m);
|
|
||||||
case Condition.LeUn: return ICompareLessOrEqualUI (n, m);
|
|
||||||
case Condition.Ge: return ICompareGreaterOrEqual (n, m);
|
|
||||||
case Condition.Lt: return ICompareLess (n, m);
|
|
||||||
case Condition.Gt: return ICompareGreater (n, m);
|
|
||||||
case Condition.Le: return ICompareLessOrEqual (n, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (cmpName == InstName.Adds && _optOpLastCompare is IOpCodeAluImm op)
|
|
||||||
{
|
|
||||||
// There are several limitations that needs to be taken into account for CMN comparisons:
|
|
||||||
// - The unsigned comparisons are not valid, as they depend on the
|
|
||||||
// carry flag value, and they will have different values for addition and
|
|
||||||
// subtraction. For addition, it's carry, and for subtraction, it's borrow.
|
|
||||||
// So, we need to make sure we're not doing a unsigned compare for the CMN case.
|
|
||||||
// - We can only do the optimization for the immediate variants,
|
|
||||||
// because when the second operand value is exactly INT_MIN, we can't
|
|
||||||
// negate the value as theres no positive counterpart.
|
|
||||||
// Such invalid values can't be encoded on the immediate encodings.
|
|
||||||
if (op.RegisterSize == RegisterSize.Int32)
|
|
||||||
{
|
|
||||||
m = Const((int)-op.Immediate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m = Const(-op.Immediate);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (condition)
|
|
||||||
{
|
|
||||||
case Condition.Eq: return ICompareEqual (n, m);
|
|
||||||
case Condition.Ne: return ICompareNotEqual (n, m);
|
|
||||||
case Condition.Ge: return ICompareGreaterOrEqual(n, m);
|
|
||||||
case Condition.Lt: return ICompareLess (n, m);
|
|
||||||
case Condition.Gt: return ICompareGreater (n, m);
|
|
||||||
case Condition.Le: return ICompareLessOrEqual (n, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
using ARMeilleure.CodeGen;
|
|
||||||
using ARMeilleure.CodeGen.Optimizations;
|
|
||||||
using ARMeilleure.CodeGen.X86;
|
|
||||||
using ARMeilleure.Diagnostics;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
static class Compiler
|
|
||||||
{
|
|
||||||
public static CompiledFunction Compile(
|
|
||||||
ControlFlowGraph cfg,
|
|
||||||
OperandType[] argTypes,
|
|
||||||
OperandType retType,
|
|
||||||
CompilerOptions options)
|
|
||||||
{
|
|
||||||
CompilerContext cctx = new(cfg, argTypes, retType, options);
|
|
||||||
|
|
||||||
if (options.HasFlag(CompilerOptions.Optimize))
|
|
||||||
{
|
|
||||||
Logger.StartPass(PassName.TailMerge);
|
|
||||||
|
|
||||||
TailMerge.RunPass(cctx);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.TailMerge, cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.HasFlag(CompilerOptions.SsaForm))
|
|
||||||
{
|
|
||||||
Logger.StartPass(PassName.Dominance);
|
|
||||||
|
|
||||||
Dominance.FindDominators(cfg);
|
|
||||||
Dominance.FindDominanceFrontiers(cfg);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.Dominance);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.SsaConstruction);
|
|
||||||
|
|
||||||
Ssa.Construct(cfg);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.SsaConstruction, cfg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.StartPass(PassName.RegisterToLocal);
|
|
||||||
|
|
||||||
RegisterToLocal.Rename(cfg);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.RegisterToLocal, cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CodeGenerator.Generate(cctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
static class DelegateHelper
|
|
||||||
{
|
|
||||||
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
|
|
||||||
|
|
||||||
private static readonly ModuleBuilder _modBuilder;
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, Type> _delegateTypesCache;
|
|
||||||
|
|
||||||
static DelegateHelper()
|
|
||||||
{
|
|
||||||
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
|
|
||||||
|
|
||||||
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
|
|
||||||
|
|
||||||
_delegateTypesCache = new Dictionary<string, Type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Delegate GetDelegate(MethodInfo info)
|
|
||||||
{
|
|
||||||
if (info == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
|
||||||
Type returnType = info.ReturnType;
|
|
||||||
|
|
||||||
Type delegateType = GetDelegateType(parameters, returnType);
|
|
||||||
|
|
||||||
return Delegate.CreateDelegate(delegateType, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type GetDelegateType(Type[] parameters, Type returnType)
|
|
||||||
{
|
|
||||||
string key = GetFunctionSignatureKey(parameters, returnType);
|
|
||||||
|
|
||||||
if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
|
|
||||||
{
|
|
||||||
delegateType = MakeDelegateType(parameters, returnType, key);
|
|
||||||
|
|
||||||
_delegateTypesCache.TryAdd(key, delegateType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return delegateType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
|
|
||||||
{
|
|
||||||
string sig = GetTypeName(returnType);
|
|
||||||
|
|
||||||
foreach (Type type in parameters)
|
|
||||||
{
|
|
||||||
sig += '_' + GetTypeName(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetTypeName(Type type)
|
|
||||||
{
|
|
||||||
return type.FullName.Replace(".", string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
private const MethodAttributes CtorAttributes =
|
|
||||||
MethodAttributes.RTSpecialName |
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.Public;
|
|
||||||
|
|
||||||
private const TypeAttributes DelegateTypeAttributes =
|
|
||||||
TypeAttributes.Class |
|
|
||||||
TypeAttributes.Public |
|
|
||||||
TypeAttributes.Sealed |
|
|
||||||
TypeAttributes.AnsiClass |
|
|
||||||
TypeAttributes.AutoClass;
|
|
||||||
|
|
||||||
private const MethodImplAttributes ImplAttributes =
|
|
||||||
MethodImplAttributes.Runtime |
|
|
||||||
MethodImplAttributes.Managed;
|
|
||||||
|
|
||||||
private const MethodAttributes InvokeAttributes =
|
|
||||||
MethodAttributes.Public |
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.NewSlot |
|
|
||||||
MethodAttributes.Virtual;
|
|
||||||
|
|
||||||
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
|
|
||||||
|
|
||||||
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
|
|
||||||
{
|
|
||||||
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
|
|
||||||
|
|
||||||
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
|
|
||||||
|
|
||||||
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
|
|
||||||
|
|
||||||
return builder.CreateTypeInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,298 +0,0 @@
|
|||||||
using ARMeilleure.Instructions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
static class Delegates
|
|
||||||
{
|
|
||||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
|
||||||
{
|
|
||||||
if (index >= 0 && index < _delegates.Count)
|
|
||||||
{
|
|
||||||
funcPtr = _delegates.Values[index].FuncPtr; // O(1).
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
funcPtr = default;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetDelegateFuncPtrByIndex(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= _delegates.Count)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException($"({nameof(index)} = {index})");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _delegates.Values[index].FuncPtr; // O(1).
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
|
||||||
{
|
|
||||||
if (info == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
string key = GetKey(info);
|
|
||||||
|
|
||||||
if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)).
|
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
|
||||||
}
|
|
||||||
|
|
||||||
return dlgInfo.FuncPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetDelegateIndex(MethodInfo info)
|
|
||||||
{
|
|
||||||
if (info == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
string key = GetKey(info);
|
|
||||||
|
|
||||||
int index = _delegates.IndexOfKey(key); // O(log(n)).
|
|
||||||
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetDelegateInfo(MethodInfo info)
|
|
||||||
{
|
|
||||||
string key = GetKey(info);
|
|
||||||
|
|
||||||
Delegate dlg = DelegateHelper.GetDelegate(info);
|
|
||||||
|
|
||||||
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetKey(MethodInfo info)
|
|
||||||
{
|
|
||||||
return $"{info.DeclaringType.Name}.{info.Name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly SortedList<string, DelegateInfo> _delegates;
|
|
||||||
|
|
||||||
static Delegates()
|
|
||||||
{
|
|
||||||
_delegates = new SortedList<string, DelegateInfo>();
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) }));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) }));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcrFz)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAcc)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinarySignedSatQSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAcc)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.BinaryUnsignedSatQSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Round)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.RoundF)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlRegSatQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcSignedDstSatQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedSrcUnsignedDstSatQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnarySignedSatQAbsOrNeg)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlReg)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShlRegSatQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcSignedDstSatQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
|
||||||
}
|
|
@ -1,394 +0,0 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.Intrinsics;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
static class RegisterUsage
|
|
||||||
{
|
|
||||||
private const int RegsCount = 32;
|
|
||||||
private const int RegsMask = RegsCount - 1;
|
|
||||||
|
|
||||||
private struct RegisterMask : IEquatable<RegisterMask>
|
|
||||||
{
|
|
||||||
public long IntMask => Mask.GetElement(0);
|
|
||||||
public long VecMask => Mask.GetElement(1);
|
|
||||||
|
|
||||||
public Vector128<long> Mask { get; }
|
|
||||||
|
|
||||||
public RegisterMask(Vector128<long> mask)
|
|
||||||
{
|
|
||||||
Mask = mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegisterMask(long intMask, long vecMask)
|
|
||||||
{
|
|
||||||
Mask = Vector128.Create(intMask, vecMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RegisterMask operator &(RegisterMask x, RegisterMask y)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
return new RegisterMask(Sse2.And(x.Mask, y.Mask));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RegisterMask(x.IntMask & y.IntMask, x.VecMask & y.VecMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RegisterMask operator |(RegisterMask x, RegisterMask y)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
return new RegisterMask(Sse2.Or(x.Mask, y.Mask));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RegisterMask(x.IntMask | y.IntMask, x.VecMask | y.VecMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RegisterMask operator ~(RegisterMask x)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
return new RegisterMask(Sse2.AndNot(x.Mask, Vector128<long>.AllBitsSet));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RegisterMask(~x.IntMask, ~x.VecMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(RegisterMask x, RegisterMask y)
|
|
||||||
{
|
|
||||||
return x.Equals(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(RegisterMask x, RegisterMask y)
|
|
||||||
{
|
|
||||||
return !x.Equals(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is RegisterMask regMask && Equals(regMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(RegisterMask other)
|
|
||||||
{
|
|
||||||
return Mask.Equals(other.Mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Mask.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
// Compute local register inputs and outputs used inside blocks.
|
|
||||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
|
||||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
|
||||||
|
|
||||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
|
||||||
{
|
|
||||||
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < node.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
Operand source = node.GetSource(index);
|
|
||||||
|
|
||||||
if (source.Kind == OperandKind.Register)
|
|
||||||
{
|
|
||||||
Register register = source.GetRegister();
|
|
||||||
|
|
||||||
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.Destination != default && node.Destination.Kind == OperandKind.Register)
|
|
||||||
{
|
|
||||||
localOutputs[block.Index] |= GetMask(node.Destination.GetRegister());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute global register inputs and outputs used across blocks.
|
|
||||||
RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Count];
|
|
||||||
|
|
||||||
RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count];
|
|
||||||
RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Count];
|
|
||||||
|
|
||||||
bool modified;
|
|
||||||
bool firstPass = true;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
modified = false;
|
|
||||||
|
|
||||||
// Compute register outputs.
|
|
||||||
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
|
|
||||||
{
|
|
||||||
BasicBlock block = cfg.PostOrderBlocks[index];
|
|
||||||
|
|
||||||
if (block.Predecessors.Count != 0 && !HasContextLoad(block))
|
|
||||||
{
|
|
||||||
BasicBlock predecessor = block.Predecessors[0];
|
|
||||||
|
|
||||||
RegisterMask cmnOutputs = localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index];
|
|
||||||
RegisterMask outputs = globalOutputs[predecessor.Index];
|
|
||||||
|
|
||||||
for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++)
|
|
||||||
{
|
|
||||||
predecessor = block.Predecessors[pIndex];
|
|
||||||
|
|
||||||
cmnOutputs &= localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index];
|
|
||||||
outputs |= globalOutputs[predecessor.Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
globalInputs[block.Index] |= outputs & ~cmnOutputs;
|
|
||||||
|
|
||||||
if (!firstPass)
|
|
||||||
{
|
|
||||||
cmnOutputs &= globalCmnOutputs[block.Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
modified |= Exchange(globalCmnOutputs, block.Index, cmnOutputs);
|
|
||||||
outputs |= localOutputs[block.Index];
|
|
||||||
modified |= Exchange(globalOutputs, block.Index, globalOutputs[block.Index] | outputs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
modified |= Exchange(globalOutputs, block.Index, localOutputs[block.Index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute register inputs.
|
|
||||||
for (int index = 0; index < cfg.PostOrderBlocks.Length; index++)
|
|
||||||
{
|
|
||||||
BasicBlock block = cfg.PostOrderBlocks[index];
|
|
||||||
|
|
||||||
RegisterMask inputs = localInputs[block.Index];
|
|
||||||
|
|
||||||
for (int i = 0; i < block.SuccessorsCount; i++)
|
|
||||||
{
|
|
||||||
inputs |= globalInputs[block.GetSuccessor(i).Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs &= ~globalCmnOutputs[block.Index];
|
|
||||||
|
|
||||||
modified |= Exchange(globalInputs, block.Index, globalInputs[block.Index] | inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
firstPass = false;
|
|
||||||
}
|
|
||||||
while (modified);
|
|
||||||
|
|
||||||
// Insert load and store context instructions where needed.
|
|
||||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
|
||||||
{
|
|
||||||
bool hasContextLoad = HasContextLoad(block);
|
|
||||||
|
|
||||||
if (hasContextLoad)
|
|
||||||
{
|
|
||||||
block.Operations.Remove(block.Operations.First);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand arg = default;
|
|
||||||
|
|
||||||
// The only block without any predecessor should be the entry block.
|
|
||||||
// It always needs a context load as it is the first block to run.
|
|
||||||
if (block.Predecessors.Count == 0 || hasContextLoad)
|
|
||||||
{
|
|
||||||
long vecMask = globalInputs[block.Index].VecMask;
|
|
||||||
long intMask = globalInputs[block.Index].IntMask;
|
|
||||||
|
|
||||||
if (vecMask != 0 || intMask != 0)
|
|
||||||
{
|
|
||||||
arg = Local(OperandType.I64);
|
|
||||||
|
|
||||||
Operation loadArg = block.Operations.AddFirst(Operation(Instruction.LoadArgument, arg, Const(0)));
|
|
||||||
|
|
||||||
LoadLocals(block, vecMask, RegisterType.Vector, mode, loadArg, arg);
|
|
||||||
LoadLocals(block, intMask, RegisterType.Integer, mode, loadArg, arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasContextStore = HasContextStore(block);
|
|
||||||
|
|
||||||
if (hasContextStore)
|
|
||||||
{
|
|
||||||
block.Operations.Remove(block.Operations.Last);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EndsWithReturn(block) || hasContextStore)
|
|
||||||
{
|
|
||||||
long vecMask = globalOutputs[block.Index].VecMask;
|
|
||||||
long intMask = globalOutputs[block.Index].IntMask;
|
|
||||||
|
|
||||||
if (vecMask != 0 || intMask != 0)
|
|
||||||
{
|
|
||||||
if (arg == default)
|
|
||||||
{
|
|
||||||
arg = Local(OperandType.I64);
|
|
||||||
|
|
||||||
block.Append(Operation(Instruction.LoadArgument, arg, Const(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
StoreLocals(block, intMask, RegisterType.Integer, mode, arg);
|
|
||||||
StoreLocals(block, vecMask, RegisterType.Vector, mode, arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool HasContextLoad(BasicBlock block)
|
|
||||||
{
|
|
||||||
return StartsWith(block, Instruction.LoadFromContext) && block.Operations.First.SourcesCount == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool HasContextStore(BasicBlock block)
|
|
||||||
{
|
|
||||||
return EndsWith(block, Instruction.StoreToContext) && block.Operations.Last.SourcesCount == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool StartsWith(BasicBlock block, Instruction inst)
|
|
||||||
{
|
|
||||||
if (block.Operations.Count > 0)
|
|
||||||
{
|
|
||||||
Operation first = block.Operations.First;
|
|
||||||
|
|
||||||
return first != default && first.Instruction == inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool EndsWith(BasicBlock block, Instruction inst)
|
|
||||||
{
|
|
||||||
if (block.Operations.Count > 0)
|
|
||||||
{
|
|
||||||
Operation last = block.Operations.Last;
|
|
||||||
|
|
||||||
return last != default && last.Instruction == inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RegisterMask GetMask(Register register)
|
|
||||||
{
|
|
||||||
long intMask = 0;
|
|
||||||
long vecMask = 0;
|
|
||||||
|
|
||||||
switch (register.Type)
|
|
||||||
{
|
|
||||||
case RegisterType.Flag: intMask = (1L << RegsCount) << register.Index; break;
|
|
||||||
case RegisterType.Integer: intMask = 1L << register.Index; break;
|
|
||||||
case RegisterType.FpFlag: vecMask = (1L << RegsCount) << register.Index; break;
|
|
||||||
case RegisterType.Vector: vecMask = 1L << register.Index; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RegisterMask(intMask, vecMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value)
|
|
||||||
{
|
|
||||||
ref RegisterMask curValue = ref masks[blkIndex];
|
|
||||||
|
|
||||||
bool changed = curValue != value;
|
|
||||||
|
|
||||||
curValue = value;
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LoadLocals(
|
|
||||||
BasicBlock block,
|
|
||||||
long inputs,
|
|
||||||
RegisterType baseType,
|
|
||||||
ExecutionMode mode,
|
|
||||||
Operation loadArg,
|
|
||||||
Operand arg)
|
|
||||||
{
|
|
||||||
while (inputs != 0)
|
|
||||||
{
|
|
||||||
int bit = 63 - BitOperations.LeadingZeroCount((ulong)inputs);
|
|
||||||
|
|
||||||
Operand dest = GetRegFromBit(bit, baseType, mode);
|
|
||||||
Operand offset = Const((long)NativeContext.GetRegisterOffset(dest.GetRegister()));
|
|
||||||
Operand addr = Local(OperandType.I64);
|
|
||||||
|
|
||||||
block.Operations.AddAfter(loadArg, Operation(Instruction.Load, dest, addr));
|
|
||||||
block.Operations.AddAfter(loadArg, Operation(Instruction.Add, addr, arg, offset));
|
|
||||||
|
|
||||||
inputs &= ~(1L << bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StoreLocals(
|
|
||||||
BasicBlock block,
|
|
||||||
long outputs,
|
|
||||||
RegisterType baseType,
|
|
||||||
ExecutionMode mode,
|
|
||||||
Operand arg)
|
|
||||||
{
|
|
||||||
while (outputs != 0)
|
|
||||||
{
|
|
||||||
int bit = BitOperations.TrailingZeroCount(outputs);
|
|
||||||
|
|
||||||
Operand source = GetRegFromBit(bit, baseType, mode);
|
|
||||||
Operand offset = Const((long)NativeContext.GetRegisterOffset(source.GetRegister()));
|
|
||||||
Operand addr = Local(OperandType.I64);
|
|
||||||
|
|
||||||
block.Append(Operation(Instruction.Add, addr, arg, offset));
|
|
||||||
block.Append(Operation(Instruction.Store, default, addr, source));
|
|
||||||
|
|
||||||
outputs &= ~(1L << bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand GetRegFromBit(int bit, RegisterType baseType, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
if (bit < RegsCount)
|
|
||||||
{
|
|
||||||
return Register(bit, baseType, GetOperandType(baseType, mode));
|
|
||||||
}
|
|
||||||
else if (baseType == RegisterType.Integer)
|
|
||||||
{
|
|
||||||
return Register(bit & RegsMask, RegisterType.Flag, OperandType.I32);
|
|
||||||
}
|
|
||||||
else if (baseType == RegisterType.Vector)
|
|
||||||
{
|
|
||||||
return Register(bit & RegsMask, RegisterType.FpFlag, OperandType.I32);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(bit));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OperandType GetOperandType(RegisterType type, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case RegisterType.Flag: return OperandType.I32;
|
|
||||||
case RegisterType.FpFlag: return OperandType.I32;
|
|
||||||
case RegisterType.Integer: return (mode == ExecutionMode.Aarch64) ? OperandType.I64 : OperandType.I32;
|
|
||||||
case RegisterType.Vector: return OperandType.V128;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"Invalid register type \"{type}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool EndsWithReturn(BasicBlock block)
|
|
||||||
{
|
|
||||||
Operation last = block.Operations.Last;
|
|
||||||
|
|
||||||
return last != default && last.Instruction == Instruction.Return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
using ARMeilleure.Common;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
class TranslatedFunction
|
|
||||||
{
|
|
||||||
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
|
|
||||||
|
|
||||||
public Counter<uint> CallCounter { get; }
|
|
||||||
public ulong GuestSize { get; }
|
|
||||||
public bool HighCq { get; }
|
|
||||||
public IntPtr FuncPtr { get; }
|
|
||||||
|
|
||||||
public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq)
|
|
||||||
{
|
|
||||||
_func = func;
|
|
||||||
CallCounter = callCounter;
|
|
||||||
GuestSize = guestSize;
|
|
||||||
HighCq = highCq;
|
|
||||||
FuncPtr = Marshal.GetFunctionPointerForDelegate(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Execute(State.ExecutionContext context)
|
|
||||||
{
|
|
||||||
return _func(context.NativeContextPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,525 +0,0 @@
|
|||||||
using ARMeilleure.CodeGen;
|
|
||||||
using ARMeilleure.Common;
|
|
||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.Diagnostics;
|
|
||||||
using ARMeilleure.Instructions;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.Memory;
|
|
||||||
using ARMeilleure.Signal;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using Ryujinx.Common;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
public class Translator
|
|
||||||
{
|
|
||||||
private static readonly AddressTable<ulong>.Level[] Levels64Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 2, 5)
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly AddressTable<ulong>.Level[] Levels32Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 1, 6)
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly IJitMemoryAllocator _allocator;
|
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<ulong, object> _backgroundSet;
|
|
||||||
private readonly ConcurrentStack<RejitRequest> _backgroundStack;
|
|
||||||
private readonly AutoResetEvent _backgroundTranslatorEvent;
|
|
||||||
private readonly ReaderWriterLock _backgroundTranslatorLock;
|
|
||||||
|
|
||||||
internal ConcurrentDictionary<ulong, TranslatedFunction> Functions { get; }
|
|
||||||
internal AddressTable<ulong> FunctionTable { get; }
|
|
||||||
internal EntryTable<uint> CountTable { get; }
|
|
||||||
internal TranslatorStubs Stubs { get; }
|
|
||||||
internal IMemoryManager Memory { get; }
|
|
||||||
|
|
||||||
private volatile int _threadCount;
|
|
||||||
|
|
||||||
// FIXME: Remove this once the init logic of the emulator will be redone.
|
|
||||||
public static readonly ManualResetEvent IsReadyForTranslation = new(false);
|
|
||||||
|
|
||||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
|
||||||
{
|
|
||||||
_allocator = allocator;
|
|
||||||
Memory = memory;
|
|
||||||
|
|
||||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
|
||||||
|
|
||||||
_backgroundSet = new ConcurrentDictionary<ulong, object>();
|
|
||||||
_backgroundStack = new ConcurrentStack<RejitRequest>();
|
|
||||||
_backgroundTranslatorEvent = new AutoResetEvent(false);
|
|
||||||
_backgroundTranslatorLock = new ReaderWriterLock();
|
|
||||||
|
|
||||||
JitCache.Initialize(allocator);
|
|
||||||
|
|
||||||
CountTable = new EntryTable<uint>();
|
|
||||||
Functions = new ConcurrentDictionary<ulong, TranslatedFunction>();
|
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? Levels64Bit : Levels32Bit);
|
|
||||||
Stubs = new TranslatorStubs(this);
|
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
|
||||||
|
|
||||||
if (memory.Type.IsHostMapped())
|
|
||||||
{
|
|
||||||
NativeSignalHandler.InitializeSignalHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TranslateStackedSubs()
|
|
||||||
{
|
|
||||||
while (_threadCount != 0)
|
|
||||||
{
|
|
||||||
_backgroundTranslatorLock.AcquireReaderLock(Timeout.Infinite);
|
|
||||||
|
|
||||||
if (_backgroundStack.TryPop(out RejitRequest request) &&
|
|
||||||
_backgroundSet.TryRemove(request.Address, out _))
|
|
||||||
{
|
|
||||||
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
|
|
||||||
|
|
||||||
Functions.AddOrUpdate(request.Address, func, (key, oldFunc) =>
|
|
||||||
{
|
|
||||||
EnqueueForDeletion(key, oldFunc);
|
|
||||||
return func;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (PtcProfiler.Enabled)
|
|
||||||
{
|
|
||||||
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterFunction(request.Address, func);
|
|
||||||
|
|
||||||
_backgroundTranslatorLock.ReleaseReaderLock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_backgroundTranslatorLock.ReleaseReaderLock();
|
|
||||||
_backgroundTranslatorEvent.WaitOne();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wake up any other background translator threads, to encourage them to exit.
|
|
||||||
_backgroundTranslatorEvent.Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute(State.ExecutionContext context, ulong address)
|
|
||||||
{
|
|
||||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
|
||||||
{
|
|
||||||
IsReadyForTranslation.WaitOne();
|
|
||||||
|
|
||||||
if (Ptc.State == PtcState.Enabled)
|
|
||||||
{
|
|
||||||
Debug.Assert(Functions.Count == 0);
|
|
||||||
Ptc.LoadTranslations(this);
|
|
||||||
Ptc.MakeAndSaveTranslations(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
PtcProfiler.Start();
|
|
||||||
|
|
||||||
Ptc.Disable();
|
|
||||||
|
|
||||||
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
|
|
||||||
// etc). All threads are normal priority except from the last, which just fills as much of the last core
|
|
||||||
// as the os lets it with a low priority. If we only have one rejit thread, it should be normal priority
|
|
||||||
// as highCq code is performance critical.
|
|
||||||
//
|
|
||||||
// TODO: Use physical cores rather than logical. This only really makes sense for processors with
|
|
||||||
// hyperthreading. Requires OS specific code.
|
|
||||||
int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3);
|
|
||||||
int threadCount = Math.Min(4, unboundedThreadCount);
|
|
||||||
|
|
||||||
for (int i = 0; i < threadCount; i++)
|
|
||||||
{
|
|
||||||
bool last = i != 0 && i == unboundedThreadCount - 1;
|
|
||||||
|
|
||||||
Thread backgroundTranslatorThread = new Thread(TranslateStackedSubs)
|
|
||||||
{
|
|
||||||
Name = "CPU.BackgroundTranslatorThread." + i,
|
|
||||||
Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal
|
|
||||||
};
|
|
||||||
|
|
||||||
backgroundTranslatorThread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Statistics.InitializeTimer();
|
|
||||||
|
|
||||||
NativeInterface.RegisterThread(context, Memory, this);
|
|
||||||
|
|
||||||
if (Optimizations.UseUnmanagedDispatchLoop)
|
|
||||||
{
|
|
||||||
Stubs.DispatchLoop(context.NativeContextPtr, address);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
address = ExecuteSingle(context, address);
|
|
||||||
}
|
|
||||||
while (context.Running && address != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeInterface.UnregisterThread();
|
|
||||||
|
|
||||||
if (Interlocked.Decrement(ref _threadCount) == 0)
|
|
||||||
{
|
|
||||||
_backgroundTranslatorEvent.Set();
|
|
||||||
|
|
||||||
ClearJitCache();
|
|
||||||
|
|
||||||
Stubs.Dispose();
|
|
||||||
FunctionTable.Dispose();
|
|
||||||
CountTable.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong ExecuteSingle(State.ExecutionContext context, ulong address)
|
|
||||||
{
|
|
||||||
TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode);
|
|
||||||
|
|
||||||
Statistics.StartTimer();
|
|
||||||
|
|
||||||
ulong nextAddr = func.Execute(context);
|
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
|
||||||
|
|
||||||
return nextAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
if (!Functions.TryGetValue(address, out TranslatedFunction func))
|
|
||||||
{
|
|
||||||
func = Translate(address, mode, highCq: false);
|
|
||||||
|
|
||||||
TranslatedFunction oldFunc = Functions.GetOrAdd(address, func);
|
|
||||||
|
|
||||||
if (oldFunc != func)
|
|
||||||
{
|
|
||||||
JitCache.Unmap(func.FuncPtr);
|
|
||||||
func = oldFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PtcProfiler.Enabled)
|
|
||||||
{
|
|
||||||
PtcProfiler.AddEntry(address, mode, highCq: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterFunction(address, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RegisterFunction(ulong guestAddress, TranslatedFunction func)
|
|
||||||
{
|
|
||||||
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
|
|
||||||
{
|
|
||||||
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq)
|
|
||||||
{
|
|
||||||
var context = new ArmEmitterContext(
|
|
||||||
Memory,
|
|
||||||
CountTable,
|
|
||||||
FunctionTable,
|
|
||||||
Stubs,
|
|
||||||
address,
|
|
||||||
highCq,
|
|
||||||
mode: Aarch32Mode.User);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.Decoding);
|
|
||||||
|
|
||||||
Block[] blocks = Decoder.Decode(Memory, address, mode, highCq, singleBlock: false);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.Decoding);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.Translation);
|
|
||||||
|
|
||||||
EmitSynchronization(context);
|
|
||||||
|
|
||||||
if (blocks[0].Address != address)
|
|
||||||
{
|
|
||||||
context.Branch(context.GetLabel(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
|
|
||||||
|
|
||||||
ulong funcSize = funcRange.End - funcRange.Start;
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.Translation, cfg);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.RegisterUsage);
|
|
||||||
|
|
||||||
RegisterUsage.RunPass(cfg, mode);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.RegisterUsage);
|
|
||||||
|
|
||||||
var retType = OperandType.I64;
|
|
||||||
var argTypes = new OperandType[] { OperandType.I64 };
|
|
||||||
|
|
||||||
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
|
||||||
|
|
||||||
if (context.HasPtc)
|
|
||||||
{
|
|
||||||
options |= CompilerOptions.Relocatable;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options);
|
|
||||||
|
|
||||||
if (context.HasPtc)
|
|
||||||
{
|
|
||||||
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
|
||||||
|
|
||||||
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
|
||||||
}
|
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.Map<GuestFunction>();
|
|
||||||
|
|
||||||
Allocators.ResetAll();
|
|
||||||
|
|
||||||
return new TranslatedFunction(func, counter, funcSize, highCq);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Range
|
|
||||||
{
|
|
||||||
public ulong Start { get; }
|
|
||||||
public ulong End { get; }
|
|
||||||
|
|
||||||
public Range(ulong start, ulong end)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ControlFlowGraph EmitAndGetCFG(
|
|
||||||
ArmEmitterContext context,
|
|
||||||
Block[] blocks,
|
|
||||||
out Range range,
|
|
||||||
out Counter<uint> counter)
|
|
||||||
{
|
|
||||||
counter = null;
|
|
||||||
|
|
||||||
ulong rangeStart = ulong.MaxValue;
|
|
||||||
ulong rangeEnd = 0;
|
|
||||||
|
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
|
||||||
{
|
|
||||||
Block block = blocks[blkIndex];
|
|
||||||
|
|
||||||
if (!block.Exit)
|
|
||||||
{
|
|
||||||
if (rangeStart > block.Address)
|
|
||||||
{
|
|
||||||
rangeStart = block.Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rangeEnd < block.EndAddress)
|
|
||||||
{
|
|
||||||
rangeEnd = block.EndAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.Address == context.EntryAddress && !context.HighCq)
|
|
||||||
{
|
|
||||||
EmitRejitCheck(context, out counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.CurrBlock = block;
|
|
||||||
|
|
||||||
context.MarkLabel(context.GetLabel(block.Address));
|
|
||||||
|
|
||||||
if (block.Exit)
|
|
||||||
{
|
|
||||||
// Left option here as it may be useful if we need to return to managed rather than tail call in
|
|
||||||
// future. (eg. for debug)
|
|
||||||
bool useReturns = false;
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int opcIndex = 0; opcIndex < block.OpCodes.Count; opcIndex++)
|
|
||||||
{
|
|
||||||
OpCode opCode = block.OpCodes[opcIndex];
|
|
||||||
|
|
||||||
context.CurrOp = opCode;
|
|
||||||
|
|
||||||
bool isLastOp = opcIndex == block.OpCodes.Count - 1;
|
|
||||||
|
|
||||||
if (isLastOp && block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address)
|
|
||||||
{
|
|
||||||
EmitSynchronization(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand lblPredicateSkip = default;
|
|
||||||
|
|
||||||
if (opCode is OpCode32 op && op.Cond < Condition.Al)
|
|
||||||
{
|
|
||||||
lblPredicateSkip = Label();
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Invert());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opCode.Instruction.Emitter != null)
|
|
||||||
{
|
|
||||||
opCode.Instruction.Emitter(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Invalid instruction \"{opCode.Instruction.Name}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lblPredicateSkip != default)
|
|
||||||
{
|
|
||||||
context.MarkLabel(lblPredicateSkip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
range = new Range(rangeStart, rangeEnd);
|
|
||||||
|
|
||||||
return context.GetControlFlowGraph();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void EmitRejitCheck(ArmEmitterContext context, out Counter<uint> counter)
|
|
||||||
{
|
|
||||||
const int MinsCallForRejit = 100;
|
|
||||||
|
|
||||||
counter = new Counter<uint>(context.CountTable);
|
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
|
||||||
|
|
||||||
Operand address = !context.HasPtc ?
|
|
||||||
Const(ref counter.Value) :
|
|
||||||
Const(ref counter.Value, Ptc.CountTableSymbol);
|
|
||||||
|
|
||||||
Operand curCount = context.Load(OperandType.I32, address);
|
|
||||||
Operand count = context.Add(curCount, Const(1));
|
|
||||||
context.Store(address, count);
|
|
||||||
context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
|
|
||||||
|
|
||||||
context.MarkLabel(lblEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void EmitSynchronization(EmitterContext context)
|
|
||||||
{
|
|
||||||
long countOffs = NativeContext.GetCounterOffset();
|
|
||||||
|
|
||||||
Operand lblNonZero = Label();
|
|
||||||
Operand lblExit = Label();
|
|
||||||
|
|
||||||
Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
|
|
||||||
Operand count = context.Load(OperandType.I32, countAddr);
|
|
||||||
context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
|
||||||
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
context.Return(Const(0L));
|
|
||||||
|
|
||||||
context.MarkLabel(lblNonZero);
|
|
||||||
count = context.Subtract(count, Const(1));
|
|
||||||
context.Store(countAddr, count);
|
|
||||||
|
|
||||||
context.MarkLabel(lblExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
|
||||||
{
|
|
||||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
|
||||||
ClearRejitQueue(allowRequeue: true);
|
|
||||||
|
|
||||||
// TODO: Completely remove functions overlapping the specified range from the cache.
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
if (_backgroundSet.TryAdd(guestAddress, null))
|
|
||||||
{
|
|
||||||
_backgroundStack.Push(new RejitRequest(guestAddress, mode));
|
|
||||||
_backgroundTranslatorEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnqueueForDeletion(ulong guestAddress, TranslatedFunction func)
|
|
||||||
{
|
|
||||||
_oldFuncs.Enqueue(new(guestAddress, func));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearJitCache()
|
|
||||||
{
|
|
||||||
// Ensure no attempt will be made to compile new functions due to rejit.
|
|
||||||
ClearRejitQueue(allowRequeue: false);
|
|
||||||
|
|
||||||
foreach (var func in Functions.Values)
|
|
||||||
{
|
|
||||||
JitCache.Unmap(func.FuncPtr);
|
|
||||||
|
|
||||||
func.CallCounter?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Functions.Clear();
|
|
||||||
|
|
||||||
while (_oldFuncs.TryDequeue(out var kv))
|
|
||||||
{
|
|
||||||
JitCache.Unmap(kv.Value.FuncPtr);
|
|
||||||
|
|
||||||
kv.Value.CallCounter?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearRejitQueue(bool allowRequeue)
|
|
||||||
{
|
|
||||||
_backgroundTranslatorLock.AcquireWriterLock(Timeout.Infinite);
|
|
||||||
|
|
||||||
if (allowRequeue)
|
|
||||||
{
|
|
||||||
while (_backgroundStack.TryPop(out var request))
|
|
||||||
{
|
|
||||||
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
|
|
||||||
{
|
|
||||||
Volatile.Write(ref func.CallCounter.Value, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
_backgroundSet.TryRemove(request.Address, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_backgroundStack.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
_backgroundTranslatorLock.ReleaseWriterLock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,248 +0,0 @@
|
|||||||
using ARMeilleure.Instructions;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a stub manager.
|
|
||||||
/// </summary>
|
|
||||||
class TranslatorStubs : IDisposable
|
|
||||||
{
|
|
||||||
private static readonly Lazy<IntPtr> _slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
private readonly Translator _translator;
|
|
||||||
private readonly Lazy<IntPtr> _dispatchStub;
|
|
||||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the dispatch stub.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
|
||||||
public IntPtr DispatchStub
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _dispatchStub.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the slow dispatch stub.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
|
||||||
public IntPtr SlowDispatchStub
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _slowDispatchStub.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the dispatch loop function.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
|
||||||
public DispatcherFunction DispatchLoop
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _dispatchLoop.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
|
||||||
/// <see cref="Translator"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="translator"><see cref="Translator"/> instance to use</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
|
||||||
public TranslatorStubs(Translator translator)
|
|
||||||
{
|
|
||||||
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
|
|
||||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
|
||||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all resources used by the <see cref="TranslatorStubs"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all unmanaged and optionally managed resources used by the <see cref="TranslatorStubs"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_disposed)
|
|
||||||
{
|
|
||||||
if (_dispatchStub.IsValueCreated)
|
|
||||||
{
|
|
||||||
JitCache.Unmap(_dispatchStub.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_dispatchLoop.IsValueCreated)
|
|
||||||
{
|
|
||||||
JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Frees resources used by the <see cref="TranslatorStubs"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
~TranslatorStubs()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a <see cref="DispatchStub"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Generated <see cref="DispatchStub"/></returns>
|
|
||||||
private IntPtr GenerateDispatchStub()
|
|
||||||
{
|
|
||||||
var context = new EmitterContext();
|
|
||||||
|
|
||||||
Operand lblFallback = Label();
|
|
||||||
Operand lblEnd = Label();
|
|
||||||
|
|
||||||
// Load the target guest address from the native context.
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
Operand guestAddress = context.Load(OperandType.I64,
|
|
||||||
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
|
|
||||||
|
|
||||||
// Check if guest address is within range of the AddressTable.
|
|
||||||
Operand masked = context.BitwiseAnd(guestAddress, Const(~_translator.FunctionTable.Mask));
|
|
||||||
context.BranchIfTrue(lblFallback, masked);
|
|
||||||
|
|
||||||
Operand index = default;
|
|
||||||
Operand page = Const((long)_translator.FunctionTable.Base);
|
|
||||||
|
|
||||||
for (int i = 0; i < _translator.FunctionTable.Levels.Length; i++)
|
|
||||||
{
|
|
||||||
ref var level = ref _translator.FunctionTable.Levels[i];
|
|
||||||
|
|
||||||
// level.Mask is not used directly because it is more often bigger than 32-bits, so it will not
|
|
||||||
// be encoded as an immediate on x86's bitwise and operation.
|
|
||||||
Operand mask = Const(level.Mask >> level.Index);
|
|
||||||
|
|
||||||
index = context.BitwiseAnd(context.ShiftRightUI(guestAddress, Const(level.Index)), mask);
|
|
||||||
|
|
||||||
if (i < _translator.FunctionTable.Levels.Length - 1)
|
|
||||||
{
|
|
||||||
page = context.Load(OperandType.I64, context.Add(page, context.ShiftLeft(index, Const(3))));
|
|
||||||
context.BranchIfFalse(lblFallback, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand hostAddress;
|
|
||||||
Operand hostAddressAddr = context.Add(page, context.ShiftLeft(index, Const(3)));
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
|
||||||
|
|
||||||
context.MarkLabel(lblFallback);
|
|
||||||
hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
|
||||||
var retType = OperandType.I64;
|
|
||||||
var argTypes = new[] { OperandType.I64 };
|
|
||||||
|
|
||||||
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
|
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a <see cref="SlowDispatchStub"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Generated <see cref="SlowDispatchStub"/></returns>
|
|
||||||
private static IntPtr GenerateSlowDispatchStub()
|
|
||||||
{
|
|
||||||
var context = new EmitterContext();
|
|
||||||
|
|
||||||
// Load the target guest address from the native context.
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
Operand guestAddress = context.Load(OperandType.I64,
|
|
||||||
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
|
|
||||||
|
|
||||||
MethodInfo getFuncAddress = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress));
|
|
||||||
Operand hostAddress = context.Call(getFuncAddress, guestAddress);
|
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
|
||||||
var retType = OperandType.I64;
|
|
||||||
var argTypes = new[] { OperandType.I64 };
|
|
||||||
|
|
||||||
var func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<GuestFunction>();
|
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a <see cref="DispatchLoop"/> function.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see cref="DispatchLoop"/> function</returns>
|
|
||||||
private DispatcherFunction GenerateDispatchLoop()
|
|
||||||
{
|
|
||||||
var context = new EmitterContext();
|
|
||||||
|
|
||||||
Operand beginLbl = Label();
|
|
||||||
Operand endLbl = Label();
|
|
||||||
|
|
||||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
|
||||||
Operand guestAddress = context.Copy(
|
|
||||||
context.AllocateLocal(OperandType.I64),
|
|
||||||
context.LoadArgument(OperandType.I64, 1));
|
|
||||||
|
|
||||||
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
|
||||||
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
|
||||||
|
|
||||||
context.MarkLabel(beginLbl);
|
|
||||||
context.Store(dispatchAddress, guestAddress);
|
|
||||||
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
|
||||||
context.BranchIfFalse(endLbl, guestAddress);
|
|
||||||
context.BranchIfFalse(endLbl, context.Load(OperandType.I32, runningAddress));
|
|
||||||
context.Branch(beginLbl);
|
|
||||||
|
|
||||||
context.MarkLabel(endLbl);
|
|
||||||
context.Return();
|
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
|
||||||
var retType = OperandType.None;
|
|
||||||
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq).Map<DispatcherFunction>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
53
Directory.Packages.props
Normal file
53
Directory.Packages.props
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
||||||
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
||||||
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
||||||
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
||||||
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
||||||
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
|
<PackageVersion Include="DynamicData" Version="7.13.8" />
|
||||||
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||||
|
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||||
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||||
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
|
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.3" />
|
||||||
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
||||||
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
|
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
|
||||||
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
|
<PackageVersion Include="System.Management" Version="7.0.1" />
|
||||||
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
54
README.md
54
README.md
@ -5,7 +5,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<b>Ryujinx</b>
|
<b>Ryujinx</b>
|
||||||
<br>
|
<br>
|
||||||
<sub><sup><b>(REE-YOU-JI-NX)</b></sup></sub>
|
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
</h1>
|
</h1>
|
||||||
@ -13,12 +13,16 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
It was written from scratch and development on the project began in September 2017. Ryujinx is available on Github under the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>. <br />
|
It was written from scratch and development on the project began in September 2017. Ryujinx is available on Github under the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>. <br />
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://ci.appveyor.com/project/gdkchan/ryujinx?branch=master">
|
<a href="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml">
|
||||||
<img src="https://ci.appveyor.com/api/projects/status/ssg4jwu6ve3k594s/branch/master?svg=true"
|
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||||
|
alt="">
|
||||||
|
</a>
|
||||||
|
<a href="https://crwd.in/ryujinx">
|
||||||
|
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
|
||||||
alt="">
|
alt="">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.com/invite/VkQYXAZ">
|
<a href="https://discord.com/invite/VkQYXAZ">
|
||||||
@ -27,27 +31,29 @@
|
|||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx-Website/master/static/public/shell_fullsize.png">
|
<img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx-Website/master/public/assets/images/shell.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h5 align="center">
|
<h5 align="center">
|
||||||
|
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of January 2022, Ryujinx has been tested on approximately 3,500 titles; over 3,200 boot past menus and into gameplay, with roughly 2,500 of those being considered playable.
|
As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable.
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit an updated test on an existing game entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To run this emulator, your PC must be equipped with at least 8GB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
To run this emulator, your PC must be equipped with at least 8GiB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
||||||
|
|
||||||
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
||||||
|
|
||||||
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
||||||
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
||||||
|
|
||||||
|
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
||||||
|
|
||||||
## Latest build
|
## Latest build
|
||||||
|
|
||||||
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
|
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
|
||||||
@ -62,7 +68,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
|
|||||||
If you wish to build the emulator yourself, follow these steps:
|
If you wish to build the emulator yourself, follow these steps:
|
||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0).
|
Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||||
@ -84,17 +90,17 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
|
|
||||||
- **CPU**
|
- **CPU**
|
||||||
|
|
||||||
The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions, including partial 32-bit support. It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
|
The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions, including partial 32-bit support. It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
|
||||||
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster). The fastest option (host, unchecked) is set by default.
|
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster). The fastest option (host, unchecked) is set by default.
|
||||||
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads. The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game. NOTE: this feature is enabled by default in the Options menu > System tab. You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch! These improvements are permanent and do not require any extra launches going forward.
|
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads. The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game. NOTE: this feature is enabled by default in the Options menu > System tab. You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch! These improvements are permanent and do not require any extra launches going forward.
|
||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using the OpenGL API (version 4.5 minimum) through a custom build of OpenTK. There are currently four graphics enhancements available to the end user in Ryujinx: disk shader caching, resolution scaling, aspect ratio adjustment and anisotropic filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers. Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers. Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
|
||||||
In all scenarios, you can set up everything inside the input configuration menu.
|
In all scenarios, you can set up everything inside the input configuration menu.
|
||||||
|
|
||||||
- **DLC & Modifications**
|
- **DLC & Modifications**
|
||||||
@ -111,29 +117,27 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx). You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions).
|
If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx). You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions).
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
If you'd like to support the project financially, Ryujinx has an active Patreon campaign.
|
If you'd like to support the project financially, Ryujinx has an active Patreon campaign.
|
||||||
|
|
||||||
<a href="https://www.patreon.com/ryujinx">
|
<a href="https://www.patreon.com/ryujinx">
|
||||||
<img src="https://images.squarespace-cdn.com/content/v1/560c1d39e4b0b4fae0c9cf2a/1567548955044-WVD994WZP76EWF15T0L3/Patreon+Button.png?format=500w" width="150">
|
<img src="https://images.squarespace-cdn.com/content/v1/560c1d39e4b0b4fae0c9cf2a/1567548955044-WVD994WZP76EWF15T0L3/Patreon+Button.png?format=500w" width="150">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
All the developers working on the project do so on their free time, but the project has several expenses:
|
All developers working on the project do so in their free time, but the project has several expenses:
|
||||||
* Hackable Nintendo Switch consoles to reverse-engineer the hardware
|
* Hackable Nintendo Switch consoles to reverse-engineer the hardware
|
||||||
* Additional computer hardware for testing purposes (e.g. GPUs to diagnose graphical bugs, etc.)
|
* Additional computer hardware for testing purposes (e.g. GPUs to diagnose graphical bugs, etc.)
|
||||||
* Licenses for various software development tools (e.g. Jetbrains, LDN servers, IDA)
|
* Licenses for various software development tools (e.g. Jetbrains, IDA)
|
||||||
* Web hosting and infrastructure maintenance
|
* Web hosting and infrastructure maintenance (e.g. LDN servers)
|
||||||
|
|
||||||
All funds received through Patreon are considered a donation to support the project. Patrons receive early access to progress reports and exclusive access to developer interviews.
|
All funds received through Patreon are considered a donation to support the project. Patrons receive early access to progress reports and exclusive access to developer interviews.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This software is licensed under the terms of the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license.</a></i><br />
|
This software is licensed under the terms of the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license.</a></i><br />
|
||||||
The Ryujinx.Audio project is licensed under the terms of the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/Ryujinx.Audio/LICENSE.txt
|
|
||||||
" target="_blank">LGPLv3 license.</a></i><br />
|
|
||||||
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
||||||
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](Ryujinx/THIRDPARTY.md) for more details.
|
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
||||||
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="OpenTK.OpenAL" Version="4.5.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public static class MarshalEx
|
|
||||||
{
|
|
||||||
public static double ReadDouble(IntPtr handle, int offset = 0)
|
|
||||||
{
|
|
||||||
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(handle, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteDouble(IntPtr handle, double value)
|
|
||||||
{
|
|
||||||
WriteDouble(handle, 0, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteDouble(IntPtr handle, int offset, double value)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt64(handle, offset, BitConverter.DoubleToInt64Bits(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float ReadFloat(IntPtr handle, int offset = 0)
|
|
||||||
{
|
|
||||||
return BitConverter.Int32BitsToSingle(Marshal.ReadInt32(handle, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteFloat(IntPtr handle, float value)
|
|
||||||
{
|
|
||||||
WriteFloat(handle, 0, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteFloat(IntPtr handle, int offset, float value)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt32(handle, offset, BitConverter.SingleToInt32Bits(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,386 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIO : IDisposable
|
|
||||||
{
|
|
||||||
Pointer<SoundIo> handle;
|
|
||||||
|
|
||||||
public SoundIO()
|
|
||||||
{
|
|
||||||
handle = Natives.soundio_create();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal SoundIO(Pointer<SoundIo> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose ()
|
|
||||||
{
|
|
||||||
foreach (var h in allocated_hglobals)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
Natives.soundio_destroy(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equality (based on handle)
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
var d = other as SoundIO;
|
|
||||||
|
|
||||||
return d != null && this.handle == d.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIO obj1, SoundIO obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIO obj1, SoundIO obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields
|
|
||||||
|
|
||||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
|
||||||
// this kind of code anywhere we need string marshaling.
|
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
|
||||||
|
|
||||||
public string ApplicationName {
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, app_name_offset)); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var existing = Marshal.ReadIntPtr(handle, app_name_offset);
|
|
||||||
if (allocated_hglobals.Contains (existing))
|
|
||||||
{
|
|
||||||
allocated_hglobals.Remove(existing);
|
|
||||||
Marshal.FreeHGlobal(existing);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
|
||||||
Marshal.WriteIntPtr(handle, app_name_offset, ptr);
|
|
||||||
allocated_hglobals.Add(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int app_name_offset = (int)Marshal.OffsetOf<SoundIo>("app_name");
|
|
||||||
|
|
||||||
public SoundIOBackend CurrentBackend
|
|
||||||
{
|
|
||||||
get { return (SoundIOBackend)Marshal.ReadInt32(handle, current_backend_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int current_backend_offset = (int)Marshal.OffsetOf<SoundIo>("current_backend");
|
|
||||||
|
|
||||||
// emit_rtprio_warning
|
|
||||||
public Action EmitRealtimePriorityWarning
|
|
||||||
{
|
|
||||||
get { return emit_rtprio_warning; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
emit_rtprio_warning = value;
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change);
|
|
||||||
|
|
||||||
Marshal.WriteIntPtr(handle, emit_rtprio_warning_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int emit_rtprio_warning_offset = (int)Marshal.OffsetOf<SoundIo>("emit_rtprio_warning");
|
|
||||||
|
|
||||||
Action emit_rtprio_warning;
|
|
||||||
|
|
||||||
// jack_error_callback
|
|
||||||
public Action<string> JackErrorCallback
|
|
||||||
{
|
|
||||||
get { return jack_error_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
jack_error_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
jack_error_callback = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jack_error_callback_native = msg => jack_error_callback(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(jack_error_callback_native);
|
|
||||||
Marshal.WriteIntPtr(handle, jack_error_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int jack_error_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_error_callback");
|
|
||||||
|
|
||||||
Action<string> jack_error_callback;
|
|
||||||
delegate void jack_error_delegate(string message);
|
|
||||||
jack_error_delegate jack_error_callback_native;
|
|
||||||
|
|
||||||
// jack_info_callback
|
|
||||||
public Action<string> JackInfoCallback
|
|
||||||
{
|
|
||||||
get { return jack_info_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
jack_info_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
jack_info_callback = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jack_info_callback_native = msg => jack_info_callback(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(jack_info_callback_native);
|
|
||||||
Marshal.WriteIntPtr(handle, jack_info_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int jack_info_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_info_callback");
|
|
||||||
|
|
||||||
Action<string> jack_info_callback;
|
|
||||||
delegate void jack_info_delegate(string message);
|
|
||||||
jack_info_delegate jack_info_callback_native;
|
|
||||||
|
|
||||||
// on_backend_disconnect
|
|
||||||
public Action<int> OnBackendDisconnect
|
|
||||||
{
|
|
||||||
get { return on_backend_disconnect; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
on_backend_disconnect = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
on_backend_disconnect_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
on_backend_disconnect_native = (sio, err) => on_backend_disconnect(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_backend_disconnect_native);
|
|
||||||
Marshal.WriteIntPtr(handle, on_backend_disconnect_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int on_backend_disconnect_offset = (int)Marshal.OffsetOf<SoundIo>("on_backend_disconnect");
|
|
||||||
|
|
||||||
Action<int> on_backend_disconnect;
|
|
||||||
delegate void on_backend_disconnect_delegate(IntPtr handle, int errorCode);
|
|
||||||
on_backend_disconnect_delegate on_backend_disconnect_native;
|
|
||||||
|
|
||||||
// on_devices_change
|
|
||||||
public Action OnDevicesChange
|
|
||||||
{
|
|
||||||
get { return on_devices_change; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
on_devices_change = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
on_devices_change_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
on_devices_change_native = sio => on_devices_change();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change_native);
|
|
||||||
Marshal.WriteIntPtr(handle, on_devices_change_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int on_devices_change_offset = (int)Marshal.OffsetOf<SoundIo>("on_devices_change");
|
|
||||||
|
|
||||||
Action on_devices_change;
|
|
||||||
delegate void on_devices_change_delegate(IntPtr handle);
|
|
||||||
on_devices_change_delegate on_devices_change_native;
|
|
||||||
|
|
||||||
// on_events_signal
|
|
||||||
public Action OnEventsSignal
|
|
||||||
{
|
|
||||||
get { return on_events_signal; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
on_events_signal = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
on_events_signal_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
on_events_signal_native = sio => on_events_signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_events_signal_native);
|
|
||||||
Marshal.WriteIntPtr(handle, on_events_signal_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int on_events_signal_offset = (int)Marshal.OffsetOf<SoundIo>("on_events_signal");
|
|
||||||
|
|
||||||
Action on_events_signal;
|
|
||||||
delegate void on_events_signal_delegate(IntPtr handle);
|
|
||||||
on_events_signal_delegate on_events_signal_native;
|
|
||||||
|
|
||||||
|
|
||||||
// functions
|
|
||||||
|
|
||||||
public int BackendCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_backend_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int InputDeviceCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_input_device_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int OutputDeviceCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_output_device_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int DefaultInputDeviceIndex
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_default_input_device_index(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int DefaultOutputDeviceIndex
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_default_output_device_index(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOBackend GetBackend(int index)
|
|
||||||
{
|
|
||||||
return (SoundIOBackend)Natives.soundio_get_backend(handle, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIODevice GetInputDevice(int index)
|
|
||||||
{
|
|
||||||
return new SoundIODevice(Natives.soundio_get_input_device(handle, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIODevice GetOutputDevice(int index)
|
|
||||||
{
|
|
||||||
return new SoundIODevice(Natives.soundio_get_output_device(handle, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Connect()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_connect(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ConnectBackend(SoundIOBackend backend)
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_connect_backend(handle, (SoundIoBackend)backend);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Disconnect()
|
|
||||||
{
|
|
||||||
Natives.soundio_disconnect(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FlushEvents()
|
|
||||||
{
|
|
||||||
Natives.soundio_flush_events(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitEvents()
|
|
||||||
{
|
|
||||||
Natives.soundio_wait_events(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Wakeup()
|
|
||||||
{
|
|
||||||
Natives.soundio_wakeup(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForceDeviceScan()
|
|
||||||
{
|
|
||||||
Natives.soundio_force_device_scan(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIORingBuffer CreateRingBuffer(int capacity)
|
|
||||||
{
|
|
||||||
return new SoundIORingBuffer(Natives.soundio_ring_buffer_create(handle, capacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
// static methods
|
|
||||||
|
|
||||||
public static string VersionString
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Natives.soundio_version_string()); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int VersionMajor
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_version_major(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int VersionMinor
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_version_minor(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int VersionPatch
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_version_patch(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetBackendName(SoundIOBackend backend)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringAnsi(Natives.soundio_backend_name((SoundIoBackend)backend));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HaveBackend(SoundIOBackend backend)
|
|
||||||
{
|
|
||||||
return Natives.soundio_have_backend((SoundIoBackend)backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBytesPerSample(SoundIOFormat format)
|
|
||||||
{
|
|
||||||
return Natives.soundio_get_bytes_per_sample((SoundIoFormat)format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBytesPerFrame(SoundIOFormat format, int channelCount)
|
|
||||||
{
|
|
||||||
return Natives.soundio_get_bytes_per_frame((SoundIoFormat)format, channelCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBytesPerSecond(SoundIOFormat format, int channelCount, int sampleRate)
|
|
||||||
{
|
|
||||||
return Natives.soundio_get_bytes_per_second((SoundIoFormat)format, channelCount, sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetSoundFormatName(SoundIOFormat format)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringAnsi(Natives.soundio_format_string((SoundIoFormat)format));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIOBackend
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Jack,
|
|
||||||
PulseAudio,
|
|
||||||
Alsa,
|
|
||||||
CoreAudio,
|
|
||||||
Wasapi,
|
|
||||||
Dummy
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOChannelArea
|
|
||||||
{
|
|
||||||
internal SoundIOChannelArea(Pointer<SoundIoChannelArea> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pointer<SoundIoChannelArea> handle;
|
|
||||||
|
|
||||||
public IntPtr Pointer
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadIntPtr(handle, ptr_offset); }
|
|
||||||
set { Marshal.WriteIntPtr(handle, ptr_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int ptr_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("ptr");
|
|
||||||
|
|
||||||
public int Step
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, step_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int step_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("step");
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user