Compare commits
840 Commits
Author | SHA1 | Date | |
---|---|---|---|
0003a7c118 | |||
2cdcfe46d8 | |||
fe30c03cac | |||
5813b2e354 | |||
af1906ea04 | |||
68848000f7 | |||
d98da47a0f | |||
306f7e93a0 | |||
8954ff3af2 | |||
d2f3adbf69 | |||
d511c845b7 | |||
21c9ac6240 | |||
81c9052847 | |||
9367e3c35d | |||
52cf141874 | |||
8a352df3c6 | |||
c545c59851 | |||
96ea4e8c8e | |||
b8f48bcf64 | |||
6966211e07 | |||
57524a4c8a | |||
f4539c49d8 | |||
12c62fdbc2 | |||
e3c6be5e29 | |||
4741a05df9 | |||
c6676007bf | |||
92b0b7d753 | |||
232237bf28 | |||
c27e453fd3 | |||
0e037d0213 | |||
0dca1fbe12 | |||
35d91a0e58 | |||
a73a5d7e85 | |||
832a5e8852 | |||
96d1f0da2d | |||
597388ecda | |||
1cf6d7b7bb | |||
7bc9d0cdad | |||
dc0dbc50ab | |||
994f4dc77d | |||
c9e297b74c | |||
dd514a115c | |||
7e0b4bd538 | |||
378080eb87 | |||
e8f5e97fa4 | |||
f3873620a3 | |||
986ac9ff83 | |||
42b9c1e8fe | |||
3b375525fb | |||
e6658c133c | |||
5b42a4d2c4 | |||
8f0c89ffd6 | |||
2c9715acf6 | |||
274af65f69 | |||
4ca78eded5 | |||
6cb6b15612 | |||
2725e40838 | |||
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 |
@ -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
|
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: Missing Shader Instruction
|
||||||
|
description: Shader Instruction is missing in Ryujinx.
|
||||||
|
title: "[GPU]"
|
||||||
|
labels: [gpu, not-implemented]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: instruction
|
||||||
|
attributes:
|
||||||
|
label: Shader instruction
|
||||||
|
description: What shader instruction is missing?
|
||||||
|
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
|
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
|
127
.github/workflows/build.yml
vendored
127
.github/workflows/build.yml
vendored
@ -18,10 +18,20 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pr-checks-${{ github.event.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
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: 45
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||||
@ -33,50 +43,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' && 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: 45
|
||||||
|
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'
|
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: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
|
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
|
27
.github/workflows/nightly_pr_comment.yml
vendored
27
.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: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
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@v6
|
||||||
|
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: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
|
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: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 14
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 14
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Publish macOS
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
artifacts: "publish_ava/*.tar.gz"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
flatpak_release:
|
||||||
|
uses: ./.github/workflows/flatpak.yml
|
||||||
|
needs: release
|
||||||
|
with:
|
||||||
|
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||||
|
secrets: inherit
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -125,6 +125,9 @@ ClientBin/
|
|||||||
packages/*
|
packages/*
|
||||||
*.config
|
*.config
|
||||||
|
|
||||||
|
# Include nuget.config
|
||||||
|
!nuget.config
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
# RIA/Silverlight projects
|
||||||
Generated_Code/
|
Generated_Code/
|
||||||
|
|
||||||
@ -167,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,384 +0,0 @@
|
|||||||
using ARMeilleure.Decoders.Optimizations;
|
|
||||||
using ARMeilleure.Instructions;
|
|
||||||
using ARMeilleure.Memory;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Decoders
|
|
||||||
{
|
|
||||||
static class Decoder
|
|
||||||
{
|
|
||||||
// We define a limit on the number of instructions that a function may have,
|
|
||||||
// this prevents functions being potentially too large, which would
|
|
||||||
// take too long to compile and use too much memory.
|
|
||||||
private const int MaxInstsPerFunction = 2500;
|
|
||||||
|
|
||||||
// For lower code quality translation, we set a lower limit since we're blocking execution.
|
|
||||||
private const int MaxInstsPerFunctionLowCq = 500;
|
|
||||||
|
|
||||||
public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, DecoderMode dMode)
|
|
||||||
{
|
|
||||||
List<Block> blocks = new List<Block>();
|
|
||||||
|
|
||||||
Queue<Block> workQueue = new Queue<Block>();
|
|
||||||
|
|
||||||
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
|
||||||
|
|
||||||
Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction);
|
|
||||||
|
|
||||||
int opsCount = 0;
|
|
||||||
|
|
||||||
int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
|
|
||||||
|
|
||||||
Block GetBlock(ulong blkAddress)
|
|
||||||
{
|
|
||||||
if (!visited.TryGetValue(blkAddress, out Block block))
|
|
||||||
{
|
|
||||||
block = new Block(blkAddress);
|
|
||||||
|
|
||||||
if ((dMode != DecoderMode.MultipleBlocks && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress))
|
|
||||||
{
|
|
||||||
block.Exit = true;
|
|
||||||
block.EndAddress = blkAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
workQueue.Enqueue(block);
|
|
||||||
|
|
||||||
visited.Add(blkAddress, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetBlock(address);
|
|
||||||
|
|
||||||
while (workQueue.TryDequeue(out Block currBlock))
|
|
||||||
{
|
|
||||||
// Check if the current block is inside another block.
|
|
||||||
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
|
||||||
{
|
|
||||||
Block nBlock = blocks[nBlkIndex];
|
|
||||||
|
|
||||||
if (nBlock.Address == currBlock.Address)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Found duplicate block address on the list.");
|
|
||||||
}
|
|
||||||
|
|
||||||
currBlock.Exit = false;
|
|
||||||
|
|
||||||
nBlock.Split(currBlock);
|
|
||||||
|
|
||||||
blocks.Insert(nBlkIndex + 1, currBlock);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currBlock.Exit)
|
|
||||||
{
|
|
||||||
// If we have a block after the current one, set the limit address.
|
|
||||||
ulong limitAddress = ulong.MaxValue;
|
|
||||||
|
|
||||||
if (nBlkIndex != blocks.Count)
|
|
||||||
{
|
|
||||||
Block nBlock = blocks[nBlkIndex];
|
|
||||||
|
|
||||||
int nextIndex = nBlkIndex + 1;
|
|
||||||
|
|
||||||
if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
|
|
||||||
{
|
|
||||||
limitAddress = blocks[nextIndex].Address;
|
|
||||||
}
|
|
||||||
else if (nBlock.Address > currBlock.Address)
|
|
||||||
{
|
|
||||||
limitAddress = blocks[nBlkIndex].Address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dMode == DecoderMode.SingleInstruction)
|
|
||||||
{
|
|
||||||
// Only read at most one instruction
|
|
||||||
limitAddress = currBlock.Address + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FillBlock(memory, mode, currBlock, limitAddress);
|
|
||||||
|
|
||||||
opsCount += currBlock.OpCodes.Count;
|
|
||||||
|
|
||||||
if (currBlock.OpCodes.Count != 0)
|
|
||||||
{
|
|
||||||
// Set child blocks. "Branch" is the block the branch instruction
|
|
||||||
// points to (when taken), "Next" is the block at the next address,
|
|
||||||
// executed when the branch is not taken. For Unconditional Branches
|
|
||||||
// (except BL/BLR that are sub calls) or end of executable, Next is null.
|
|
||||||
OpCode lastOp = currBlock.GetLastOp();
|
|
||||||
|
|
||||||
bool isCall = IsCall(lastOp);
|
|
||||||
|
|
||||||
if (lastOp is IOpCodeBImm op && !isCall)
|
|
||||||
{
|
|
||||||
currBlock.Branch = GetBlock((ulong)op.Immediate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCall || !(IsUnconditionalBranch(lastOp) || IsTrap(lastOp)))
|
|
||||||
{
|
|
||||||
currBlock.Next = GetBlock(currBlock.EndAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the new block on the list (sorted by address).
|
|
||||||
if (blocks.Count != 0)
|
|
||||||
{
|
|
||||||
Block nBlock = blocks[nBlkIndex];
|
|
||||||
|
|
||||||
blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
blocks.Add(currBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blocks.Count == 1 && blocks[0].OpCodes.Count == 0)
|
|
||||||
{
|
|
||||||
Debug.Assert(blocks[0].Exit);
|
|
||||||
Debug.Assert(blocks[0].Address == blocks[0].EndAddress);
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Decoded a single empty exit block. Entry point = 0x{address:X}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dMode == DecoderMode.MultipleBlocks)
|
|
||||||
{
|
|
||||||
return TailCallRemover.RunPass(address, blocks);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return blocks.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool BinarySearch(List<Block> blocks, ulong address, out int index)
|
|
||||||
{
|
|
||||||
index = 0;
|
|
||||||
|
|
||||||
int left = 0;
|
|
||||||
int right = blocks.Count - 1;
|
|
||||||
|
|
||||||
while (left <= right)
|
|
||||||
{
|
|
||||||
int size = right - left;
|
|
||||||
|
|
||||||
int middle = left + (size >> 1);
|
|
||||||
|
|
||||||
Block block = blocks[middle];
|
|
||||||
|
|
||||||
index = middle;
|
|
||||||
|
|
||||||
if (address >= block.Address && address < block.EndAddress)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address < block.Address)
|
|
||||||
{
|
|
||||||
right = middle - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
left = middle + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void FillBlock(
|
|
||||||
IMemoryManager memory,
|
|
||||||
ExecutionMode mode,
|
|
||||||
Block block,
|
|
||||||
ulong limitAddress)
|
|
||||||
{
|
|
||||||
ulong address = block.Address;
|
|
||||||
int itBlockSize = 0;
|
|
||||||
|
|
||||||
OpCode opCode;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (address >= limitAddress && itBlockSize == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
opCode = DecodeOpCode(memory, address, mode);
|
|
||||||
|
|
||||||
block.OpCodes.Add(opCode);
|
|
||||||
|
|
||||||
address += (ulong)opCode.OpCodeSizeInBytes;
|
|
||||||
|
|
||||||
if (opCode is OpCodeT16IfThen it)
|
|
||||||
{
|
|
||||||
itBlockSize = it.IfThenBlockSize;
|
|
||||||
}
|
|
||||||
else if (itBlockSize > 0)
|
|
||||||
{
|
|
||||||
itBlockSize--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (!(IsBranch(opCode) || IsException(opCode)));
|
|
||||||
|
|
||||||
block.EndAddress = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsBranch(OpCode opCode)
|
|
||||||
{
|
|
||||||
return opCode is OpCodeBImm ||
|
|
||||||
opCode is OpCodeBReg || IsAarch32Branch(opCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsUnconditionalBranch(OpCode opCode)
|
|
||||||
{
|
|
||||||
return opCode is OpCodeBImmAl ||
|
|
||||||
opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsAarch32UnconditionalBranch(OpCode opCode)
|
|
||||||
{
|
|
||||||
if (!(opCode is OpCode32 op))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: On ARM32, most instructions have conditional execution,
|
|
||||||
// so there's no "Always" (unconditional) branch like on ARM64.
|
|
||||||
// We need to check if the condition is "Always" instead.
|
|
||||||
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsAarch32Branch(OpCode opCode)
|
|
||||||
{
|
|
||||||
// Note: On ARM32, most ALU operations can write to R15 (PC),
|
|
||||||
// so we must consider such operations as a branch in potential aswell.
|
|
||||||
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
|
||||||
{
|
|
||||||
if (opCode is OpCodeT32)
|
|
||||||
{
|
|
||||||
return opCode.Instruction.Name != InstName.Tst && opCode.Instruction.Name != InstName.Teq &&
|
|
||||||
opCode.Instruction.Name != InstName.Cmp && opCode.Instruction.Name != InstName.Cmn;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same thing for memory operations. We have the cases where PC is a target
|
|
||||||
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
|
||||||
// a write back to PC (wback == true && Rn == 15), however the later may
|
|
||||||
// be "undefined" depending on the CPU, so compilers should not produce that.
|
|
||||||
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
|
||||||
{
|
|
||||||
int rt, rn;
|
|
||||||
|
|
||||||
bool wBack, isLoad;
|
|
||||||
|
|
||||||
if (opCode is IOpCode32Mem opMem)
|
|
||||||
{
|
|
||||||
rt = opMem.Rt;
|
|
||||||
rn = opMem.Rn;
|
|
||||||
wBack = opMem.WBack;
|
|
||||||
isLoad = opMem.IsLoad;
|
|
||||||
|
|
||||||
// For the dual load, we also need to take into account the
|
|
||||||
// case were Rt2 == 15 (PC).
|
|
||||||
if (rt == 14 && opMem.Instruction.Name == InstName.Ldrd)
|
|
||||||
{
|
|
||||||
rt = RegisterAlias.Aarch32Pc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (opCode is IOpCode32MemMult opMemMult)
|
|
||||||
{
|
|
||||||
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
|
|
||||||
|
|
||||||
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
|
|
||||||
rn = opMemMult.Rn;
|
|
||||||
wBack = opMemMult.PostOffset != 0;
|
|
||||||
isLoad = opMemMult.IsLoad;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
|
|
||||||
(rn == RegisterAlias.Aarch32Pc && wBack))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicit branch instructions.
|
|
||||||
return opCode is IOpCode32BImm ||
|
|
||||||
opCode is IOpCode32BReg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCall(OpCode opCode)
|
|
||||||
{
|
|
||||||
return opCode.Instruction.Name == InstName.Bl ||
|
|
||||||
opCode.Instruction.Name == InstName.Blr ||
|
|
||||||
opCode.Instruction.Name == InstName.Blx;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsException(OpCode opCode)
|
|
||||||
{
|
|
||||||
return IsTrap(opCode) || opCode.Instruction.Name == InstName.Svc;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTrap(OpCode opCode)
|
|
||||||
{
|
|
||||||
return opCode.Instruction.Name == InstName.Brk ||
|
|
||||||
opCode.Instruction.Name == InstName.Trap ||
|
|
||||||
opCode.Instruction.Name == InstName.Und;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
int opCode = memory.Read<int>(address);
|
|
||||||
|
|
||||||
InstDescriptor inst;
|
|
||||||
|
|
||||||
OpCodeTable.MakeOp makeOp;
|
|
||||||
|
|
||||||
if (mode == ExecutionMode.Aarch64)
|
|
||||||
{
|
|
||||||
(inst, makeOp) = OpCodeTable.GetInstA64(opCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mode == ExecutionMode.Aarch32Arm)
|
|
||||||
{
|
|
||||||
(inst, makeOp) = OpCodeTable.GetInstA32(opCode);
|
|
||||||
}
|
|
||||||
else /* if (mode == ExecutionMode.Aarch32Thumb) */
|
|
||||||
{
|
|
||||||
(inst, makeOp) = OpCodeTable.GetInstT32(opCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (makeOp != null)
|
|
||||||
{
|
|
||||||
return makeOp(inst, address, opCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mode == ExecutionMode.Aarch32Thumb)
|
|
||||||
{
|
|
||||||
return new OpCodeT16(inst, address, opCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new OpCode(inst, address, opCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders
|
|
||||||
{
|
|
||||||
interface IOpCode32AluBf
|
|
||||||
{
|
|
||||||
int Rd { get; }
|
|
||||||
int Rn { get; }
|
|
||||||
|
|
||||||
int Msb { get; }
|
|
||||||
int Lsb { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders;
|
|
||||||
|
|
||||||
interface IOpCode32Exception
|
|
||||||
{
|
|
||||||
int Id { get; }
|
|
||||||
}
|
|
@ -1,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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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,613 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static class InstEmitAluHelper
|
|
||||||
{
|
|
||||||
public static bool ShouldSetFlags(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
IOpCode32HasSetFlags op = (IOpCode32HasSetFlags)context.CurrOp;
|
|
||||||
|
|
||||||
if (op.SetFlags == null)
|
|
||||||
{
|
|
||||||
return !context.IsInIfThenBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
return op.SetFlags.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d)
|
|
||||||
{
|
|
||||||
SetFlag(context, PState.NFlag, context.ICompareLess (d, Const(d.Type, 0)));
|
|
||||||
SetFlag(context, PState.ZFlag, context.ICompareEqual(d, Const(d.Type, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitAdcsCCheck(ArmEmitterContext context, Operand n, Operand d)
|
|
||||||
{
|
|
||||||
// C = (Rd == Rn && CIn) || Rd < Rn
|
|
||||||
Operand cIn = GetFlag(PState.CFlag);
|
|
||||||
|
|
||||||
Operand cOut = context.BitwiseAnd(context.ICompareEqual(d, n), cIn);
|
|
||||||
|
|
||||||
cOut = context.BitwiseOr(cOut, context.ICompareLessUI(d, n));
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitAddsCCheck(ArmEmitterContext context, Operand n, Operand d)
|
|
||||||
{
|
|
||||||
// C = Rd < Rn
|
|
||||||
SetFlag(context, PState.CFlag, context.ICompareLessUI(d, n));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitAddsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d)
|
|
||||||
{
|
|
||||||
// V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
|
||||||
Operand vOut = context.BitwiseExclusiveOr(d, n);
|
|
||||||
|
|
||||||
vOut = context.BitwiseAnd(vOut, context.BitwiseNot(context.BitwiseExclusiveOr(n, m)));
|
|
||||||
|
|
||||||
vOut = context.ICompareLess(vOut, Const(vOut.Type, 0));
|
|
||||||
|
|
||||||
SetFlag(context, PState.VFlag, vOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSbcsCCheck(ArmEmitterContext context, Operand n, Operand m)
|
|
||||||
{
|
|
||||||
// C = (Rn == Rm && CIn) || Rn > Rm
|
|
||||||
Operand cIn = GetFlag(PState.CFlag);
|
|
||||||
|
|
||||||
Operand cOut = context.BitwiseAnd(context.ICompareEqual(n, m), cIn);
|
|
||||||
|
|
||||||
cOut = context.BitwiseOr(cOut, context.ICompareGreaterUI(n, m));
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSubsCCheck(ArmEmitterContext context, Operand n, Operand m)
|
|
||||||
{
|
|
||||||
// C = Rn >= Rm
|
|
||||||
SetFlag(context, PState.CFlag, context.ICompareGreaterOrEqualUI(n, m));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSubsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d)
|
|
||||||
{
|
|
||||||
// V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
|
||||||
Operand vOut = context.BitwiseExclusiveOr(d, n);
|
|
||||||
|
|
||||||
vOut = context.BitwiseAnd(vOut, context.BitwiseExclusiveOr(n, m));
|
|
||||||
|
|
||||||
vOut = context.ICompareLess(vOut, Const(vOut.Type, 0));
|
|
||||||
|
|
||||||
SetFlag(context, PState.VFlag, vOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
|
|
||||||
{
|
|
||||||
Debug.Assert(op.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
|
|
||||||
context.ShiftLeft(context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
|
|
||||||
|
|
||||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
|
|
||||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
|
|
||||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
|
|
||||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
|
|
||||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
|
|
||||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
|
|
||||||
|
|
||||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
|
|
||||||
{
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
|
||||||
|
|
||||||
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
|
|
||||||
context.ShiftLeft(context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
|
|
||||||
{
|
|
||||||
Debug.Assert(op.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
|
|
||||||
|
|
||||||
return context.ConvertI64ToI32(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
|
|
||||||
{
|
|
||||||
Debug.Assert(value.Type == OperandType.I32);
|
|
||||||
|
|
||||||
if (((OpCode32)context.CurrOp).IsThumb())
|
|
||||||
{
|
|
||||||
bool isReturn = IsA32Return(context);
|
|
||||||
if (!isReturn)
|
|
||||||
{
|
|
||||||
context.StoreToContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitVirtualJump(context, value, isReturn);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitBxWritePc(context, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value)
|
|
||||||
{
|
|
||||||
Debug.Assert(value.Type == OperandType.I32);
|
|
||||||
|
|
||||||
if (rd == RegisterAlias.Aarch32Pc && setFlags)
|
|
||||||
{
|
|
||||||
if (setFlags)
|
|
||||||
{
|
|
||||||
// TODO: Load SPSR etc.
|
|
||||||
|
|
||||||
EmitBxWritePc(context, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitAluWritePc(context, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetIntA32(context, rd, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetAluN(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
if (context.CurrOp is IOpCodeAlu op)
|
|
||||||
{
|
|
||||||
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs)
|
|
||||||
{
|
|
||||||
return GetIntOrZR(context, op.Rn);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return GetIntOrSP(context, op.Rn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (context.CurrOp is IOpCode32Alu op32)
|
|
||||||
{
|
|
||||||
return GetIntA32(context, op32.Rn);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw InvalidOpCodeType(context.CurrOp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetAluM(ArmEmitterContext context, bool setCarry = true)
|
|
||||||
{
|
|
||||||
switch (context.CurrOp)
|
|
||||||
{
|
|
||||||
// ARM32.
|
|
||||||
case IOpCode32AluImm op:
|
|
||||||
{
|
|
||||||
if (ShouldSetFlags(context) && op.IsRotated && setCarry)
|
|
||||||
{
|
|
||||||
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Const(op.Immediate);
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode32AluImm16 op: return Const(op.Immediate);
|
|
||||||
|
|
||||||
case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
|
||||||
case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
|
|
||||||
|
|
||||||
case IOpCode32AluReg op: return GetIntA32(context, op.Rm);
|
|
||||||
|
|
||||||
// ARM64.
|
|
||||||
case IOpCodeAluImm op:
|
|
||||||
{
|
|
||||||
if (op.GetOperandType() == OperandType.I32)
|
|
||||||
{
|
|
||||||
return Const((int)op.Immediate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Const(op.Immediate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case IOpCodeAluRs op:
|
|
||||||
{
|
|
||||||
Operand value = GetIntOrZR(context, op.Rm);
|
|
||||||
|
|
||||||
switch (op.ShiftType)
|
|
||||||
{
|
|
||||||
case ShiftType.Lsl: value = context.ShiftLeft (value, Const(op.Shift)); break;
|
|
||||||
case ShiftType.Lsr: value = context.ShiftRightUI(value, Const(op.Shift)); break;
|
|
||||||
case ShiftType.Asr: value = context.ShiftRightSI(value, Const(op.Shift)); break;
|
|
||||||
case ShiftType.Ror: value = context.RotateRight (value, Const(op.Shift)); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IOpCodeAluRx op:
|
|
||||||
{
|
|
||||||
Operand value = GetExtendedM(context, op.Rm, op.IntType);
|
|
||||||
|
|
||||||
value = context.ShiftLeft(value, Const(op.Shift));
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: throw InvalidOpCodeType(context.CurrOp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Exception InvalidOpCodeType(OpCode opCode)
|
|
||||||
{
|
|
||||||
return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ARM32 helpers.
|
|
||||||
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, IOpCode32AluRsImm op, bool setCarry)
|
|
||||||
{
|
|
||||||
Operand m = GetIntA32(context, op.Rm);
|
|
||||||
|
|
||||||
int shift = op.Immediate;
|
|
||||||
|
|
||||||
if (shift == 0)
|
|
||||||
{
|
|
||||||
switch (op.ShiftType)
|
|
||||||
{
|
|
||||||
case ShiftType.Lsr: shift = 32; break;
|
|
||||||
case ShiftType.Asr: shift = 32; break;
|
|
||||||
case ShiftType.Ror: shift = 1; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shift != 0)
|
|
||||||
{
|
|
||||||
setCarry &= ShouldSetFlags(context);
|
|
||||||
|
|
||||||
switch (op.ShiftType)
|
|
||||||
{
|
|
||||||
case ShiftType.Lsl: m = GetLslC(context, m, setCarry, shift); break;
|
|
||||||
case ShiftType.Lsr: m = GetLsrC(context, m, setCarry, shift); break;
|
|
||||||
case ShiftType.Asr: m = GetAsrC(context, m, setCarry, shift); break;
|
|
||||||
case ShiftType.Ror:
|
|
||||||
if (op.Immediate != 0)
|
|
||||||
{
|
|
||||||
m = GetRorC(context, m, setCarry, shift);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m = GetRrxC(context, m, setCarry);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int DecodeImmShift(ShiftType shiftType, int shift)
|
|
||||||
{
|
|
||||||
if (shift == 0)
|
|
||||||
{
|
|
||||||
switch (shiftType)
|
|
||||||
{
|
|
||||||
case ShiftType.Lsr: shift = 32; break;
|
|
||||||
case ShiftType.Asr: shift = 32; break;
|
|
||||||
case ShiftType.Ror: shift = 1; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry)
|
|
||||||
{
|
|
||||||
Operand m = GetIntA32(context, op.Rm);
|
|
||||||
Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
|
|
||||||
Operand shiftIsZero = context.ICompareEqual(s, Const(0));
|
|
||||||
|
|
||||||
Operand zeroResult = m;
|
|
||||||
Operand shiftResult = m;
|
|
||||||
|
|
||||||
setCarry &= ShouldSetFlags(context);
|
|
||||||
|
|
||||||
switch (op.ShiftType)
|
|
||||||
{
|
|
||||||
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break;
|
|
||||||
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
|
|
||||||
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
|
|
||||||
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true)
|
|
||||||
{
|
|
||||||
Debug.Assert(boolValue.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand endLabel = Label();
|
|
||||||
|
|
||||||
if (expected)
|
|
||||||
{
|
|
||||||
context.BranchIfFalse(endLabel, boolValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.BranchIfTrue(endLabel, boolValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
action();
|
|
||||||
|
|
||||||
context.MarkLabel(endLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
|
||||||
Operand result = context.ShiftLeft(m, shift);
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
EmitIfHelper(context, shiftIsZero, () =>
|
|
||||||
{
|
|
||||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
|
|
||||||
|
|
||||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
|
||||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
if ((uint)shift > 32)
|
|
||||||
{
|
|
||||||
return GetShiftByMoreThan32(context, setCarry);
|
|
||||||
}
|
|
||||||
else if (shift == 32)
|
|
||||||
{
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMLsb(context, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Const(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
Operand cOut = context.ShiftRightUI(m, Const(32 - shift));
|
|
||||||
|
|
||||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ShiftLeft(m, Const(shift));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
|
||||||
Operand result = context.ShiftRightUI(m, shift);
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
EmitIfHelper(context, shiftIsZero, () =>
|
|
||||||
{
|
|
||||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
|
||||||
|
|
||||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
|
||||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
if ((uint)shift > 32)
|
|
||||||
{
|
|
||||||
return GetShiftByMoreThan32(context, setCarry);
|
|
||||||
}
|
|
||||||
else if (shift == 32)
|
|
||||||
{
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMMsb(context, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Const(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMShrOut(context, m, shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ShiftRightUI(m, Const(shift));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand GetShiftByMoreThan32(ArmEmitterContext context, bool setCarry)
|
|
||||||
{
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetFlag(context, PState.CFlag, Const(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Const(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand l32Result;
|
|
||||||
Operand ge32Result;
|
|
||||||
|
|
||||||
Operand less32 = context.ICompareLess(shift, Const(32));
|
|
||||||
|
|
||||||
ge32Result = context.ShiftRightSI(m, Const(31));
|
|
||||||
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
|
|
||||||
{
|
|
||||||
SetCarryMLsb(context, ge32Result);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
l32Result = context.ShiftRightSI(m, shift);
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
|
|
||||||
{
|
|
||||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
|
||||||
|
|
||||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ConditionalSelect(less32, l32Result, ge32Result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
if ((uint)shift >= 32)
|
|
||||||
{
|
|
||||||
m = context.ShiftRightSI(m, Const(31));
|
|
||||||
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMLsb(context, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMShrOut(context, m, shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ShiftRightSI(m, Const(shift));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
|
||||||
|
|
||||||
shift = context.BitwiseAnd(shift, Const(0x1f));
|
|
||||||
m = context.RotateRight(m, shift);
|
|
||||||
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
EmitIfHelper(context, shiftIsZero, () =>
|
|
||||||
{
|
|
||||||
SetCarryMMsb(context, m);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
shift &= 0x1f;
|
|
||||||
|
|
||||||
m = context.RotateRight(m, Const(shift));
|
|
||||||
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMMsb(context, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
// Rotate right by 1 with carry.
|
|
||||||
Operand cIn = context.Copy(GetFlag(PState.CFlag));
|
|
||||||
|
|
||||||
if (setCarry)
|
|
||||||
{
|
|
||||||
SetCarryMLsb(context, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
m = context.ShiftRightUI(m, Const(1));
|
|
||||||
|
|
||||||
m = context.BitwiseOr(m, context.ShiftLeft(cIn, Const(31)));
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetCarryMLsb(ArmEmitterContext context, Operand m)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetCarryMMsb(ArmEmitterContext context, Operand m)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift)
|
|
||||||
{
|
|
||||||
Debug.Assert(m.Type == OperandType.I32);
|
|
||||||
|
|
||||||
Operand cOut = context.ShiftRightUI(m, Const(shift - 1));
|
|
||||||
|
|
||||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
|
||||||
|
|
||||||
SetFlag(context, PState.CFlag, cOut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitFlowHelper;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit32
|
|
||||||
{
|
|
||||||
public static void B(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
|
||||||
|
|
||||||
context.Branch(context.GetLabel((ulong)op.Immediate));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Bl(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
Blx(context, x: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Blx(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
Blx(context, x: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Blx(ArmEmitterContext context, bool x)
|
|
||||||
{
|
|
||||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
|
||||||
|
|
||||||
uint pc = op.GetPc();
|
|
||||||
|
|
||||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
|
||||||
|
|
||||||
uint currentPc = isThumb
|
|
||||||
? pc | 1
|
|
||||||
: pc - 4;
|
|
||||||
|
|
||||||
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
|
||||||
|
|
||||||
// If x is true, then this is a branch with link and exchange.
|
|
||||||
// In this case we need to swap the mode between Arm <-> Thumb.
|
|
||||||
if (x)
|
|
||||||
{
|
|
||||||
SetFlag(context, PState.TFlag, Const(isThumb ? 0 : 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitCall(context, (ulong)op.Immediate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Blxr(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
|
||||||
|
|
||||||
uint pc = op.GetPc();
|
|
||||||
|
|
||||||
Operand addr = context.Copy(GetIntA32(context, op.Rm));
|
|
||||||
Operand bitOne = context.BitwiseAnd(addr, Const(1));
|
|
||||||
|
|
||||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
|
||||||
|
|
||||||
uint currentPc = isThumb
|
|
||||||
? (pc - 2) | 1
|
|
||||||
: pc - 4;
|
|
||||||
|
|
||||||
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
|
||||||
|
|
||||||
SetFlag(context, PState.TFlag, bitOne);
|
|
||||||
|
|
||||||
EmitBxWritePc(context, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Bx(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
|
||||||
|
|
||||||
EmitBxWritePc(context, GetIntA32(context, op.Rm), op.Rm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Cbnz(ArmEmitterContext context) => EmitCb(context, onNotZero: true);
|
|
||||||
public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false);
|
|
||||||
|
|
||||||
private static void EmitCb(ArmEmitterContext context, bool onNotZero)
|
|
||||||
{
|
|
||||||
OpCodeT16BImmCmp op = (OpCodeT16BImmCmp)context.CurrOp;
|
|
||||||
|
|
||||||
Operand value = GetIntOrZR(context, op.Rn);
|
|
||||||
Operand lblTarget = context.GetLabel((ulong)op.Immediate);
|
|
||||||
|
|
||||||
if (onNotZero)
|
|
||||||
{
|
|
||||||
context.BranchIfTrue(lblTarget, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.BranchIfFalse(lblTarget, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void It(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCodeT16IfThen op = (OpCodeT16IfThen)context.CurrOp;
|
|
||||||
|
|
||||||
context.SetIfThenBlockState(op.IfThenBlockConds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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,164 +0,0 @@
|
|||||||
using ARMeilleure.Decoders;
|
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using ARMeilleure.State;
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit
|
|
||||||
{
|
|
||||||
private const int DczSizeLog2 = 4; // Log2 size in words
|
|
||||||
public const int DczSizeInBytes = 4 << DczSizeLog2;
|
|
||||||
|
|
||||||
public static void 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 < DczSizeInBytes; offset += 8)
|
|
||||||
{
|
|
||||||
Operand address = context.Add(t, Const(offset));
|
|
||||||
|
|
||||||
InstEmitMemoryHelper.EmitStore(context, address, RegisterConsts.ZeroIndex, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No-op
|
|
||||||
case 0b11_011_0111_1110_001: // DC CIVAC
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0b11_011_0111_0101_001: // IC IVAU
|
|
||||||
Operand target = Register(op.Rt, RegisterType.Integer, OperandType.I64);
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetPackedId(OpCodeSystem op)
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
|
|
||||||
id = op.Op2 << 0;
|
|
||||||
id |= op.CRm << 3;
|
|
||||||
id |= op.CRn << 7;
|
|
||||||
id |= op.Op1 << 11;
|
|
||||||
id |= op.Op0 << 14;
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitGetNzcv(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
|
||||||
|
|
||||||
Operand 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,273 +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 void InvalidateCacheLine(ulong address)
|
|
||||||
{
|
|
||||||
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CheckSynchronization()
|
|
||||||
{
|
|
||||||
Statistics.PauseTimer();
|
|
||||||
|
|
||||||
ExecutionContext context = GetContext();
|
|
||||||
|
|
||||||
context.CheckInterrupt();
|
|
||||||
|
|
||||||
Statistics.ResumeTimer();
|
|
||||||
|
|
||||||
return context.Running;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ExecutionContext GetContext()
|
|
||||||
{
|
|
||||||
return Context.Context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMemoryManager GetMemoryManager()
|
|
||||||
{
|
|
||||||
return Context.Memory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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,169 +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 uint Pstate
|
|
||||||
{
|
|
||||||
get => _nativeContext.GetPstate();
|
|
||||||
set => _nativeContext.SetPstate(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,13 +0,0 @@
|
|||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public enum PState
|
|
||||||
{
|
|
||||||
TFlag = 5,
|
|
||||||
EFlag = 9,
|
|
||||||
QFlag = 27,
|
|
||||||
VFlag = 28,
|
|
||||||
CFlag = 29,
|
|
||||||
ZFlag = 30,
|
|
||||||
NFlag = 31
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
using ARMeilleure.CodeGen;
|
|
||||||
using ARMeilleure.CodeGen.Unwinding;
|
|
||||||
using ARMeilleure.Memory;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
|
||||||
{
|
|
||||||
static class JitCache
|
|
||||||
{
|
|
||||||
private const int PageSize = 4 * 1024;
|
|
||||||
private const int PageMask = PageSize - 1;
|
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
|
||||||
|
|
||||||
private static readonly List<CacheEntry> _cacheEntries = new List<CacheEntry>();
|
|
||||||
|
|
||||||
private static readonly object _lock = new object();
|
|
||||||
private static bool _initialized;
|
|
||||||
|
|
||||||
public static IntPtr Base => _jitRegion.Pointer;
|
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
|
||||||
{
|
|
||||||
if (_initialized) return;
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_initialized) return;
|
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
|
||||||
|
|
||||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(PageSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
_initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr Map(CompiledFunction func)
|
|
||||||
{
|
|
||||||
byte[] code = func.Code;
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
Debug.Assert(_initialized);
|
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
|
||||||
|
|
||||||
IntPtr funcPtr = _jitRegion.Pointer + funcOffset;
|
|
||||||
|
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
|
||||||
|
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
|
||||||
|
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
|
||||||
|
|
||||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
|
||||||
|
|
||||||
return funcPtr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Unmap(IntPtr pointer)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
Debug.Assert(_initialized);
|
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
|
||||||
|
|
||||||
bool result = TryFind(funcOffset, out CacheEntry entry);
|
|
||||||
Debug.Assert(result);
|
|
||||||
|
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
|
||||||
|
|
||||||
Remove(funcOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
|
||||||
{
|
|
||||||
int endOffs = offset + size;
|
|
||||||
|
|
||||||
int regionStart = offset & ~PageMask;
|
|
||||||
int regionEnd = (endOffs + PageMask) & ~PageMask;
|
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
|
||||||
{
|
|
||||||
int endOffs = offset + size;
|
|
||||||
|
|
||||||
int regionStart = offset & ~PageMask;
|
|
||||||
int regionEnd = (endOffs + PageMask) & ~PageMask;
|
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
|
||||||
{
|
|
||||||
codeSize = AlignCodeSize(codeSize);
|
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
|
||||||
|
|
||||||
if (allocOffset < 0)
|
|
||||||
{
|
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
|
||||||
|
|
||||||
return allocOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
|
||||||
{
|
|
||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Add(int offset, int size, UnwindInfo unwindInfo)
|
|
||||||
{
|
|
||||||
CacheEntry entry = new CacheEntry(offset, size, unwindInfo);
|
|
||||||
|
|
||||||
int index = _cacheEntries.BinarySearch(entry);
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cacheEntries.Insert(index, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Remove(int offset)
|
|
||||||
{
|
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
_cacheEntries.RemoveAt(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryFind(int offset, out CacheEntry entry)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
entry = _cacheEntries[index];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,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,299 +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.InvalidateCacheLine)));
|
|
||||||
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,756 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="K">Key</typeparam>
|
|
||||||
/// <typeparam name="V">Value</typeparam>
|
|
||||||
public class IntervalTree<K, V> where K : IComparable<K>
|
|
||||||
{
|
|
||||||
private const int ArrayGrowthSize = 32;
|
|
||||||
|
|
||||||
private const bool Black = true;
|
|
||||||
private const bool Red = false;
|
|
||||||
private IntervalTreeNode<K, V> _root = null;
|
|
||||||
private int _count = 0;
|
|
||||||
|
|
||||||
public int Count => _count;
|
|
||||||
|
|
||||||
public IntervalTree() { }
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the values of the interval whose key is <paramref name="key"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">Key of the node value to get</param>
|
|
||||||
/// <param name="value">Value with the given <paramref name="key"/></param>
|
|
||||||
/// <returns>True if the key is on the dictionary, false otherwise</returns>
|
|
||||||
public bool TryGet(K key, out V value)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> node = GetNode(key);
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
value = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = node.Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the start addresses of the intervals whose start and end keys overlap the given range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Start of the range</param>
|
|
||||||
/// <param name="end">End of the range</param>
|
|
||||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
|
||||||
/// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
|
|
||||||
/// <returns>Number of intervals found</returns>
|
|
||||||
public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0)
|
|
||||||
{
|
|
||||||
GetValues(_root, start, end, ref overlaps, ref overlapCount);
|
|
||||||
|
|
||||||
return overlapCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Start of the range to add</param>
|
|
||||||
/// <param name="end">End of the range to insert</param>
|
|
||||||
/// <param name="value">Value to add</param>
|
|
||||||
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
|
|
||||||
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
|
|
||||||
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an existing or adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Start of the range to add</param>
|
|
||||||
/// <param name="end">End of the range to insert</param>
|
|
||||||
/// <param name="value">Value to add</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
|
|
||||||
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
|
|
||||||
public V GetOrAdd(K start, K end, V value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
|
|
||||||
return node.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a value from the tree, searching for it with <paramref name="key"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">Key of the node to remove</param>
|
|
||||||
/// <returns>Number of deleted values</returns>
|
|
||||||
public int Remove(K key)
|
|
||||||
{
|
|
||||||
int removed = Delete(key);
|
|
||||||
|
|
||||||
_count -= removed;
|
|
||||||
|
|
||||||
return removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A list of all values sorted by Key Order</returns>
|
|
||||||
public List<V> AsList()
|
|
||||||
{
|
|
||||||
List<V> list = new List<V>();
|
|
||||||
|
|
||||||
AddToList(_root, list);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods (BST)
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds all values that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The node to search for values within</param>
|
|
||||||
/// <param name="list">The list to add values to</param>
|
|
||||||
private void AddToList(IntervalTreeNode<K, V> node, List<V> list)
|
|
||||||
{
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddToList(node.Left, list);
|
|
||||||
|
|
||||||
list.Add(node.Value);
|
|
||||||
|
|
||||||
AddToList(node.Right, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">Key of the node to get</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
|
||||||
/// <returns>Node reference in the tree</returns>
|
|
||||||
private IntervalTreeNode<K, V> GetNode(K key)
|
|
||||||
{
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalTreeNode<K, V> node = _root;
|
|
||||||
while (node != null)
|
|
||||||
{
|
|
||||||
int cmp = key.CompareTo(node.Start);
|
|
||||||
if (cmp < 0)
|
|
||||||
{
|
|
||||||
node = node.Left;
|
|
||||||
}
|
|
||||||
else if (cmp > 0)
|
|
||||||
{
|
|
||||||
node = node.Right;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve all values that overlap the given start and end keys.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Start of the range</param>
|
|
||||||
/// <param name="end">End of the range</param>
|
|
||||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
|
||||||
/// <param name="overlapCount">Overlaps count to update</param>
|
|
||||||
private void GetValues(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount)
|
|
||||||
{
|
|
||||||
if (node == null || start.CompareTo(node.Max) >= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetValues(node.Left, start, end, ref overlaps, ref overlapCount);
|
|
||||||
|
|
||||||
bool endsOnRight = end.CompareTo(node.Start) > 0;
|
|
||||||
if (endsOnRight)
|
|
||||||
{
|
|
||||||
if (start.CompareTo(node.End) < 0)
|
|
||||||
{
|
|
||||||
if (overlaps.Length >= overlapCount)
|
|
||||||
{
|
|
||||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
overlaps[overlapCount++] = node.Start;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetValues(node.Right, start, end, ref overlaps, ref overlapCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Propagate an increase in max value starting at the given node, heading up the tree.
|
|
||||||
/// This should only be called if the max increases - not for rebalancing or removals.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The node to start propagating from</param>
|
|
||||||
private void PropagateIncrease(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
K max = node.Max;
|
|
||||||
IntervalTreeNode<K, V> ptr = node;
|
|
||||||
|
|
||||||
while ((ptr = ptr.Parent) != null)
|
|
||||||
{
|
|
||||||
if (max.CompareTo(ptr.Max) > 0)
|
|
||||||
{
|
|
||||||
ptr.Max = max;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Propagate recalculating max value starting at the given node, heading up the tree.
|
|
||||||
/// This fully recalculates the max value from all children when there is potential for it to decrease.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The node to start propagating from</param>
|
|
||||||
private void PropagateFull(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> ptr = node;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
K max = ptr.End;
|
|
||||||
|
|
||||||
if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0)
|
|
||||||
{
|
|
||||||
max = ptr.Left.Max;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0)
|
|
||||||
{
|
|
||||||
max = ptr.Right.Max;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr.Max = max;
|
|
||||||
} while ((ptr = ptr.Parent) != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
|
|
||||||
/// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
|
|
||||||
/// Each node can contain multiple values, and has an end address which is the maximum of all those values.
|
|
||||||
/// Post insertion, the "max" value of the node and all parents are updated.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">Start of the range to insert</param>
|
|
||||||
/// <param name="end">End of the range to insert</param>
|
|
||||||
/// <param name="value">Value to insert</param>
|
|
||||||
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
|
|
||||||
/// <param name="outNode">Node that was inserted or modified</param>
|
|
||||||
/// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns>
|
|
||||||
private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> parent = null;
|
|
||||||
IntervalTreeNode<K, V> node = _root;
|
|
||||||
|
|
||||||
while (node != null)
|
|
||||||
{
|
|
||||||
parent = node;
|
|
||||||
int cmp = start.CompareTo(node.Start);
|
|
||||||
if (cmp < 0)
|
|
||||||
{
|
|
||||||
node = node.Left;
|
|
||||||
}
|
|
||||||
else if (cmp > 0)
|
|
||||||
{
|
|
||||||
node = node.Right;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outNode = node;
|
|
||||||
|
|
||||||
if (updateFactoryCallback != null)
|
|
||||||
{
|
|
||||||
// Replace
|
|
||||||
node.Value = updateFactoryCallback(start, node.Value);
|
|
||||||
|
|
||||||
int endCmp = end.CompareTo(node.End);
|
|
||||||
|
|
||||||
if (endCmp > 0)
|
|
||||||
{
|
|
||||||
node.End = end;
|
|
||||||
if (end.CompareTo(node.Max) > 0)
|
|
||||||
{
|
|
||||||
node.Max = end;
|
|
||||||
PropagateIncrease(node);
|
|
||||||
RestoreBalanceAfterInsertion(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (endCmp < 0)
|
|
||||||
{
|
|
||||||
node.End = end;
|
|
||||||
PropagateFull(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent);
|
|
||||||
if (newNode.Parent == null)
|
|
||||||
{
|
|
||||||
_root = newNode;
|
|
||||||
}
|
|
||||||
else if (start.CompareTo(parent.Start) < 0)
|
|
||||||
{
|
|
||||||
parent.Left = newNode;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parent.Right = newNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropagateIncrease(newNode);
|
|
||||||
_count++;
|
|
||||||
RestoreBalanceAfterInsertion(newNode);
|
|
||||||
outNode = newNode;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the value from the dictionary after searching for it with <paramref name="key">.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">Key to search for</param>
|
|
||||||
/// <returns>Number of deleted values</returns>
|
|
||||||
private int Delete(K key)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> nodeToDelete = GetNode(key);
|
|
||||||
|
|
||||||
if (nodeToDelete == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalTreeNode<K, V> replacementNode;
|
|
||||||
|
|
||||||
if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
|
|
||||||
{
|
|
||||||
replacementNode = nodeToDelete;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
replacementNode = PredecessorOf(nodeToDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
|
||||||
|
|
||||||
if (tmp != null)
|
|
||||||
{
|
|
||||||
tmp.Parent = ParentOf(replacementNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ParentOf(replacementNode) == null)
|
|
||||||
{
|
|
||||||
_root = tmp;
|
|
||||||
}
|
|
||||||
else if (replacementNode == LeftOf(ParentOf(replacementNode)))
|
|
||||||
{
|
|
||||||
ParentOf(replacementNode).Left = tmp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ParentOf(replacementNode).Right = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (replacementNode != nodeToDelete)
|
|
||||||
{
|
|
||||||
nodeToDelete.Start = replacementNode.Start;
|
|
||||||
nodeToDelete.Value = replacementNode.Value;
|
|
||||||
nodeToDelete.End = replacementNode.End;
|
|
||||||
nodeToDelete.Max = replacementNode.Max;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropagateFull(replacementNode);
|
|
||||||
|
|
||||||
if (tmp != null && ColorOf(replacementNode) == Black)
|
|
||||||
{
|
|
||||||
RestoreBalanceAfterRemoval(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the node with the largest key where <paramref name="node"/> is considered the root node.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Root Node</param>
|
|
||||||
/// <returns>Node with the maximum key in the tree of <paramref name="node"/></returns>
|
|
||||||
private static IntervalTreeNode<K, V> Maximum(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> tmp = node;
|
|
||||||
while (tmp.Right != null)
|
|
||||||
{
|
|
||||||
tmp = tmp.Right;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds the node whose key is immediately less than <paramref name="node"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Node to find the predecessor of</param>
|
|
||||||
/// <returns>Predecessor of <paramref name="node"/></returns>
|
|
||||||
private static IntervalTreeNode<K, V> PredecessorOf(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
if (node.Left != null)
|
|
||||||
{
|
|
||||||
return Maximum(node.Left);
|
|
||||||
}
|
|
||||||
IntervalTreeNode<K, V> parent = node.Parent;
|
|
||||||
while (parent != null && node == parent.Left)
|
|
||||||
{
|
|
||||||
node = parent;
|
|
||||||
parent = parent.Parent;
|
|
||||||
}
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods (RBL)
|
|
||||||
|
|
||||||
private void RestoreBalanceAfterRemoval(IntervalTreeNode<K, V> balanceNode)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> ptr = balanceNode;
|
|
||||||
|
|
||||||
while (ptr != _root && ColorOf(ptr) == Black)
|
|
||||||
{
|
|
||||||
if (ptr == LeftOf(ParentOf(ptr)))
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ptr));
|
|
||||||
|
|
||||||
if (ColorOf(sibling) == Red)
|
|
||||||
{
|
|
||||||
SetColor(sibling, Black);
|
|
||||||
SetColor(ParentOf(ptr), Red);
|
|
||||||
RotateLeft(ParentOf(ptr));
|
|
||||||
sibling = RightOf(ParentOf(ptr));
|
|
||||||
}
|
|
||||||
if (ColorOf(LeftOf(sibling)) == Black && ColorOf(RightOf(sibling)) == Black)
|
|
||||||
{
|
|
||||||
SetColor(sibling, Red);
|
|
||||||
ptr = ParentOf(ptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ColorOf(RightOf(sibling)) == Black)
|
|
||||||
{
|
|
||||||
SetColor(LeftOf(sibling), Black);
|
|
||||||
SetColor(sibling, Red);
|
|
||||||
RotateRight(sibling);
|
|
||||||
sibling = RightOf(ParentOf(ptr));
|
|
||||||
}
|
|
||||||
SetColor(sibling, ColorOf(ParentOf(ptr)));
|
|
||||||
SetColor(ParentOf(ptr), Black);
|
|
||||||
SetColor(RightOf(sibling), Black);
|
|
||||||
RotateLeft(ParentOf(ptr));
|
|
||||||
ptr = _root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ptr));
|
|
||||||
|
|
||||||
if (ColorOf(sibling) == Red)
|
|
||||||
{
|
|
||||||
SetColor(sibling, Black);
|
|
||||||
SetColor(ParentOf(ptr), Red);
|
|
||||||
RotateRight(ParentOf(ptr));
|
|
||||||
sibling = LeftOf(ParentOf(ptr));
|
|
||||||
}
|
|
||||||
if (ColorOf(RightOf(sibling)) == Black && ColorOf(LeftOf(sibling)) == Black)
|
|
||||||
{
|
|
||||||
SetColor(sibling, Red);
|
|
||||||
ptr = ParentOf(ptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ColorOf(LeftOf(sibling)) == Black)
|
|
||||||
{
|
|
||||||
SetColor(RightOf(sibling), Black);
|
|
||||||
SetColor(sibling, Red);
|
|
||||||
RotateLeft(sibling);
|
|
||||||
sibling = LeftOf(ParentOf(ptr));
|
|
||||||
}
|
|
||||||
SetColor(sibling, ColorOf(ParentOf(ptr)));
|
|
||||||
SetColor(ParentOf(ptr), Black);
|
|
||||||
SetColor(LeftOf(sibling), Black);
|
|
||||||
RotateRight(ParentOf(ptr));
|
|
||||||
ptr = _root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetColor(ptr, Black);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestoreBalanceAfterInsertion(IntervalTreeNode<K, V> balanceNode)
|
|
||||||
{
|
|
||||||
SetColor(balanceNode, Red);
|
|
||||||
while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red)
|
|
||||||
{
|
|
||||||
if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode))))
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ParentOf(balanceNode)));
|
|
||||||
|
|
||||||
if (ColorOf(sibling) == Red)
|
|
||||||
{
|
|
||||||
SetColor(ParentOf(balanceNode), Black);
|
|
||||||
SetColor(sibling, Black);
|
|
||||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
|
||||||
balanceNode = ParentOf(ParentOf(balanceNode));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (balanceNode == RightOf(ParentOf(balanceNode)))
|
|
||||||
{
|
|
||||||
balanceNode = ParentOf(balanceNode);
|
|
||||||
RotateLeft(balanceNode);
|
|
||||||
}
|
|
||||||
SetColor(ParentOf(balanceNode), Black);
|
|
||||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
|
||||||
RotateRight(ParentOf(ParentOf(balanceNode)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ParentOf(balanceNode)));
|
|
||||||
|
|
||||||
if (ColorOf(sibling) == Red)
|
|
||||||
{
|
|
||||||
SetColor(ParentOf(balanceNode), Black);
|
|
||||||
SetColor(sibling, Black);
|
|
||||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
|
||||||
balanceNode = ParentOf(ParentOf(balanceNode));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (balanceNode == LeftOf(ParentOf(balanceNode)))
|
|
||||||
{
|
|
||||||
balanceNode = ParentOf(balanceNode);
|
|
||||||
RotateRight(balanceNode);
|
|
||||||
}
|
|
||||||
SetColor(ParentOf(balanceNode), Black);
|
|
||||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
|
||||||
RotateLeft(ParentOf(ParentOf(balanceNode)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetColor(_root, Black);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateLeft(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> right = RightOf(node);
|
|
||||||
node.Right = LeftOf(right);
|
|
||||||
if (node.Right != null)
|
|
||||||
{
|
|
||||||
node.Right.Parent = node;
|
|
||||||
}
|
|
||||||
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
|
|
||||||
right.Parent = nodeParent;
|
|
||||||
if (nodeParent == null)
|
|
||||||
{
|
|
||||||
_root = right;
|
|
||||||
}
|
|
||||||
else if (node == LeftOf(nodeParent))
|
|
||||||
{
|
|
||||||
nodeParent.Left = right;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nodeParent.Right = right;
|
|
||||||
}
|
|
||||||
right.Left = node;
|
|
||||||
node.Parent = right;
|
|
||||||
|
|
||||||
PropagateFull(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateRight(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
IntervalTreeNode<K, V> left = LeftOf(node);
|
|
||||||
node.Left = RightOf(left);
|
|
||||||
if (node.Left != null)
|
|
||||||
{
|
|
||||||
node.Left.Parent = node;
|
|
||||||
}
|
|
||||||
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
|
|
||||||
left.Parent = nodeParent;
|
|
||||||
if (nodeParent == null)
|
|
||||||
{
|
|
||||||
_root = left;
|
|
||||||
}
|
|
||||||
else if (node == RightOf(nodeParent))
|
|
||||||
{
|
|
||||||
nodeParent.Right = left;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nodeParent.Left = left;
|
|
||||||
}
|
|
||||||
left.Right = node;
|
|
||||||
node.Parent = left;
|
|
||||||
|
|
||||||
PropagateFull(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Safety-Methods
|
|
||||||
|
|
||||||
// These methods save memory by allowing us to forego sentinel nil nodes, as well as serve as protection against NullReferenceExceptions.
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the color of <paramref name="node"/>, or Black if it is null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Node</param>
|
|
||||||
/// <returns>The boolean color of <paramref name="node"/>, or black if null</returns>
|
|
||||||
private static bool ColorOf(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
return node == null || node.Color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the color of <paramref name="node"/> node to <paramref name="color"/>.
|
|
||||||
/// <br></br>
|
|
||||||
/// This method does nothing if <paramref name="node"/> is null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Node to set the color of</param>
|
|
||||||
/// <param name="color">Color (Boolean)</param>
|
|
||||||
private static void SetColor(IntervalTreeNode<K, V> node, bool color)
|
|
||||||
{
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
node.Color = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method returns the left node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Node to retrieve the left child from</param>
|
|
||||||
/// <returns>Left child of <paramref name="node"/></returns>
|
|
||||||
private static IntervalTreeNode<K, V> LeftOf(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
return node?.Left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method returns the right node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Node to retrieve the right child from</param>
|
|
||||||
/// <returns>Right child of <paramref name="node"/></returns>
|
|
||||||
private static IntervalTreeNode<K, V> RightOf(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
return node?.Right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the parent node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">Node to retrieve the parent from</param>
|
|
||||||
/// <returns>Parent of <paramref name="node"/></returns>
|
|
||||||
private static IntervalTreeNode<K, V> ParentOf(IntervalTreeNode<K, V> node)
|
|
||||||
{
|
|
||||||
return node?.Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public bool ContainsKey(K key)
|
|
||||||
{
|
|
||||||
return GetNode(key) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_root = null;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="K">Key type of the node</typeparam>
|
|
||||||
/// <typeparam name="V">Value type of the node</typeparam>
|
|
||||||
internal class IntervalTreeNode<K, V>
|
|
||||||
{
|
|
||||||
internal bool Color = true;
|
|
||||||
internal IntervalTreeNode<K, V> Left = null;
|
|
||||||
internal IntervalTreeNode<K, V> Right = null;
|
|
||||||
internal IntervalTreeNode<K, V> Parent = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The start of the range.
|
|
||||||
/// </summary>
|
|
||||||
internal K Start;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The end of the range.
|
|
||||||
/// </summary>
|
|
||||||
internal K End;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum end value of this node and all its children.
|
|
||||||
/// </summary>
|
|
||||||
internal K Max;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Value stored on this node.
|
|
||||||
/// </summary>
|
|
||||||
internal V Value;
|
|
||||||
|
|
||||||
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
|
||||||
{
|
|
||||||
this.Start = start;
|
|
||||||
this.End = end;
|
|
||||||
this.Max = end;
|
|
||||||
this.Value = value;
|
|
||||||
this.Parent = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,567 +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 TranslatorCache<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 TranslatorCache<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.GuestSize, 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong ExecuteSingle(State.ExecutionContext context, ulong address)
|
|
||||||
{
|
|
||||||
TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode);
|
|
||||||
|
|
||||||
Statistics.StartTimer();
|
|
||||||
|
|
||||||
ulong nextAddr = func.Execute(context);
|
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
|
||||||
|
|
||||||
return nextAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Step(State.ExecutionContext context, ulong address)
|
|
||||||
{
|
|
||||||
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
|
||||||
|
|
||||||
address = func.Execute(context);
|
|
||||||
|
|
||||||
EnqueueForDeletion(address, func);
|
|
||||||
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
if (!Functions.TryGetValue(address, out TranslatedFunction func))
|
|
||||||
{
|
|
||||||
func = Translate(address, mode, highCq: false);
|
|
||||||
|
|
||||||
TranslatedFunction oldFunc = Functions.GetOrAdd(address, func.GuestSize, func);
|
|
||||||
|
|
||||||
if (oldFunc != func)
|
|
||||||
{
|
|
||||||
JitCache.Unmap(func.FuncPtr);
|
|
||||||
func = oldFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PtcProfiler.Enabled)
|
|
||||||
{
|
|
||||||
PtcProfiler.AddEntry(address, mode, highCq: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterFunction(address, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RegisterFunction(ulong guestAddress, TranslatedFunction func)
|
|
||||||
{
|
|
||||||
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
|
|
||||||
{
|
|
||||||
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false)
|
|
||||||
{
|
|
||||||
var context = new ArmEmitterContext(
|
|
||||||
Memory,
|
|
||||||
CountTable,
|
|
||||||
FunctionTable,
|
|
||||||
Stubs,
|
|
||||||
address,
|
|
||||||
highCq,
|
|
||||||
mode: Aarch32Mode.User);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.Decoding);
|
|
||||||
|
|
||||||
Block[] blocks = Decoder.Decode(Memory, address, mode, highCq, singleStep ? DecoderMode.SingleInstruction : DecoderMode.MultipleBlocks);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.Decoding);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.Translation);
|
|
||||||
|
|
||||||
EmitSynchronization(context);
|
|
||||||
|
|
||||||
if (blocks[0].Address != address)
|
|
||||||
{
|
|
||||||
context.Branch(context.GetLabel(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
|
|
||||||
|
|
||||||
ulong funcSize = funcRange.End - funcRange.Start;
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.Translation, cfg);
|
|
||||||
|
|
||||||
Logger.StartPass(PassName.RegisterUsage);
|
|
||||||
|
|
||||||
RegisterUsage.RunPass(cfg, mode);
|
|
||||||
|
|
||||||
Logger.EndPass(PassName.RegisterUsage);
|
|
||||||
|
|
||||||
var retType = OperandType.I64;
|
|
||||||
var argTypes = new OperandType[] { OperandType.I64 };
|
|
||||||
|
|
||||||
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
|
||||||
|
|
||||||
if (context.HasPtc && !singleStep)
|
|
||||||
{
|
|
||||||
options |= CompilerOptions.Relocatable;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options);
|
|
||||||
|
|
||||||
if (context.HasPtc && !singleStep)
|
|
||||||
{
|
|
||||||
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
|
||||||
|
|
||||||
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
|
||||||
}
|
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.Map<GuestFunction>();
|
|
||||||
|
|
||||||
Allocators.ResetAll();
|
|
||||||
|
|
||||||
return new TranslatedFunction(func, counter, funcSize, highCq);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Range
|
|
||||||
{
|
|
||||||
public ulong Start { get; }
|
|
||||||
public ulong End { get; }
|
|
||||||
|
|
||||||
public Range(ulong start, ulong end)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ControlFlowGraph EmitAndGetCFG(
|
|
||||||
ArmEmitterContext context,
|
|
||||||
Block[] blocks,
|
|
||||||
out Range range,
|
|
||||||
out Counter<uint> counter)
|
|
||||||
{
|
|
||||||
counter = null;
|
|
||||||
|
|
||||||
ulong rangeStart = ulong.MaxValue;
|
|
||||||
ulong rangeEnd = 0;
|
|
||||||
|
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
|
||||||
{
|
|
||||||
Block block = blocks[blkIndex];
|
|
||||||
|
|
||||||
if (!block.Exit)
|
|
||||||
{
|
|
||||||
if (rangeStart > block.Address)
|
|
||||||
{
|
|
||||||
rangeStart = block.Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rangeEnd < block.EndAddress)
|
|
||||||
{
|
|
||||||
rangeEnd = block.EndAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.Address == context.EntryAddress && !context.HighCq)
|
|
||||||
{
|
|
||||||
EmitRejitCheck(context, out counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.CurrBlock = block;
|
|
||||||
|
|
||||||
context.MarkLabel(context.GetLabel(block.Address));
|
|
||||||
|
|
||||||
if (block.Exit)
|
|
||||||
{
|
|
||||||
// Left option here as it may be useful if we need to return to managed rather than tail call in
|
|
||||||
// future. (eg. for debug)
|
|
||||||
bool useReturns = false;
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int opcIndex = 0; opcIndex < block.OpCodes.Count; opcIndex++)
|
|
||||||
{
|
|
||||||
OpCode opCode = block.OpCodes[opcIndex];
|
|
||||||
|
|
||||||
context.CurrOp = opCode;
|
|
||||||
|
|
||||||
bool isLastOp = opcIndex == block.OpCodes.Count - 1;
|
|
||||||
|
|
||||||
if (isLastOp && block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address)
|
|
||||||
{
|
|
||||||
EmitSynchronization(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand lblPredicateSkip = default;
|
|
||||||
|
|
||||||
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
|
|
||||||
{
|
|
||||||
lblPredicateSkip = Label();
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Invert());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opCode is OpCode32 op && op.Cond < Condition.Al)
|
|
||||||
{
|
|
||||||
lblPredicateSkip = Label();
|
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Invert());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opCode.Instruction.Emitter != null)
|
|
||||||
{
|
|
||||||
opCode.Instruction.Emitter(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Invalid instruction \"{opCode.Instruction.Name}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lblPredicateSkip != default)
|
|
||||||
{
|
|
||||||
context.MarkLabel(lblPredicateSkip);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.IsInIfThenBlock && opCode.Instruction.Name != InstName.It)
|
|
||||||
{
|
|
||||||
context.AdvanceIfThenBlockState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
range = new Range(rangeStart, rangeEnd);
|
|
||||||
|
|
||||||
return context.GetControlFlowGraph();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void EmitRejitCheck(ArmEmitterContext context, out Counter<uint> counter)
|
|
||||||
{
|
|
||||||
const int MinsCallForRejit = 100;
|
|
||||||
|
|
||||||
counter = new Counter<uint>(context.CountTable);
|
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
|
||||||
|
|
||||||
Operand address = !context.HasPtc ?
|
|
||||||
Const(ref counter.Value) :
|
|
||||||
Const(ref counter.Value, Ptc.CountTableSymbol);
|
|
||||||
|
|
||||||
Operand curCount = context.Load(OperandType.I32, address);
|
|
||||||
Operand count = context.Add(curCount, Const(1));
|
|
||||||
context.Store(address, count);
|
|
||||||
context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
|
|
||||||
|
|
||||||
context.MarkLabel(lblEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void EmitSynchronization(EmitterContext context)
|
|
||||||
{
|
|
||||||
long countOffs = NativeContext.GetCounterOffset();
|
|
||||||
|
|
||||||
Operand lblNonZero = Label();
|
|
||||||
Operand lblExit = Label();
|
|
||||||
|
|
||||||
Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
|
|
||||||
Operand count = context.Load(OperandType.I32, countAddr);
|
|
||||||
context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
|
||||||
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
|
|
||||||
|
|
||||||
context.Return(Const(0L));
|
|
||||||
|
|
||||||
context.MarkLabel(lblNonZero);
|
|
||||||
count = context.Subtract(count, Const(1));
|
|
||||||
context.Store(countAddr, count);
|
|
||||||
|
|
||||||
context.MarkLabel(lblExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
|
||||||
{
|
|
||||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
|
||||||
ClearRejitQueue(allowRequeue: true);
|
|
||||||
|
|
||||||
ulong[] overlapAddresses = Array.Empty<ulong>();
|
|
||||||
|
|
||||||
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
|
|
||||||
|
|
||||||
for (int index = 0; index < overlapsCount; index++)
|
|
||||||
{
|
|
||||||
ulong overlapAddress = overlapAddresses[index];
|
|
||||||
|
|
||||||
if (Functions.TryGetValue(overlapAddress, out TranslatedFunction overlap))
|
|
||||||
{
|
|
||||||
Functions.Remove(overlapAddress);
|
|
||||||
Volatile.Write(ref FunctionTable.GetValue(overlapAddress), FunctionTable.Fill);
|
|
||||||
EnqueueForDeletion(overlapAddress, overlap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove overlapping functions from the JitCache aswell.
|
|
||||||
// This should be done safely, with a mechanism to ensure the function is not being executed.
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
List<TranslatedFunction> functions = Functions.AsList();
|
|
||||||
|
|
||||||
foreach (var func in functions)
|
|
||||||
{
|
|
||||||
JitCache.Unmap(func.FuncPtr);
|
|
||||||
|
|
||||||
func.CallCounter?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Functions.Clear();
|
|
||||||
|
|
||||||
while (_oldFuncs.TryDequeue(out var kv))
|
|
||||||
{
|
|
||||||
JitCache.Unmap(kv.Value.FuncPtr);
|
|
||||||
|
|
||||||
kv.Value.CallCounter?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearRejitQueue(bool allowRequeue)
|
|
||||||
{
|
|
||||||
_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.21" />
|
||||||
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" />
|
||||||
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.21" />
|
||||||
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" />
|
||||||
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" />
|
||||||
|
<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.14.2" />
|
||||||
|
<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.6.1" />
|
||||||
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
|
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
||||||
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
||||||
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
|
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
|
||||||
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
|
<PackageVersion Include="System.Management" Version="7.0.1" />
|
||||||
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
28
README.md
28
README.md
@ -21,13 +21,17 @@
|
|||||||
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||||
alt="">
|
alt="">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://crwd.in/ryujinx">
|
||||||
|
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
|
||||||
|
alt="">
|
||||||
|
</a>
|
||||||
<a href="https://discord.com/invite/VkQYXAZ">
|
<a href="https://discord.com/invite/VkQYXAZ">
|
||||||
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
|
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
|
||||||
alt="Discord">
|
alt="Discord">
|
||||||
</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">
|
||||||
@ -36,18 +40,20 @@
|
|||||||
|
|
||||||
## 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.
|
||||||
@ -90,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
|
|
||||||
- **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**
|
||||||
|
|
||||||
@ -118,21 +124,19 @@ If you'd like to support the project financially, Ryujinx has an active Patreon
|
|||||||
<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.
|
||||||
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOChannelAreas
|
|
||||||
{
|
|
||||||
static readonly int native_size = Marshal.SizeOf<SoundIoChannelArea>();
|
|
||||||
|
|
||||||
internal SoundIOChannelAreas(IntPtr head, int channelCount, int frameCount)
|
|
||||||
{
|
|
||||||
this.head = head;
|
|
||||||
this.channel_count = channelCount;
|
|
||||||
this.frame_count = frameCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr head;
|
|
||||||
int channel_count;
|
|
||||||
int frame_count;
|
|
||||||
|
|
||||||
public bool IsEmpty
|
|
||||||
{
|
|
||||||
get { return head == IntPtr.Zero; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOChannelArea GetArea(int channel)
|
|
||||||
{
|
|
||||||
return new SoundIOChannelArea(head + native_size * channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ChannelCount => channel_count;
|
|
||||||
public int FrameCount => frame_count;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIOChannelId
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
FrontLeft,
|
|
||||||
FrontRight,
|
|
||||||
FrontCenter,
|
|
||||||
Lfe,
|
|
||||||
BackLeft,
|
|
||||||
BackRight,
|
|
||||||
FrontLeftCenter,
|
|
||||||
FrontRightCenter,
|
|
||||||
BackCenter,
|
|
||||||
SideLeft,
|
|
||||||
SideRight,
|
|
||||||
TopCenter,
|
|
||||||
TopFrontLeft,
|
|
||||||
TopFrontCenter,
|
|
||||||
TopFrontRight,
|
|
||||||
TopBackLeft,
|
|
||||||
TopBackCenter,
|
|
||||||
TopBackRight,
|
|
||||||
BackLeftCenter,
|
|
||||||
BackRightCenter,
|
|
||||||
FrontLeftWide,
|
|
||||||
FrontRightWide,
|
|
||||||
FrontLeftHigh,
|
|
||||||
FrontCenterHigh,
|
|
||||||
FrontRightHigh,
|
|
||||||
TopFrontLeftCenter,
|
|
||||||
TopFrontRightCenter,
|
|
||||||
TopSideLeft,
|
|
||||||
TopSideRight,
|
|
||||||
LeftLfe,
|
|
||||||
RightLfe,
|
|
||||||
Lfe2,
|
|
||||||
BottomCenter,
|
|
||||||
BottomLeftCenter,
|
|
||||||
BottomRightCenter,
|
|
||||||
MsMid,
|
|
||||||
MsSide,
|
|
||||||
AmbisonicW,
|
|
||||||
AmbisonicX,
|
|
||||||
AmbisonicY,
|
|
||||||
AmbisonicZ,
|
|
||||||
XyX,
|
|
||||||
XyY,
|
|
||||||
HeadphonesLeft,
|
|
||||||
HeadphonesRight,
|
|
||||||
ClickTrack,
|
|
||||||
ForeignLanguage,
|
|
||||||
HearingImpaired,
|
|
||||||
Narration,
|
|
||||||
Haptic,
|
|
||||||
DialogCentricMix,
|
|
||||||
Aux,
|
|
||||||
Aux0,
|
|
||||||
Aux1,
|
|
||||||
Aux2,
|
|
||||||
Aux3,
|
|
||||||
Aux4,
|
|
||||||
Aux5,
|
|
||||||
Aux6,
|
|
||||||
Aux7,
|
|
||||||
Aux8,
|
|
||||||
Aux9,
|
|
||||||
Aux10,
|
|
||||||
Aux11,
|
|
||||||
Aux12,
|
|
||||||
Aux13,
|
|
||||||
Aux14,
|
|
||||||
Aux15
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOChannelLayout
|
|
||||||
{
|
|
||||||
public static int BuiltInCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_channel_layout_builtin_count(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelLayout GetBuiltIn(int index)
|
|
||||||
{
|
|
||||||
return new SoundIOChannelLayout(Natives.soundio_channel_layout_get_builtin(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelLayout GetDefault(int channelCount)
|
|
||||||
{
|
|
||||||
var handle = Natives.soundio_channel_layout_get_default(channelCount);
|
|
||||||
|
|
||||||
return new SoundIOChannelLayout (handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelId ParseChannelId(string name)
|
|
||||||
{
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(name);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (SoundIOChannelId)Natives.soundio_parse_channel_id(ptr, name.Length);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// instance members
|
|
||||||
|
|
||||||
internal SoundIOChannelLayout(Pointer<SoundIoChannelLayout> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly Pointer<SoundIoChannelLayout> handle;
|
|
||||||
|
|
||||||
public bool IsNull
|
|
||||||
{
|
|
||||||
get { return handle.Handle == IntPtr.Zero; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IntPtr Handle
|
|
||||||
{
|
|
||||||
get { return handle; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ChannelCount
|
|
||||||
{
|
|
||||||
get { return IsNull ? 0 : Marshal.ReadInt32((IntPtr)handle + channel_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int channel_count_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channel_count");
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return IsNull ? null : Marshal.PtrToStringAnsi(Marshal.ReadIntPtr((IntPtr)handle + name_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("name");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOChannelId> Channels
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsNull) yield break;
|
|
||||||
|
|
||||||
for (int i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
yield return (SoundIOChannelId)Marshal.ReadInt32((IntPtr)handle + channels_offset + sizeof(SoundIoChannelId) * i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int channels_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channels");
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
if (!(other is SoundIOChannelLayout)) return false;
|
|
||||||
|
|
||||||
var s = (SoundIOChannelLayout) other;
|
|
||||||
|
|
||||||
return handle == s.handle || Natives.soundio_channel_layout_equal(handle, s.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return handle.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string DetectBuiltInName()
|
|
||||||
{
|
|
||||||
if (IsNull) throw new InvalidOperationException();
|
|
||||||
|
|
||||||
return Natives.soundio_channel_layout_detect_builtin(handle) ? Name : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int FindChannel(SoundIOChannelId channel)
|
|
||||||
{
|
|
||||||
if (IsNull) throw new InvalidOperationException();
|
|
||||||
|
|
||||||
return Natives.soundio_channel_layout_find_channel(handle, (SoundIoChannelId)channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIODevice
|
|
||||||
{
|
|
||||||
public static SoundIOChannelLayout BestMatchingChannelLayout(SoundIODevice device1, SoundIODevice device2)
|
|
||||||
{
|
|
||||||
var ptr1 = Marshal.ReadIntPtr(device1.handle, layouts_offset);
|
|
||||||
var ptr2 = Marshal.ReadIntPtr(device2.handle, layouts_offset);
|
|
||||||
|
|
||||||
return new SoundIOChannelLayout(Natives.soundio_best_matching_channel_layout(ptr1, device1.LayoutCount, ptr2, device2.LayoutCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal SoundIODevice(Pointer<SoundIoDevice> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly Pointer<SoundIoDevice> handle;
|
|
||||||
|
|
||||||
// Equality (based on handle and native func)
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
var d = other as SoundIODevice;
|
|
||||||
|
|
||||||
return d != null && (this.handle == d.handle || Natives.soundio_device_equal (this.handle, d.handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIODevice obj1, SoundIODevice obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIODevice obj1, SoundIODevice obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields
|
|
||||||
|
|
||||||
public SoundIODeviceAim Aim
|
|
||||||
{
|
|
||||||
get { return (SoundIODeviceAim)Marshal.ReadInt32(handle, aim_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int aim_offset = (int)Marshal.OffsetOf<SoundIoDevice>("aim");
|
|
||||||
|
|
||||||
public SoundIOFormat CurrentFormat
|
|
||||||
{
|
|
||||||
get { return (SoundIOFormat)Marshal.ReadInt32(handle, current_format_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int current_format_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_format");
|
|
||||||
|
|
||||||
public SoundIOChannelLayout CurrentLayout
|
|
||||||
{
|
|
||||||
get { return new SoundIOChannelLayout((IntPtr)handle + current_layout_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int current_layout_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_layout");
|
|
||||||
|
|
||||||
public int FormatCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, format_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int format_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("format_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOFormat> Formats
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var ptr = Marshal.ReadIntPtr(handle, formats_offset);
|
|
||||||
|
|
||||||
for (int i = 0; i < FormatCount; i++)
|
|
||||||
{
|
|
||||||
yield return (SoundIOFormat)Marshal.ReadInt32(ptr, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int formats_offset = (int)Marshal.OffsetOf<SoundIoDevice>("formats");
|
|
||||||
|
|
||||||
public string Id
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, id_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int id_offset = (int)Marshal.OffsetOf<SoundIoDevice>("id");
|
|
||||||
|
|
||||||
public bool IsRaw
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, is_raw_offset) != 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int is_raw_offset = (int)Marshal.OffsetOf<SoundIoDevice>("is_raw");
|
|
||||||
|
|
||||||
public int LayoutCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, layout_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layout_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layout_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOChannelLayout> Layouts
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var ptr = Marshal.ReadIntPtr (handle, layouts_offset);
|
|
||||||
|
|
||||||
for (int i = 0; i < LayoutCount; i++)
|
|
||||||
{
|
|
||||||
yield return new SoundIOChannelLayout(ptr + i * Marshal.SizeOf<SoundIoChannelLayout>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layouts_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layouts");
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoDevice>("name");
|
|
||||||
|
|
||||||
public int ProbeError
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, probe_error_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int probe_error_offset = (int)Marshal.OffsetOf<SoundIoDevice>("probe_error");
|
|
||||||
|
|
||||||
public int ReferenceCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, ref_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int ref_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("ref_count");
|
|
||||||
|
|
||||||
public int SampleRateCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, sample_rate_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int sample_rate_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rate_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOSampleRateRange> SampleRates
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var ptr = Marshal.ReadIntPtr(handle, sample_rates_offset);
|
|
||||||
|
|
||||||
for (int i = 0; i < SampleRateCount; i++)
|
|
||||||
{
|
|
||||||
yield return new SoundIOSampleRateRange(Marshal.ReadInt32(ptr, i * 2), Marshal.ReadInt32(ptr, i * 2 + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int sample_rates_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rates");
|
|
||||||
|
|
||||||
public double SoftwareLatencyCurrent
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_current_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_current_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_current_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_current");
|
|
||||||
|
|
||||||
public double SoftwareLatencyMin
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_min_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_min_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_min_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_min");
|
|
||||||
|
|
||||||
public double SoftwareLatencyMax
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_max_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_max_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_max_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_max");
|
|
||||||
|
|
||||||
public SoundIO SoundIO
|
|
||||||
{
|
|
||||||
get { return new SoundIO(Marshal.ReadIntPtr(handle, soundio_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int soundio_offset = (int)Marshal.OffsetOf<SoundIoDevice>("soundio");
|
|
||||||
|
|
||||||
// functions
|
|
||||||
|
|
||||||
public void AddReference()
|
|
||||||
{
|
|
||||||
Natives.soundio_device_ref(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveReference()
|
|
||||||
{
|
|
||||||
Natives.soundio_device_unref(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortDeviceChannelLayouts()
|
|
||||||
{
|
|
||||||
Natives.soundio_device_sort_channel_layouts(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly SoundIOFormat S16NE = BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
|
||||||
public static readonly SoundIOFormat U16NE = BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
|
||||||
public static readonly SoundIOFormat S24NE = BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
|
||||||
public static readonly SoundIOFormat U24NE = BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
|
||||||
public static readonly SoundIOFormat S32NE = BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
|
||||||
public static readonly SoundIOFormat U32NE = BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
|
||||||
public static readonly SoundIOFormat Float32NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
|
||||||
public static readonly SoundIOFormat Float64NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
|
||||||
public static readonly SoundIOFormat S16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
|
||||||
public static readonly SoundIOFormat U16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
|
||||||
public static readonly SoundIOFormat S24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
|
||||||
public static readonly SoundIOFormat U24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
|
||||||
public static readonly SoundIOFormat S32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
|
||||||
public static readonly SoundIOFormat U32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
|
||||||
public static readonly SoundIOFormat Float32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
|
||||||
public static readonly SoundIOFormat Float64FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
|
||||||
|
|
||||||
public bool SupportsFormat(SoundIOFormat format)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_format(handle, (SoundIoFormat)format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsSampleRate(int sampleRate)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_sample_rate(handle, sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsChannelCount(int channelCount)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_layout(handle, SoundIOChannelLayout.GetDefault(channelCount).Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetNearestSampleRate(int sampleRate)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_nearest_sample_rate(handle, sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOInStream CreateInStream()
|
|
||||||
{
|
|
||||||
return new SoundIOInStream(Natives.soundio_instream_create(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOOutStream CreateOutStream()
|
|
||||||
{
|
|
||||||
return new SoundIOOutStream(Natives.soundio_outstream_create(handle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIODeviceAim // soundio.h (228, 6)
|
|
||||||
{
|
|
||||||
Input,
|
|
||||||
Output
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIOException : Exception
|
|
||||||
{
|
|
||||||
internal SoundIOException(SoundIoError errorCode) : base (Marshal.PtrToStringAnsi(Natives.soundio_strerror((int) errorCode))) { }
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user