mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 07:00:20 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb60a8a032 | ||
|
|
f40bc134df | ||
|
|
f3811f04bd | ||
|
|
94bb7eb311 | ||
|
|
03ca3e7e68 | ||
|
|
2c3e6cbe65 | ||
|
|
f9bdf0bd01 | ||
|
|
4afc7adb05 | ||
|
|
2152d1b2e9 | ||
|
|
0ecfc651b6 | ||
|
|
4a3250ddea | ||
|
|
633f624a69 | ||
|
|
26a8a2717c | ||
|
|
4c0e6d5779 | ||
|
|
a350ef5d1b | ||
|
|
9d9bd750e2 | ||
|
|
85d1b573ef | ||
|
|
2f8c5b4820 | ||
|
|
a1f55f0b0b | ||
|
|
7b1d9540b7 | ||
|
|
1007f874bf | ||
|
|
24ea4b7537 | ||
|
|
93ab57454a | ||
|
|
4882f10536 | ||
|
|
fe43a2bcb2 | ||
|
|
6700511cdf | ||
|
|
e836639427 | ||
|
|
7eb3f33162 | ||
|
|
b35a514ea6 | ||
|
|
8f372821f8 | ||
|
|
6376d06bed | ||
|
|
1bb293d4be | ||
|
|
0e069f1e97 | ||
|
|
9074b810a9 | ||
|
|
c5fe8723d3 | ||
|
|
434bffac33 | ||
|
|
e84848b16b | ||
|
|
69ed39d49e | ||
|
|
230bde6aef | ||
|
|
c24d7aacba | ||
|
|
e613876e9d | ||
|
|
1473129a8f | ||
|
|
c42808cb70 | ||
|
|
054c119e2e | ||
|
|
f75bd2f09b | ||
|
|
a7424416d9 | ||
|
|
cadb0a2ddb | ||
|
|
2da819c0f3 | ||
|
|
e0c783de74 | ||
|
|
6c003fcb9a | ||
|
|
c4faffc0e2 | ||
|
|
cc2d21f411 | ||
|
|
59686a6c60 | ||
|
|
0ab864da17 | ||
|
|
3c32271dd0 | ||
|
|
507a95b817 | ||
|
|
9bb9e954c2 | ||
|
|
7f3582bb23 | ||
|
|
efe15ce336 | ||
|
|
21b0f35ef4 | ||
|
|
ccf332d48e | ||
|
|
802a32ce8a | ||
|
|
70c02d5c58 | ||
|
|
2e4fb47848 | ||
|
|
a4d5302369 | ||
|
|
6ff3c90af3 | ||
|
|
c114279118 | ||
|
|
7ffd3e55d5 | ||
|
|
201fe6ee23 | ||
|
|
83fedd6c8f | ||
|
|
dedf4a93d0 | ||
|
|
1f59f0e226 | ||
|
|
c3c2b6115d | ||
|
|
115fbb5039 | ||
|
|
27973d5637 | ||
|
|
f121be649d | ||
|
|
af9bcb3efd | ||
|
|
4877bb3f19 | ||
|
|
e2583249c6 | ||
|
|
f48071dcbe | ||
|
|
fa72be2ec5 | ||
|
|
b7ff6dd9c6 | ||
|
|
cb6d60aa87 | ||
|
|
80c9a43eef | ||
|
|
05155778d4 | ||
|
|
49b8dae189 | ||
|
|
3e59fc0a8c | ||
|
|
f98c010854 | ||
|
|
10ee963f52 | ||
|
|
af7462ee6a | ||
|
|
be4777110c | ||
|
|
696503680a | ||
|
|
dd4d3bcf38 | ||
|
|
f26bb6bf53 | ||
|
|
2c4fd79304 | ||
|
|
910ec4aadd | ||
|
|
fb7275b3d8 | ||
|
|
51b4bfc6a6 | ||
|
|
9f7bc94f9d | ||
|
|
069e2ce62a | ||
|
|
5bbbced1bd | ||
|
|
941fd9c6ea | ||
|
|
3332220d06 | ||
|
|
5933a59c09 | ||
|
|
aee8c9def2 | ||
|
|
c2136272bf | ||
|
|
dd26b0c879 | ||
|
|
3d9114b74b | ||
|
|
3f3e937967 | ||
|
|
4beb29141b | ||
|
|
9a8e7eaace | ||
|
|
4227e012aa | ||
|
|
40bbcb7061 | ||
|
|
67663b812e | ||
|
|
d24d0a95a0 | ||
|
|
87fbcf754d | ||
|
|
403fd62b34 | ||
|
|
d92b6a9ac4 | ||
|
|
c2092bfed0 | ||
|
|
a6cf7fa508 | ||
|
|
5ff09f5091 | ||
|
|
9af7ee6bd2 | ||
|
|
d1e36f264f | ||
|
|
b1ec50c7c2 | ||
|
|
93eead243f | ||
|
|
ceac38a6ac | ||
|
|
3b2e657fd4 | ||
|
|
380ba0a014 | ||
|
|
1fe497d1dd | ||
|
|
7816b150d0 | ||
|
|
ce8bc9d25c | ||
|
|
4634688aca | ||
|
|
dd3e3ed189 | ||
|
|
f8ef6feff9 | ||
|
|
9201ac5a6b | ||
|
|
8ebf049fb9 | ||
|
|
3c5b59d985 | ||
|
|
6b91e0cb0e | ||
|
|
587b924de9 | ||
|
|
d507f4c9b1 | ||
|
|
39bc2a82c1 | ||
|
|
774325dcf2 | ||
|
|
a1378f94ce | ||
|
|
77ec950ff2 | ||
|
|
592d6cc43f | ||
|
|
610caf8529 | ||
|
|
d20b46e46f | ||
|
|
4094aa1b9a | ||
|
|
f8c6baae97 | ||
|
|
5c9bb6594c | ||
|
|
56df57e980 | ||
|
|
f7b4d25803 | ||
|
|
4fffe68f81 | ||
|
|
95b15d788b | ||
|
|
ae9312bdab | ||
|
|
b78da2e5ad | ||
|
|
54fc8cb0bd | ||
|
|
228009c283 | ||
|
|
1f878ce4cd | ||
|
|
e4b7a65a49 | ||
|
|
c77a707dbe | ||
|
|
f81fc4e4f0 | ||
|
|
d385e496d3 | ||
|
|
f8c4c543e3 | ||
|
|
d2f903ae55 | ||
|
|
d1249ec5cf | ||
|
|
bf9a6d763c | ||
|
|
0b829d2c46 | ||
|
|
bddb533fa0 | ||
|
|
1c35eeffeb | ||
|
|
c7254e31ed | ||
|
|
b0bd8a62a2 | ||
|
|
17a55fbb39 | ||
|
|
dedec83881 | ||
|
|
9fd5c73633 | ||
|
|
bdb890a8b0 | ||
|
|
5043d09771 | ||
|
|
da51169ba9 | ||
|
|
f72cee480f | ||
|
|
7546160811 | ||
|
|
e7d5a01c5f | ||
|
|
0c3a8d0bc8 | ||
|
|
19e58cac62 | ||
|
|
c4ba7eee87 | ||
|
|
09c4a5594a | ||
|
|
d204155661 | ||
|
|
924b8c10a9 | ||
|
|
9017cd14c8 | ||
|
|
8d89adef2e | ||
|
|
6615b55c12 | ||
|
|
66865dd177 | ||
|
|
1e709d1150 | ||
|
|
476ee0cd7d | ||
|
|
6df51a57b3 | ||
|
|
e65545a537 | ||
|
|
b8e864ffdf | ||
|
|
ed87c01470 | ||
|
|
6c21b86a8f | ||
|
|
d79b7fcc49 | ||
|
|
b9a6caea8d | ||
|
|
9688f5e17d | ||
|
|
f6f8d26426 | ||
|
|
dba0a1d09e | ||
|
|
af3145674e | ||
|
|
3c19e634b3 | ||
|
|
f964a5187e | ||
|
|
8e0fdfc325 | ||
|
|
839f9ecd3b | ||
|
|
95fc69b628 | ||
|
|
b9da95838a | ||
|
|
1059279d5d | ||
|
|
5dc85307a6 | ||
|
|
549e06aade | ||
|
|
3b189f6d7d | ||
|
|
a5d3692b53 | ||
|
|
97a68cb643 | ||
|
|
d1b5dfd4b1 | ||
|
|
6cdaea680d | ||
|
|
870e395ac4 | ||
|
|
04592f82f5 | ||
|
|
19e849283f | ||
|
|
b6e1469cd4 | ||
|
|
5ef0db994d | ||
|
|
b523407a3e | ||
|
|
8021dc10a1 | ||
|
|
7e8d734e43 | ||
|
|
3d90d1ab4f | ||
|
|
3c7318d7c8 | ||
|
|
fc0b233046 | ||
|
|
e25918d846 | ||
|
|
d78b0ea435 | ||
|
|
3a334c4585 | ||
|
|
8dae4bcd44 | ||
|
|
294f10fdd0 | ||
|
|
b9829ed316 | ||
|
|
4ccec17676 | ||
|
|
f45082043b | ||
|
|
3222f13dde | ||
|
|
b282620a48 | ||
|
|
3ff1ff8f74 | ||
|
|
e24b01b6cb | ||
|
|
9a8694c2f3 | ||
|
|
070a9148aa | ||
|
|
f19fe3b6f3 | ||
|
|
8d2b15665d | ||
|
|
4dec8f22f8 | ||
|
|
a39b3aca78 | ||
|
|
5dc4ab062d | ||
|
|
31f82c1d96 | ||
|
|
548fd9daf8 | ||
|
|
f831f5a0e1 | ||
|
|
4c21aa2604 | ||
|
|
c9efb75714 | ||
|
|
5e56bdc0fd | ||
|
|
3554d5c2f7 | ||
|
|
5fe405e1fb | ||
|
|
8381d44bbd | ||
|
|
56bb3744a5 | ||
|
|
470b435afd | ||
|
|
a4f8bbff02 | ||
|
|
cf5ab05b90 | ||
|
|
3a2ce240f9 | ||
|
|
1f01dd53f7 | ||
|
|
72d41d70b6 | ||
|
|
42b5b1f64c | ||
|
|
2949bc211d | ||
|
|
5e0952159d | ||
|
|
441187470e | ||
|
|
59fd13cc2f | ||
|
|
c9e7bfdf16 | ||
|
|
2d700c381e | ||
|
|
72d6c8ebd6 | ||
|
|
991c6941c1 | ||
|
|
f974696e34 | ||
|
|
3ef9ea94e5 | ||
|
|
381ce23fd7 | ||
|
|
af6a0be832 | ||
|
|
287fe5beac | ||
|
|
b9c214e6e8 | ||
|
|
d3d76aa8ce | ||
|
|
3bea08da5f | ||
|
|
3d5cacbdc3 | ||
|
|
7ccb252069 | ||
|
|
31547462bb | ||
|
|
b3a7a973a1 | ||
|
|
22b26696ba | ||
|
|
495241f8ca | ||
|
|
4afbfcae17 | ||
|
|
3627de4cbc | ||
|
|
007c07e612 | ||
|
|
ec7c8fd922 | ||
|
|
c5a0ae7b34 | ||
|
|
4bd207ebf3 | ||
|
|
45011234d9 | ||
|
|
24017f379e | ||
|
|
aad7656b38 | ||
|
|
80de890f05 | ||
|
|
62cec7b6b2 | ||
|
|
c9c163cd7b | ||
|
|
95a9f32bf0 | ||
|
|
c4ae761a0e | ||
|
|
0653b346e0 | ||
|
|
fa587398bd | ||
|
|
6b67857151 | ||
|
|
81165f0c40 | ||
|
|
df40515087 | ||
|
|
c77922e3e5 | ||
|
|
0f9abe68b9 | ||
|
|
c168ee6940 | ||
|
|
0d4414fdd0 | ||
|
|
968d5e0d8f | ||
|
|
635182b57c | ||
|
|
9d0b6ce75e | ||
|
|
2fdd80fe3a | ||
|
|
dbac23b749 | ||
|
|
7fa7061aa5 | ||
|
|
e45e631199 | ||
|
|
b21e77c1e0 | ||
|
|
ba33294225 | ||
|
|
97c21cc3a7 | ||
|
|
7d7e6f5326 | ||
|
|
5e15bd935e | ||
|
|
9bad09c45f | ||
|
|
47d077ff22 | ||
|
|
bbf8dde3ca | ||
|
|
6e8ca3bc6c | ||
|
|
11a494d7b3 | ||
|
|
9b570de33f | ||
|
|
b67343fc5a | ||
|
|
5a3c0eb83c | ||
|
|
10391608a0 | ||
|
|
51c57cc5ae | ||
|
|
05e4678e65 | ||
|
|
0f0e402db4 | ||
|
|
837bccb1d8 | ||
|
|
adc709db2f | ||
|
|
395573720d | ||
|
|
0e62759d24 | ||
|
|
926b6c3117 | ||
|
|
c9f9304ba5 | ||
|
|
fabd6be5af | ||
|
|
1bf31d20b6 | ||
|
|
653bf04db0 | ||
|
|
b77a25b21a | ||
|
|
9db6931cea | ||
|
|
bad5cef52b | ||
|
|
94bd79b2bf | ||
|
|
b746146f4e | ||
|
|
8ac9bb5c72 | ||
|
|
1b552a6f62 | ||
|
|
97329ccc7a | ||
|
|
f2d1f2de56 | ||
|
|
692c2fae96 | ||
|
|
3d65b701a2 | ||
|
|
ecaca0fe15 | ||
|
|
8955f83ef6 | ||
|
|
1a8aaebd79 | ||
|
|
38a823cc54 | ||
|
|
25306cb373 | ||
|
|
1084a031e7 | ||
|
|
f6ec99bede | ||
|
|
90a6647fa4 | ||
|
|
a926bb81a9 | ||
|
|
fbf41e3149 | ||
|
|
a38205069b | ||
|
|
2d75801024 | ||
|
|
504511fe7e | ||
|
|
d3399a261b | ||
|
|
d2437e6a21 | ||
|
|
95dd6ceba8 | ||
|
|
1a0d135201 | ||
|
|
f453e1523e | ||
|
|
622b0bfbc9 | ||
|
|
ad52514b97 | ||
|
|
02a218c6e3 | ||
|
|
2d617ad173 | ||
|
|
0d06e3e47d | ||
|
|
78aee4d96e | ||
|
|
ba04da87e5 | ||
|
|
2e6b08cbcb | ||
|
|
472a373861 | ||
|
|
a451420911 | ||
|
|
2e84f21c18 | ||
|
|
fb7167c2d2 | ||
|
|
d884eb9287 | ||
|
|
8b9b1a90e4 | ||
|
|
e2d4010b59 | ||
|
|
babde31bf0 | ||
|
|
c282239077 | ||
|
|
8d28a441ab | ||
|
|
5821054d91 | ||
|
|
4626145374 | ||
|
|
a786d3621d | ||
|
|
1393dc2a5b | ||
|
|
c4604465ba | ||
|
|
cffae9cb0f | ||
|
|
cf24d3c33f | ||
|
|
672e885e40 | ||
|
|
7d05610da7 | ||
|
|
a843ecf4c8 | ||
|
|
f4ff1b0688 | ||
|
|
2b4cec8385 | ||
|
|
8ab4ab29f8 | ||
|
|
cc0509c0f3 | ||
|
|
ebfa65fedc | ||
|
|
a34ae24b3f | ||
|
|
58ea76eb24 | ||
|
|
e9a17b19c5 | ||
|
|
ce8d111453 | ||
|
|
47fd73f6cf | ||
|
|
76f3391ebc | ||
|
|
be6ff52709 | ||
|
|
e99e252188 | ||
|
|
98b980f7e3 | ||
|
|
f2f90eeb82 | ||
|
|
f267fd2250 | ||
|
|
500ad34769 | ||
|
|
1700d54012 | ||
|
|
70d8a10484 | ||
|
|
9e94784e26 | ||
|
|
4060f4018e | ||
|
|
739ac0f18f | ||
|
|
98d62a7eb1 | ||
|
|
aba7a3a830 | ||
|
|
9027d1eee7 | ||
|
|
4e5da4946d | ||
|
|
a70e3e42b2 | ||
|
|
09f476924f | ||
|
|
230e3245fd | ||
|
|
8de876daf2 | ||
|
|
53b1d155cc | ||
|
|
b0eb63ab9a | ||
|
|
2e3242682d | ||
|
|
ad4d4c9e67 | ||
|
|
3250d4e405 | ||
|
|
196a0531e0 | ||
|
|
e61cb5b2c3 | ||
|
|
f9b53c6b51 | ||
|
|
58e949e148 | ||
|
|
dad47b7bda | ||
|
|
e519bf5978 | ||
|
|
fc50e52157 | ||
|
|
7669df0e16 | ||
|
|
4d56fec5f1 | ||
|
|
8181552b16 | ||
|
|
c6c147daf6 | ||
|
|
975069825e | ||
|
|
5133f480d1 | ||
|
|
ce4b252e5c | ||
|
|
031d56de35 | ||
|
|
3cdaf6736b | ||
|
|
b5e696b3cb | ||
|
|
43aef377d7 | ||
|
|
add0e7a8db | ||
|
|
52e541d453 | ||
|
|
a031a49546 | ||
|
|
4d821b8dd8 | ||
|
|
f277025c9a | ||
|
|
ba28e6f82e | ||
|
|
3a89df9bed | ||
|
|
f6a0866fbb | ||
|
|
756fa2ecc5 | ||
|
|
cf834aa6da | ||
|
|
d2324f4a93 | ||
|
|
6226c7f4f3 | ||
|
|
991ecd558e | ||
|
|
a4fa3a460e | ||
|
|
77ba708933 | ||
|
|
662d50a966 | ||
|
|
5472d1cc04 | ||
|
|
d1d41f5645 | ||
|
|
94fd100fc7 | ||
|
|
b9ff36b5d9 | ||
|
|
cd5a809ec9 | ||
|
|
045a8efbeb | ||
|
|
54a1f7d833 | ||
|
|
1b496cda8f | ||
|
|
a5b24bfe4c | ||
|
|
46676ca376 | ||
|
|
7d939a3b3d | ||
|
|
a515061465 | ||
|
|
1c24d63f73 | ||
|
|
7e10dba5e2 | ||
|
|
e2d73014f1 | ||
|
|
53aa30596e | ||
|
|
122ae5b710 | ||
|
|
45c27b2965 | ||
|
|
832b247fc1 | ||
|
|
0e8b53d566 | ||
|
|
d03d69273b | ||
|
|
efa05ba19d | ||
|
|
5da205d91a | ||
|
|
41923bac99 | ||
|
|
c6148f6bf1 | ||
|
|
77aaa9af4d | ||
|
|
00cf8d530c | ||
|
|
98aa58e9f5 | ||
|
|
6911917819 | ||
|
|
a8255aa475 | ||
|
|
48e7aae38f | ||
|
|
7069643ae6 | ||
|
|
34272fc134 | ||
|
|
1d41002dfe | ||
|
|
563bf342d5 | ||
|
|
efd5fabb95 | ||
|
|
c1da525110 | ||
|
|
5ce6c88a88 | ||
|
|
64cce7c6fa | ||
|
|
4544e5b51f | ||
|
|
eb3e314946 | ||
|
|
8b65c3de10 | ||
|
|
c2beb27a9d | ||
|
|
05fdec9e72 | ||
|
|
e8e3c95349 | ||
|
|
a87fa3f246 | ||
|
|
b31ad523f5 | ||
|
|
34bce540ff | ||
|
|
8ea38e1d80 | ||
|
|
ce591a9541 | ||
|
|
a48c65cd65 | ||
|
|
c283f80f48 | ||
|
|
d6bf276b5a | ||
|
|
96a51650b1 | ||
|
|
f35a9c74a2 | ||
|
|
e2457943f5 | ||
|
|
a05644172a | ||
|
|
cc168ce0fb | ||
|
|
76bd22d279 | ||
|
|
18574f3cf1 | ||
|
|
665215ab47 | ||
|
|
6009f36403 | ||
|
|
2580efda0d | ||
|
|
3a310b8815 | ||
|
|
7ff96227c0 | ||
|
|
3e8d78051c | ||
|
|
bd24ebc96a | ||
|
|
6d3745b8f1 | ||
|
|
d0f0b975be | ||
|
|
ff2e6ed59f | ||
|
|
99b2018d0e | ||
|
|
f0d9c8c10a | ||
|
|
dce1b24c00 | ||
|
|
b47e981932 | ||
|
|
dc44eb4caf | ||
|
|
dfda6733f0 | ||
|
|
21c6986dc7 | ||
|
|
635720fe12 | ||
|
|
8c751d7423 | ||
|
|
702ecf7637 | ||
|
|
0595f1e044 | ||
|
|
cebb032bd3 | ||
|
|
8e32763ada | ||
|
|
7532337231 | ||
|
|
4a66d4570e | ||
|
|
6f5e99d47d | ||
|
|
9ee9f5bddd | ||
|
|
ddb9f6d3ad | ||
|
|
d29139d88a | ||
|
|
317575ba99 | ||
|
|
d4f2638a2e | ||
|
|
b67d9be227 | ||
|
|
d52add8fad | ||
|
|
aa9159d25c | ||
|
|
94c777259e | ||
|
|
c9f8fa5662 | ||
|
|
64ee6b119e | ||
|
|
d2ec9a8936 | ||
|
|
2a927453f7 | ||
|
|
c19d489c9a | ||
|
|
6012eb051b | ||
|
|
3974746473 | ||
|
|
e1bcdcf387 | ||
|
|
fd5fbddae9 | ||
|
|
8ff72beddb | ||
|
|
cba5f7877b | ||
|
|
9d7e9fd9fc | ||
|
|
082a0baff3 | ||
|
|
3a4914315b | ||
|
|
448b5a338a | ||
|
|
9b68617fa8 | ||
|
|
4c9890d7f8 | ||
|
|
b2db04f5d7 | ||
|
|
be8ff9ccb9 | ||
|
|
9c531d97b0 | ||
|
|
055d8d75a2 | ||
|
|
9fcf79ce0e | ||
|
|
6edf4619d4 | ||
|
|
8f769ce5a3 | ||
|
|
96ac71750a | ||
|
|
d0852cf1bb | ||
|
|
f5fea8af96 | ||
|
|
d52a1da501 | ||
|
|
abdcaa7c86 | ||
|
|
ad122cf463 | ||
|
|
b58a57d225 | ||
|
|
28d679de98 | ||
|
|
d1dd055e6a | ||
|
|
3045578da4 | ||
|
|
9566dda73e | ||
|
|
a0ced2b685 | ||
|
|
df232f567b | ||
|
|
2a6d6a9d13 | ||
|
|
cd03932bd1 | ||
|
|
2e5fa1ef1b | ||
|
|
0c6c4cd532 | ||
|
|
25f8a87429 | ||
|
|
edf1a7970d | ||
|
|
fac9972bad | ||
|
|
9ecb960f3a | ||
|
|
3d26e23891 | ||
|
|
7bbbd95775 | ||
|
|
bb308899b9 | ||
|
|
e95c8d703c | ||
|
|
903d6a742e | ||
|
|
424218e327 | ||
|
|
17dc03d414 | ||
|
|
baf699c6e1 | ||
|
|
1431af1ff5 | ||
|
|
775a41b903 | ||
|
|
2da1e90dd5 | ||
|
|
e614340c0c | ||
|
|
3c293b9aed | ||
|
|
283c2861c9 | ||
|
|
757dc95116 | ||
|
|
6192250b8a | ||
|
|
f489135b1d | ||
|
|
4d00a52761 | ||
|
|
6941a59223 | ||
|
|
3f232e631e | ||
|
|
6e3643c3ef | ||
|
|
d7348c8aff | ||
|
|
e7bdb8679d | ||
|
|
c28824f94d | ||
|
|
664d766b45 | ||
|
|
fce694ed92 | ||
|
|
96aafb4f07 | ||
|
|
a474f86ea8 | ||
|
|
dbaf95a8f3 | ||
|
|
e67df96ad9 | ||
|
|
56de94578d | ||
|
|
06fc2f5ef0 | ||
|
|
b3ba315cbd | ||
|
|
e5a531e683 | ||
|
|
e2de57bd04 | ||
|
|
4eebca93e3 | ||
|
|
3919ec9692 | ||
|
|
02aeb0ac1a | ||
|
|
206544ad09 | ||
|
|
3854cd2b2f | ||
|
|
b2eb8aaf66 | ||
|
|
acbd920c9a | ||
|
|
db0bdd48e5 | ||
|
|
da21ee3cda | ||
|
|
d2baef2b36 | ||
|
|
df96bc83cc | ||
|
|
ec03831a21 | ||
|
|
9ca821316a | ||
|
|
025a060337 | ||
|
|
371d6f0730 | ||
|
|
643bc10d52 | ||
|
|
8fb801069f | ||
|
|
542ed8b6ad | ||
|
|
053620f4f5 | ||
|
|
88b01a0ca9 | ||
|
|
197140498b | ||
|
|
9acd325aa4 | ||
|
|
2483329ef6 | ||
|
|
f6b58b4219 | ||
|
|
6c6d86f761 | ||
|
|
359221b379 | ||
|
|
87fe1d672e | ||
|
|
9257221b3b | ||
|
|
f9b38a1de7 | ||
|
|
67e1ac0442 | ||
|
|
c57e9e008f | ||
|
|
b34c23fe3d | ||
|
|
29f644235d | ||
|
|
01da5972fc | ||
|
|
643e964edd | ||
|
|
30e3d795da | ||
|
|
89b05a2ea4 | ||
|
|
2e009be27c | ||
|
|
32150cf7b5 | ||
|
|
bf812aae8f | ||
|
|
9a71443005 | ||
|
|
ee165249bc | ||
|
|
7c7d767195 | ||
|
|
af8cfb79e5 | ||
|
|
27c8bf3021 | ||
|
|
b0a09b31bb | ||
|
|
13ebfb1a49 | ||
|
|
f863b30951 | ||
|
|
1ce27a5e6b | ||
|
|
933d622860 | ||
|
|
5d67223236 | ||
|
|
825d2c948c | ||
|
|
29390b439a | ||
|
|
799c17eb90 | ||
|
|
5fb84866e0 | ||
|
|
4965344ef5 | ||
|
|
46ca53ad0d | ||
|
|
61ff1b3584 | ||
|
|
7c0c5de4bd | ||
|
|
8d134b8df8 | ||
|
|
a9bacc1b6b | ||
|
|
e4ff3dac86 | ||
|
|
9443b18076 | ||
|
|
bb4e81aa19 | ||
|
|
a9a9f6782a | ||
|
|
2fa6c3c918 | ||
|
|
4bd84eb523 | ||
|
|
e2073dcd30 | ||
|
|
fd72669c7e | ||
|
|
81c144697b | ||
|
|
aecf180dfe | ||
|
|
10fa4a4f20 | ||
|
|
534732564b | ||
|
|
6a314bc9cd | ||
|
|
1d4356b97e | ||
|
|
a11566012d | ||
|
|
8d929027c8 | ||
|
|
1d1ed012d8 | ||
|
|
b092b7a937 | ||
|
|
d133fa6dc1 | ||
|
|
afa7de969e | ||
|
|
41b6a89ffd | ||
|
|
9aa82ec5bf | ||
|
|
b17a2e9f96 | ||
|
|
af4e9ceeed | ||
|
|
9c62c41f5f | ||
|
|
184c9d21bb | ||
|
|
9744d8de99 | ||
|
|
f3e6ecb2c3 |
No files matched your search
@@ -13,7 +13,6 @@ env:
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
FEX_ENABLEAVX: 1
|
||||
|
||||
jobs:
|
||||
build_plus_test:
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
@@ -20,7 +20,6 @@ env:
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
FEX_ENABLEAVX: 1
|
||||
|
||||
jobs:
|
||||
glibc_fault_test:
|
||||
@@ -71,7 +70,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DBUILD_FEX_LINUX_TESTS=True -DENABLE_GLIBC_ALLOCATOR_HOOK_FAULT=True -DENABLE_JEMALLOC_GLIBC_ALLOC=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_FEX_LINUX_TESTS=True -DENABLE_GLIBC_ALLOCATOR_HOOK_FAULT=True -DENABLE_JEMALLOC_GLIBC_ALLOC=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
@@ -13,7 +13,6 @@ env:
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
FEX_ENABLEAVX: 1
|
||||
|
||||
jobs:
|
||||
hostrunner_tests:
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
@@ -13,7 +13,6 @@ env:
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
FEX_ENABLEAVX: 1
|
||||
|
||||
jobs:
|
||||
instcountci_tests:
|
||||
@@ -74,7 +73,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=$VIXL_SIM_ENABLED -DENABLE_VIXL_DISASSEMBLER=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=$VIXL_SIM_ENABLED -DENABLE_VIXL_DISASSEMBLER=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
@@ -10,14 +10,13 @@ on:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Debug
|
||||
FEX_ENABLEAVX: 1
|
||||
|
||||
jobs:
|
||||
mingw_build:
|
||||
runs-on: ${{ matrix.arch }}
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [[self-hosted, ARM64, mingw]]
|
||||
arch: [[self-hosted, ARM64, mingw], [self-hosted, ARM64EC, mingw, ARM64]]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
@@ -39,6 +38,11 @@ jobs:
|
||||
run: |
|
||||
echo "MINGW_TRIPLE=aarch64-w64-mingw32" >> $GITHUB_ENV
|
||||
|
||||
- name: Set CC Arm64EC
|
||||
if: matrix.arch[1] == 'ARM64EC'
|
||||
run: |
|
||||
echo "MINGW_TRIPLE=arm64ec-w64-mingw32" >> $GITHUB_ENV
|
||||
|
||||
- name: Set rootfs paths
|
||||
run: |
|
||||
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
|
||||
@@ -74,7 +78,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DBUILD_TESTS=False -DENABLE_JEMALLOC=False -DENABLE_JEMALLOC_GLIBC_ALLOC=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_TESTS=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
@@ -13,7 +13,6 @@ env:
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
FEX_ENABLEAVX: 1
|
||||
|
||||
jobs:
|
||||
vixl_simulator:
|
||||
@@ -65,7 +64,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=True -DENABLE_VIXL_DISASSEMBLER=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=True -DENABLE_VIXL_DISASSEMBLER=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@@ -73,23 +72,22 @@ jobs:
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: ASM Tests
|
||||
- name: ASM Tests - SVE256
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target asm_tests
|
||||
|
||||
- name: ASM Test Results move
|
||||
- name: ASM Test SVE256 Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM_SVE256Bit.log || true
|
||||
|
||||
- name: ASM Tests 128-bit
|
||||
- name: ASM Tests - SVE128
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
env:
|
||||
FEX_HOSTFEATURES: "disableavx"
|
||||
FEX_FORCESVEWIDTH: "128"
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target asm_tests
|
||||
@@ -98,7 +96,21 @@ jobs:
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM128bit.log || true
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM_SVE128Bit.log || true
|
||||
|
||||
- name: ASM Tests - ASIMD
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
env:
|
||||
FEX_HOSTFEATURES: "disablesve"
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target asm_tests
|
||||
|
||||
- name: ASM Test ASIMD Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM_ASIMD.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
|
||||
+71
-8
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(FEX)
|
||||
project(FEX C CXX ASM)
|
||||
|
||||
INCLUDE (CheckIncludeFiles)
|
||||
CHECK_INCLUDE_FILES ("gdb/jit-reader.h" HAVE_GDB_JIT_READER_H)
|
||||
@@ -15,6 +15,7 @@ option(ENABLE_XRAY "Enable building with LLVM X-Ray" FALSE)
|
||||
set(USE_LINKER "" CACHE STRING "Allow overriding the linker path directly")
|
||||
option(ENABLE_ASAN "Enables Clang ASAN" FALSE)
|
||||
option(ENABLE_TSAN "Enables Clang TSAN" FALSE)
|
||||
option(ENABLE_COVERAGE "Enables Coverage" FALSE)
|
||||
option(ENABLE_ASSERTIONS "Enables assertions in build" FALSE)
|
||||
option(ENABLE_GDB_SYMBOLS "Enables GDBSymbols integration support" ${HAVE_GDB_JIT_READER_H})
|
||||
option(ENABLE_STRICT_WERROR "Enables stricter -Werror for CI" FALSE)
|
||||
@@ -31,6 +32,7 @@ option(COMPILE_VIXL_DISASSEMBLER "Compiles the vixl disassembler in to vixl" FAL
|
||||
option(ENABLE_FEXCORE_PROFILER "Enables use of the FEXCore timeline profiling capabilities" FALSE)
|
||||
set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend you want to use for the FEXCore profiler")
|
||||
option(ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT "Enables glibc memory allocation hooking with fault for CI testing")
|
||||
option(USE_PDB_DEBUGINFO "Builds debug info in PDB format" FALSE)
|
||||
|
||||
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
|
||||
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
|
||||
@@ -40,7 +42,16 @@ string(FIND ${CMAKE_BASE_NAME} mingw CONTAINS_MINGW)
|
||||
if (NOT CONTAINS_MINGW EQUAL -1)
|
||||
message (STATUS "Mingw build")
|
||||
set (MINGW_BUILD TRUE)
|
||||
set (ENABLE_JEMALLOC FALSE)
|
||||
set (ENABLE_JEMALLOC TRUE)
|
||||
set (ENABLE_JEMALLOC_GLIBC_ALLOC FALSE)
|
||||
endif()
|
||||
|
||||
if (NOT MINGW_BUILD)
|
||||
message (STATUS "Clang version ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
set (CLANG_MINIMUM_VERSION 12.0)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${CLANG_MINIMUM_VERSION})
|
||||
message (FATAL_ERROR "Clang version too old for FEX. Need at least ${CLANG_MINIMUM_VERSION} but has ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_FEXCORE_PROFILER)
|
||||
@@ -110,6 +121,12 @@ else()
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE)
|
||||
if (NOT ENABLE_X86_HOST_DEBUG)
|
||||
message(FATAL_ERROR
|
||||
" FEX-Emu doesn't support compiling for x86-64 hosts!"
|
||||
" This is /only/ a supported configuration for FEX CI and nothing else!")
|
||||
endif()
|
||||
set(_M_X86_64 1)
|
||||
add_definitions(-D_M_X86_64=1)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
@@ -123,9 +140,39 @@ endif()
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm64ec")
|
||||
set(_M_ARM_64EC 1)
|
||||
add_definitions(-D_M_ARM_64EC=1)
|
||||
endif()
|
||||
|
||||
# Required as FEX is not allowed to lock the CRT heap lock during compilation or callbacks
|
||||
set(ENABLE_JEMALLOC TRUE)
|
||||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -Wattributes -Werror=attributes")
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
__attribute__((preserve_all))
|
||||
int Testy(int a, int b, int c, int d, int e, int f) {
|
||||
return a + b + c + d + e + f;
|
||||
}
|
||||
int main() {
|
||||
return Testy(0, 1, 2, 3, 4, 5);
|
||||
}"
|
||||
HAS_CLANG_PRESERVE_ALL)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if (HAS_CLANG_PRESERVE_ALL)
|
||||
if (MINGW_BUILD)
|
||||
message(STATUS "Ignoring broken clang::preserve_all support")
|
||||
set(HAS_CLANG_PRESERVE_ALL FALSE)
|
||||
else()
|
||||
message(STATUS "Has clang::preserve_all")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (_M_ARM_64 AND HAS_CLANG_PRESERVE_ALL)
|
||||
add_definitions("-DFEX_PRESERVE_ALL_ATTR=__attribute__((preserve_all))" "-DFEX_HAS_PRESERVE_ALL_ATTR=1")
|
||||
else()
|
||||
add_definitions("-DFEX_PRESERVE_ALL_ATTR=" "-DFEX_HAS_PRESERVE_ALL_ATTR=0")
|
||||
endif()
|
||||
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
# We can run the simulator on both x86-64 or AArch64 hosts
|
||||
add_definitions(-DVIXL_SIMULATOR=1 -DVIXL_INCLUDE_SIMULATOR_AARCH64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_CCACHE)
|
||||
@@ -175,13 +222,18 @@ if (ENABLE_TSAN)
|
||||
link_libraries(-fno-omit-frame-pointer -fsanitize=thread)
|
||||
endif()
|
||||
|
||||
if (ENABLE_COVERAGE)
|
||||
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
|
||||
link_libraries(-fprofile-instr-generate -fcoverage-mapping)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JEMALLOC_GLIBC_ALLOC)
|
||||
# The glibc jemalloc subproject which hooks the glibc allocator.
|
||||
# Required for thunks to work.
|
||||
# All host native libraries will use this allocator, while *most* other FEX internal allocations will use the other jemalloc allocator.
|
||||
add_definitions(-DENABLE_JEMALLOC_GLIBC=1)
|
||||
add_subdirectory(External/jemalloc_glibc/)
|
||||
else()
|
||||
elseif (NOT MINGW_BUILD)
|
||||
message (STATUS
|
||||
" jemalloc glibc allocator disabled!\n"
|
||||
" This is not a recommended configuration!\n"
|
||||
@@ -194,7 +246,7 @@ if (ENABLE_JEMALLOC)
|
||||
add_definitions(-DENABLE_JEMALLOC=1)
|
||||
add_subdirectory(External/jemalloc/)
|
||||
include_directories(External/jemalloc/pregen/include/)
|
||||
else()
|
||||
elseif (NOT MINGW_BUILD)
|
||||
message (STATUS
|
||||
" jemalloc disabled!\n"
|
||||
" This is not a recommended configuration!\n"
|
||||
@@ -202,6 +254,11 @@ else()
|
||||
" Use at your own risk!")
|
||||
endif()
|
||||
|
||||
if (USE_PDB_DEBUGINFO)
|
||||
add_compile_options(-g -gcodeview)
|
||||
add_link_options(-g -Wl,--pdb=)
|
||||
endif()
|
||||
|
||||
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
|
||||
set (CMAKE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_LINKER_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
|
||||
|
||||
@@ -255,8 +312,6 @@ include_directories(External/json-maker/)
|
||||
add_subdirectory(External/tiny-json/)
|
||||
include_directories(External/tiny-json/)
|
||||
|
||||
include_directories(External/xbyak/)
|
||||
|
||||
include_directories(Source/)
|
||||
include_directories("${CMAKE_BINARY_DIR}/Source/")
|
||||
|
||||
@@ -354,6 +409,14 @@ if (BUILD_TESTS)
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
message(STATUS "Unit tests are enabled")
|
||||
|
||||
set (TEST_JOB_COUNT "" CACHE STRING "Override number of parallel jobs to use while running tests")
|
||||
if (TEST_JOB_COUNT)
|
||||
message(STATUS "Running tests with ${TEST_JOB_COUNT} jobs")
|
||||
elseif(CMAKE_VERSION VERSION_LESS "3.29")
|
||||
execute_process(COMMAND "nproc" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE TEST_JOB_COUNT)
|
||||
endif()
|
||||
set(TEST_JOB_FLAG "-j${TEST_JOB_COUNT}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(FEXHeaderUtils/)
|
||||
|
||||
@@ -602,6 +602,18 @@ constexpr bool AreVectorsSequential(T first, const Args&... args) {
|
||||
return (fn(first, args) && ...);
|
||||
}
|
||||
|
||||
// Returns if the immediate can fit in to add/sub immediate instruction encodings.
|
||||
constexpr bool IsImmAddSub(uint64_t imm) {
|
||||
constexpr uint64_t U12Mask = 0xFFF;
|
||||
auto FitsWithin12Bits = [](uint64_t imm) {
|
||||
return (imm & ~U12Mask) == 0;
|
||||
};
|
||||
// Can fit in to the instruction encoding:
|
||||
// - if only bits [11:0] are set.
|
||||
// - if only bits [23:12] are set.
|
||||
return FitsWithin12Bits(imm) || (FitsWithin12Bits(imm >> 12) && (imm & U12Mask) == 0);
|
||||
}
|
||||
|
||||
// This is an emitter that is designed around the smallest code bloat as possible.
|
||||
// Eschewing most developer convenience in order to keep code as small as possible.
|
||||
|
||||
|
||||
@@ -3780,8 +3780,7 @@ public:
|
||||
if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED &&
|
||||
MemSrc.MetaType.ExtendedType.rm.Idx() != ARMEmitter::Reg::r31.Idx()) {
|
||||
LOGMAN_THROW_AA_FMT(MemSrc.MetaType.ExtendedType.Shift == false, "Can't shift byte");
|
||||
LOGMAN_MSG_A_FMT("Nope"); // XXX: Implement
|
||||
// strb(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option);
|
||||
strb(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option);
|
||||
}
|
||||
else if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED) {
|
||||
strb(rt, MemSrc.rn);
|
||||
@@ -3811,8 +3810,7 @@ public:
|
||||
if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED &&
|
||||
MemSrc.MetaType.ExtendedType.rm.Idx() != ARMEmitter::Reg::r31.Idx()) {
|
||||
LOGMAN_THROW_AA_FMT(MemSrc.MetaType.ExtendedType.Shift == false, "Can't shift byte");
|
||||
LOGMAN_MSG_A_FMT("Nope"); // XXX: Implement
|
||||
// ldrb(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option);
|
||||
ldrb(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option);
|
||||
}
|
||||
else if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED) {
|
||||
ldrb(rt, MemSrc.rn);
|
||||
@@ -3841,9 +3839,7 @@ public:
|
||||
void strh(ARMEmitter::VRegister rt, ARMEmitter::ExtendedMemOperand MemSrc) {
|
||||
if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED &&
|
||||
MemSrc.MetaType.ExtendedType.rm.Idx() != ARMEmitter::Reg::r31.Idx()) {
|
||||
LOGMAN_THROW_AA_FMT(MemSrc.MetaType.ExtendedType.Shift == false, "Can't shift byte");
|
||||
LOGMAN_MSG_A_FMT("Nope"); // XXX: Implement
|
||||
// strh(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option);
|
||||
strh(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option, MemSrc.MetaType.ExtendedType.Shift);
|
||||
}
|
||||
else if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED) {
|
||||
strh(rt, MemSrc.rn);
|
||||
@@ -3872,9 +3868,7 @@ public:
|
||||
void ldrh(ARMEmitter::VRegister rt, ARMEmitter::ExtendedMemOperand MemSrc) {
|
||||
if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED &&
|
||||
MemSrc.MetaType.ExtendedType.rm.Idx() != ARMEmitter::Reg::r31.Idx()) {
|
||||
LOGMAN_THROW_AA_FMT(MemSrc.MetaType.ExtendedType.Shift == false, "Can't shift byte");
|
||||
LOGMAN_MSG_A_FMT("Nope"); // XXX: Implement
|
||||
// ldrh(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option);
|
||||
ldrh(rt, MemSrc.rn, MemSrc.MetaType.ExtendedType.rm, MemSrc.MetaType.ExtendedType.Option, MemSrc.MetaType.ExtendedType.Shift);
|
||||
}
|
||||
else if (MemSrc.MetaType.Header.MemType == ARMEmitter::ExtendedMemOperand::Type::TYPE_EXTENDED) {
|
||||
ldrh(rt, MemSrc.rn);
|
||||
|
||||
@@ -2569,7 +2569,19 @@ public:
|
||||
}
|
||||
|
||||
// SVE contiguous non-temporal load (scalar plus immediate)
|
||||
// XXX:
|
||||
void ldnt1b(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalLoad(0b00, zt, pg, rn, Imm);
|
||||
}
|
||||
void ldnt1h(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalLoad(0b01, zt, pg, rn, Imm);
|
||||
}
|
||||
void ldnt1w(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalLoad(0b10, zt, pg, rn, Imm);
|
||||
}
|
||||
void ldnt1d(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalLoad(0b11, zt, pg, rn, Imm);
|
||||
}
|
||||
|
||||
// SVE contiguous non-temporal load (scalar plus scalar)
|
||||
// XXX:
|
||||
// SVE load multiple structures (scalar plus immediate)
|
||||
@@ -3321,7 +3333,18 @@ public:
|
||||
|
||||
// SVE Memory - Contiguous Store with Immediate Offset
|
||||
// SVE contiguous non-temporal store (scalar plus immediate)
|
||||
// XXX:
|
||||
void stnt1b(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalStore(0b00, zt, pg, rn, Imm);
|
||||
}
|
||||
void stnt1h(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalStore(0b01, zt, pg, rn, Imm);
|
||||
}
|
||||
void stnt1w(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalStore(0b10, zt, pg, rn, Imm);
|
||||
}
|
||||
void stnt1d(ZRegister zt, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
SVEContiguousNontemporalStore(0b11, zt, pg, rn, Imm);
|
||||
}
|
||||
|
||||
// SVE store multiple structures (scalar plus immediate)
|
||||
void st2b(ZRegister zt1, ZRegister zt2, PRegister pg, Register rn, int32_t Imm = 0) {
|
||||
@@ -4481,6 +4504,38 @@ private:
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
// SVE contiguous non-temporal load (scalar plus immediate)
|
||||
void SVEContiguousNontemporalLoad(uint32_t msz, ZRegister zt, PRegister pg, Register rn, int32_t imm) {
|
||||
LOGMAN_THROW_A_FMT(pg <= PReg::p7, "Can only use p0-p7 as a governing predicate");
|
||||
LOGMAN_THROW_AA_FMT(imm >= -8 && imm <= 7,
|
||||
"Invalid loadstore offset ({}). Must be between [-8, 7]", imm);
|
||||
|
||||
const auto imm4 = static_cast<uint32_t>(imm) & 0xF;
|
||||
uint32_t Instr = 0b1010'0100'0000'0000'1110'0000'0000'0000;
|
||||
Instr |= msz << 23;
|
||||
Instr |= imm4 << 16;
|
||||
Instr |= pg.Idx() << 10;
|
||||
Instr |= Encode_rn(rn);
|
||||
Instr |= zt.Idx();
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
// SVE contiguous non-temporal store (scalar plus immediate)
|
||||
void SVEContiguousNontemporalStore(uint32_t msz, ZRegister zt, PRegister pg, Register rn, int32_t imm) {
|
||||
LOGMAN_THROW_A_FMT(pg <= PReg::p7, "Can only use p0-p7 as a governing predicate");
|
||||
LOGMAN_THROW_AA_FMT(imm >= -8 && imm <= 7,
|
||||
"Invalid loadstore offset ({}). Must be between [-8, 7]", imm);
|
||||
|
||||
const auto imm4 = static_cast<uint32_t>(imm) & 0xF;
|
||||
uint32_t Instr = 0b1110'0100'0001'0000'1110'0000'0000'0000;
|
||||
Instr |= msz << 23;
|
||||
Instr |= imm4 << 16;
|
||||
Instr |= pg.Idx() << 10;
|
||||
Instr |= Encode_rn(rn);
|
||||
Instr |= zt.Idx();
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
void SVEContiguousLoadImm(bool is_store, uint32_t dtype, int32_t imm, PRegister pg, Register rn, ZRegister zt) {
|
||||
LOGMAN_THROW_A_FMT(pg <= PReg::p7, "Can only use p0-p7 as a governing predicate");
|
||||
LOGMAN_THROW_AA_FMT(imm >= -8 && imm <= 7,
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
// to by n, imm_s and imm_r are undefined.
|
||||
static bool IsImmLogical(uint64_t value,
|
||||
unsigned width,
|
||||
unsigned* n,
|
||||
unsigned* imm_s,
|
||||
unsigned* imm_r) {
|
||||
unsigned* n = nullptr,
|
||||
unsigned* imm_s = nullptr,
|
||||
unsigned* imm_r = nullptr) {
|
||||
[[maybe_unused]] constexpr auto kBRegSize = 8;
|
||||
[[maybe_unused]] constexpr auto kHRegSize = 16;
|
||||
[[maybe_unused]] constexpr auto kSRegSize = 32;
|
||||
@@ -243,6 +243,46 @@ static bool IsImmLogical(uint64_t value,
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool IsIntN(unsigned n, int64_t x) {
|
||||
if (n == 64) return true;
|
||||
int64_t limit = INT64_C(1) << (n - 1);
|
||||
return (-limit <= x) && (x < limit);
|
||||
}
|
||||
|
||||
static inline bool IsUintN(unsigned n, int64_t x) {
|
||||
// Convert to an unsigned integer to avoid implementation-defined behavior.
|
||||
return !(static_cast<uint64_t>(x) >> n);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define INT_1_TO_32_LIST(V) \
|
||||
V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \
|
||||
V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \
|
||||
V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \
|
||||
V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32)
|
||||
|
||||
#define INT_33_TO_63_LIST(V) \
|
||||
V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \
|
||||
V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \
|
||||
V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \
|
||||
V(57) V(58) V(59) V(60) V(61) V(62) V(63)
|
||||
|
||||
#define INT_1_TO_63_LIST(V) INT_1_TO_32_LIST(V) INT_33_TO_63_LIST(V)
|
||||
|
||||
// clang-format on
|
||||
|
||||
#define DECLARE_IS_INT_N(N) \
|
||||
static inline bool IsInt##N(int64_t x) { return IsIntN(N, x); }
|
||||
|
||||
#define DECLARE_IS_UINT_N(N) \
|
||||
static inline bool IsUint##N(int64_t x) { return IsUintN(N, x); }
|
||||
|
||||
INT_1_TO_63_LIST(DECLARE_IS_INT_N)
|
||||
INT_1_TO_63_LIST(DECLARE_IS_UINT_N)
|
||||
|
||||
#undef DECLARE_IS_INT_N
|
||||
#undef DECLARE_IS_UINT_N
|
||||
|
||||
private:
|
||||
|
||||
template <typename V>
|
||||
|
||||
+4
-72
@@ -20,8 +20,7 @@ the coding style of LLVM. It can also be installed as a pre-commit git hook to
|
||||
check the coding style before submitting it. The canonical source of this script
|
||||
is in the LLVM source tree under llvm/utils/git.
|
||||
|
||||
For C/C++ code it uses clang-format and for Python code it uses darker (which
|
||||
in turn invokes black).
|
||||
For C/C++ code it uses clang-format.
|
||||
|
||||
You can learn more about the LLVM coding style on llvm.org:
|
||||
https://llvm.org/docs/CodingStandards.html
|
||||
@@ -31,8 +30,8 @@ directory:
|
||||
|
||||
ln -s $(pwd)/llvm/utils/git/code-format-helper.py .git/hooks/pre-commit
|
||||
|
||||
You can control the exact path to clang-format or darker with the following
|
||||
environment variables: $CLANG_FORMAT_PATH and $DARKER_FORMAT_PATH.
|
||||
You can control the exact path to clang-format with the following
|
||||
environment variable: $CLANG_FORMAT_PATH.
|
||||
"""
|
||||
|
||||
|
||||
@@ -245,74 +244,7 @@ class ClangFormatHelper(FormatHelper):
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class DarkerFormatHelper(FormatHelper):
|
||||
name = "darker"
|
||||
friendly_name = "Python code formatter"
|
||||
|
||||
@property
|
||||
def instructions(self) -> str:
|
||||
return " ".join(self.darker_cmd)
|
||||
|
||||
def filter_changed_files(self, changed_files: List[str]) -> List[str]:
|
||||
filtered_files = []
|
||||
for path in changed_files:
|
||||
name, ext = os.path.splitext(path)
|
||||
if ext == ".py":
|
||||
filtered_files.append(path)
|
||||
|
||||
return filtered_files
|
||||
|
||||
@property
|
||||
def darker_fmt_path(self) -> str:
|
||||
if "DARKER_FORMAT_PATH" in os.environ:
|
||||
return os.environ["DARKER_FORMAT_PATH"]
|
||||
return "darker"
|
||||
|
||||
def has_tool(self) -> bool:
|
||||
cmd = [self.darker_fmt_path, "--version"]
|
||||
proc = None
|
||||
try:
|
||||
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except:
|
||||
return False
|
||||
return proc.returncode == 0
|
||||
|
||||
def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]:
|
||||
py_files = self.filter_changed_files(changed_files)
|
||||
if not py_files:
|
||||
return None
|
||||
darker_cmd = [
|
||||
self.darker_fmt_path,
|
||||
"--check",
|
||||
"--diff",
|
||||
]
|
||||
if args.start_rev and args.end_rev:
|
||||
darker_cmd += ["-r", f"{args.start_rev}...{args.end_rev}"]
|
||||
darker_cmd += py_files
|
||||
if args.verbose:
|
||||
print(f"Running: {' '.join(darker_cmd)}")
|
||||
self.darker_cmd = darker_cmd
|
||||
proc = subprocess.run(
|
||||
darker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
if args.verbose:
|
||||
sys.stdout.write(proc.stderr.decode("utf-8"))
|
||||
|
||||
if proc.returncode != 0:
|
||||
# formatting needed, or the command otherwise failed
|
||||
if args.verbose:
|
||||
print(f"error: {self.name} exited with code {proc.returncode}")
|
||||
# Print the diff in the log so that it is viewable there
|
||||
print(proc.stdout.decode("utf-8"))
|
||||
return proc.stdout.decode("utf-8")
|
||||
else:
|
||||
sys.stdout.write(proc.stdout.decode("utf-8"))
|
||||
return None
|
||||
|
||||
|
||||
ALL_FORMATTERS = (DarkerFormatHelper(), ClangFormatHelper())
|
||||
|
||||
ALL_FORMATTERS = [ClangFormatHelper()]
|
||||
|
||||
def hook_main():
|
||||
# fill out args
|
||||
|
||||
Vendored
+1
-1
Submodule External/jemalloc updated: 5695452413...7ae889695b.
Vendored
+1
-1
Submodule External/vixl updated: 7725aec177...a90f5d5020.
@@ -24,27 +24,6 @@ include(CheckCXXCompilerFlag)
|
||||
include(CheckIncludeFileCXX)
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -Wattributes -Werror=attributes")
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
__attribute__((preserve_all))
|
||||
int Testy(int a, int b, int c, int d, int e, int f) {
|
||||
return a + b + c + d + e + f;
|
||||
}
|
||||
int main() {
|
||||
return Testy(0, 1, 2, 3, 4, 5);
|
||||
}"
|
||||
HAS_CLANG_PRESERVE_ALL)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if (HAS_CLANG_PRESERVE_ALL)
|
||||
if (MINGW_BUILD)
|
||||
message(STATUS "Ignoring broken clang::preserve_all support")
|
||||
set(HAS_CLANG_PRESERVE_ALL FALSE)
|
||||
else()
|
||||
message(STATUS "Has clang::preserve_all")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
|
||||
# Useful to have for freestanding libFEXCore
|
||||
add_subdirectory(External/vixl/)
|
||||
|
||||
@@ -148,9 +148,8 @@ def print_man_options(options):
|
||||
if (value_type == "strenum"):
|
||||
Enums = op_vals["Enums"]
|
||||
output_man.write("\\fBAvailable Options:\\fR\n")
|
||||
for enum_op_key, enum_op_vals in Enums.items():
|
||||
output_man.write("{}, ".format(enum_op_vals))
|
||||
output_man.write("\n")
|
||||
output_man.write(", ".join(f"{enum_op_val}" for [_, enum_op_val] in Enums.items()))
|
||||
output_man.write("\n.sp\n")
|
||||
|
||||
output_man.write(".El\n")
|
||||
|
||||
@@ -179,9 +178,8 @@ def print_man_environment(options):
|
||||
if (value_type == "strenum"):
|
||||
Enums = op_vals["Enums"]
|
||||
output_man.write("\\fBAvailable Options:\\fR\n")
|
||||
for enum_op_key, enum_op_vals in Enums.items():
|
||||
output_man.write("{}, ".format(enum_op_vals))
|
||||
output_man.write("\n")
|
||||
output_man.write(", ".join(f"{enum_op_val}" for [_, enum_op_val] in Enums.items()))
|
||||
output_man.write("\n.sp\n")
|
||||
|
||||
print_man_environment_tail()
|
||||
output_man.write(".El\n")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
import textwrap
|
||||
|
||||
def ExitError(msg):
|
||||
print(msg)
|
||||
@@ -53,6 +54,7 @@ class OpDefinition:
|
||||
SSAArgNum: int
|
||||
NonSSAArgNum: int
|
||||
DynamicDispatch: bool
|
||||
LoweredX87: bool
|
||||
JITDispatch: bool
|
||||
JITDispatchOverride: str
|
||||
TiedSource: int
|
||||
@@ -76,6 +78,7 @@ class OpDefinition:
|
||||
self.SSAArgNum = 0
|
||||
self.NonSSAArgNum = 0
|
||||
self.DynamicDispatch = False
|
||||
self.LoweredX87 = False
|
||||
self.JITDispatch = True
|
||||
self.JITDispatchOverride = None
|
||||
self.TiedSource = -1
|
||||
@@ -204,7 +207,7 @@ def parse_ops(ops):
|
||||
(OpArg.Type == "GPR" or
|
||||
OpArg.Type == "GPRPair" or
|
||||
OpArg.Type == "FPR")):
|
||||
OpDef.EmitValidation.append("GetOpRegClass({}) == InvalidClass || WalkFindRegClass({}) == {}Class".format(NameWithPrefix, NameWithPrefix, OpArg.Type))
|
||||
OpDef.EmitValidation.append(f"GetOpRegClass({ArgName}) == InvalidClass || WalkFindRegClass({ArgName}) == {OpArg.Type}Class")
|
||||
|
||||
OpArg.Name = ArgName
|
||||
OpArg.NameWithPrefix = NameWithPrefix
|
||||
@@ -250,6 +253,13 @@ def parse_ops(ops):
|
||||
if "JITDispatchOverride" in op_val:
|
||||
OpDef.JITDispatchOverride = op_val["JITDispatchOverride"]
|
||||
|
||||
if "X87" in op_val:
|
||||
OpDef.LoweredX87 = op_val["X87"]
|
||||
|
||||
# X87 implies !JITDispatch
|
||||
assert("JITDispatch" not in op_val)
|
||||
OpDef.JITDispatch = False
|
||||
|
||||
if "TiedSource" in op_val:
|
||||
OpDef.TiedSource = op_val["TiedSource"]
|
||||
|
||||
@@ -258,12 +268,8 @@ def parse_ops(ops):
|
||||
for i in range(len(OpDef.EmitValidation)):
|
||||
# Patch up all the argument names
|
||||
for Arg in OpDef.Arguments:
|
||||
if Arg.Temporary:
|
||||
# Temporary ops just replace all instances no prefix variant
|
||||
OpDef.EmitValidation[i] = OpDef.EmitValidation[i].replace(Arg.NameWithPrefix, Arg.Name)
|
||||
else:
|
||||
# All other ops replace $ with _ variant for argument passed in
|
||||
OpDef.EmitValidation[i] = OpDef.EmitValidation[i].replace(Arg.NameWithPrefix, "_{}".format(Arg.Name))
|
||||
# Temporary ops just replace all instances no prefix variant
|
||||
OpDef.EmitValidation[i] = OpDef.EmitValidation[i].replace(Arg.NameWithPrefix, Arg.Name)
|
||||
|
||||
#OpDef.print()
|
||||
|
||||
@@ -368,42 +374,28 @@ def print_ir_sizes():
|
||||
if op.Name == "Last":
|
||||
output_file.write("\t-1ULL,\n")
|
||||
else:
|
||||
output_file.write("\tsizeof(IROp_{}),\n".format(op.Name))
|
||||
output_file.write(f"\tsizeof(IROp_{op.Name}),\n")
|
||||
|
||||
output_file.write("};\n\n")
|
||||
output_file.write(textwrap.dedent("""
|
||||
};
|
||||
|
||||
output_file.write("// Make sure our array maps directly to the IROps enum\n")
|
||||
output_file.write("static_assert(IRSizes[IROps::OP_LAST] == -1ULL);\n\n")
|
||||
// Make sure our array maps directly to the IROps enum
|
||||
static_assert(IRSizes[IROps::OP_LAST] == -1ULL);
|
||||
|
||||
output_file.write("[[maybe_unused, nodiscard]] static size_t GetSize(IROps Op) { return IRSizes[Op]; }\n\n")
|
||||
[[maybe_unused, nodiscard]] static size_t GetSize(IROps Op) { return IRSizes[Op]; }
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] std::string_view const& GetName(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] uint8_t GetArgs(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] uint8_t GetRAArgs(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] bool HasSideEffects(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] bool ImplicitFlagClobber(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] bool GetHasDest(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] bool LoweredX87(IROps Op);
|
||||
[[nodiscard, gnu::const, gnu::visibility("default")]] int8_t TiedSource(IROps Op);
|
||||
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] std::string_view const& GetName(IROps Op);\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] uint8_t GetArgs(IROps Op);\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] uint8_t GetRAArgs(IROps Op);\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] bool HasSideEffects(IROps Op);\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] bool ImplicitFlagClobber(IROps Op);\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] bool GetHasDest(IROps Op);\n'
|
||||
)
|
||||
output_file.write(
|
||||
'[[nodiscard, gnu::const, gnu::visibility("default")]] int8_t TiedSource(IROps Op);\n'
|
||||
)
|
||||
|
||||
output_file.write("#undef IROP_SIZES\n")
|
||||
output_file.write("#endif\n\n")
|
||||
#undef IROP_SIZES
|
||||
#endif
|
||||
"""))
|
||||
|
||||
def print_ir_reg_classes():
|
||||
output_file.write("#ifdef IROP_REG_CLASSES_IMPL\n")
|
||||
@@ -493,13 +485,14 @@ def print_ir_getraargs():
|
||||
def print_ir_hassideeffects():
|
||||
output_file.write("#ifdef IROP_HASSIDEEFFECTS_IMPL\n")
|
||||
|
||||
for array, prop, T in [
|
||||
("SideEffects", "HasSideEffects", "bool"),
|
||||
("ImplicitFlagClobbers", "ImplicitFlagClobber", "bool"),
|
||||
("TiedSources", "TiedSource", "int8_t"),
|
||||
for prop, T in [
|
||||
("HasSideEffects", "bool"),
|
||||
("ImplicitFlagClobber", "bool"),
|
||||
("LoweredX87", "bool"),
|
||||
("TiedSource", "int8_t"),
|
||||
]:
|
||||
output_file.write(
|
||||
f"constexpr std::array<{'uint8_t' if T == 'bool' else T}, OP_LAST + 1> {array} = {{\n"
|
||||
f"constexpr std::array<{'uint8_t' if T == 'bool' else T}, OP_LAST + 1> {prop}_ = {{\n"
|
||||
)
|
||||
for op in IROps:
|
||||
if T == "bool":
|
||||
@@ -512,7 +505,7 @@ def print_ir_hassideeffects():
|
||||
output_file.write("};\n\n")
|
||||
|
||||
output_file.write(f"{T} {prop}(IROps Op) {{\n")
|
||||
output_file.write(f" return {array}[Op];\n")
|
||||
output_file.write(f" return {prop}_[Op];\n")
|
||||
output_file.write("}\n")
|
||||
|
||||
output_file.write("#undef IROP_HASSIDEEFFECTS_IMPL\n")
|
||||
@@ -552,14 +545,20 @@ def print_ir_arg_printer():
|
||||
output_file.write("\t*out << \" \";\n")
|
||||
|
||||
SSAArgNum = 0
|
||||
FirstArg = True
|
||||
for i in range(0, len(op.Arguments)):
|
||||
arg = op.Arguments[i]
|
||||
LastArg = len(op.Arguments) - i - 1 == 0
|
||||
|
||||
# No point printing temporaries that we can't recover
|
||||
if arg.Temporary:
|
||||
# Temporary that we can't recover
|
||||
output_file.write("\t*out << \"{}:Tmp:{}\";\n".format(arg.Type, arg.Name))
|
||||
elif arg.IsSSA:
|
||||
continue
|
||||
|
||||
if FirstArg:
|
||||
FirstArg = False
|
||||
else:
|
||||
output_file.write('\t*out << ", ";\n')
|
||||
|
||||
if arg.IsSSA:
|
||||
# SSA value
|
||||
output_file.write("\tPrintArg(out, IR, Op->Header.Args[{}], RAData);\n".format(SSAArgNum))
|
||||
SSAArgNum = SSAArgNum + 1
|
||||
@@ -567,9 +566,6 @@ def print_ir_arg_printer():
|
||||
# User defined op that is stored
|
||||
output_file.write("\tPrintArg(out, IR, Op->{});\n".format(arg.Name))
|
||||
|
||||
if not LastArg:
|
||||
output_file.write("\t*out << \", \";\n")
|
||||
|
||||
output_file.write("break;\n")
|
||||
output_file.write("}\n")
|
||||
|
||||
@@ -668,11 +664,11 @@ def print_ir_allocator_helpers():
|
||||
output_file.write("{} {}".format(CType, arg.Name));
|
||||
elif arg.IsSSA:
|
||||
# SSA value
|
||||
output_file.write("OrderedNode *_{}".format(arg.Name))
|
||||
output_file.write("OrderedNode *{}".format(arg.Name))
|
||||
else:
|
||||
# User defined op that is stored
|
||||
CType = IRTypesToCXX[arg.Type].CXXName
|
||||
output_file.write("{} _{}".format(CType, arg.Name));
|
||||
output_file.write("{} {}".format(CType, arg.Name));
|
||||
|
||||
if arg.DefaultInitializer != None:
|
||||
output_file.write(" = {}".format(arg.DefaultInitializer))
|
||||
@@ -686,23 +682,28 @@ def print_ir_allocator_helpers():
|
||||
if op.ImplicitFlagClobber:
|
||||
output_file.write("\t\tSaveNZCV(IROps::OP_{});".format(op.Name.upper()))
|
||||
|
||||
output_file.write("\t\tauto Op = AllocateOp<IROp_{}, IROps::OP_{}>();\n".format(op.Name, op.Name.upper()))
|
||||
# We gather the "has x87?" flag as we go. This saves the user from
|
||||
# having to keep track of whether they emitted any x87.
|
||||
if op.LoweredX87:
|
||||
output_file.write("\t\tRecordX87Use();\n")
|
||||
|
||||
output_file.write("\t\tauto _Op = AllocateOp<IROp_{}, IROps::OP_{}>();\n".format(op.Name, op.Name.upper()))
|
||||
|
||||
if op.SSAArgNum != 0:
|
||||
output_file.write("\t\tauto ListDataBegin = DualListData.ListBegin();\n")
|
||||
for arg in op.Arguments:
|
||||
if arg.IsSSA:
|
||||
output_file.write("\t\tOp.first->{} = _{}->Wrapped(ListDataBegin);\n".format(arg.Name, arg.Name))
|
||||
output_file.write("\t\t_Op.first->{} = {}->Wrapped(ListDataBegin);\n".format(arg.Name, arg.Name))
|
||||
|
||||
if op.SSAArgNum != 0:
|
||||
for arg in op.Arguments:
|
||||
if arg.IsSSA:
|
||||
output_file.write("\t\t_{}->AddUse();\n".format(arg.Name))
|
||||
output_file.write("\t\t{}->AddUse();\n".format(arg.Name))
|
||||
|
||||
if len(op.Arguments) != 0:
|
||||
for arg in op.Arguments:
|
||||
if not arg.Temporary and not arg.IsSSA:
|
||||
output_file.write("\t\tOp.first->{} = _{};\n".format(arg.Name, arg.Name))
|
||||
output_file.write("\t\t_Op.first->{} = {};\n".format(arg.Name, arg.Name))
|
||||
|
||||
if (op.HasDest):
|
||||
# We can only infer a size if we have arguments
|
||||
@@ -712,22 +713,22 @@ def print_ir_allocator_helpers():
|
||||
if len(op.Arguments) != 0:
|
||||
for arg in op.Arguments:
|
||||
if arg.IsSSA:
|
||||
output_file.write("\t\tuint8_t Size{} = GetOpSize(_{});\n".format(arg.Name, arg.Name))
|
||||
output_file.write("\t\tuint8_t Size{} = GetOpSize({});\n".format(arg.Name, arg.Name))
|
||||
for arg in op.Arguments:
|
||||
if arg.IsSSA:
|
||||
output_file.write("\t\tInferSize = std::max(InferSize, Size{});\n".format(arg.Name))
|
||||
|
||||
output_file.write("\t\tOp.first->Header.Size = InferSize;\n")
|
||||
output_file.write("\t\t_Op.first->Header.Size = InferSize;\n")
|
||||
|
||||
# Some ops without a destination still need an operating size
|
||||
# Effectively reusing the destination size value for operation size
|
||||
if op.DestSize != None:
|
||||
output_file.write("\t\tOp.first->Header.Size = {};\n".format(op.DestSize))
|
||||
output_file.write("\t\t_Op.first->Header.Size = {};\n".format(op.DestSize))
|
||||
|
||||
if op.NumElements == None:
|
||||
output_file.write("\t\tOp.first->Header.ElementSize = Op.first->Header.Size / ({});\n".format(1))
|
||||
output_file.write("\t\t_Op.first->Header.ElementSize = _Op.first->Header.Size / ({});\n".format(1))
|
||||
else:
|
||||
output_file.write("\t\tOp.first->Header.ElementSize = Op.first->Header.Size / ({});\n".format(op.NumElements))
|
||||
output_file.write("\t\t_Op.first->Header.ElementSize = _Op.first->Header.Size / ({});\n".format(op.NumElements))
|
||||
|
||||
# Insert validation here
|
||||
if op.EmitValidation != None:
|
||||
@@ -738,58 +739,12 @@ def print_ir_allocator_helpers():
|
||||
output_file.write("\tLOGMAN_THROW_A_FMT({}, \"{}\");\n".format(Validation, Sanitized))
|
||||
output_file.write("\t\t#endif\n")
|
||||
|
||||
output_file.write("\t\treturn Op;\n")
|
||||
output_file.write("\t\treturn _Op;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("#undef IROP_ALLOCATE_HELPERS\n")
|
||||
output_file.write("#endif\n")
|
||||
|
||||
def print_ir_parser_switch_helper():
|
||||
output_file.write("#ifdef IROP_PARSER_SWITCH_HELPERS\n")
|
||||
for op in IROps:
|
||||
if op.Name != "Last" and op.SwitchGen:
|
||||
output_file.write("\tcase FEXCore::IR::IROps::OP_%s: {\n" % (op.Name.upper()))
|
||||
|
||||
for i in range(0, len(op.Arguments)):
|
||||
arg = op.Arguments[i]
|
||||
LastArg = len(op.Arguments) - i - 1 == 0
|
||||
|
||||
if arg.Temporary:
|
||||
CType = IRTypesToCXX[arg.Type].CXXName
|
||||
output_file.write("\t\tauto arg{} = DecodeValue<{}>(Def.Args[{}]);\n".format(i, CType, i))
|
||||
output_file.write("\t\tif (!CheckPrintErrorArg(Def, arg{}.first, {})) return false;\n".format(i, i))
|
||||
elif arg.IsSSA:
|
||||
# SSA value
|
||||
output_file.write("\t\tauto arg{} = DecodeValue<OrderedNode*>(Def.Args[{}]);\n".format(i, i))
|
||||
output_file.write("\t\tif (!CheckPrintErrorArg(Def, arg{}.first, {})) return false;\n".format(i, i))
|
||||
else:
|
||||
# User defined op that is stored
|
||||
CType = IRTypesToCXX[arg.Type].CXXName
|
||||
output_file.write("\t\tauto arg{} = DecodeValue<{}>(Def.Args[{}]);\n".format(i, CType, i))
|
||||
output_file.write("\t\tif (!CheckPrintErrorArg(Def, arg{}.first, {})) return false;\n".format(i, i))
|
||||
|
||||
output_file.write("\t\tDef.Node = _{}(\n".format(op.Name))
|
||||
|
||||
for i in range(0, len(op.Arguments)):
|
||||
arg = op.Arguments[i]
|
||||
LastArg = len(op.Arguments) - i - 1 == 0
|
||||
output_file.write("\t\t\targ{}.second".format(i))
|
||||
if not LastArg:
|
||||
output_file.write(",\n")
|
||||
else:
|
||||
output_file.write("\n")
|
||||
|
||||
output_file.write("\t\t);\n")
|
||||
|
||||
output_file.write("\t\tSSANameMapper[Def.Definition] = Def.Node;\n")
|
||||
|
||||
output_file.write("\t\tbreak;\n")
|
||||
output_file.write("\t}\n")
|
||||
|
||||
|
||||
output_file.write("#undef IROP_PARSER_SWITCH_HELPERS\n")
|
||||
output_file.write("#endif\n")
|
||||
|
||||
def print_ir_dispatcher_defs():
|
||||
output_dispatch_file.write("#ifdef IROP_DISPATCH_DEFS\n")
|
||||
for op in IROps:
|
||||
@@ -848,7 +803,6 @@ print_ir_hassideeffects()
|
||||
print_ir_gethasdest()
|
||||
print_ir_arg_printer()
|
||||
print_ir_allocator_helpers()
|
||||
print_ir_parser_switch_helper()
|
||||
|
||||
output_file.close()
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@ set (SRCS
|
||||
Common/SoftFloat-3e/s_approxRecipSqrt32_1.c
|
||||
Common/SoftFloat-3e/s_approxRecipSqrt_1Ks.c
|
||||
Common/SoftFloat-3e/softfloat_raiseFlags.c
|
||||
Common/SoftFloat-3e/softfloat_state.c
|
||||
Common/SoftFloat-3e/f64_to_extF80.c
|
||||
Common/SoftFloat-3e/s_commonNaNToExtF80UI.c
|
||||
Common/SoftFloat-3e/s_normSubnormalF64Sig.c
|
||||
@@ -91,10 +90,10 @@ set (SRCS
|
||||
Interface/Core/CPUBackend.cpp
|
||||
Interface/Core/CPUID.cpp
|
||||
Interface/Core/Frontend.cpp
|
||||
Interface/Core/HostFeatures.cpp
|
||||
Interface/Core/ObjectCache/JobHandling.cpp
|
||||
Interface/Core/ObjectCache/NamedRegionObjectHandler.cpp
|
||||
Interface/Core/ObjectCache/ObjectCacheService.cpp
|
||||
Interface/Core/OpcodeDispatcher/AVX_128.cpp
|
||||
Interface/Core/OpcodeDispatcher/Crypto.cpp
|
||||
Interface/Core/OpcodeDispatcher/Flags.cpp
|
||||
Interface/Core/OpcodeDispatcher/Vector.cpp
|
||||
@@ -112,7 +111,6 @@ set (SRCS
|
||||
Interface/Core/JIT/Arm64/BranchOps.cpp
|
||||
Interface/Core/JIT/Arm64/ConversionOps.cpp
|
||||
Interface/Core/JIT/Arm64/EncryptionOps.cpp
|
||||
Interface/Core/JIT/Arm64/FlagOps.cpp
|
||||
Interface/Core/JIT/Arm64/MemoryOps.cpp
|
||||
Interface/Core/JIT/Arm64/MiscOps.cpp
|
||||
Interface/Core/JIT/Arm64/MoveOps.cpp
|
||||
@@ -137,13 +135,13 @@ set (SRCS
|
||||
Interface/IR/IREmitter.cpp
|
||||
Interface/IR/PassManager.cpp
|
||||
Interface/IR/Passes/ConstProp.cpp
|
||||
Interface/IR/Passes/DeadContextStoreElimination.cpp
|
||||
Interface/IR/Passes/IRDumperPass.cpp
|
||||
Interface/IR/Passes/IRValidation.cpp
|
||||
Interface/IR/Passes/RAValidation.cpp
|
||||
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
|
||||
Interface/IR/Passes/DeadStoreElimination.cpp
|
||||
Interface/IR/Passes/RegisterAllocationPass.cpp
|
||||
Interface/IR/Passes/x87StackOptimizationPass.cpp
|
||||
Utils/Telemetry.cpp
|
||||
Utils/Threads.cpp
|
||||
Utils/Profiler.cpp
|
||||
@@ -160,7 +158,7 @@ if (ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT)
|
||||
Utils/AllocatorOverride.cpp)
|
||||
endif()
|
||||
|
||||
set(DEFINES -DTHREAD_LOCAL=_Thread_local -DJIT_ARM64)
|
||||
set(DEFINES -DJIT_ARM64)
|
||||
|
||||
if (_M_X86_64)
|
||||
list(APPEND DEFINES -D_M_X86_64=1)
|
||||
@@ -170,11 +168,6 @@ if (_M_ARM_64)
|
||||
list(APPEND DEFINES -D_M_ARM_64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
# We can run the simulator on both x86-64 or AArch64 hosts
|
||||
list(APPEND DEFINES -DVIXL_SIMULATOR=1 -DVIXL_INCLUDE_SIMULATOR_AARCH64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VIXL_DISASSEMBLER)
|
||||
list(APPEND DEFINES -DVIXL_DISASSEMBLER=1)
|
||||
endif()
|
||||
@@ -367,16 +360,6 @@ function(AddLibrary Name Type)
|
||||
target_link_libraries(${Name} FEXCore_Base)
|
||||
target_compile_options(${Name} PRIVATE ${FEX_TUNE_COMPILE_FLAGS})
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
if (MINGW_BUILD)
|
||||
# Mingw build isn't building a linux shared library, so it can't have a SONAME.
|
||||
set_target_properties(${Name} PROPERTIES NO_SONAME ON)
|
||||
# Change the suffixes otherwise cmake continues using .a and .so
|
||||
if (${Type} STREQUAL SHARED)
|
||||
set_target_properties(${Name} PROPERTIES SUFFIX ".dll")
|
||||
elseif(${Type} STREQUAL STATIC)
|
||||
set_target_properties(${Name} PROPERTIES SUFFIX ".lib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
AddDefaultOptionsToTarget(${Name})
|
||||
endfunction()
|
||||
|
||||
@@ -41,7 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_add( extFloat80_t a, extFloat80_t b )
|
||||
extFloat80_t extF80_add( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -53,7 +53,7 @@ extFloat80_t extF80_add( extFloat80_t a, extFloat80_t b )
|
||||
bool signB;
|
||||
extFloat80_t
|
||||
(*magsFuncPtr)(
|
||||
uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
struct softfloat_state *, uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
|
||||
uA.f = a;
|
||||
uiA64 = uA.s.signExp;
|
||||
@@ -65,6 +65,6 @@ extFloat80_t extF80_add( extFloat80_t a, extFloat80_t b )
|
||||
signB = signExtF80UI64( uiB64 );
|
||||
magsFuncPtr =
|
||||
(signA == signB) ? softfloat_addMagsExtF80 : softfloat_subMagsExtF80;
|
||||
return (*magsFuncPtr)( uiA64, uiA0, uiB64, uiB0, signA );
|
||||
return (*magsFuncPtr)( state, uiA64, uiA0, uiB64, uiB0, signA );
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_div( extFloat80_t a, extFloat80_t b )
|
||||
extFloat80_t extF80_div( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -107,7 +107,7 @@ extFloat80_t extF80_div( extFloat80_t a, extFloat80_t b )
|
||||
if ( ! (sigB & UINT64_C( 0x8000000000000000 )) ) {
|
||||
if ( ! sigB ) {
|
||||
if ( ! sigA ) goto invalid;
|
||||
softfloat_raiseFlags( softfloat_flag_infinite );
|
||||
softfloat_raiseFlags( state, softfloat_flag_infinite );
|
||||
goto infinity;
|
||||
}
|
||||
normExpSig = softfloat_normSubnormalExtF80Sig( sigB );
|
||||
@@ -169,18 +169,18 @@ extFloat80_t extF80_div( extFloat80_t a, extFloat80_t b )
|
||||
sigZExtra = (uint64_t) ((uint_fast64_t) q<<41);
|
||||
return
|
||||
softfloat_roundPackToExtF80(
|
||||
signZ, expZ, sigZ, sigZExtra, extF80_roundingPrecision );
|
||||
state, signZ, expZ, sigZ, sigZExtra, state->roundingPrecision );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
propagateNaN:
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
goto uiZ;
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
invalid:
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
uiZ64 = defaultNaNExtF80UI64;
|
||||
uiZ0 = defaultNaNExtF80UI0;
|
||||
goto uiZ;
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
bool extF80_eq( extFloat80_t a, extFloat80_t b )
|
||||
bool extF80_eq( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -62,7 +62,7 @@ bool extF80_eq( extFloat80_t a, extFloat80_t b )
|
||||
softfloat_isSigNaNExtF80UI( uiA64, uiA0 )
|
||||
|| softfloat_isSigNaNExtF80UI( uiB64, uiB0 )
|
||||
) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
bool extF80_lt( extFloat80_t a, extFloat80_t b )
|
||||
bool extF80_lt( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -59,7 +59,7 @@ bool extF80_lt( extFloat80_t a, extFloat80_t b )
|
||||
uiB64 = uB.s.signExp;
|
||||
uiB0 = uB.s.signif;
|
||||
if ( isNaNExtF80UI( uiA64, uiA0 ) || isNaNExtF80UI( uiB64, uiB0 ) ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return false;
|
||||
}
|
||||
signA = signExtF80UI64( uiA64 );
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_mul( extFloat80_t a, extFloat80_t b )
|
||||
extFloat80_t extF80_mul( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -125,11 +125,11 @@ extFloat80_t extF80_mul( extFloat80_t a, extFloat80_t b )
|
||||
}
|
||||
return
|
||||
softfloat_roundPackToExtF80(
|
||||
signZ, expZ, sig128Z.v64, sig128Z.v0, extF80_roundingPrecision );
|
||||
state, signZ, expZ, sig128Z.v64, sig128Z.v0, state->roundingPrecision );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
propagateNaN:
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
goto uiZ;
|
||||
@@ -137,7 +137,7 @@ extFloat80_t extF80_mul( extFloat80_t a, extFloat80_t b )
|
||||
*------------------------------------------------------------------------*/
|
||||
infArg:
|
||||
if ( ! magBits ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
uiZ64 = defaultNaNExtF80UI64;
|
||||
uiZ0 = defaultNaNExtF80UI0;
|
||||
} else {
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_rem( extFloat80_t a, extFloat80_t b )
|
||||
extFloat80_t extF80_rem( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -193,18 +193,18 @@ extFloat80_t extF80_rem( extFloat80_t a, extFloat80_t b )
|
||||
}
|
||||
return
|
||||
softfloat_normRoundPackToExtF80(
|
||||
signRem, rem.v64 | rem.v0 ? expB + 32 : 0, rem.v64, rem.v0, 80 );
|
||||
state, signRem, rem.v64 | rem.v0 ? expB + 32 : 0, rem.v64, rem.v0, 80 );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
propagateNaN:
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
goto uiZ;
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
invalid:
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
uiZ64 = defaultNaNExtF80UI64;
|
||||
uiZ0 = defaultNaNExtF80UI0;
|
||||
goto uiZ;
|
||||
|
||||
@@ -43,7 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t
|
||||
extF80_roundToInt( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
extF80_roundToInt( struct softfloat_state *state, extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64, signUI64;
|
||||
@@ -80,7 +80,7 @@ extFloat80_t
|
||||
if ( 0x403E <= exp ) {
|
||||
if ( exp == 0x7FFF ) {
|
||||
if ( sigA & UINT64_C( 0x7FFFFFFFFFFFFFFF ) ) {
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, sigA, 0, 0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, sigA, 0, 0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
sigZ = uiZ.v0;
|
||||
goto uiZ;
|
||||
@@ -93,7 +93,7 @@ extFloat80_t
|
||||
goto uiZ;
|
||||
}
|
||||
if ( exp <= 0x3FFE ) {
|
||||
if ( exact ) softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( exact ) state->exceptionFlags |= softfloat_flag_inexact;
|
||||
switch ( roundingMode ) {
|
||||
case softfloat_round_near_even:
|
||||
if ( !(sigA & UINT64_C( 0x7FFFFFFFFFFFFFFF )) ) break;
|
||||
@@ -145,7 +145,7 @@ extFloat80_t
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) sigZ |= lastBitMask;
|
||||
#endif
|
||||
if ( exact ) softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( exact ) state->exceptionFlags |= softfloat_flag_inexact;
|
||||
}
|
||||
uiZ:
|
||||
uZ.s.signExp = uiZ64;
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_sqrt( extFloat80_t a )
|
||||
extFloat80_t extF80_sqrt( struct softfloat_state *state, extFloat80_t a )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -74,7 +74,7 @@ extFloat80_t extF80_sqrt( extFloat80_t a )
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( expA == 0x7FFF ) {
|
||||
if ( sigA & UINT64_C( 0x7FFFFFFFFFFFFFFF ) ) {
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, uiA0, 0, 0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, uiA0, 0, 0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
goto uiZ;
|
||||
@@ -155,11 +155,11 @@ extFloat80_t extF80_sqrt( extFloat80_t a )
|
||||
}
|
||||
return
|
||||
softfloat_roundPackToExtF80(
|
||||
0, expZ, sigZ, sigZExtra, extF80_roundingPrecision );
|
||||
state, 0, expZ, sigZ, sigZExtra, state->roundingPrecision );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
invalid:
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
uiZ64 = defaultNaNExtF80UI64;
|
||||
uiZ0 = defaultNaNExtF80UI0;
|
||||
goto uiZ;
|
||||
|
||||
@@ -41,7 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_sub( extFloat80_t a, extFloat80_t b )
|
||||
extFloat80_t extF80_sub( struct softfloat_state *state, extFloat80_t a, extFloat80_t b )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -54,7 +54,7 @@ extFloat80_t extF80_sub( extFloat80_t a, extFloat80_t b )
|
||||
#if ! defined INLINE_LEVEL || (INLINE_LEVEL < 2)
|
||||
extFloat80_t
|
||||
(*magsFuncPtr)(
|
||||
uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
struct softfloat_state *, uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
#endif
|
||||
|
||||
uA.f = a;
|
||||
@@ -67,14 +67,14 @@ extFloat80_t extF80_sub( extFloat80_t a, extFloat80_t b )
|
||||
signB = signExtF80UI64( uiB64 );
|
||||
#if defined INLINE_LEVEL && (2 <= INLINE_LEVEL)
|
||||
if ( signA == signB ) {
|
||||
return softfloat_subMagsExtF80( uiA64, uiA0, uiB64, uiB0, signA );
|
||||
return softfloat_subMagsExtF80( state, uiA64, uiA0, uiB64, uiB0, signA );
|
||||
} else {
|
||||
return softfloat_addMagsExtF80( uiA64, uiA0, uiB64, uiB0, signA );
|
||||
return softfloat_addMagsExtF80( state, uiA64, uiA0, uiB64, uiB0, signA );
|
||||
}
|
||||
#else
|
||||
magsFuncPtr =
|
||||
(signA == signB) ? softfloat_subMagsExtF80 : softfloat_addMagsExtF80;
|
||||
return (*magsFuncPtr)( uiA64, uiA0, uiB64, uiB0, signA );
|
||||
return (*magsFuncPtr)( state, uiA64, uiA0, uiB64, uiB0, signA );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float128_t extF80_to_f128( extFloat80_t a )
|
||||
float128_t extF80_to_f128( struct softfloat_state *state, extFloat80_t a )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -61,7 +61,7 @@ float128_t extF80_to_f128( extFloat80_t a )
|
||||
exp = expExtF80UI64( uiA64 );
|
||||
frac = uiA0 & UINT64_C( 0x7FFFFFFFFFFFFFFF );
|
||||
if ( (exp == 0x7FFF) && frac ) {
|
||||
softfloat_extF80UIToCommonNaN( uiA64, uiA0, &commonNaN );
|
||||
softfloat_extF80UIToCommonNaN( state, uiA64, uiA0, &commonNaN );
|
||||
uiZ = softfloat_commonNaNToF128UI( &commonNaN );
|
||||
} else {
|
||||
sign = signExtF80UI64( uiA64 );
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float32_t extF80_to_f32( extFloat80_t a )
|
||||
float32_t extF80_to_f32( struct softfloat_state *state, extFloat80_t a )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -66,7 +66,7 @@ float32_t extF80_to_f32( extFloat80_t a )
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( exp == 0x7FFF ) {
|
||||
if ( sig & UINT64_C( 0x7FFFFFFFFFFFFFFF ) ) {
|
||||
softfloat_extF80UIToCommonNaN( uiA64, uiA0, &commonNaN );
|
||||
softfloat_extF80UIToCommonNaN( state, uiA64, uiA0, &commonNaN );
|
||||
uiZ = softfloat_commonNaNToF32UI( &commonNaN );
|
||||
} else {
|
||||
uiZ = packToF32UI( sign, 0xFF, 0 );
|
||||
@@ -86,7 +86,7 @@ float32_t extF80_to_f32( extFloat80_t a )
|
||||
if ( sizeof (int_fast16_t) < sizeof (int_fast32_t) ) {
|
||||
if ( exp < -0x1000 ) exp = -0x1000;
|
||||
}
|
||||
return softfloat_roundPackToF32( sign, exp, sig32 );
|
||||
return softfloat_roundPackToF32( state, sign, exp, sig32 );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
uiZ:
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float64_t extF80_to_f64( extFloat80_t a )
|
||||
float64_t extF80_to_f64( struct softfloat_state *state, extFloat80_t a )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -72,7 +72,7 @@ float64_t extF80_to_f64( extFloat80_t a )
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( exp == 0x7FFF ) {
|
||||
if ( sig & UINT64_C( 0x7FFFFFFFFFFFFFFF ) ) {
|
||||
softfloat_extF80UIToCommonNaN( uiA64, uiA0, &commonNaN );
|
||||
softfloat_extF80UIToCommonNaN( state, uiA64, uiA0, &commonNaN );
|
||||
uiZ = softfloat_commonNaNToF64UI( &commonNaN );
|
||||
} else {
|
||||
uiZ = packToF64UI( sign, 0x7FF, 0 );
|
||||
@@ -86,7 +86,7 @@ float64_t extF80_to_f64( extFloat80_t a )
|
||||
if ( sizeof (int_fast16_t) < sizeof (int_fast32_t) ) {
|
||||
if ( exp < -0x1000 ) exp = -0x1000;
|
||||
}
|
||||
return softfloat_roundPackToF64( sign, exp, sig );
|
||||
return softfloat_roundPackToF64( state, sign, exp, sig );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
uiZ:
|
||||
|
||||
@@ -43,7 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast32_t
|
||||
extF80_to_i32( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
extF80_to_i32( struct softfloat_state *state, extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -68,7 +68,7 @@ int_fast32_t
|
||||
#elif (i32_fromNaN == i32_fromNegOverflow)
|
||||
sign = 1;
|
||||
#else
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return i32_fromNaN;
|
||||
#endif
|
||||
}
|
||||
@@ -78,7 +78,7 @@ int_fast32_t
|
||||
shiftDist = 0x4032 - exp;
|
||||
if ( shiftDist <= 0 ) shiftDist = 1;
|
||||
sig = softfloat_shiftRightJam64( sig, shiftDist );
|
||||
return softfloat_roundToI32( sign, sig, roundingMode, exact );
|
||||
return softfloat_roundToI32( state, sign, sig, roundingMode, exact );
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast64_t
|
||||
extF80_to_i64( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
extF80_to_i64( struct softfloat_state *state, extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -68,7 +68,7 @@ int_fast64_t
|
||||
/*--------------------------------------------------------------------
|
||||
*--------------------------------------------------------------------*/
|
||||
if ( shiftDist ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return
|
||||
(exp == 0x7FFF) && (sig & UINT64_C( 0x7FFFFFFFFFFFFFFF ))
|
||||
? i64_fromNaN
|
||||
@@ -84,7 +84,7 @@ int_fast64_t
|
||||
sig = sig64Extra.v;
|
||||
sigExtra = sig64Extra.extra;
|
||||
}
|
||||
return softfloat_roundToI64( sign, sig, sigExtra, roundingMode, exact );
|
||||
return softfloat_roundToI64( state, sign, sig, sigExtra, roundingMode, exact );
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
uint_fast64_t
|
||||
extF80_to_ui64( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
extF80_to_ui64( struct softfloat_state *state, extFloat80_t a, uint_fast8_t roundingMode, bool exact )
|
||||
{
|
||||
union { struct extFloat80M s; extFloat80_t f; } uA;
|
||||
uint_fast16_t uiA64;
|
||||
@@ -65,7 +65,7 @@ uint_fast64_t
|
||||
*------------------------------------------------------------------------*/
|
||||
shiftDist = 0x403E - exp;
|
||||
if ( shiftDist < 0 ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return
|
||||
(exp == 0x7FFF) && (sig & UINT64_C( 0x7FFFFFFFFFFFFFFF ))
|
||||
? ui64_fromNaN
|
||||
@@ -79,7 +79,7 @@ uint_fast64_t
|
||||
sig = sig64Extra.v;
|
||||
sigExtra = sig64Extra.extra;
|
||||
}
|
||||
return softfloat_roundToUI64( sign, sig, sigExtra, roundingMode, exact );
|
||||
return softfloat_roundToUI64( state, sign, sig, sigExtra, roundingMode, exact );
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t f128_to_extF80( float128_t a )
|
||||
extFloat80_t f128_to_extF80( struct softfloat_state *state, float128_t a )
|
||||
{
|
||||
union ui128_f128 uA;
|
||||
uint_fast64_t uiA64, uiA0;
|
||||
@@ -70,7 +70,7 @@ extFloat80_t f128_to_extF80( float128_t a )
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( exp == 0x7FFF ) {
|
||||
if ( frac64 | frac0 ) {
|
||||
softfloat_f128UIToCommonNaN( uiA64, uiA0, &commonNaN );
|
||||
softfloat_f128UIToCommonNaN( state, uiA64, uiA0, &commonNaN );
|
||||
uiZ = softfloat_commonNaNToExtF80UI( &commonNaN );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
@@ -98,7 +98,7 @@ extFloat80_t f128_to_extF80( float128_t a )
|
||||
sig128 =
|
||||
softfloat_shortShiftLeft128(
|
||||
frac64 | UINT64_C( 0x0001000000000000 ), frac0, 15 );
|
||||
return softfloat_roundPackToExtF80( sign, exp, sig128.v64, sig128.v0, 80 );
|
||||
return softfloat_roundPackToExtF80( state, sign, exp, sig128.v64, sig128.v0, 80 );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
uiZ:
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t f32_to_extF80( float32_t a )
|
||||
extFloat80_t f32_to_extF80( struct softfloat_state *state, float32_t a )
|
||||
{
|
||||
union ui32_f32 uA;
|
||||
uint_fast32_t uiA;
|
||||
@@ -67,7 +67,7 @@ extFloat80_t f32_to_extF80( float32_t a )
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( exp == 0xFF ) {
|
||||
if ( frac ) {
|
||||
softfloat_f32UIToCommonNaN( uiA, &commonNaN );
|
||||
softfloat_f32UIToCommonNaN( state, uiA, &commonNaN );
|
||||
uiZ = softfloat_commonNaNToExtF80UI( &commonNaN );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "softfloat.h"
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t f64_to_extF80( float64_t a )
|
||||
extFloat80_t f64_to_extF80( struct softfloat_state *state, float64_t a )
|
||||
{
|
||||
union ui64_f64 uA;
|
||||
uint_fast64_t uiA;
|
||||
@@ -67,7 +67,7 @@ extFloat80_t f64_to_extF80( float64_t a )
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( exp == 0x7FF ) {
|
||||
if ( frac ) {
|
||||
softfloat_f64UIToCommonNaN( uiA, &commonNaN );
|
||||
softfloat_f64UIToCommonNaN( state, uiA, &commonNaN );
|
||||
uiZ = softfloat_commonNaNToExtF80UI( &commonNaN );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
|
||||
@@ -63,19 +63,19 @@ uint_fast32_t softfloat_roundToUI32( bool, uint_fast64_t, uint_fast8_t, bool );
|
||||
#ifdef SOFTFLOAT_FAST_INT64
|
||||
uint_fast64_t
|
||||
softfloat_roundToUI64(
|
||||
bool, uint_fast64_t, uint_fast64_t, uint_fast8_t, bool );
|
||||
struct softfloat_state *, bool, uint_fast64_t, uint_fast64_t, uint_fast8_t, bool );
|
||||
#else
|
||||
uint_fast64_t softfloat_roundMToUI64( bool, uint32_t *, uint_fast8_t, bool );
|
||||
#endif
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast32_t softfloat_roundToI32( bool, uint_fast64_t, uint_fast8_t, bool );
|
||||
int_fast32_t softfloat_roundToI32( struct softfloat_state *, bool, uint_fast64_t, uint_fast8_t, bool );
|
||||
|
||||
#ifdef SOFTFLOAT_FAST_INT64
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast64_t
|
||||
softfloat_roundToI64(
|
||||
bool, uint_fast64_t, uint_fast64_t, uint_fast8_t, bool );
|
||||
struct softfloat_state *, bool, uint_fast64_t, uint_fast64_t, uint_fast8_t, bool );
|
||||
#else
|
||||
int_fast64_t softfloat_roundMToI64( bool, uint32_t *, uint_fast8_t, bool );
|
||||
#endif
|
||||
@@ -115,7 +115,7 @@ FEXCORE_PRESERVE_ALL_ATTR
|
||||
struct exp16_sig32 softfloat_normSubnormalF32Sig( uint_fast32_t );
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float32_t softfloat_roundPackToF32( bool, int_fast16_t, uint_fast32_t );
|
||||
float32_t softfloat_roundPackToF32( struct softfloat_state *, bool, int_fast16_t, uint_fast32_t );
|
||||
float32_t softfloat_normRoundPackToF32( bool, int_fast16_t, uint_fast32_t );
|
||||
|
||||
float32_t softfloat_addMagsF32( uint_fast32_t, uint_fast32_t );
|
||||
@@ -138,7 +138,7 @@ FEXCORE_PRESERVE_ALL_ATTR
|
||||
struct exp16_sig64 softfloat_normSubnormalF64Sig( uint_fast64_t );
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float64_t softfloat_roundPackToF64( bool, int_fast16_t, uint_fast64_t );
|
||||
float64_t softfloat_roundPackToF64( struct softfloat_state *, bool, int_fast16_t, uint_fast64_t );
|
||||
float64_t softfloat_normRoundPackToF64( bool, int_fast16_t, uint_fast64_t );
|
||||
|
||||
float64_t softfloat_addMagsF64( uint_fast64_t, uint_fast64_t, bool );
|
||||
@@ -167,18 +167,18 @@ struct exp32_sig64 softfloat_normSubnormalExtF80Sig( uint_fast64_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t
|
||||
softfloat_roundPackToExtF80(
|
||||
bool, int_fast32_t, uint_fast64_t, uint_fast64_t, uint_fast8_t );
|
||||
struct softfloat_state *, bool, int_fast32_t, uint_fast64_t, uint_fast64_t, uint_fast8_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t
|
||||
softfloat_normRoundPackToExtF80(
|
||||
bool, int_fast32_t, uint_fast64_t, uint_fast64_t, uint_fast8_t );
|
||||
struct softfloat_state *, bool, int_fast32_t, uint_fast64_t, uint_fast64_t, uint_fast8_t );
|
||||
|
||||
extFloat80_t
|
||||
softfloat_addMagsExtF80(
|
||||
uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
struct softfloat_state *, uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
extFloat80_t
|
||||
softfloat_subMagsExtF80(
|
||||
uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
struct softfloat_state *, uint_fast16_t, uint_fast64_t, uint_fast16_t, uint_fast64_t, bool );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
@@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
extFloat80_t
|
||||
softfloat_addMagsExtF80(
|
||||
struct softfloat_state *state,
|
||||
uint_fast16_t uiA64,
|
||||
uint_fast64_t uiA0,
|
||||
uint_fast16_t uiB64,
|
||||
@@ -140,11 +141,11 @@ extFloat80_t
|
||||
roundAndPack:
|
||||
return
|
||||
softfloat_roundPackToExtF80(
|
||||
signZ, expZ, sigZ, sigZExtra, extF80_roundingPrecision );
|
||||
state, signZ, expZ, sigZ, sigZExtra, state->roundingPrecision );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
propagateNaN:
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
uiZ:
|
||||
|
||||
@@ -49,11 +49,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void
|
||||
softfloat_extF80UIToCommonNaN(
|
||||
uint_fast16_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr )
|
||||
struct softfloat_state *state, uint_fast16_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr )
|
||||
{
|
||||
|
||||
if ( softfloat_isSigNaNExtF80UI( uiA64, uiA0 ) ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
}
|
||||
zPtr->sign = uiA64>>15;
|
||||
zPtr->v64 = uiA0<<1;
|
||||
|
||||
@@ -50,12 +50,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void
|
||||
softfloat_f128UIToCommonNaN(
|
||||
uint_fast64_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr )
|
||||
struct softfloat_state *state, uint_fast64_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr )
|
||||
{
|
||||
struct uint128 NaNSig;
|
||||
|
||||
if ( softfloat_isSigNaNF128UI( uiA64, uiA0 ) ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
}
|
||||
NaNSig = softfloat_shortShiftLeft128( uiA64, uiA0, 16 );
|
||||
zPtr->sign = uiA64>>63;
|
||||
|
||||
@@ -46,11 +46,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void softfloat_f32UIToCommonNaN( uint_fast32_t uiA, struct commonNaN *zPtr )
|
||||
void softfloat_f32UIToCommonNaN( struct softfloat_state *state, uint_fast32_t uiA, struct commonNaN *zPtr )
|
||||
{
|
||||
|
||||
if ( softfloat_isSigNaNF32UI( uiA ) ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
}
|
||||
zPtr->sign = uiA>>31;
|
||||
zPtr->v64 = (uint_fast64_t) uiA<<41;
|
||||
|
||||
@@ -46,11 +46,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void softfloat_f64UIToCommonNaN( uint_fast64_t uiA, struct commonNaN *zPtr )
|
||||
void softfloat_f64UIToCommonNaN( struct softfloat_state *state, uint_fast64_t uiA, struct commonNaN *zPtr )
|
||||
{
|
||||
|
||||
if ( softfloat_isSigNaNF64UI( uiA ) ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
}
|
||||
zPtr->sign = uiA>>63;
|
||||
zPtr->v64 = uiA<<12;
|
||||
|
||||
@@ -42,6 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t
|
||||
softfloat_normRoundPackToExtF80(
|
||||
struct softfloat_state *state,
|
||||
bool sign,
|
||||
int_fast32_t exp,
|
||||
uint_fast64_t sig,
|
||||
@@ -66,7 +67,7 @@ extFloat80_t
|
||||
}
|
||||
return
|
||||
softfloat_roundPackToExtF80(
|
||||
sign, exp, sig, sigExtra, roundingPrecision );
|
||||
state, sign, exp, sig, sigExtra, roundingPrecision );
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
struct uint128
|
||||
softfloat_propagateNaNExtF80UI(
|
||||
struct softfloat_state *state,
|
||||
uint_fast16_t uiA64,
|
||||
uint_fast64_t uiA0,
|
||||
uint_fast16_t uiB64,
|
||||
@@ -76,7 +77,7 @@ struct uint128
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( isSigNaNA | isSigNaNB ) {
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
if ( isSigNaNA ) {
|
||||
if ( isSigNaNB ) goto returnLargerMag;
|
||||
if ( isNaNExtF80UI( uiB64, uiB0 ) ) goto returnB;
|
||||
|
||||
@@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t
|
||||
softfloat_roundPackToExtF80(
|
||||
struct softfloat_state *state,
|
||||
bool sign,
|
||||
int_fast32_t exp,
|
||||
uint_fast64_t sig,
|
||||
@@ -59,7 +60,7 @@ extFloat80_t
|
||||
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
roundingMode = softfloat_roundingMode;
|
||||
roundingMode = state->roundingMode;
|
||||
roundNearEven = (roundingMode == softfloat_round_near_even);
|
||||
if ( roundingPrecision == 80 ) goto precision80;
|
||||
if ( roundingPrecision == 64 ) {
|
||||
@@ -87,15 +88,15 @@ extFloat80_t
|
||||
/*----------------------------------------------------------------
|
||||
*----------------------------------------------------------------*/
|
||||
isTiny =
|
||||
(softfloat_detectTininess
|
||||
(state->detectTininess
|
||||
== softfloat_tininess_beforeRounding)
|
||||
|| (exp < 0)
|
||||
|| (sig <= (uint64_t) (sig + roundIncrement));
|
||||
sig = softfloat_shiftRightJam64( sig, 1 - exp );
|
||||
roundBits = sig & roundMask;
|
||||
if ( roundBits ) {
|
||||
if ( isTiny ) softfloat_raiseFlags( softfloat_flag_underflow );
|
||||
softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( isTiny ) softfloat_raiseFlags( state, softfloat_flag_underflow );
|
||||
state->exceptionFlags |= softfloat_flag_inexact;
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) {
|
||||
sig |= roundMask + 1;
|
||||
@@ -121,7 +122,7 @@ extFloat80_t
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( roundBits ) {
|
||||
softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
state->exceptionFlags |= softfloat_flag_inexact;
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) {
|
||||
sig = (sig & ~roundMask) | (roundMask + 1);
|
||||
@@ -157,7 +158,7 @@ extFloat80_t
|
||||
/*----------------------------------------------------------------
|
||||
*----------------------------------------------------------------*/
|
||||
isTiny =
|
||||
(softfloat_detectTininess
|
||||
(state->detectTininess
|
||||
== softfloat_tininess_beforeRounding)
|
||||
|| (exp < 0)
|
||||
|| ! doIncrement
|
||||
@@ -168,8 +169,8 @@ extFloat80_t
|
||||
sig = sig64Extra.v;
|
||||
sigExtra = sig64Extra.extra;
|
||||
if ( sigExtra ) {
|
||||
if ( isTiny ) softfloat_raiseFlags( softfloat_flag_underflow );
|
||||
softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( isTiny ) softfloat_raiseFlags( state, softfloat_flag_underflow );
|
||||
state->exceptionFlags |= softfloat_flag_inexact;
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) {
|
||||
sig |= 1;
|
||||
@@ -207,7 +208,7 @@ extFloat80_t
|
||||
roundMask = 0;
|
||||
overflow:
|
||||
softfloat_raiseFlags(
|
||||
softfloat_flag_overflow | softfloat_flag_inexact );
|
||||
state, softfloat_flag_overflow | softfloat_flag_inexact );
|
||||
if (
|
||||
roundNearEven
|
||||
|| (roundingMode == softfloat_round_near_maxMag)
|
||||
@@ -226,7 +227,7 @@ extFloat80_t
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
if ( sigExtra ) {
|
||||
softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
state->exceptionFlags |= softfloat_flag_inexact;
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) {
|
||||
sig |= 1;
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float32_t
|
||||
softfloat_roundPackToF32( bool sign, int_fast16_t exp, uint_fast32_t sig )
|
||||
softfloat_roundPackToF32( struct softfloat_state *state, bool sign, int_fast16_t exp, uint_fast32_t sig )
|
||||
{
|
||||
uint_fast8_t roundingMode;
|
||||
bool roundNearEven;
|
||||
@@ -53,7 +53,7 @@ float32_t
|
||||
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
roundingMode = softfloat_roundingMode;
|
||||
roundingMode = state->roundingMode;
|
||||
roundNearEven = (roundingMode == softfloat_round_near_even);
|
||||
roundIncrement = 0x40;
|
||||
if ( ! roundNearEven && (roundingMode != softfloat_round_near_maxMag) ) {
|
||||
@@ -71,19 +71,19 @@ float32_t
|
||||
/*----------------------------------------------------------------
|
||||
*----------------------------------------------------------------*/
|
||||
isTiny =
|
||||
(softfloat_detectTininess == softfloat_tininess_beforeRounding)
|
||||
(state->detectTininess == softfloat_tininess_beforeRounding)
|
||||
|| (exp < -1) || (sig + roundIncrement < 0x80000000);
|
||||
sig = softfloat_shiftRightJam32( sig, -exp );
|
||||
exp = 0;
|
||||
roundBits = sig & 0x7F;
|
||||
if ( isTiny && roundBits ) {
|
||||
softfloat_raiseFlags( softfloat_flag_underflow );
|
||||
softfloat_raiseFlags( state, softfloat_flag_underflow );
|
||||
}
|
||||
} else if ( (0xFD < exp) || (0x80000000 <= sig + roundIncrement) ) {
|
||||
/*----------------------------------------------------------------
|
||||
*----------------------------------------------------------------*/
|
||||
softfloat_raiseFlags(
|
||||
softfloat_flag_overflow | softfloat_flag_inexact );
|
||||
state, softfloat_flag_overflow | softfloat_flag_inexact );
|
||||
uiZ = packToF32UI( sign, 0xFF, 0 ) - ! roundIncrement;
|
||||
goto uiZ;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ float32_t
|
||||
*------------------------------------------------------------------------*/
|
||||
sig = (sig + roundIncrement)>>7;
|
||||
if ( roundBits ) {
|
||||
softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
state->exceptionFlags |= softfloat_flag_inexact;
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) {
|
||||
sig |= 1;
|
||||
|
||||
@@ -42,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float64_t
|
||||
softfloat_roundPackToF64( bool sign, int_fast16_t exp, uint_fast64_t sig )
|
||||
softfloat_roundPackToF64( struct softfloat_state *state, bool sign, int_fast16_t exp, uint_fast64_t sig )
|
||||
{
|
||||
uint_fast8_t roundingMode;
|
||||
bool roundNearEven;
|
||||
@@ -53,7 +53,7 @@ float64_t
|
||||
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
roundingMode = softfloat_roundingMode;
|
||||
roundingMode = state->roundingMode;
|
||||
roundNearEven = (roundingMode == softfloat_round_near_even);
|
||||
roundIncrement = 0x200;
|
||||
if ( ! roundNearEven && (roundingMode != softfloat_round_near_maxMag) ) {
|
||||
@@ -71,14 +71,14 @@ float64_t
|
||||
/*----------------------------------------------------------------
|
||||
*----------------------------------------------------------------*/
|
||||
isTiny =
|
||||
(softfloat_detectTininess == softfloat_tininess_beforeRounding)
|
||||
(state->detectTininess == softfloat_tininess_beforeRounding)
|
||||
|| (exp < -1)
|
||||
|| (sig + roundIncrement < UINT64_C( 0x8000000000000000 ));
|
||||
sig = softfloat_shiftRightJam64( sig, -exp );
|
||||
exp = 0;
|
||||
roundBits = sig & 0x3FF;
|
||||
if ( isTiny && roundBits ) {
|
||||
softfloat_raiseFlags( softfloat_flag_underflow );
|
||||
softfloat_raiseFlags( state, softfloat_flag_underflow );
|
||||
}
|
||||
} else if (
|
||||
(0x7FD < exp)
|
||||
@@ -87,7 +87,7 @@ float64_t
|
||||
/*----------------------------------------------------------------
|
||||
*----------------------------------------------------------------*/
|
||||
softfloat_raiseFlags(
|
||||
softfloat_flag_overflow | softfloat_flag_inexact );
|
||||
state, softfloat_flag_overflow | softfloat_flag_inexact );
|
||||
uiZ = packToF64UI( sign, 0x7FF, 0 ) - ! roundIncrement;
|
||||
goto uiZ;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ float64_t
|
||||
*------------------------------------------------------------------------*/
|
||||
sig = (sig + roundIncrement)>>10;
|
||||
if ( roundBits ) {
|
||||
softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
state->exceptionFlags |= softfloat_flag_inexact;
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) {
|
||||
sig |= 1;
|
||||
|
||||
@@ -44,7 +44,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast32_t
|
||||
softfloat_roundToI32(
|
||||
bool sign, uint_fast64_t sig, uint_fast8_t roundingMode, bool exact )
|
||||
struct softfloat_state *state, bool sign, uint_fast64_t sig, uint_fast8_t roundingMode, bool exact )
|
||||
{
|
||||
uint_fast16_t roundIncrement, roundBits;
|
||||
uint_fast32_t sig32;
|
||||
@@ -86,13 +86,13 @@ int_fast32_t
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) z |= 1;
|
||||
#endif
|
||||
if ( exact ) softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( exact ) state->exceptionFlags |= softfloat_flag_inexact;
|
||||
}
|
||||
return z;
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
invalid:
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return sign ? i32_fromNegOverflow : i32_fromPosOverflow;
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast64_t
|
||||
softfloat_roundToI64(
|
||||
struct softfloat_state *state,
|
||||
bool sign,
|
||||
uint_fast64_t sig,
|
||||
uint_fast64_t sigExtra,
|
||||
@@ -89,13 +90,13 @@ int_fast64_t
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) z |= 1;
|
||||
#endif
|
||||
if ( exact ) softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( exact ) state->exceptionFlags |= softfloat_flag_inexact;
|
||||
}
|
||||
return z;
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
invalid:
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return sign ? i64_fromNegOverflow : i64_fromPosOverflow;
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
uint_fast64_t
|
||||
softfloat_roundToUI64(
|
||||
struct softfloat_state *state,
|
||||
bool sign,
|
||||
uint_fast64_t sig,
|
||||
uint_fast64_t sigExtra,
|
||||
@@ -84,13 +85,13 @@ uint_fast64_t
|
||||
#ifdef SOFTFLOAT_ROUND_ODD
|
||||
if ( roundingMode == softfloat_round_odd ) sig |= 1;
|
||||
#endif
|
||||
if ( exact ) softfloat_exceptionFlags |= softfloat_flag_inexact;
|
||||
if ( exact ) state->exceptionFlags |= softfloat_flag_inexact;
|
||||
}
|
||||
return sig;
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
invalid:
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
return sign ? ui64_fromNegOverflow : ui64_fromPosOverflow;
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
extFloat80_t
|
||||
softfloat_subMagsExtF80(
|
||||
struct softfloat_state *state,
|
||||
uint_fast16_t uiA64,
|
||||
uint_fast64_t uiA0,
|
||||
uint_fast16_t uiB64,
|
||||
@@ -77,7 +78,7 @@ extFloat80_t
|
||||
if ( (sigA | sigB) & UINT64_C( 0x7FFFFFFFFFFFFFFF ) ) {
|
||||
goto propagateNaN;
|
||||
}
|
||||
softfloat_raiseFlags( softfloat_flag_invalid );
|
||||
softfloat_raiseFlags( state, softfloat_flag_invalid );
|
||||
uiZ64 = defaultNaNExtF80UI64;
|
||||
uiZ0 = defaultNaNExtF80UI0;
|
||||
goto uiZ;
|
||||
@@ -90,7 +91,7 @@ extFloat80_t
|
||||
if ( sigB < sigA ) goto aBigger;
|
||||
if ( sigA < sigB ) goto bBigger;
|
||||
uiZ64 =
|
||||
packToExtF80UI64( (softfloat_roundingMode == softfloat_round_min), 0 );
|
||||
packToExtF80UI64( (state->roundingMode == softfloat_round_min), 0 );
|
||||
uiZ0 = 0;
|
||||
goto uiZ;
|
||||
/*------------------------------------------------------------------------
|
||||
@@ -142,11 +143,11 @@ extFloat80_t
|
||||
normRoundPack:
|
||||
return
|
||||
softfloat_normRoundPackToExtF80(
|
||||
signZ, expZ, sig128.v64, sig128.v0, extF80_roundingPrecision );
|
||||
state, signZ, expZ, sig128.v64, sig128.v0, state->roundingPrecision );
|
||||
/*------------------------------------------------------------------------
|
||||
*------------------------------------------------------------------------*/
|
||||
propagateNaN:
|
||||
uiZ = softfloat_propagateNaNExtF80UI( uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ = softfloat_propagateNaNExtF80UI( state, uiA64, uiA0, uiB64, uiB0 );
|
||||
uiZ64 = uiZ.v64;
|
||||
uiZ0 = uiZ.v0;
|
||||
uiZ:
|
||||
|
||||
@@ -50,50 +50,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <stdint.h>
|
||||
#include "softfloat_types.h"
|
||||
|
||||
#ifndef THREAD_LOCAL
|
||||
#define THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software floating-point underflow tininess-detection mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
extern THREAD_LOCAL uint_fast8_t softfloat_detectTininess;
|
||||
enum {
|
||||
softfloat_tininess_beforeRounding = 0,
|
||||
softfloat_tininess_afterRounding = 1
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software floating-point rounding mode. (Mode "odd" is supported only if
|
||||
| SoftFloat is compiled with macro 'SOFTFLOAT_ROUND_ODD' defined.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
extern THREAD_LOCAL uint_fast8_t softfloat_roundingMode;
|
||||
enum {
|
||||
softfloat_round_near_even = 0,
|
||||
softfloat_round_minMag = 1,
|
||||
softfloat_round_min = 2,
|
||||
softfloat_round_max = 3,
|
||||
softfloat_round_near_maxMag = 4,
|
||||
softfloat_round_odd = 6
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software floating-point exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
extern THREAD_LOCAL uint_fast8_t softfloat_exceptionFlags;
|
||||
enum {
|
||||
softfloat_flag_inexact = 1,
|
||||
softfloat_flag_underflow = 2,
|
||||
softfloat_flag_overflow = 4,
|
||||
softfloat_flag_infinite = 8,
|
||||
softfloat_flag_invalid = 16
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Routine to raise any or all of the software floating-point exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void softfloat_raiseFlags( uint_fast8_t );
|
||||
void softfloat_raiseFlags( struct softfloat_state *, uint_fast8_t );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Integer-to-floating-point conversion routines.
|
||||
@@ -187,7 +148,7 @@ float16_t f32_to_f16( float32_t );
|
||||
float64_t f32_to_f64( float32_t );
|
||||
#ifdef SOFTFLOAT_FAST_INT64
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t f32_to_extF80( float32_t );
|
||||
extFloat80_t f32_to_extF80( struct softfloat_state *, float32_t );
|
||||
float128_t f32_to_f128( float32_t );
|
||||
#endif
|
||||
void f32_to_extF80M( float32_t, extFloat80_t * );
|
||||
@@ -223,7 +184,7 @@ float16_t f64_to_f16( float64_t );
|
||||
float32_t f64_to_f32( float64_t );
|
||||
#ifdef SOFTFLOAT_FAST_INT64
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t f64_to_extF80( float64_t );
|
||||
extFloat80_t f64_to_extF80( struct softfloat_state *, float64_t );
|
||||
float128_t f64_to_f128( float64_t );
|
||||
#endif
|
||||
void f64_to_extF80M( float64_t, extFloat80_t * );
|
||||
@@ -244,53 +205,47 @@ bool f64_le_quiet( float64_t, float64_t );
|
||||
bool f64_lt_quiet( float64_t, float64_t );
|
||||
bool f64_isSignalingNaN( float64_t );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Rounding precision for 80-bit extended double-precision floating-point.
|
||||
| Valid values are 32, 64, and 80.
|
||||
*----------------------------------------------------------------------------*/
|
||||
extern THREAD_LOCAL uint_fast8_t extF80_roundingPrecision;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| 80-bit extended double-precision floating-point operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#ifdef SOFTFLOAT_FAST_INT64
|
||||
uint_fast32_t extF80_to_ui32( extFloat80_t, uint_fast8_t, bool );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
uint_fast64_t extF80_to_ui64( extFloat80_t, uint_fast8_t, bool );
|
||||
uint_fast64_t extF80_to_ui64( struct softfloat_state *, extFloat80_t, uint_fast8_t, bool );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast32_t extF80_to_i32( extFloat80_t, uint_fast8_t, bool );
|
||||
int_fast32_t extF80_to_i32( struct softfloat_state *, extFloat80_t, uint_fast8_t, bool );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
int_fast64_t extF80_to_i64( extFloat80_t, uint_fast8_t, bool );
|
||||
int_fast64_t extF80_to_i64( struct softfloat_state *, extFloat80_t, uint_fast8_t, bool );
|
||||
uint_fast32_t extF80_to_ui32_r_minMag( extFloat80_t, bool );
|
||||
uint_fast64_t extF80_to_ui64_r_minMag( extFloat80_t, bool );
|
||||
int_fast32_t extF80_to_i32_r_minMag( extFloat80_t, bool );
|
||||
int_fast64_t extF80_to_i64_r_minMag( extFloat80_t, bool );
|
||||
float16_t extF80_to_f16( extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float32_t extF80_to_f32( extFloat80_t );
|
||||
float32_t extF80_to_f32( struct softfloat_state *, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float64_t extF80_to_f64( extFloat80_t );
|
||||
float64_t extF80_to_f64( struct softfloat_state *, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
float128_t extF80_to_f128( extFloat80_t );
|
||||
float128_t extF80_to_f128( struct softfloat_state *, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_roundToInt( extFloat80_t, uint_fast8_t, bool );
|
||||
extFloat80_t extF80_roundToInt( struct softfloat_state *, extFloat80_t, uint_fast8_t, bool );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_add( extFloat80_t, extFloat80_t );
|
||||
extFloat80_t extF80_add( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_sub( extFloat80_t, extFloat80_t );
|
||||
extFloat80_t extF80_sub( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_mul( extFloat80_t, extFloat80_t );
|
||||
extFloat80_t extF80_mul( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_div( extFloat80_t, extFloat80_t );
|
||||
extFloat80_t extF80_div( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_rem( extFloat80_t, extFloat80_t );
|
||||
extFloat80_t extF80_rem( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t extF80_sqrt( extFloat80_t );
|
||||
extFloat80_t extF80_sqrt( struct softfloat_state *, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
bool extF80_eq( extFloat80_t, extFloat80_t );
|
||||
bool extF80_eq( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
bool extF80_le( extFloat80_t, extFloat80_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
bool extF80_lt( extFloat80_t, extFloat80_t );
|
||||
bool extF80_lt( struct softfloat_state *, extFloat80_t, extFloat80_t );
|
||||
bool extF80_eq_signaling( extFloat80_t, extFloat80_t );
|
||||
bool extF80_le_quiet( extFloat80_t, extFloat80_t );
|
||||
bool extF80_lt_quiet( extFloat80_t, extFloat80_t );
|
||||
@@ -341,7 +296,7 @@ float16_t f128_to_f16( float128_t );
|
||||
float32_t f128_to_f32( float128_t );
|
||||
float64_t f128_to_f64( float128_t );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
extFloat80_t f128_to_extF80( float128_t );
|
||||
extFloat80_t f128_to_extF80( struct softfloat_state *, float128_t );
|
||||
float128_t f128_roundToInt( float128_t, uint_fast8_t, bool );
|
||||
float128_t f128_add( float128_t, float128_t );
|
||||
float128_t f128_sub( float128_t, float128_t );
|
||||
|
||||
@@ -44,10 +44,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
| should be simply `softfloat_exceptionFlags |= flags;'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void softfloat_raiseFlags( uint_fast8_t flags )
|
||||
void softfloat_raiseFlags( struct softfloat_state *state, uint_fast8_t flags )
|
||||
{
|
||||
|
||||
softfloat_exceptionFlags |= flags;
|
||||
state->exceptionFlags |= flags;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
/*============================================================================
|
||||
|
||||
This C source file is part of the SoftFloat IEEE Floating-Point Arithmetic
|
||||
Package, Release 3e, by John R. Hauser.
|
||||
|
||||
Copyright 2011, 2012, 2013, 2014, 2015, 2016 The Regents of the University of
|
||||
California. All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions, and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions, and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the University nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS", AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "platform.h"
|
||||
#include "internals.h"
|
||||
#include "specialize.h"
|
||||
#include "softfloat.h"
|
||||
|
||||
#ifndef THREAD_LOCAL
|
||||
#define THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
THREAD_LOCAL uint_fast8_t softfloat_roundingMode = softfloat_round_near_even;
|
||||
THREAD_LOCAL uint_fast8_t softfloat_detectTininess = init_detectTininess;
|
||||
THREAD_LOCAL uint_fast8_t softfloat_exceptionFlags = 0;
|
||||
|
||||
THREAD_LOCAL uint_fast8_t extF80_roundingPrecision = 80;
|
||||
|
||||
@@ -77,5 +77,50 @@ struct extFloat80M { uint16_t signExp; uint64_t signif; };
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef struct extFloat80M extFloat80_t;
|
||||
|
||||
enum {
|
||||
softfloat_tininess_beforeRounding = 0,
|
||||
softfloat_tininess_afterRounding = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
softfloat_round_near_even = 0,
|
||||
softfloat_round_minMag = 1,
|
||||
softfloat_round_min = 2,
|
||||
softfloat_round_max = 3,
|
||||
softfloat_round_near_maxMag = 4,
|
||||
softfloat_round_odd = 6
|
||||
};
|
||||
|
||||
enum {
|
||||
softfloat_flag_inexact = 1,
|
||||
softfloat_flag_underflow = 2,
|
||||
softfloat_flag_overflow = 4,
|
||||
softfloat_flag_infinite = 8,
|
||||
softfloat_flag_invalid = 16
|
||||
};
|
||||
|
||||
struct softfloat_state {
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software floating-point underflow tininess-detection mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
uint8_t detectTininess; /* = init_detectTininess */
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software floating-point rounding mode. (Mode "odd" is supported only if
|
||||
| SoftFloat is compiled with macro 'SOFTFLOAT_ROUND_ODD' defined.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
uint8_t roundingMode; /* = softfloat_round_near_even */
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software floating-point exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
uint8_t exceptionFlags; /* = 0 */
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Rounding precision for 80-bit extended double-precision floating-point.
|
||||
| Valid values are 32, 64, and 80.
|
||||
*----------------------------------------------------------------------------*/
|
||||
uint8_t roundingPrecision; /* = 80 */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -136,7 +136,7 @@ uint_fast16_t
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void softfloat_f32UIToCommonNaN( uint_fast32_t uiA, struct commonNaN *zPtr );
|
||||
void softfloat_f32UIToCommonNaN( struct softfloat_state *, uint_fast32_t uiA, struct commonNaN *zPtr );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Converts the common NaN pointed to by 'aPtr' into a 32-bit floating-point
|
||||
@@ -173,7 +173,7 @@ uint_fast32_t
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void softfloat_f64UIToCommonNaN( uint_fast64_t uiA, struct commonNaN *zPtr );
|
||||
void softfloat_f64UIToCommonNaN( struct softfloat_state *, uint_fast64_t uiA, struct commonNaN *zPtr );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Converts the common NaN pointed to by 'aPtr' into a 64-bit floating-point
|
||||
@@ -222,7 +222,7 @@ uint_fast64_t
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void
|
||||
softfloat_extF80UIToCommonNaN(
|
||||
uint_fast16_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr );
|
||||
struct softfloat_state *, uint_fast16_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Converts the common NaN pointed to by 'aPtr' into an 80-bit extended
|
||||
@@ -244,6 +244,7 @@ struct uint128 softfloat_commonNaNToExtF80UI( const struct commonNaN *aPtr );
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
struct uint128
|
||||
softfloat_propagateNaNExtF80UI(
|
||||
struct softfloat_state *,
|
||||
uint_fast16_t uiA64,
|
||||
uint_fast64_t uiA0,
|
||||
uint_fast16_t uiB64,
|
||||
@@ -274,7 +275,7 @@ struct uint128
|
||||
FEXCORE_PRESERVE_ALL_ATTR
|
||||
void
|
||||
softfloat_f128UIToCommonNaN(
|
||||
uint_fast64_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr );
|
||||
struct softfloat_state *, uint_fast64_t uiA64, uint_fast64_t uiA0, struct commonNaN *zPtr );
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Converts the common NaN pointed to by 'aPtr' into a 128-bit floating-point
|
||||
|
||||
@@ -63,7 +63,7 @@ struct FEX_PACKED X80SoftFloat {
|
||||
}
|
||||
|
||||
// Ops
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FADD(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FADD(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -79,11 +79,11 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_add(lhs, rhs);
|
||||
return extF80_add(state, lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSUB(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSUB(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -99,11 +99,11 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_sub(lhs, rhs);
|
||||
return extF80_sub(state, lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FMUL(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FMUL(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -119,11 +119,11 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_mul(lhs, rhs);
|
||||
return extF80_mul(state, lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FDIV(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FDIV(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -139,11 +139,11 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_div(lhs, rhs);
|
||||
return extF80_div(state, lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FREM(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FREM(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
#if defined(DEBUG_X86_FLOAT)
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -160,11 +160,11 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_rem(lhs, rhs);
|
||||
return extF80_rem(state, lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FREM1(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FREM1(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
#if defined(DEBUG_X86_FLOAT)
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -181,16 +181,16 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_rem(lhs, rhs);
|
||||
return extF80_rem(state, lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FRNDINT(const X80SoftFloat& lhs) {
|
||||
return extF80_roundToInt(lhs, softfloat_roundingMode, false);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FRNDINT(softfloat_state* state, const X80SoftFloat& lhs) {
|
||||
return extF80_roundToInt(state, lhs, state->roundingMode, false);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FRNDINT(const X80SoftFloat& lhs, uint_fast8_t RoundMode) {
|
||||
return extF80_roundToInt(lhs, RoundMode, false);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FRNDINT(softfloat_state* state, const X80SoftFloat& lhs, uint_fast8_t RoundMode) {
|
||||
return extF80_roundToInt(state, lhs, RoundMode, false);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FXTRACT_SIG(const X80SoftFloat& lhs) {
|
||||
@@ -237,13 +237,14 @@ struct FEX_PACKED X80SoftFloat {
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static void FCMP(const X80SoftFloat& lhs, const X80SoftFloat& rhs, bool* eq, bool* lt, bool* nan) {
|
||||
*eq = extF80_eq(lhs, rhs);
|
||||
*lt = extF80_lt(lhs, rhs);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static void
|
||||
FCMP(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs, bool* eq, bool* lt, bool* nan) {
|
||||
*eq = extF80_eq(state, lhs, rhs);
|
||||
*lt = extF80_lt(state, lhs, rhs);
|
||||
*nan = IsNan(lhs) || IsNan(rhs);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSCALE(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSCALE(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
WARN_ONCE_FMT("x87: Application used FSCALE which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -261,16 +262,16 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
X80SoftFloat Int = FRNDINT(rhs, softfloat_round_minMag);
|
||||
LIBRARY_PRECISION Src2_d = Int;
|
||||
X80SoftFloat Int = FRNDINT(state, rhs, softfloat_round_minMag);
|
||||
LIBRARY_PRECISION Src2_d = Int.ToFMax(state);
|
||||
Src2_d = exp2l(Src2_d);
|
||||
X80SoftFloat Src2_X80 = Src2_d;
|
||||
X80SoftFloat Result = extF80_mul(lhs, Src2_X80);
|
||||
X80SoftFloat Src2_X80(state, Src2_d);
|
||||
X80SoftFloat Result = extF80_mul(state, lhs, Src2_X80);
|
||||
return Result;
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat F2XM1(const X80SoftFloat& lhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat F2XM1(softfloat_state* state, const X80SoftFloat& lhs) {
|
||||
WARN_ONCE_FMT("x87: Application used F2XM1 which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -286,14 +287,14 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
LIBRARY_PRECISION Src1_d = lhs;
|
||||
LIBRARY_PRECISION Src1_d = lhs.ToFMax(state);
|
||||
LIBRARY_PRECISION Result = exp2l(Src1_d);
|
||||
Result -= 1.0;
|
||||
return Result;
|
||||
return X80SoftFloat(state, Result);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FYL2X(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FYL2X(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
WARN_ONCE_FMT("x87: Application used FYL2X which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -310,14 +311,14 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
LIBRARY_PRECISION Src1_d = lhs;
|
||||
LIBRARY_PRECISION Src2_d = rhs;
|
||||
LIBRARY_PRECISION Src1_d = lhs.ToFMax(state);
|
||||
LIBRARY_PRECISION Src2_d = rhs.ToFMax(state);
|
||||
LIBRARY_PRECISION Tmp = Src2_d * log2l(Src1_d);
|
||||
return Tmp;
|
||||
return X80SoftFloat(state, Tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FATAN(const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FATAN(softfloat_state* state, const X80SoftFloat& lhs, const X80SoftFloat& rhs) {
|
||||
WARN_ONCE_FMT("x87: Application used FATAN which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -334,14 +335,14 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
LIBRARY_PRECISION Src1_d = lhs;
|
||||
LIBRARY_PRECISION Src2_d = rhs;
|
||||
LIBRARY_PRECISION Src1_d = lhs.ToFMax(state);
|
||||
LIBRARY_PRECISION Src2_d = rhs.ToFMax(state);
|
||||
LIBRARY_PRECISION Tmp = atan2l(Src1_d, Src2_d);
|
||||
return Tmp;
|
||||
return X80SoftFloat(state, Tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FTAN(const X80SoftFloat& lhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FTAN(softfloat_state* state, const X80SoftFloat& lhs) {
|
||||
WARN_ONCE_FMT("x87: Application used FTAN which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -358,13 +359,13 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
LIBRARY_PRECISION Src_d = lhs;
|
||||
LIBRARY_PRECISION Src_d = lhs.ToFMax(state);
|
||||
Src_d = tanl(Src_d);
|
||||
return Src_d;
|
||||
return X80SoftFloat(state, Src_d);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSIN(const X80SoftFloat& lhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSIN(softfloat_state* state, const X80SoftFloat& lhs) {
|
||||
WARN_ONCE_FMT("x87: Application used FSIN which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -380,13 +381,13 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
LIBRARY_PRECISION Src_d = lhs;
|
||||
LIBRARY_PRECISION Src_d = lhs.ToFMax(state);
|
||||
Src_d = sinl(Src_d);
|
||||
return Src_d;
|
||||
return X80SoftFloat(state, Src_d);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FCOS(const X80SoftFloat& lhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FCOS(softfloat_state* state, const X80SoftFloat& lhs) {
|
||||
WARN_ONCE_FMT("x87: Application used FCOS which may have accuracy problems");
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
@@ -402,13 +403,13 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
LIBRARY_PRECISION Src_d = lhs;
|
||||
LIBRARY_PRECISION Src_d = lhs.ToFMax(state);
|
||||
Src_d = cosl(Src_d);
|
||||
return Src_d;
|
||||
return X80SoftFloat(state, Src_d);
|
||||
#endif
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSQRT(const X80SoftFloat& lhs) {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat FSQRT(softfloat_state* state, const X80SoftFloat& lhs) {
|
||||
#ifdef DEBUG_X86_FLOAT
|
||||
BIGFLOAT Result;
|
||||
asm(R"(
|
||||
@@ -423,62 +424,55 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_sqrt(lhs);
|
||||
return extF80_sqrt(state, lhs);
|
||||
#endif
|
||||
}
|
||||
|
||||
operator float() const {
|
||||
const float32_t Result = extF80_to_f32(*this);
|
||||
float ToF32(softfloat_state* state) const {
|
||||
const float32_t Result = extF80_to_f32(state, *this);
|
||||
return FEXCore::BitCast<float>(Result);
|
||||
}
|
||||
|
||||
operator double() const {
|
||||
const float64_t Result = extF80_to_f64(*this);
|
||||
double ToF64(softfloat_state* state) const {
|
||||
const float64_t Result = extF80_to_f64(state, *this);
|
||||
return FEXCore::BitCast<double>(Result);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
operator BIGFLOAT() const {
|
||||
LIBRARY_PRECISION ToFMax(softfloat_state* state) const {
|
||||
#ifdef _WIN32
|
||||
return ToF64(state);
|
||||
#else
|
||||
#if BIGFLOATSIZE == 16
|
||||
const float128_t Result = extF80_to_f128(*this);
|
||||
const float128_t Result = extF80_to_f128(state, *this);
|
||||
return FEXCore::BitCast<BIGFLOAT>(Result);
|
||||
#else
|
||||
BIGFLOAT result {};
|
||||
memcpy(&result, this, sizeof(result));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
operator int16_t() const {
|
||||
auto rv = extF80_to_i32(*this, softfloat_roundingMode, false);
|
||||
if (rv > INT16_MAX) {
|
||||
return INT16_MAX;
|
||||
} else if (rv < INT16_MIN) {
|
||||
int16_t ToI16(softfloat_state* state) const {
|
||||
auto rv = extF80_to_i32(state, *this, state->roundingMode, false);
|
||||
if (rv > INT16_MAX || rv < INT16_MIN) {
|
||||
///< Indefinite value for 16-bit conversions.
|
||||
return INT16_MIN;
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
operator int32_t() const {
|
||||
return extF80_to_i32(*this, softfloat_roundingMode, false);
|
||||
int32_t ToI32(softfloat_state* state) const {
|
||||
return extF80_to_i32(state, *this, state->roundingMode, false);
|
||||
}
|
||||
|
||||
operator int64_t() const {
|
||||
return extF80_to_i64(*this, softfloat_roundingMode, false);
|
||||
int64_t ToI64(softfloat_state* state) const {
|
||||
return extF80_to_i64(state, *this, state->roundingMode, false);
|
||||
}
|
||||
|
||||
operator uint64_t() const {
|
||||
return extF80_to_ui64(*this, softfloat_roundingMode, false);
|
||||
}
|
||||
|
||||
void operator=(const float rhs) {
|
||||
*this = f32_to_extF80(FEXCore::BitCast<float32_t>(rhs));
|
||||
}
|
||||
|
||||
void operator=(const double rhs) {
|
||||
*this = f64_to_extF80(FEXCore::BitCast<float64_t>(rhs));
|
||||
uint64_t ToUI64(softfloat_state* state) const {
|
||||
return extF80_to_ui64(state, *this, state->roundingMode, false);
|
||||
}
|
||||
|
||||
void operator=(const int16_t rhs) {
|
||||
@@ -509,18 +503,18 @@ struct FEX_PACKED X80SoftFloat {
|
||||
Sign = rhs.signExp >> 15;
|
||||
}
|
||||
|
||||
X80SoftFloat(const float rhs) {
|
||||
*this = f32_to_extF80(FEXCore::BitCast<float32_t>(rhs));
|
||||
X80SoftFloat(softfloat_state* state, const float rhs) {
|
||||
*this = f32_to_extF80(state, FEXCore::BitCast<float32_t>(rhs));
|
||||
}
|
||||
|
||||
X80SoftFloat(const double rhs) {
|
||||
*this = f64_to_extF80(FEXCore::BitCast<float64_t>(rhs));
|
||||
X80SoftFloat(softfloat_state* state, const double rhs) {
|
||||
*this = f64_to_extF80(state, FEXCore::BitCast<float64_t>(rhs));
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
X80SoftFloat(BIGFLOAT rhs) {
|
||||
X80SoftFloat(softfloat_state* state, BIGFLOAT rhs) {
|
||||
#if BIGFLOATSIZE == 16
|
||||
*this = f128_to_extF80(FEXCore::BitCast<float128_t>(rhs));
|
||||
*this = f128_to_extF80(state, FEXCore::BitCast<float128_t>(rhs));
|
||||
#else
|
||||
*this = FEXCore::BitCast<long double>(rhs);
|
||||
#endif
|
||||
|
||||
@@ -50,8 +50,6 @@
|
||||
"DISABLESVE": "disablesve",
|
||||
"ENABLEAVX": "enableavx",
|
||||
"DISABLEAVX": "disableavx",
|
||||
"ENABLEAVX2": "enableavx2",
|
||||
"DISABLEAVX2": "disableavx2",
|
||||
"ENABLEAFP": "enableafp",
|
||||
"DISABLEAFP": "disableafp",
|
||||
"ENABLELRCPC": "enablelrcpc",
|
||||
@@ -78,6 +76,8 @@
|
||||
"DISABLECRYPTO": "disablecrypto",
|
||||
"ENABLERPRES": "enablerpres",
|
||||
"DISABLERPRES": "disablerpres",
|
||||
"ENABLESVEBITPERM": "enablesvebitperm",
|
||||
"DISABLESVEBITPERM": "disablesvebitperm",
|
||||
"ENABLEPRESERVEALLABI": "enablepreserveallabi",
|
||||
"DISABLEPRESERVEALLABI": "disablepreserveallabi"
|
||||
},
|
||||
@@ -86,7 +86,6 @@
|
||||
"\toff: Default CPU features queried from CPU features",
|
||||
"\t{enable,disable}sve: Will force enable or disable sve even if the host doesn't support it",
|
||||
"\t{enable,disable}avx: Will force enable or disable avx even if the host doesn't support it",
|
||||
"\t{enable,disable}avx2: Will force enable or disable avx2 even if the host doesn't support it",
|
||||
"\t{enable,disable}afp: Will force enable or disable afp even if the host doesn't support it",
|
||||
"\t{enable,disable}lrcpc: Will force enable or disable lrcpc even if the host doesn't support it",
|
||||
"\t{enable,disable}lrcpc2: Will force enable or disable lrcpc2 even if the host doesn't support it",
|
||||
@@ -100,6 +99,7 @@
|
||||
"\t{enable,disable}flagm2: Will force enable or disable flagm2 even if the host doesn't support it",
|
||||
"\t{enable,disable}crypto: Will force enable or disable crypto extensions even if the host doesn't support it",
|
||||
"\t{enable,disable}rpres: Will force enable or disable rpres even if the host doesn't support it",
|
||||
"\t{enable,disable}svebitperm: Will force enable or disable svebitperm even if the host doesn't support it",
|
||||
"\t{enable,disable}preserveallabi: Will force enable or disable preserve_all abi even if the host doesn't support it"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
#include <FEXCore/Core/HostFeatures.h>
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include "FEXCore/Debug/InternalThreadState.h"
|
||||
|
||||
@@ -18,8 +19,8 @@ void InitializeStaticTables(OperatingMode Mode) {
|
||||
IR::InstallOpcodeHandlers(Mode);
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::Context::Context> FEXCore::Context::Context::CreateNewContext() {
|
||||
return fextl::make_unique<FEXCore::Context::ContextImpl>();
|
||||
fextl::unique_ptr<FEXCore::Context::Context> FEXCore::Context::Context::CreateNewContext(const FEXCore::HostFeatures& Features) {
|
||||
return fextl::make_unique<FEXCore::Context::ContextImpl>(Features);
|
||||
}
|
||||
|
||||
void FEXCore::Context::ContextImpl::SetExitHandler(ExitHandler handler) {
|
||||
@@ -42,10 +43,6 @@ void FEXCore::Context::ContextImpl::SetCustomCPUBackendFactory(CustomCPUFactoryT
|
||||
CustomCPUFactory = std::move(Factory);
|
||||
}
|
||||
|
||||
HostFeatures FEXCore::Context::ContextImpl::GetHostFeatures() const {
|
||||
return HostFeatures;
|
||||
}
|
||||
|
||||
void FEXCore::Context::ContextImpl::SetSignalDelegator(FEXCore::SignalDelegator* _SignalDelegation) {
|
||||
SignalDelegation = _SignalDelegation;
|
||||
}
|
||||
|
||||
@@ -96,14 +96,15 @@ public:
|
||||
|
||||
void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) override;
|
||||
|
||||
HostFeatures GetHostFeatures() const override;
|
||||
|
||||
void HandleCallback(FEXCore::Core::InternalThreadState* Thread, uint64_t RIP) override;
|
||||
|
||||
uint64_t RestoreRIPFromHostPC(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPC) override;
|
||||
uint32_t ReconstructCompactedEFLAGS(FEXCore::Core::InternalThreadState* Thread, bool WasInJIT, uint64_t* HostGPRs, uint64_t PSTATE) override;
|
||||
void SetFlagsFromCompactedEFLAGS(FEXCore::Core::InternalThreadState* Thread, uint32_t EFLAGS) override;
|
||||
|
||||
void ReconstructXMMRegisters(const FEXCore::Core::InternalThreadState* Thread, __uint128_t* XMM_Low, __uint128_t* YMM_High) override;
|
||||
void SetXMMRegistersFromState(FEXCore::Core::InternalThreadState* Thread, const __uint128_t* XMM_Low, const __uint128_t* YMM_High) override;
|
||||
|
||||
/**
|
||||
* @brief Used to create FEX thread objects in preparation for creating a true OS thread. Does set a TID or PID.
|
||||
*
|
||||
@@ -192,7 +193,7 @@ public:
|
||||
// returns false if a handler was already registered
|
||||
CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator = nullptr, void* Data = nullptr);
|
||||
|
||||
void AppendThunkDefinitions(const fextl::vector<FEXCore::IR::ThunkDefinition>& Definitions) override;
|
||||
void AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) override;
|
||||
|
||||
public:
|
||||
friend class FEXCore::HLE::SyscallHandler;
|
||||
@@ -261,7 +262,7 @@ public:
|
||||
SignalDelegator* SignalDelegation {};
|
||||
X86GeneratedCode X86CodeGen;
|
||||
|
||||
ContextImpl();
|
||||
ContextImpl(const FEXCore::HostFeatures& Features);
|
||||
~ContextImpl();
|
||||
|
||||
static void ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP);
|
||||
@@ -280,9 +281,6 @@ public:
|
||||
// Must be called from owning thread
|
||||
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame* Frame, uint64_t GuestRIP) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
LOGMAN_THROW_A_FMT(Thread->ThreadManager.GetTID() == FHU::Syscalls::gettid(), "Must be called from owning thread {}, not {}",
|
||||
Thread->ThreadManager.GetTID(), FHU::Syscalls::gettid());
|
||||
auto lk = GuardSignalDeferringSection(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex, Thread);
|
||||
|
||||
ThreadRemoveCodeEntry(Thread, GuestRIP);
|
||||
@@ -375,8 +373,6 @@ public:
|
||||
return ExitOnHLT;
|
||||
}
|
||||
|
||||
FEXCore::CPU::CPUBackendFeatures BackendFeatures;
|
||||
|
||||
protected:
|
||||
void UpdateAtomicTSOEmulationConfig() {
|
||||
if (SupportsHardwareTSO) {
|
||||
|
||||
@@ -14,20 +14,18 @@
|
||||
#include <CodeEmitter/Emitter.h>
|
||||
#include <CodeEmitter/Registers.h>
|
||||
|
||||
#ifdef VIXL_DISASSEMBLER
|
||||
#include <aarch64/cpu-aarch64.h>
|
||||
#include <aarch64/instructions-aarch64.h>
|
||||
#include <cpu-features.h>
|
||||
#include <utils-vixl.h>
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
// Register x18 is unused in the current configuration.
|
||||
// This is due to it being a platform register on wine platforms.
|
||||
// TODO: Allow x18 register allocation on Linux in the future to gain one more register.
|
||||
|
||||
namespace x64 {
|
||||
#ifndef _M_ARM_64EC
|
||||
// All but x19 and x29 are caller saved
|
||||
@@ -353,8 +351,6 @@ Arm64Emitter::Arm64Emitter(FEXCore::Context::ContextImpl* ctx, void* EmissionPtr
|
||||
}
|
||||
#endif
|
||||
|
||||
CPU.SetUp();
|
||||
|
||||
// Number of register available is dependent on what operating mode the proccess is in.
|
||||
if (EmitterCTX->Config.Is64BitMode()) {
|
||||
StaticRegisters = x64::SRA;
|
||||
@@ -425,7 +421,7 @@ void Arm64Emitter::LoadConstant(ARMEmitter::Size s, ARMEmitter::Register Reg, ui
|
||||
if (RequiredMoveSegments > 1) {
|
||||
// Only try to use this path if the number of segments is > 1.
|
||||
// `movz` is better than `orr` since hardware will rename or merge if possible when `movz` is used.
|
||||
const auto IsImm = vixl::aarch64::Assembler::IsImmLogical(Constant, RegSizeInBits(s));
|
||||
const auto IsImm = ARMEmitter::Emitter::IsImmLogical(Constant, RegSizeInBits(s));
|
||||
if (IsImm) {
|
||||
orr(s, Reg, ARMEmitter::Reg::zr, Constant);
|
||||
if (NOPPad) {
|
||||
@@ -462,7 +458,7 @@ void Arm64Emitter::LoadConstant(ARMEmitter::Size s, ARMEmitter::Register Reg, ui
|
||||
|
||||
// If the aligned offset is within the 4GB window then we can use ADRP+ADD
|
||||
// and the number of move segments more than 1
|
||||
if (RequiredMoveSegments > 1 && vixl::IsInt32(AlignedOffset)) {
|
||||
if (RequiredMoveSegments > 1 && ARMEmitter::Emitter::IsInt32(AlignedOffset)) {
|
||||
// If this is 4k page aligned then we only need ADRP
|
||||
if ((AlignedOffset & 0xFFF) == 0) {
|
||||
adrp(Reg, AlignedOffset >> 12);
|
||||
@@ -470,7 +466,7 @@ void Arm64Emitter::LoadConstant(ARMEmitter::Size s, ARMEmitter::Register Reg, ui
|
||||
// If the constant is within 1MB of PC then we can still use ADR to load in a single instruction
|
||||
// 21-bit signed integer here
|
||||
int64_t SmallOffset = static_cast<int64_t>(Constant) - static_cast<int64_t>(PC);
|
||||
if (vixl::IsInt21(SmallOffset)) {
|
||||
if (ARMEmitter::Emitter::IsInt21(SmallOffset)) {
|
||||
adr(Reg, SmallOffset);
|
||||
} else {
|
||||
// Need to use ADRP + ADD
|
||||
@@ -574,12 +570,53 @@ void Arm64Emitter::PopCalleeSavedRegisters() {
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::FillSpecialRegs(ARMEmitter::Register TmpReg, ARMEmitter::Register TmpReg2, bool SetFIZ, bool SetPredRegs) {
|
||||
#ifndef VIXL_SIMULATOR
|
||||
if (EmitterCTX->HostFeatures.SupportsAFP) {
|
||||
// Enable AFP features when filling JIT state.
|
||||
mrs(TmpReg, ARMEmitter::SystemRegister::FPCR);
|
||||
|
||||
// Enable FPCR.NEP and FPCR.AH
|
||||
// NEP(2): Changes ASIMD scalar instructions to insert in to the lower bits of the destination.
|
||||
// AH(1): Changes NaN behaviour in some instructions. Specifically fmin, fmax.
|
||||
//
|
||||
// Additional interesting AFP bits:
|
||||
// FIZ(0): Flush Inputs to Zero
|
||||
orr(ARMEmitter::Size::i64Bit, TmpReg, TmpReg,
|
||||
(1U << 2) | // NEP
|
||||
(1U << 1)); // AH
|
||||
|
||||
if (SetFIZ) {
|
||||
// Insert MXCSR.DAZ in to FIZ
|
||||
ldr(TmpReg2.W(), STATE.R(), offsetof(FEXCore::Core::CPUState, mxcsr));
|
||||
bfxil(ARMEmitter::Size::i64Bit, TmpReg, TmpReg2, 6, 1);
|
||||
}
|
||||
|
||||
msr(ARMEmitter::SystemRegister::FPCR, TmpReg);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SetPredRegs) {
|
||||
// Set up predicate registers.
|
||||
// We don't bother spilling these in SpillStaticRegs,
|
||||
// since all that matters is we restore them on a fill.
|
||||
// It's not a concern if they get trounced by something else.
|
||||
if (EmitterCTX->HostFeatures.SupportsSVE256) {
|
||||
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_32B, ARMEmitter::PredicatePattern::SVE_VL32);
|
||||
}
|
||||
|
||||
if (EmitterCTX->HostFeatures.SupportsSVE128) {
|
||||
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_16B, ARMEmitter::PredicatePattern::SVE_VL16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint32_t GPRSpillMask, uint32_t FPRSpillMask) {
|
||||
#ifndef VIXL_SIMULATOR
|
||||
if (EmitterCTX->HostFeatures.SupportsAFP) {
|
||||
// Disable AFP features when spilling registers.
|
||||
//
|
||||
// Disable FPCR.NEP and FPCR.AH
|
||||
// Disable FPCR.NEP and FPCR.AH and FPCR.FIZ
|
||||
// NEP(2): Changes ASIMD scalar instructions to insert in to the lower bits of the destination.
|
||||
// AH(1): Changes NaN behaviour in some instructions. Specifically fmin, fmax.
|
||||
// Also interacts with RPRES to change reciprocal/rsqrt precision from 8-bit mantissa to 12-bit.
|
||||
@@ -589,7 +626,8 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint3
|
||||
mrs(TmpReg, ARMEmitter::SystemRegister::FPCR);
|
||||
bic(ARMEmitter::Size::i64Bit, TmpReg, TmpReg,
|
||||
(1U << 2) | // NEP
|
||||
(1U << 1)); // AH
|
||||
(1U << 1) | // AH
|
||||
(1U << 0)); // FIZ
|
||||
msr(ARMEmitter::SystemRegister::FPCR, TmpReg);
|
||||
}
|
||||
#endif
|
||||
@@ -628,7 +666,7 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint3
|
||||
}
|
||||
|
||||
if (FPRs) {
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX) {
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX && EmitterCTX->HostFeatures.SupportsSVE256) {
|
||||
for (size_t i = 0; i < StaticFPRegisters.size(); i++) {
|
||||
const auto Reg = StaticFPRegisters[i];
|
||||
|
||||
@@ -667,37 +705,37 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs, uint3
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRFillMask) {
|
||||
ARMEmitter::Register TmpReg = ARMEmitter::Reg::r0;
|
||||
LOGMAN_THROW_A_FMT(GPRFillMask != 0, "Must fill at least 1 GPR for a temp");
|
||||
[[maybe_unused]] bool FoundRegister {};
|
||||
for (auto Reg : StaticRegisters) {
|
||||
if (((1U << Reg.Idx()) & GPRFillMask)) {
|
||||
TmpReg = Reg;
|
||||
FoundRegister = true;
|
||||
break;
|
||||
void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRFillMask, std::optional<ARMEmitter::Register> OptionalReg,
|
||||
std::optional<ARMEmitter::Register> OptionalReg2) {
|
||||
auto FindTempReg = [this](uint32_t* GPRFillMask) -> std::optional<ARMEmitter::Register> {
|
||||
for (auto Reg : StaticRegisters) {
|
||||
if (((1U << Reg.Idx()) & *GPRFillMask)) {
|
||||
*GPRFillMask &= ~(1U << Reg.Idx());
|
||||
return std::make_optional(Reg);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
LOGMAN_THROW_A_FMT(GPRFillMask != 0, "Must fill at least 2 GPRs for a temp");
|
||||
uint32_t TempGPRFillMask = GPRFillMask;
|
||||
if (!OptionalReg.has_value()) {
|
||||
OptionalReg = FindTempReg(&TempGPRFillMask);
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(FoundRegister, "Didn't have an SRA register to use as a temporary while spilling!");
|
||||
|
||||
#ifndef VIXL_SIMULATOR
|
||||
if (EmitterCTX->HostFeatures.SupportsAFP) {
|
||||
// Enable AFP features when filling JIT state.
|
||||
LOGMAN_THROW_A_FMT(GPRFillMask != 0, "Must fill at least 1 GPR for a temp");
|
||||
mrs(TmpReg, ARMEmitter::SystemRegister::FPCR);
|
||||
|
||||
// Enable FPCR.NEP and FPCR.AH
|
||||
// NEP(2): Changes ASIMD scalar instructions to insert in to the lower bits of the destination.
|
||||
// AH(1): Changes NaN behaviour in some instructions. Specifically fmin, fmax.
|
||||
//
|
||||
// Additional interesting AFP bits:
|
||||
// FIZ(0): Flush Inputs to Zero
|
||||
orr(ARMEmitter::Size::i64Bit, TmpReg, TmpReg,
|
||||
(1U << 2) | // NEP
|
||||
(1U << 1)); // AH
|
||||
msr(ARMEmitter::SystemRegister::FPCR, TmpReg);
|
||||
if (!OptionalReg2.has_value()) {
|
||||
OptionalReg2 = FindTempReg(&TempGPRFillMask);
|
||||
}
|
||||
LOGMAN_THROW_A_FMT(OptionalReg.has_value() && OptionalReg2.has_value(), "Didn't have an SRA register to use as a temporary while "
|
||||
"spilling!");
|
||||
|
||||
auto TmpReg = *OptionalReg;
|
||||
auto TmpReg2 = *OptionalReg2;
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
// Load STATE in from the CPU area as x28 is not callee saved in the ARM64EC ABI.
|
||||
ldr(TmpReg.X(), ARMEmitter::Reg::r18, TEB_CPU_AREA_OFFSET);
|
||||
ldr(STATE, TmpReg, CPU_AREA_EMULATOR_DATA_OFFSET);
|
||||
#endif
|
||||
|
||||
// Regardless of what GPRs/FPRs we're filling, we need to fill NZCV since it
|
||||
@@ -708,18 +746,10 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
ldr(TmpReg.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24]));
|
||||
msr(ARMEmitter::SystemRegister::NZCV, TmpReg);
|
||||
|
||||
FillSpecialRegs(TmpReg, TmpReg2, true, FPRs);
|
||||
|
||||
if (FPRs) {
|
||||
// Set up predicate registers.
|
||||
// We don't bother spilling these in SpillStaticRegs,
|
||||
// since all that matters is we restore them on a fill.
|
||||
// It's not a concern if they get trounced by something else.
|
||||
if (EmitterCTX->HostFeatures.SupportsSVE) {
|
||||
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_16B, ARMEmitter::PredicatePattern::SVE_VL16);
|
||||
}
|
||||
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX) {
|
||||
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_32B, ARMEmitter::PredicatePattern::SVE_VL32);
|
||||
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX && EmitterCTX->HostFeatures.SupportsSVE256) {
|
||||
for (size_t i = 0; i < StaticFPRegisters.size(); i++) {
|
||||
const auto Reg = StaticFPRegisters[i];
|
||||
if (((1U << Reg.Idx()) & FPRFillMask) != 0) {
|
||||
@@ -783,8 +813,8 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::PushVectorRegisters(ARMEmitter::Register TmpReg, bool SVERegs, std::span<const ARMEmitter::VRegister> VRegs) {
|
||||
if (SVERegs) {
|
||||
void Arm64Emitter::PushVectorRegisters(ARMEmitter::Register TmpReg, bool SVE256Regs, std::span<const ARMEmitter::VRegister> VRegs) {
|
||||
if (SVE256Regs) {
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < (VRegs.size() % 4); i += 2) {
|
||||
@@ -834,8 +864,8 @@ void Arm64Emitter::PushGeneralRegisters(ARMEmitter::Register TmpReg, std::span<c
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::PopVectorRegisters(bool SVERegs, std::span<const ARMEmitter::VRegister> VRegs) {
|
||||
if (SVERegs) {
|
||||
void Arm64Emitter::PopVectorRegisters(bool SVE256Regs, std::span<const ARMEmitter::VRegister> VRegs) {
|
||||
if (SVE256Regs) {
|
||||
size_t i = 0;
|
||||
for (; i < (VRegs.size() % 4); i += 2) {
|
||||
const auto Reg1 = VRegs[i];
|
||||
@@ -884,9 +914,9 @@ void Arm64Emitter::PopGeneralRegisters(std::span<const ARMEmitter::Register> Reg
|
||||
}
|
||||
|
||||
void Arm64Emitter::PushDynamicRegsAndLR(ARMEmitter::Register TmpReg) {
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
|
||||
const auto GPRSize = (ConfiguredDynamicRegisterBase.size() + 1) * Core::CPUState::GPR_REG_SIZE;
|
||||
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto FPRRegSize = CanUseSVE256 ? 32 : 16;
|
||||
const auto FPRSize = GeneralFPRegisters.size() * FPRRegSize;
|
||||
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
|
||||
|
||||
@@ -898,7 +928,7 @@ void Arm64Emitter::PushDynamicRegsAndLR(ARMEmitter::Register TmpReg) {
|
||||
LOGMAN_THROW_A_FMT(GeneralFPRegisters.size() % 2 == 0, "Needs to have multiple of 2 FPRs for RA");
|
||||
|
||||
// Push the vector registers
|
||||
PushVectorRegisters(TmpReg, CanUseSVE, GeneralFPRegisters);
|
||||
PushVectorRegisters(TmpReg, CanUseSVE256, GeneralFPRegisters);
|
||||
|
||||
// Push the general registers.
|
||||
PushGeneralRegisters(TmpReg, ConfiguredDynamicRegisterBase);
|
||||
@@ -909,10 +939,10 @@ void Arm64Emitter::PushDynamicRegsAndLR(ARMEmitter::Register TmpReg) {
|
||||
}
|
||||
|
||||
void Arm64Emitter::PopDynamicRegsAndLR() {
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
|
||||
|
||||
// Pop vectors first
|
||||
PopVectorRegisters(CanUseSVE, GeneralFPRegisters);
|
||||
PopVectorRegisters(CanUseSVE256, GeneralFPRegisters);
|
||||
|
||||
// Pop GPRs second
|
||||
PopGeneralRegisters(ConfiguredDynamicRegisterBase);
|
||||
@@ -923,8 +953,8 @@ void Arm64Emitter::PopDynamicRegsAndLR() {
|
||||
}
|
||||
|
||||
void Arm64Emitter::SpillForPreserveAllABICall(ARMEmitter::Register TmpReg, bool FPRs) {
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
|
||||
const auto FPRRegSize = CanUseSVE256 ? 32 : 16;
|
||||
|
||||
std::span<const ARMEmitter::Register> DynamicGPRs {};
|
||||
std::span<const ARMEmitter::VRegister> DynamicFPRs {};
|
||||
@@ -936,7 +966,7 @@ void Arm64Emitter::SpillForPreserveAllABICall(ARMEmitter::Register TmpReg, bool
|
||||
PreserveSRAMask = x64::PreserveAll_SRAMask;
|
||||
PreserveSRAFPRMask = x64::PreserveAll_SRAFPRMask;
|
||||
|
||||
if (CanUseSVE) {
|
||||
if (CanUseSVE256) {
|
||||
DynamicFPRs = x64::PreserveAll_DynamicFPRSVE;
|
||||
PreserveSRAFPRMask = x64::PreserveAll_SRAFPRSVEMask;
|
||||
}
|
||||
@@ -946,7 +976,7 @@ void Arm64Emitter::SpillForPreserveAllABICall(ARMEmitter::Register TmpReg, bool
|
||||
PreserveSRAMask = x32::PreserveAll_SRAMask;
|
||||
PreserveSRAFPRMask = x32::PreserveAll_SRAFPRMask;
|
||||
|
||||
if (CanUseSVE) {
|
||||
if (CanUseSVE256) {
|
||||
DynamicFPRs = x32::PreserveAll_DynamicFPRSVE;
|
||||
PreserveSRAFPRMask = x32::PreserveAll_SRAFPRSVEMask;
|
||||
}
|
||||
@@ -965,14 +995,14 @@ void Arm64Emitter::SpillForPreserveAllABICall(ARMEmitter::Register TmpReg, bool
|
||||
add(ARMEmitter::Size::i64Bit, TmpReg, ARMEmitter::Reg::rsp, 0);
|
||||
|
||||
// Push the vector registers.
|
||||
PushVectorRegisters(TmpReg, CanUseSVE, DynamicFPRs);
|
||||
PushVectorRegisters(TmpReg, CanUseSVE256, DynamicFPRs);
|
||||
|
||||
// Push the general registers.
|
||||
PushGeneralRegisters(TmpReg, DynamicGPRs);
|
||||
}
|
||||
|
||||
void Arm64Emitter::FillForPreserveAllABICall(bool FPRs) {
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
|
||||
|
||||
std::span<const ARMEmitter::Register> DynamicGPRs {};
|
||||
std::span<const ARMEmitter::VRegister> DynamicFPRs {};
|
||||
@@ -985,7 +1015,7 @@ void Arm64Emitter::FillForPreserveAllABICall(bool FPRs) {
|
||||
PreserveSRAMask = x64::PreserveAll_SRAMask;
|
||||
PreserveSRAFPRMask = x64::PreserveAll_SRAFPRMask;
|
||||
|
||||
if (CanUseSVE) {
|
||||
if (CanUseSVE256) {
|
||||
DynamicFPRs = x64::PreserveAll_DynamicFPRSVE;
|
||||
PreserveSRAFPRMask = x64::PreserveAll_SRAFPRSVEMask;
|
||||
}
|
||||
@@ -995,7 +1025,7 @@ void Arm64Emitter::FillForPreserveAllABICall(bool FPRs) {
|
||||
PreserveSRAMask = x32::PreserveAll_SRAMask;
|
||||
PreserveSRAFPRMask = x32::PreserveAll_SRAFPRMask;
|
||||
|
||||
if (CanUseSVE) {
|
||||
if (CanUseSVE256) {
|
||||
DynamicFPRs = x32::PreserveAll_DynamicFPRSVE;
|
||||
PreserveSRAFPRMask = x32::PreserveAll_SRAFPRSVEMask;
|
||||
}
|
||||
@@ -1005,7 +1035,7 @@ void Arm64Emitter::FillForPreserveAllABICall(bool FPRs) {
|
||||
FillStaticRegs(true, PreserveSRAMask, PreserveSRAFPRMask);
|
||||
|
||||
// Pop the vector registers.
|
||||
PopVectorRegisters(CanUseSVE, DynamicFPRs);
|
||||
PopVectorRegisters(CanUseSVE256, DynamicFPRs);
|
||||
|
||||
// Pop the general registers.
|
||||
PopGeneralRegisters(DynamicGPRs);
|
||||
|
||||
@@ -70,6 +70,14 @@ constexpr auto VTMP2 = ARMEmitter::VReg::v17;
|
||||
// Entry/Exit ABI
|
||||
constexpr auto EC_CALL_CHECKER_PC_REG = ARMEmitter::XReg::x9;
|
||||
constexpr auto EC_ENTRY_CPUAREA_REG = ARMEmitter::XReg::x17;
|
||||
|
||||
// These structures are not included in the standard Windows headers, define the offsets of members we care about for EC here.
|
||||
constexpr size_t TEB_CPU_AREA_OFFSET = 0x1788;
|
||||
constexpr size_t TEB_PEB_OFFSET = 0x60;
|
||||
constexpr size_t PEB_EC_CODE_BITMAP_OFFSET = 0x368;
|
||||
constexpr size_t CPU_AREA_IN_SYSCALL_CALLBACK_OFFSET = 0x1;
|
||||
constexpr size_t CPU_AREA_EMULATOR_STACK_BASE_OFFSET = 0x8;
|
||||
constexpr size_t CPU_AREA_EMULATOR_DATA_OFFSET = 0x30;
|
||||
#endif
|
||||
|
||||
// Predicate register temporaries (used when AVX support is enabled)
|
||||
@@ -86,7 +94,6 @@ protected:
|
||||
Arm64Emitter(FEXCore::Context::ContextImpl* ctx, void* EmissionPtr = nullptr, size_t size = 0);
|
||||
|
||||
FEXCore::Context::ContextImpl* EmitterCTX;
|
||||
vixl::aarch64::CPU CPU;
|
||||
|
||||
std::span<const ARMEmitter::Register> ConfiguredDynamicRegisterBase {};
|
||||
std::span<const ARMEmitter::Register> StaticRegisters {};
|
||||
@@ -97,12 +104,15 @@ protected:
|
||||
|
||||
void LoadConstant(ARMEmitter::Size s, ARMEmitter::Register Reg, uint64_t Constant, bool NOPPad = false);
|
||||
|
||||
void FillSpecialRegs(ARMEmitter::Register TmpReg, ARMEmitter::Register TmpReg2, bool SetFIZ, bool SetPredRegs);
|
||||
|
||||
// NOTE: These functions WILL clobber the register TMP4 if AVX support is enabled
|
||||
// and FPRs are being spilled or filled. If only GPRs are spilled/filled, then
|
||||
// TMP4 is left alone.
|
||||
void SpillStaticRegs(ARMEmitter::Register TmpReg, bool FPRs = true, uint32_t GPRSpillMask = ~0U, uint32_t FPRSpillMask = ~0U);
|
||||
void FillStaticRegs(bool FPRs = true, uint32_t GPRFillMask = ~0U, uint32_t FPRFillMask = ~0U);
|
||||
void FillStaticRegs(bool FPRs = true, uint32_t GPRFillMask = ~0U, uint32_t FPRFillMask = ~0U,
|
||||
std::optional<ARMEmitter::Register> OptionalReg = std::nullopt,
|
||||
std::optional<ARMEmitter::Register> OptionalReg2 = std::nullopt);
|
||||
|
||||
// Register 0-18 + 29 + 30 are caller saved
|
||||
static constexpr uint32_t CALLER_GPR_MASK = 0b0110'0000'0000'0111'1111'1111'1111'1111U;
|
||||
|
||||
@@ -19,6 +19,10 @@ namespace CPU {
|
||||
{0x0000'0000'8000'0000ULL, 0x0000'0000'8000'0000ULL}, // NAMED_VECTOR_PADDSUBPS_INVERT_UPPER
|
||||
{0x8000'0000'0000'0000ULL, 0x0000'0000'0000'0000ULL}, // NAMED_VECTOR_PADDSUBPD_INVERT
|
||||
{0x8000'0000'0000'0000ULL, 0x0000'0000'0000'0000ULL}, // NAMED_VECTOR_PADDSUBPD_INVERT_UPPER
|
||||
{0x8000'0000'0000'0000ULL, 0x8000'0000'0000'0000ULL}, // NAMED_VECTOR_PSUBADDPS_INVERT
|
||||
{0x8000'0000'0000'0000ULL, 0x8000'0000'0000'0000ULL}, // NAMED_VECTOR_PSUBADDPS_INVERT_UPPER
|
||||
{0x0000'0000'0000'0000ULL, 0x8000'0000'0000'0000ULL}, // NAMED_VECTOR_PSUBADDPD_INVERT
|
||||
{0x0000'0000'0000'0000ULL, 0x8000'0000'0000'0000ULL}, // NAMED_VECTOR_PSUBADDPD_INVERT_UPPER
|
||||
{0x0000'0001'0000'0000ULL, 0x0000'0003'0000'0002ULL}, // NAMED_VECTOR_MOVMSKPS_SHIFT
|
||||
{0x040B'0E01'0B0E'0104ULL, 0x0C03'0609'0306'090CULL}, // NAMED_VECTOR_AESKEYGENASSIST_SWIZZLE
|
||||
{0x0706'0504'FFFF'FFFFULL, 0xFFFF'FFFF'0B0A'0908ULL}, // NAMED_VECTOR_BLENDPS_0110B
|
||||
|
||||
@@ -34,12 +34,6 @@ namespace CodeSerialize {
|
||||
}
|
||||
|
||||
namespace CPU {
|
||||
struct CPUBackendFeatures {
|
||||
bool SupportsFlags = false;
|
||||
bool SupportsSaturatingRoundingShifts = false;
|
||||
bool SupportsVTBL2 = false;
|
||||
};
|
||||
|
||||
class CPUBackend {
|
||||
public:
|
||||
struct CodeBuffer {
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace ProductNames {
|
||||
static const char ARM_A76AE[] = "Cortex-A76AE";
|
||||
static const char ARM_V1[] = "Neoverse V1";
|
||||
static const char ARM_V2[] = "Neoverse V2";
|
||||
static const char ARM_V3[] = "Neoverse V3";
|
||||
static const char ARM_V3AE[] = "Neoverse V3AE";
|
||||
static const char ARM_A77[] = "Cortex-A77";
|
||||
static const char ARM_A78[] = "Cortex-A78";
|
||||
static const char ARM_A78AE[] = "Cortex-A78AE";
|
||||
@@ -41,13 +43,16 @@ namespace ProductNames {
|
||||
static const char ARM_A710[] = "Cortex-A710";
|
||||
static const char ARM_A715[] = "Cortex-A715";
|
||||
static const char ARM_A720[] = "Cortex-A720";
|
||||
static const char ARM_A725[] = "Cortex-A725";
|
||||
static const char ARM_X1[] = "Cortex-X1";
|
||||
static const char ARM_X1C[] = "Cortex-X1C";
|
||||
static const char ARM_X2[] = "Cortex-X2";
|
||||
static const char ARM_X3[] = "Cortex-X3";
|
||||
static const char ARM_X4[] = "Cortex-X4";
|
||||
static const char ARM_X925[] = "Cortex-X925";
|
||||
static const char ARM_N1[] = "Neoverse N1";
|
||||
static const char ARM_N2[] = "Neoverse N2";
|
||||
static const char ARM_N3[] = "Neoverse N3";
|
||||
static const char ARM_E1[] = "Neoverse E1";
|
||||
static const char ARM_A35[] = "Cortex-A35";
|
||||
static const char ARM_A53[] = "Cortex-A53";
|
||||
@@ -142,12 +147,17 @@ void CPUIDEmu::SetupHostHybridFlag() {
|
||||
// CPU priority order
|
||||
// This is mostly arbitrary but will sort by some sort of CPU priority by performance
|
||||
// Relative list so things they will commonly end up in big.little configurations sort of relate
|
||||
static constexpr std::array<CPUMIDR, 43> CPUMIDRs = {{
|
||||
static constexpr std::array<CPUMIDR, 48> CPUMIDRs = {{
|
||||
// Typically big CPU cores
|
||||
{0x51, 0x001, 1, ProductNames::ARM_ORYON_1}, // Qualcomm Oryon-1
|
||||
|
||||
{0x61, 0x023, 1, ProductNames::ARM_Firestorm}, // Apple M1 Firestorm
|
||||
|
||||
{0x41, 0xd85, 1, ProductNames::ARM_X925}, // X925
|
||||
{0x41, 0xd87, 1, ProductNames::ARM_A725}, // A725
|
||||
{0x41, 0xd84, 1, ProductNames::ARM_V3}, // V3
|
||||
{0x41, 0xd83, 1, ProductNames::ARM_V3AE}, // V3AE
|
||||
{0x41, 0xd8e, 1, ProductNames::ARM_N3}, // N3
|
||||
{0x41, 0xd82, 1, ProductNames::ARM_X4}, // X4
|
||||
{0x41, 0xd81, 1, ProductNames::ARM_A720}, // A720
|
||||
{0x41, 0xd4e, 1, ProductNames::ARM_X3}, // X3
|
||||
@@ -347,8 +357,7 @@ void CPUIDEmu::SetupHostHybridFlag() {}
|
||||
|
||||
|
||||
void CPUIDEmu::SetupFeatures() {
|
||||
// TODO: Enable once AVX is supported.
|
||||
if (false && CTX->HostFeatures.SupportsAVX) {
|
||||
if (CTX->HostFeatures.SupportsAVX) {
|
||||
XCR0 |= XCR0_AVX;
|
||||
}
|
||||
|
||||
@@ -417,7 +426,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) const {
|
||||
(1 << 9) | // SSSE3
|
||||
(0 << 10) | // L1 context ID
|
||||
(0 << 11) | // Silicon debug
|
||||
(0 << 12) | // FMA3
|
||||
(SupportsAVX() << 12) | // FMA3
|
||||
(1 << 13) | // CMPXCHG16B
|
||||
(0 << 14) | // xTPR update control
|
||||
(0 << 15) | // Perfmon and debug capability
|
||||
@@ -434,7 +443,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) const {
|
||||
(SupportsAVX() << 26) | // XSAVE
|
||||
(SupportsAVX() << 27) | // OSXSAVE
|
||||
(SupportsAVX() << 28) | // AVX
|
||||
(0 << 29) | // F16C
|
||||
(SupportsAVX() << 29) | // F16C
|
||||
(CTX->HostFeatures.SupportsRAND << 30) | // RDRAND
|
||||
(Hypervisor << 31);
|
||||
|
||||
@@ -601,6 +610,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const {
|
||||
// This is due to LRCPC performance on Cortex being abysmal.
|
||||
// Only enable EnhancedREPMOVS if SoftwareTSO isn't required OR if MemcpySetTSO is not enabled.
|
||||
const uint32_t SupportsEnhancedREPMOVS = CTX->SoftwareTSORequired() == false || MemcpySetTSOEnabled() == false;
|
||||
const uint32_t SupportsVPCLMULQDQ = CTX->HostFeatures.SupportsPMULL_128Bit && SupportsAVX();
|
||||
|
||||
// Number of subfunctions
|
||||
Res.eax = 0x0;
|
||||
@@ -609,7 +619,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const {
|
||||
(0 << 2) | // SGX
|
||||
(SupportsAVX() << 3) | // BMI1
|
||||
(0 << 4) | // Intel Hardware Lock Elison
|
||||
(0 << 5) | // AVX2 support
|
||||
(SupportsAVX() << 5) | // AVX2 support
|
||||
(1 << 6) | // FPU data pointer updated only on exception
|
||||
(1 << 7) | // SMEP support
|
||||
(SupportsAVX() << 8) | // BMI2
|
||||
@@ -628,7 +638,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const {
|
||||
(0 << 21) | // Reserved
|
||||
(0 << 22) | // Reserved
|
||||
(1 << 23) | // CLFLUSHOPT instruction
|
||||
(CTX->HostFeatures.SupportsCLWB << 24) | // CLWB instruction
|
||||
(1 << 24) | // CLWB instruction
|
||||
(0 << 25) | // Intel processor trace
|
||||
(0 << 26) | // Reserved
|
||||
(0 << 27) | // Reserved
|
||||
@@ -637,38 +647,38 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const {
|
||||
(0 << 30) | // Reserved
|
||||
(0 << 31); // Reserved
|
||||
|
||||
Res.ecx = (1 << 0) | // PREFETCHWT1
|
||||
(0 << 1) | // AVX512VBMI
|
||||
(0 << 2) | // Usermode instruction prevention
|
||||
(0 << 3) | // Protection keys for user mode pages
|
||||
(0 << 4) | // OS protection keys
|
||||
(0 << 5) | // waitpkg
|
||||
(0 << 6) | // AVX512_VBMI2
|
||||
(0 << 7) | // CET shadow stack
|
||||
(0 << 8) | // GFNI
|
||||
(0 << 9) | // VAES
|
||||
(0 << 10) | // VPCLMULQDQ
|
||||
(0 << 11) | // AVX512_VNNI
|
||||
(0 << 12) | // AVX512_BITALG
|
||||
(0 << 13) | // Intel Total Memory Encryption
|
||||
(0 << 14) | // AVX512_VPOPCNTDQ
|
||||
(0 << 15) | // Reserved
|
||||
(0 << 16) | // 5 Level page tables
|
||||
(0 << 17) | // MPX MAWAU
|
||||
(0 << 18) | // MPX MAWAU
|
||||
(0 << 19) | // MPX MAWAU
|
||||
(0 << 20) | // MPX MAWAU
|
||||
(0 << 21) | // MPX MAWAU
|
||||
(1 << 22) | // RDPID Read Processor ID
|
||||
(0 << 23) | // Reserved
|
||||
(0 << 24) | // Reserved
|
||||
(0 << 25) | // CLDEMOTE
|
||||
(0 << 26) | // Reserved
|
||||
(0 << 27) | // MOVDIRI
|
||||
(0 << 28) | // MOVDIR64B
|
||||
(0 << 29) | // Reserved
|
||||
(0 << 30) | // SGX Launch configuration
|
||||
(0 << 31); // Reserved
|
||||
Res.ecx = (1 << 0) | // PREFETCHWT1
|
||||
(0 << 1) | // AVX512VBMI
|
||||
(0 << 2) | // Usermode instruction prevention
|
||||
(0 << 3) | // Protection keys for user mode pages
|
||||
(0 << 4) | // OS protection keys
|
||||
(0 << 5) | // waitpkg
|
||||
(0 << 6) | // AVX512_VBMI2
|
||||
(0 << 7) | // CET shadow stack
|
||||
(0 << 8) | // GFNI
|
||||
(CTX->HostFeatures.SupportsAES256 << 9) | // VAES
|
||||
(SupportsVPCLMULQDQ << 10) | // VPCLMULQDQ
|
||||
(0 << 11) | // AVX512_VNNI
|
||||
(0 << 12) | // AVX512_BITALG
|
||||
(0 << 13) | // Intel Total Memory Encryption
|
||||
(0 << 14) | // AVX512_VPOPCNTDQ
|
||||
(0 << 15) | // Reserved
|
||||
(0 << 16) | // 5 Level page tables
|
||||
(0 << 17) | // MPX MAWAU
|
||||
(0 << 18) | // MPX MAWAU
|
||||
(0 << 19) | // MPX MAWAU
|
||||
(0 << 20) | // MPX MAWAU
|
||||
(0 << 21) | // MPX MAWAU
|
||||
(1 << 22) | // RDPID Read Processor ID
|
||||
(0 << 23) | // Reserved
|
||||
(0 << 24) | // Reserved
|
||||
(0 << 25) | // CLDEMOTE
|
||||
(0 << 26) | // Reserved
|
||||
(0 << 27) | // MOVDIRI
|
||||
(0 << 28) | // MOVDIR64B
|
||||
(0 << 29) | // Reserved
|
||||
(0 << 30) | // SGX Launch configuration
|
||||
(0 << 31); // Reserved
|
||||
|
||||
Res.edx = (0 << 0) | // Reserved
|
||||
(0 << 1) | // Reserved
|
||||
@@ -887,7 +897,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0001h(uint32_t Leaf) con
|
||||
(0 << 18) | // Reserved
|
||||
(0 << 19) | // Reserved
|
||||
(0 << 20) | // Reserved
|
||||
(0 << 21) | // Reserved
|
||||
(0 << 21) | // XOP-TBM
|
||||
(0 << 22) | // Topology extensions support
|
||||
(0 << 23) | // Core performance counter extensions
|
||||
(0 << 24) | // NB performance counter extensions
|
||||
@@ -895,7 +905,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0001h(uint32_t Leaf) con
|
||||
(0 << 26) | // Data breakpoints extensions
|
||||
(0 << 27) | // Performance TSC
|
||||
(0 << 28) | // L2 perf counter extensions
|
||||
(0 << 29) | // Reserved
|
||||
(0 << 29) | // MONITORX
|
||||
(0 << 30) | // Reserved
|
||||
(0 << 31); // Reserved
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ $end_info$
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
#include <FEXCore/HLE/SourcecodeResolver.h>
|
||||
#include <FEXCore/HLE/Linux/ThreadManagement.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <FEXCore/Utils/File.h>
|
||||
@@ -75,8 +74,9 @@ $end_info$
|
||||
#include <xxhash.h>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
ContextImpl::ContextImpl()
|
||||
: CPUID {this}
|
||||
ContextImpl::ContextImpl(const FEXCore::HostFeatures& Features)
|
||||
: HostFeatures {Features}
|
||||
, CPUID {this}
|
||||
, IRCaptureCache {this} {
|
||||
#ifdef BLOCKSTATS
|
||||
BlockData = std::make_unique<FEXCore::BlockSamplingData>();
|
||||
@@ -216,6 +216,55 @@ uint32_t ContextImpl::ReconstructCompactedEFLAGS(FEXCore::Core::InternalThreadSt
|
||||
return EFLAGS;
|
||||
}
|
||||
|
||||
void ContextImpl::ReconstructXMMRegisters(const FEXCore::Core::InternalThreadState* Thread, __uint128_t* XMM_Low, __uint128_t* YMM_High) {
|
||||
const size_t MaximumRegisters = Config.Is64BitMode ? FEXCore::Core::CPUState::NUM_XMMS : 8;
|
||||
|
||||
if (YMM_High != nullptr && HostFeatures.SupportsAVX) {
|
||||
const bool SupportsConvergedRegisters = HostFeatures.SupportsSVE256;
|
||||
|
||||
if (SupportsConvergedRegisters) {
|
||||
///< Output wants to de-interleave
|
||||
for (size_t i = 0; i < MaximumRegisters; ++i) {
|
||||
memcpy(&XMM_Low[i], &Thread->CurrentFrame->State.xmm.avx.data[i][0], sizeof(__uint128_t));
|
||||
memcpy(&YMM_High[i], &Thread->CurrentFrame->State.xmm.avx.data[i][2], sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
///< Matches what FEX wants with non-converged registers
|
||||
for (size_t i = 0; i < MaximumRegisters; ++i) {
|
||||
memcpy(&XMM_Low[i], &Thread->CurrentFrame->State.xmm.sse.data[i][0], sizeof(__uint128_t));
|
||||
memcpy(&YMM_High[i], &Thread->CurrentFrame->State.avx_high[i][0], sizeof(__uint128_t));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only support SSE, no AVX here, even if requested.
|
||||
memcpy(XMM_Low, Thread->CurrentFrame->State.xmm.sse.data, MaximumRegisters * sizeof(__uint128_t));
|
||||
}
|
||||
}
|
||||
|
||||
void ContextImpl::SetXMMRegistersFromState(FEXCore::Core::InternalThreadState* Thread, const __uint128_t* XMM_Low, const __uint128_t* YMM_High) {
|
||||
const size_t MaximumRegisters = Config.Is64BitMode ? FEXCore::Core::CPUState::NUM_XMMS : 8;
|
||||
if (YMM_High != nullptr && HostFeatures.SupportsAVX) {
|
||||
const bool SupportsConvergedRegisters = HostFeatures.SupportsSVE256;
|
||||
|
||||
if (SupportsConvergedRegisters) {
|
||||
///< Output wants to de-interleave
|
||||
for (size_t i = 0; i < MaximumRegisters; ++i) {
|
||||
memcpy(&Thread->CurrentFrame->State.xmm.avx.data[i][0], &XMM_Low[i], sizeof(__uint128_t));
|
||||
memcpy(&Thread->CurrentFrame->State.xmm.avx.data[i][2], &YMM_High[i], sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
///< Matches what FEX wants with non-converged registers
|
||||
for (size_t i = 0; i < MaximumRegisters; ++i) {
|
||||
memcpy(&Thread->CurrentFrame->State.xmm.sse.data[i][0], &XMM_Low[i], sizeof(__uint128_t));
|
||||
memcpy(&Thread->CurrentFrame->State.avx_high[i][0], &YMM_High[i], sizeof(__uint128_t));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only support SSE, no AVX here, even if requested.
|
||||
memcpy(Thread->CurrentFrame->State.xmm.sse.data, XMM_Low, MaximumRegisters * sizeof(__uint128_t));
|
||||
}
|
||||
}
|
||||
|
||||
void ContextImpl::SetFlagsFromCompactedEFLAGS(FEXCore::Core::InternalThreadState* Thread, uint32_t EFLAGS) {
|
||||
const auto Frame = Thread->CurrentFrame;
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_EFLAG_BITS; ++i) {
|
||||
@@ -260,14 +309,6 @@ void ContextImpl::SetFlagsFromCompactedEFLAGS(FEXCore::Core::InternalThreadState
|
||||
|
||||
bool ContextImpl::InitCore() {
|
||||
// Initialize the CPU core signal handlers & DispatcherConfig
|
||||
switch (Config.Core) {
|
||||
case FEXCore::Config::CONFIG_IRJIT: BackendFeatures = FEXCore::CPU::GetArm64JITBackendFeatures(); break;
|
||||
case FEXCore::Config::CONFIG_CUSTOM:
|
||||
// Do nothing
|
||||
break;
|
||||
default: LogMan::Msg::EFmt("Unknown core configuration"); return false;
|
||||
}
|
||||
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::Create(this);
|
||||
|
||||
// Set up the SignalDelegator config since core is initialized.
|
||||
@@ -324,14 +365,15 @@ void ContextImpl::HandleCallback(FEXCore::Core::InternalThreadState* Thread, uin
|
||||
|
||||
FEXCore::Context::ExitReason ContextImpl::RunUntilExit(FEXCore::Core::InternalThreadState* Thread) {
|
||||
ExecutionThread(Thread);
|
||||
while (true) {
|
||||
auto reason = Thread->ExitReason;
|
||||
|
||||
// Don't return if a custom exit handling the exit
|
||||
if (!CustomExitHandler || reason == ExitReason::EXIT_SHUTDOWN) {
|
||||
return reason;
|
||||
}
|
||||
CoreShuttingDown.store(true);
|
||||
|
||||
if (CustomExitHandler) {
|
||||
CustomExitHandler(Thread, FEXCore::Context::ExitReason::EXIT_SHUTDOWN);
|
||||
return Thread->ExitReason;
|
||||
}
|
||||
|
||||
return FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
|
||||
}
|
||||
|
||||
void ContextImpl::ExecuteThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
@@ -341,9 +383,6 @@ void ContextImpl::ExecuteThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
|
||||
void ContextImpl::InitializeThreadTLSData(FEXCore::Core::InternalThreadState* Thread) {
|
||||
// Let's do some initial bookkeeping here
|
||||
Thread->ThreadManager.TID = FHU::Syscalls::gettid();
|
||||
Thread->ThreadManager.PID = ::getpid();
|
||||
|
||||
if (ThunkHandler) {
|
||||
ThunkHandler->RegisterTLSState(Thread);
|
||||
}
|
||||
@@ -397,7 +436,6 @@ ContextImpl::CreateThread(uint64_t InitialRIP, uint64_t StackPointer, FEXCore::C
|
||||
}
|
||||
|
||||
// Set up the thread manager state
|
||||
Thread->ThreadManager.parent_tid = ParentTID;
|
||||
Thread->CurrentFrame->Thread = Thread;
|
||||
|
||||
InitializeCompiler(Thread);
|
||||
@@ -565,6 +603,20 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
|
||||
DecodedInfo = &Block.DecodedInstructions[i];
|
||||
bool IsLocked = DecodedInfo->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK;
|
||||
|
||||
// Do a partial register cache flush before every instruction. This
|
||||
// prevents cross-instruction static register caching, while allowing
|
||||
// context load/stores to be optimized within a block. Theoretically,
|
||||
// this flush is not required for correctness, all mandatory flushes are
|
||||
// included in instruction-specific handlers. Instead, this is a blunt
|
||||
// heuristic to make the register cache less aggressive, as the current
|
||||
// RA generates bad code in common cases with tied registers otherwise.
|
||||
//
|
||||
// However, it makes our exception handling behaviour more predictable.
|
||||
// It is potentially correctness bearing in that sense, but that is a
|
||||
// side effect here and (if that behaviour is required) we should handle
|
||||
// that more explicitly later.
|
||||
Thread->OpDispatcher->FlushRegisterCache(true);
|
||||
|
||||
if (ExtendedDebugInfo || Thread->OpDispatcher->CanHaveSideEffects(TableInfo, DecodedInfo)) {
|
||||
Thread->OpDispatcher->_GuestOpcode(Block.Entry + BlockInstructionsLength - GuestRIP);
|
||||
}
|
||||
@@ -583,7 +635,7 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
|
||||
|
||||
Thread->OpDispatcher->SetCurrentCodeBlock(CodeWasChangedBlock);
|
||||
Thread->OpDispatcher->_ThreadRemoveCodeEntry();
|
||||
Thread->OpDispatcher->_ExitFunction(
|
||||
Thread->OpDispatcher->ExitFunction(
|
||||
Thread->OpDispatcher->_EntrypointOffset(IR::SizeToOpSize(GPRSize), Block.Entry + BlockInstructionsLength - GuestRIP));
|
||||
|
||||
auto NextOpBlock = Thread->OpDispatcher->CreateNewCodeBlockAfter(CurrentBlock);
|
||||
@@ -614,7 +666,7 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
|
||||
}
|
||||
// Invalid instruction
|
||||
Thread->OpDispatcher->InvalidOp(DecodedInfo);
|
||||
Thread->OpDispatcher->_ExitFunction(Thread->OpDispatcher->_EntrypointOffset(IR::SizeToOpSize(GPRSize), Block.Entry - GuestRIP));
|
||||
Thread->OpDispatcher->ExitFunction(Thread->OpDispatcher->_EntrypointOffset(IR::SizeToOpSize(GPRSize), Block.Entry - GuestRIP));
|
||||
}
|
||||
|
||||
const bool NeedsBlockEnd =
|
||||
@@ -631,7 +683,7 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
|
||||
const uint8_t GPRSize = GetGPRSize();
|
||||
|
||||
// We had some instructions. Early exit
|
||||
Thread->OpDispatcher->_ExitFunction(
|
||||
Thread->OpDispatcher->ExitFunction(
|
||||
Thread->OpDispatcher->_EntrypointOffset(IR::SizeToOpSize(GPRSize), Block.Entry + BlockInstructionsLength - GuestRIP));
|
||||
break;
|
||||
}
|
||||
@@ -755,14 +807,6 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_
|
||||
FEXCORE_PROFILE_SCOPED("CompileBlock");
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
// If the target PC is EC code, mark it in the L2 and return straight to the dispatcher
|
||||
// so it can handle the call/return.
|
||||
if (Thread->LookupCache->CheckPageEC(GuestRIP)) {
|
||||
return GuestRIP;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Invalidate might take a unique lock on this, to guarantee that during invalidation no code gets compiled
|
||||
auto lk = GuardSignalDeferringSection<std::shared_lock>(CodeInvalidationMutex, Thread);
|
||||
|
||||
@@ -869,15 +913,6 @@ void ContextImpl::ExecutionThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
// If it is the parent thread that died then just leave
|
||||
FEX_TODO("This doesn't make sense when the parent thread doesn't outlive its children");
|
||||
|
||||
if (Thread->ThreadManager.parent_tid == 0) {
|
||||
CoreShuttingDown.store(true);
|
||||
Thread->ExitReason = FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
|
||||
|
||||
if (CustomExitHandler) {
|
||||
CustomExitHandler(Thread->ThreadManager.TID, Thread->ExitReason);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
Alloc::OSAllocator::UninstallTLSData(Thread);
|
||||
#endif
|
||||
@@ -972,7 +1007,7 @@ void ContextImpl::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry* Entry) {
|
||||
IRCaptureCache.UnloadAOTIRCacheEntry(Entry);
|
||||
}
|
||||
|
||||
void ContextImpl::AppendThunkDefinitions(const fextl::vector<FEXCore::IR::ThunkDefinition>& Definitions) {
|
||||
void ContextImpl::AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) {
|
||||
if (ThunkHandler) {
|
||||
ThunkHandler->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
|
||||
@@ -62,14 +62,6 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
ARMEmitter::ForwardLabel l_CTX;
|
||||
ARMEmitter::SingleUseForwardLabel l_Sleep;
|
||||
#ifdef _M_ARM_64EC
|
||||
// These structures are not included in the standard Windows headers, define them here
|
||||
static constexpr size_t TEBCPUAreaOffset = 0x1788;
|
||||
static constexpr size_t CPUAreaInSyscallCallbackOffset = 0x1;
|
||||
static constexpr size_t CPUAreaEmulatorStackLimitOffset = 0x8;
|
||||
static constexpr size_t CPUAreaEmulatorDataOffset = 0x30;
|
||||
ARMEmitter::SingleUseForwardLabel ExitEC;
|
||||
#endif
|
||||
ARMEmitter::SingleUseForwardLabel l_CompileBlock;
|
||||
|
||||
// Push all the register we need to save
|
||||
@@ -94,7 +86,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
b(&LoopTop);
|
||||
|
||||
AbsoluteLoopTopAddressEnterECFillSRA = GetCursorAddress<uint64_t>();
|
||||
ldr(STATE, EC_ENTRY_CPUAREA_REG, CPUAreaEmulatorDataOffset);
|
||||
ldr(STATE, EC_ENTRY_CPUAREA_REG, CPU_AREA_EMULATOR_DATA_OFFSET);
|
||||
FillStaticRegs();
|
||||
|
||||
// Enter JIT
|
||||
@@ -102,17 +94,15 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
AbsoluteLoopTopAddressEnterEC = GetCursorAddress<uint64_t>();
|
||||
// Load ThreadState and write the target PC there
|
||||
ldr(STATE, EC_ENTRY_CPUAREA_REG, CPUAreaEmulatorDataOffset);
|
||||
ldr(STATE, EC_ENTRY_CPUAREA_REG, CPU_AREA_EMULATOR_DATA_OFFSET);
|
||||
str(EC_CALL_CHECKER_PC_REG, STATE_PTR(CpuStateFrame, State.rip));
|
||||
|
||||
// Swap stacks to the emulator stack
|
||||
ldr(TMP1, EC_ENTRY_CPUAREA_REG, CPUAreaEmulatorStackLimitOffset);
|
||||
ldr(TMP1, EC_ENTRY_CPUAREA_REG, CPU_AREA_EMULATOR_STACK_BASE_OFFSET);
|
||||
add(ARMEmitter::Size::i64Bit, StaticRegisters[X86State::REG_RSP], ARMEmitter::Reg::rsp, 0);
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, TMP1, 0);
|
||||
|
||||
if (EmitterCTX->HostFeatures.SupportsSVE) {
|
||||
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_16B, ARMEmitter::PredicatePattern::SVE_VL16);
|
||||
}
|
||||
FillSpecialRegs(TMP1, TMP2, false, true);
|
||||
|
||||
// Enter JIT
|
||||
#endif
|
||||
@@ -168,10 +158,6 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
// If page pointer is zero then we have no block
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &NoBlock);
|
||||
#ifdef _M_ARM_64EC
|
||||
// The LSB of an L2 page entry indicates if this page contains EC code
|
||||
tbnz(TMP1, 0, &ExitEC);
|
||||
#endif
|
||||
|
||||
// Steal the page offset
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, TMP4, 0x0FFF);
|
||||
@@ -198,23 +184,13 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, RipReg.R(), LookupCache::L1_ENTRIES_MASK);
|
||||
add(TMP1, TMP1, TMP2, ARMEmitter::ShiftType::LSL, 4);
|
||||
stp<ARMEmitter::IndexType::OFFSET>(TMP4, TMP3, TMP1);
|
||||
stp<ARMEmitter::IndexType::OFFSET>(TMP4, RipReg, TMP1);
|
||||
|
||||
// Jump to the block
|
||||
br(TMP4);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
{
|
||||
Bind(&ExitEC);
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, StaticRegisters[X86State::REG_RSP], 0);
|
||||
mov(EC_CALL_CHECKER_PC_REG, RipReg);
|
||||
ldr(TMP2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionEC));
|
||||
br(TMP2);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
ThreadStopHandlerAddressSpillSRA = GetCursorAddress<uint64_t>();
|
||||
SpillStaticRegs(TMP1);
|
||||
@@ -232,14 +208,16 @@ void Dispatcher::EmitDispatcher() {
|
||||
ExitFunctionLinkerAddress = GetCursorAddress<uint64_t>();
|
||||
SpillStaticRegs(TMP1);
|
||||
|
||||
#ifndef _WIN32
|
||||
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::XReg::x0, ARMEmitter::XReg::x0, 1);
|
||||
str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
#endif
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
ldr(ARMEmitter::XReg::x0, ARMEmitter::XReg::x18, TEBCPUAreaOffset);
|
||||
ldr(ARMEmitter::XReg::x0, ARMEmitter::XReg::x18, TEB_CPU_AREA_OFFSET);
|
||||
LoadConstant(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r1, 1);
|
||||
strb(ARMEmitter::WReg::w1, ARMEmitter::XReg::x0, CPUAreaInSyscallCallbackOffset);
|
||||
strb(ARMEmitter::WReg::w1, ARMEmitter::XReg::x0, CPU_AREA_IN_SYSCALL_CALLBACK_OFFSET);
|
||||
#endif
|
||||
|
||||
mov(ARMEmitter::XReg::x0, STATE);
|
||||
@@ -259,10 +237,11 @@ void Dispatcher::EmitDispatcher() {
|
||||
FillStaticRegs();
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
ldr(TMP2, ARMEmitter::XReg::x18, TEBCPUAreaOffset);
|
||||
strb(ARMEmitter::WReg::zr, TMP2, CPUAreaInSyscallCallbackOffset);
|
||||
ldr(TMP2, ARMEmitter::XReg::x18, TEB_CPU_AREA_OFFSET);
|
||||
strb(ARMEmitter::WReg::zr, TMP2, CPU_AREA_IN_SYSCALL_CALLBACK_OFFSET);
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
ldr(TMP2, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, 1);
|
||||
str(TMP2, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
@@ -270,6 +249,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
// Trigger segfault if any deferred signals are pending
|
||||
strb(ARMEmitter::XReg::zr, STATE,
|
||||
offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState));
|
||||
#endif
|
||||
|
||||
br(TMP1);
|
||||
}
|
||||
@@ -278,20 +258,43 @@ void Dispatcher::EmitDispatcher() {
|
||||
{
|
||||
Bind(&NoBlock);
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
// Check the EC code bitmap incase we need to exit the JIT to call into native code.
|
||||
ARMEmitter::SingleUseForwardLabel l_NotECCode;
|
||||
ldr(TMP1, ARMEmitter::XReg::x18, TEB_PEB_OFFSET);
|
||||
ldr(TMP1, TMP1, PEB_EC_CODE_BITMAP_OFFSET);
|
||||
|
||||
lsr(ARMEmitter::Size::i64Bit, TMP2, RipReg, 15);
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, TMP2, 0x1fffffffffff8);
|
||||
ldr(TMP1, TMP1, TMP2, ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
lsr(ARMEmitter::Size::i64Bit, TMP2, RipReg, 12);
|
||||
lsrv(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP2);
|
||||
tbz(TMP1, 0, &l_NotECCode);
|
||||
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, StaticRegisters[X86State::REG_RSP], 0);
|
||||
mov(EC_CALL_CHECKER_PC_REG, RipReg);
|
||||
ldr(TMP2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionEC));
|
||||
br(TMP2);
|
||||
|
||||
Bind(&l_NotECCode);
|
||||
#endif
|
||||
|
||||
SpillStaticRegs(TMP1);
|
||||
|
||||
if (!TMP_ABIARGS) {
|
||||
mov(ARMEmitter::XReg::x2, TMP3);
|
||||
mov(ARMEmitter::XReg::x2, RipReg);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::XReg::x0, ARMEmitter::XReg::x0, 1);
|
||||
str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
#endif
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
ldr(ARMEmitter::XReg::x0, ARMEmitter::XReg::x18, TEBCPUAreaOffset);
|
||||
ldr(ARMEmitter::XReg::x0, ARMEmitter::XReg::x18, TEB_CPU_AREA_OFFSET);
|
||||
LoadConstant(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r1, 1);
|
||||
strb(ARMEmitter::WReg::w1, ARMEmitter::XReg::x0, CPUAreaInSyscallCallbackOffset);
|
||||
strb(ARMEmitter::WReg::w1, ARMEmitter::XReg::x0, CPU_AREA_IN_SYSCALL_CALLBACK_OFFSET);
|
||||
#endif
|
||||
|
||||
ldr(ARMEmitter::XReg::x0, &l_CTX);
|
||||
@@ -309,10 +312,11 @@ void Dispatcher::EmitDispatcher() {
|
||||
FillStaticRegs();
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
ldr(TMP1, ARMEmitter::XReg::x18, TEBCPUAreaOffset);
|
||||
strb(ARMEmitter::WReg::zr, TMP1, CPUAreaInSyscallCallbackOffset);
|
||||
ldr(TMP1, ARMEmitter::XReg::x18, TEB_CPU_AREA_OFFSET);
|
||||
strb(ARMEmitter::WReg::zr, TMP1, CPU_AREA_IN_SYSCALL_CALLBACK_OFFSET);
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
ldr(TMP1, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
|
||||
str(TMP1, STATE, offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount));
|
||||
@@ -320,6 +324,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
// Trigger segfault if any deferred signals are pending
|
||||
strb(ARMEmitter::XReg::zr, STATE,
|
||||
offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState));
|
||||
#endif
|
||||
|
||||
b(&LoopTop);
|
||||
}
|
||||
|
||||
@@ -221,14 +221,16 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand* Operand, X86Tables::ModR
|
||||
{
|
||||
// If we have a VSIB byte (as opposed to SIB), then the index register is a vector.
|
||||
const bool IsIndexVector = (DecodeInst->TableInfo->Flags & InstFlags::FLAGS_VEX_VSIB) != 0;
|
||||
uint8_t InvalidSIBIndex = 0b100; ///< SIB Index where there is no register encoding.
|
||||
if (IsIndexVector) {
|
||||
DecodeInst->Flags |= X86Tables::DecodeFlags::FLAG_VSIB_BYTE;
|
||||
InvalidSIBIndex = ~0; ///< No Invalid SIB Index with Index Vectors.
|
||||
}
|
||||
|
||||
const uint8_t IndexREX = (DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_X) != 0 ? 1 : 0;
|
||||
const uint8_t BaseREX = (DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B) != 0 ? 1 : 0;
|
||||
|
||||
Operand->Data.SIB.Index = MapModRMToReg(IndexREX, SIB.index, false, false, IsIndexVector, false, 0b100);
|
||||
Operand->Data.SIB.Index = MapModRMToReg(IndexREX, SIB.index, false, false, IsIndexVector, false, InvalidSIBIndex);
|
||||
Operand->Data.SIB.Base = MapModRMToReg(BaseREX, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
|
||||
}
|
||||
|
||||
@@ -630,7 +632,6 @@ bool Decoder::NormalOpHeader(const FEXCore::X86Tables::X86InstInfo* Info, uint16
|
||||
uint16_t X87Op = ((Op - 0xD8) << 8) | ModRMByte;
|
||||
return NormalOp(&X87Ops[X87Op], X87Op);
|
||||
} else if (Info->Type == FEXCore::X86Tables::TYPE_VEX_TABLE_PREFIX) {
|
||||
FEXCORE_TELEMETRY_SET(VEXOpTelem, 1);
|
||||
uint16_t map_select = 1;
|
||||
uint16_t pp = 0;
|
||||
const uint8_t Byte1 = ReadByte();
|
||||
@@ -659,6 +660,9 @@ bool Decoder::NormalOpHeader(const FEXCore::X86Tables::X86InstInfo* Info, uint16
|
||||
if (CTX->Config.Is64BitMode && (Byte1 & 0b00100000) == 0) {
|
||||
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_B;
|
||||
}
|
||||
if (options.w) {
|
||||
DecodeInst->Flags |= DecodeFlags::FLAG_OPTION_AVX_W;
|
||||
}
|
||||
if (!(map_select >= 1 && map_select <= 3)) {
|
||||
LogMan::Msg::EFmt("We don't understand a map_select of: {}", map_select);
|
||||
return false;
|
||||
@@ -673,7 +677,6 @@ bool Decoder::NormalOpHeader(const FEXCore::X86Tables::X86InstInfo* Info, uint16
|
||||
FEXCore::X86Tables::X86InstInfo* LocalInfo = &VEXTableOps[Op];
|
||||
|
||||
if (LocalInfo->Type >= FEXCore::X86Tables::TYPE_VEX_GROUP_12 && LocalInfo->Type <= FEXCore::X86Tables::TYPE_VEX_GROUP_17) {
|
||||
FEXCORE_TELEMETRY_SET(VEXOpTelem, 1);
|
||||
// We have ModRM
|
||||
uint8_t ModRMByte = ReadByte();
|
||||
DecodeInst->ModRM = ModRMByte;
|
||||
@@ -941,14 +944,12 @@ void Decoder::BranchTargetInMultiblockRange() {
|
||||
// auto RIPOffset = LoadSource(Op, Op->Src[0], Op->Flags);
|
||||
// auto RIPTargetConst = _Constant(Op->PC + Op->InstSize);
|
||||
// Target offset is PC + InstSize + Literal
|
||||
LOGMAN_THROW_A_FMT(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Literal();
|
||||
break;
|
||||
}
|
||||
case 0xE9:
|
||||
case 0xEB: // Both are unconditional JMP instructions
|
||||
LOGMAN_THROW_A_FMT(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Literal();
|
||||
Conditional = false;
|
||||
break;
|
||||
case 0xE8: // Call - Immediate target, We don't want to inline calls
|
||||
@@ -1000,8 +1001,7 @@ bool Decoder::BranchTargetCanContinue(bool FinalInstruction) const {
|
||||
|
||||
if (DecodeInst->OP == 0xE8) { // Call - immediate target
|
||||
const uint64_t NextRIP = DecodeInst->PC + DecodeInst->InstSize;
|
||||
LOGMAN_THROW_A_FMT(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Literal();
|
||||
|
||||
if (GPRSize == 4) {
|
||||
// If we are running a 32bit guest then wrap around addresses that go above 32bit
|
||||
|
||||
@@ -120,7 +120,6 @@ private:
|
||||
|
||||
const uint8_t* AdjustAddrForSpecialRegion(const uint8_t* _InstStream, uint64_t EntryPoint, uint64_t RIP);
|
||||
|
||||
FEXCORE_TELEMETRY_INIT(VEXOpTelem, TYPE_USES_VEX_OPS);
|
||||
FEXCORE_TELEMETRY_INIT(EVEXOpTelem, TYPE_USES_EVEX_OPS);
|
||||
};
|
||||
} // namespace FEXCore::Frontend
|
||||
@@ -6,54 +6,59 @@
|
||||
#include "Interface/IR/IR.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static void LoadDeferredFCW(uint16_t NewFCW) {
|
||||
auto PC = (NewFCW >> 8) & 3;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static softfloat_state SoftFloatStateFromFCW(uint16_t FCW) {
|
||||
softfloat_state State;
|
||||
State.detectTininess = softfloat_tininess_afterRounding;
|
||||
State.exceptionFlags = 0;
|
||||
|
||||
auto PC = (FCW >> 8) & 3;
|
||||
switch (PC) {
|
||||
case 0: extF80_roundingPrecision = 32; break;
|
||||
case 2: extF80_roundingPrecision = 64; break;
|
||||
case 3: extF80_roundingPrecision = 80; break;
|
||||
case 0: State.roundingPrecision = 32; break;
|
||||
case 2: State.roundingPrecision = 64; break;
|
||||
case 3: State.roundingPrecision = 80; break;
|
||||
case 1: LOGMAN_MSG_A_FMT("Invalid x87 precision mode, {}", PC);
|
||||
}
|
||||
|
||||
auto RC = (NewFCW >> 10) & 3;
|
||||
auto RC = (FCW >> 10) & 3;
|
||||
switch (RC) {
|
||||
case 0: softfloat_roundingMode = softfloat_round_near_even; break;
|
||||
case 1: softfloat_roundingMode = softfloat_round_min; break;
|
||||
case 2: softfloat_roundingMode = softfloat_round_max; break;
|
||||
case 3: softfloat_roundingMode = softfloat_round_minMag; break;
|
||||
case 0: State.roundingMode = softfloat_round_near_even; break;
|
||||
case 1: State.roundingMode = softfloat_round_min; break;
|
||||
case 2: State.roundingMode = softfloat_round_max; break;
|
||||
case 3: State.roundingMode = softfloat_round_minMag; break;
|
||||
}
|
||||
|
||||
return State;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80CVTTO> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle4(uint16_t NewFCW, float src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle4(uint16_t FCW, float src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat(&State, src);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle8(uint16_t NewFCW, double src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle8(uint16_t FCW, double src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat(&State, src);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80CMP> {
|
||||
template<uint32_t Flags>
|
||||
FEXCORE_PRESERVE_ALL_ATTR static uint64_t handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static uint64_t handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
|
||||
bool eq, lt, nan;
|
||||
uint64_t ResultFlags = 0;
|
||||
|
||||
X80SoftFloat::FCMP(Src1, Src2, &eq, <, &nan);
|
||||
if (Flags & (1 << IR::FCMP_FLAG_LT) && lt) {
|
||||
X80SoftFloat::FCMP(&State, Src1, Src2, &eq, <, &nan);
|
||||
if (lt) {
|
||||
ResultFlags |= (1 << IR::FCMP_FLAG_LT);
|
||||
}
|
||||
if (Flags & (1 << IR::FCMP_FLAG_UNORDERED) && nan) {
|
||||
if (nan) {
|
||||
ResultFlags |= (1 << IR::FCMP_FLAG_UNORDERED);
|
||||
}
|
||||
if (Flags & (1 << IR::FCMP_FLAG_EQ) && eq) {
|
||||
if (eq) {
|
||||
ResultFlags |= (1 << IR::FCMP_FLAG_EQ);
|
||||
}
|
||||
return ResultFlags;
|
||||
@@ -62,275 +67,261 @@ struct OpHandlers<IR::OP_F80CMP> {
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80CVT> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static float handle4(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static float handle4(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return src.ToF32(&State);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static double handle8(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static double handle8(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return src.ToF64(&State);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80CVTINT> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int16_t handle2(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int16_t handle2(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return src.ToI16(&State);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int32_t handle4(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int32_t handle4(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return src.ToI32(&State);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int64_t handle8(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return src;
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int64_t handle8(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return src.ToI64(&State);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int16_t handle2t(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
auto rv = extF80_to_i32(src, softfloat_round_minMag, false);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int16_t handle2t(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
auto rv = extF80_to_i32(&State, src, softfloat_round_minMag, false);
|
||||
|
||||
if (rv > INT16_MAX) {
|
||||
return INT16_MAX;
|
||||
} else if (rv < INT16_MIN) {
|
||||
if (rv > INT16_MAX || rv < INT16_MIN) {
|
||||
///< Indefinite value for 16-bit conversions.
|
||||
return INT16_MIN;
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int32_t handle4t(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return extF80_to_i32(src, softfloat_round_minMag, false);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int32_t handle4t(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return extF80_to_i32(&State, src, softfloat_round_minMag, false);
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int64_t handle8t(uint16_t NewFCW, X80SoftFloat src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return extF80_to_i64(src, softfloat_round_minMag, false);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static int64_t handle8t(uint16_t FCW, X80SoftFloat src) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return extF80_to_i64(&State, src, softfloat_round_minMag, false);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80CVTTOINT> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle2(uint16_t NewFCW, int16_t src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle2(uint16_t FCW, int16_t src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle4(uint16_t NewFCW, int32_t src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle4(uint16_t FCW, int32_t src) {
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80ROUND> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FRNDINT(Src1);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FRNDINT(&State, Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80F2XM1> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::F2XM1(Src1);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::F2XM1(&State, Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80TAN> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FTAN(Src1);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FTAN(&State, Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80SQRT> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FSQRT(Src1);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FSQRT(&State, Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80SIN> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FSIN(Src1);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FSIN(&State, Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80COS> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FCOS(Src1);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FCOS(&State, Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80XTRACT_EXP> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
return X80SoftFloat::FXTRACT_EXP(Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80XTRACT_SIG> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
return X80SoftFloat::FXTRACT_SIG(Src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80ADD> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FADD(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FADD(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80SUB> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FSUB(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FSUB(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80MUL> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FMUL(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FMUL(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80DIV> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FDIV(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FDIV(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80FYL2X> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FYL2X(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FYL2X(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80ATAN> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FATAN(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FATAN(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80FPREM1> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FREM1(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FREM1(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80FPREM> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FREM(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FREM(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80SCALE> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
return X80SoftFloat::FSCALE(Src1, Src2);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1, X80SoftFloat Src2) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
return X80SoftFloat::FSCALE(&State, Src1, Src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64SIN> {
|
||||
static double handle(uint16_t NewFCW, double src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src) {
|
||||
return sin(src);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64COS> {
|
||||
static double handle(uint16_t NewFCW, double src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src) {
|
||||
return cos(src);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64TAN> {
|
||||
static double handle(uint16_t NewFCW, double src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src) {
|
||||
return tan(src);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64F2XM1> {
|
||||
static double handle(uint16_t NewFCW, double src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src) {
|
||||
return exp2(src) - 1.0;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64ATAN> {
|
||||
static double handle(uint16_t NewFCW, double src1, double src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src1, double src2) {
|
||||
return atan2(src1, src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64FPREM> {
|
||||
static double handle(uint16_t NewFCW, double src1, double src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src1, double src2) {
|
||||
return fmod(src1, src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64FPREM1> {
|
||||
static double handle(uint16_t NewFCW, double src1, double src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src1, double src2) {
|
||||
return remainder(src1, src2);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64FYL2X> {
|
||||
static double handle(uint16_t NewFCW, double src1, double src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src1, double src2) {
|
||||
return src2 * log2(src1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64SCALE> {
|
||||
static double handle(uint16_t NewFCW, double src1, double src2) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
static double handle(uint16_t FCW, double src1, double src2) {
|
||||
double trunc = (double)(int64_t)(src2); // truncate
|
||||
return src1 * exp2(trunc);
|
||||
}
|
||||
@@ -338,16 +329,16 @@ struct OpHandlers<IR::OP_F64SCALE> {
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80BCDSTORE> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src1) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src1) {
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW);
|
||||
bool Negative = Src1.Sign;
|
||||
|
||||
Src1 = X80SoftFloat::FRNDINT(Src1);
|
||||
Src1 = X80SoftFloat::FRNDINT(&State, Src1);
|
||||
|
||||
// Clear the Sign bit
|
||||
Src1.Sign = 0;
|
||||
|
||||
uint64_t Tmp = Src1;
|
||||
uint64_t Tmp = Src1.ToI64(&State);
|
||||
X80SoftFloat Rv;
|
||||
uint8_t* BCD = reinterpret_cast<uint8_t*>(&Rv);
|
||||
memset(BCD, 0, 10);
|
||||
@@ -379,8 +370,7 @@ struct OpHandlers<IR::OP_F80BCDSTORE> {
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80BCDLOAD> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t NewFCW, X80SoftFloat Src) {
|
||||
LoadDeferredFCW(NewFCW);
|
||||
FEXCORE_PRESERVE_ALL_ATTR static X80SoftFloat handle(uint16_t FCW, X80SoftFloat Src) {
|
||||
uint8_t* Src1 = reinterpret_cast<uint8_t*>(&Src);
|
||||
uint64_t BCD {};
|
||||
// We walk through each uint8_t and pull out the BCD encoding
|
||||
|
||||
@@ -35,14 +35,7 @@ void InterpreterOps::FillFallbackIndexPointers(uint64_t* Info) {
|
||||
Info[Core::OPINDEX_F80CVTINT_TRUNC2] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2t);
|
||||
Info[Core::OPINDEX_F80CVTINT_TRUNC4] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4t);
|
||||
Info[Core::OPINDEX_F80CVTINT_TRUNC8] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8t);
|
||||
Info[Core::OPINDEX_F80CMP_0] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<0>);
|
||||
Info[Core::OPINDEX_F80CMP_1] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<1>);
|
||||
Info[Core::OPINDEX_F80CMP_2] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<2>);
|
||||
Info[Core::OPINDEX_F80CMP_3] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<3>);
|
||||
Info[Core::OPINDEX_F80CMP_4] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<4>);
|
||||
Info[Core::OPINDEX_F80CMP_5] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<5>);
|
||||
Info[Core::OPINDEX_F80CMP_6] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<6>);
|
||||
Info[Core::OPINDEX_F80CMP_7] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<7>);
|
||||
Info[Core::OPINDEX_F80CMP] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle);
|
||||
Info[Core::OPINDEX_F80CVTTOINT_2] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTOINT>::handle2);
|
||||
Info[Core::OPINDEX_F80CVTTOINT_4] = reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTOINT>::handle4);
|
||||
|
||||
@@ -154,17 +147,8 @@ bool InterpreterOps::GetFallbackHandler(bool SupportsPreserveAllABI, const IR::I
|
||||
break;
|
||||
}
|
||||
case IR::OP_F80CMP: {
|
||||
auto Op = IROp->C<IR::IROp_F80Cmp>();
|
||||
|
||||
static constexpr std::array handlers {
|
||||
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<0>, &FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<1>,
|
||||
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<2>, &FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<3>,
|
||||
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<4>, &FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<5>,
|
||||
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<6>, &FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<7>,
|
||||
};
|
||||
|
||||
*Info = {FABI_I64_I16_F80_F80, (void*)handlers[Op->Flags], (Core::FallbackHandlerIndex)(Core::OPINDEX_F80CMP_0 + Op->Flags),
|
||||
SupportsPreserveAllABI};
|
||||
*Info = {FABI_I64_I16_F80_F80, (void*)&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle,
|
||||
(Core::FallbackHandlerIndex)(Core::OPINDEX_F80CMP), SupportsPreserveAllABI};
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -625,13 +625,17 @@ DEF_OP(ShiftFlags) {
|
||||
|
||||
// Set the output outside the branch to avoid needing an extra leg of the
|
||||
// branch. We specifically do not hardcode the PF register anywhere (relying
|
||||
// on a tied SRA register instead) to avoid fighting with RA/RCLSE.
|
||||
// on a tied SRA register instead) to avoid fighting with RA.
|
||||
if (PFTemp != PFInput) {
|
||||
mov(ARMEmitter::Size::i64Bit, PFTemp, PFInput);
|
||||
}
|
||||
|
||||
// We need to mask the source before comparing it. We don't just skip flag
|
||||
// updates for Src2=0 but anything that masks to zero.
|
||||
and_(ARMEmitter::Size::i32Bit, TMP1, Src2, OpSize == 8 ? 0x3f : 0x1f);
|
||||
|
||||
ARMEmitter::SingleUseForwardLabel Done;
|
||||
cbz(EmitSize, Src2, &Done);
|
||||
cbz(EmitSize, TMP1, &Done);
|
||||
{
|
||||
// PF/SF/ZF/OF
|
||||
if (OpSize >= 4) {
|
||||
@@ -642,20 +646,23 @@ DEF_OP(ShiftFlags) {
|
||||
mov(ARMEmitter::Size::i64Bit, PFTemp, Dst);
|
||||
}
|
||||
|
||||
auto CFWord = TMP1;
|
||||
unsigned CFBit = 0;
|
||||
|
||||
// Extract the last bit shifted in to CF
|
||||
if (Op->Shift == IR::ShiftType::LSL) {
|
||||
if (OpSize >= 4) {
|
||||
neg(EmitSize, TMP1, Src2);
|
||||
neg(EmitSize, CFWord, Src2);
|
||||
lsrv(EmitSize, CFWord, Src1, CFWord);
|
||||
} else {
|
||||
mov(EmitSize, TMP1, OpSize * 8);
|
||||
sub(EmitSize, TMP1, TMP1, Src2);
|
||||
CFWord = Dst.X();
|
||||
CFBit = (OpSize * 8);
|
||||
}
|
||||
} else {
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, Src2, 1);
|
||||
sub(ARMEmitter::Size::i64Bit, CFWord, Src2, 1);
|
||||
lsrv(EmitSize, CFWord, Src1, CFWord);
|
||||
}
|
||||
|
||||
lsrv(EmitSize, TMP1, Src1, TMP1);
|
||||
|
||||
bool SetOF = Op->Shift != IR::ShiftType::ASR;
|
||||
if (SetOF) {
|
||||
// Only defined when Shift is 1 else undefined
|
||||
@@ -664,14 +671,20 @@ DEF_OP(ShiftFlags) {
|
||||
}
|
||||
|
||||
if (CTX->HostFeatures.SupportsFlagM) {
|
||||
rmif(TMP1, 63, (1 << 1) /* C */);
|
||||
rmif(CFWord, (CFBit - 1) % 64, (1 << 1) /* C */);
|
||||
|
||||
if (SetOF) {
|
||||
rmif(TMP3, OpSize * 8 - 1, (1 << 0) /* V */);
|
||||
}
|
||||
} else {
|
||||
mrs(TMP2, ARMEmitter::SystemRegister::NZCV);
|
||||
bfi(ARMEmitter::Size::i32Bit, TMP2, TMP1, 29 /* C */, 1);
|
||||
|
||||
if (CFBit != 0) {
|
||||
lsr(ARMEmitter::Size::i64Bit, TMP1, CFWord, CFBit);
|
||||
CFWord = TMP1;
|
||||
}
|
||||
|
||||
bfi(ARMEmitter::Size::i32Bit, TMP2, CFWord, 29 /* C */, 1);
|
||||
|
||||
if (SetOF) {
|
||||
lsr(EmitSize, TMP3, TMP3, OpSize * 8 - 1);
|
||||
@@ -704,59 +717,74 @@ DEF_OP(PDep) {
|
||||
|
||||
const auto Dest = GetReg(Node);
|
||||
|
||||
// PDep implementation follows the ideas from
|
||||
// http://0x80.pl/articles/pdep-soft-emu.html ... Basically, iterate the *set*
|
||||
// bits only, which will be faster than the naive implementation as long as
|
||||
// there are enough holes in the mask.
|
||||
//
|
||||
// The specific arm64 assembly used is based on the sequence that clang
|
||||
// generates for the C code, giving context to the scheduling yielding better
|
||||
// ILP than I would do by hand. The registers are allocated by hand however,
|
||||
// to fit within the tight constraints we have here withot spilling. Also, we
|
||||
// use cbz/cbnz for conditional branching to avoid clobbering NZCV.
|
||||
|
||||
// We can't clobber these
|
||||
const auto OrigInput = GetReg(Op->Input.ID());
|
||||
const auto OrigMask = GetReg(Op->Mask.ID());
|
||||
|
||||
// So we have shadow as temporaries
|
||||
const auto Input = TMP1.R();
|
||||
const auto Mask = TMP2.R();
|
||||
if (CTX->HostFeatures.SupportsSVEBitPerm) {
|
||||
// SVE added support for PDEP but it needs to be done in a vector register.
|
||||
if (EmitSize == ARMEmitter::Size::i32Bit) {
|
||||
fmov(ARMEmitter::Size::i32Bit, VTMP1.S(), OrigInput.W());
|
||||
fmov(ARMEmitter::Size::i32Bit, VTMP2.S(), OrigMask.W());
|
||||
bdep(ARMEmitter::SubRegSize::i32Bit, VTMP1.Z(), VTMP1.Z(), VTMP2.Z());
|
||||
umov<ARMEmitter::SubRegSize::i32Bit>(Dest, VTMP1, 0);
|
||||
} else {
|
||||
fmov(ARMEmitter::Size::i64Bit, VTMP1.D(), OrigInput.X());
|
||||
fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), OrigMask.X());
|
||||
bdep(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), VTMP1.Z(), VTMP2.Z());
|
||||
umov<ARMEmitter::SubRegSize::i64Bit>(Dest, VTMP1, 0);
|
||||
}
|
||||
} else {
|
||||
// PDep implementation follows the ideas from
|
||||
// http://0x80.pl/articles/pdep-soft-emu.html ... Basically, iterate the *set*
|
||||
// bits only, which will be faster than the naive implementation as long as
|
||||
// there are enough holes in the mask.
|
||||
//
|
||||
// The specific arm64 assembly used is based on the sequence that clang
|
||||
// generates for the C code, giving context to the scheduling yielding better
|
||||
// ILP than I would do by hand. The registers are allocated by hand however,
|
||||
// to fit within the tight constraints we have here withot spilling. Also, we
|
||||
// use cbz/cbnz for conditional branching to avoid clobbering NZCV.
|
||||
|
||||
// these get used variously as scratch
|
||||
const auto T0 = TMP3.R();
|
||||
const auto T1 = TMP4.R();
|
||||
// So we have shadow as temporaries
|
||||
const auto Input = TMP1.R();
|
||||
const auto Mask = TMP2.R();
|
||||
|
||||
ARMEmitter::BackwardLabel NextBit;
|
||||
ARMEmitter::SingleUseForwardLabel Done;
|
||||
// these get used variously as scratch
|
||||
const auto T0 = TMP3.R();
|
||||
const auto T1 = TMP4.R();
|
||||
|
||||
// First, copy the input/mask, since we'll be clobbering. Copy as 64-bit to
|
||||
// make this 0-uop on Firestorm.
|
||||
mov(ARMEmitter::Size::i64Bit, Input, OrigInput);
|
||||
mov(ARMEmitter::Size::i64Bit, Mask, OrigMask);
|
||||
ARMEmitter::BackwardLabel NextBit;
|
||||
ARMEmitter::SingleUseForwardLabel Done;
|
||||
|
||||
// Now, they're copied, so we can start setting Dest (even if it overlaps with
|
||||
// one of them). Handle early exit case
|
||||
mov(EmitSize, Dest, 0);
|
||||
cbz(EmitSize, OrigMask, &Done);
|
||||
// First, copy the input/mask, since we'll be clobbering. Copy as 64-bit to
|
||||
// make this 0-uop on Firestorm.
|
||||
mov(ARMEmitter::Size::i64Bit, Input, OrigInput);
|
||||
mov(ARMEmitter::Size::i64Bit, Mask, OrigMask);
|
||||
|
||||
// Setup for first iteration
|
||||
neg(EmitSize, T0, Mask);
|
||||
and_(EmitSize, T0, T0, Mask);
|
||||
// Now, they're copied, so we can start setting Dest (even if it overlaps with
|
||||
// one of them). Handle early exit case
|
||||
mov(EmitSize, Dest, 0);
|
||||
cbz(EmitSize, OrigMask, &Done);
|
||||
|
||||
// Main loop
|
||||
Bind(&NextBit);
|
||||
sbfx(EmitSize, T1, Input, 0, 1);
|
||||
eor(EmitSize, Mask, Mask, T0);
|
||||
and_(EmitSize, T0, T1, T0);
|
||||
neg(EmitSize, T1, Mask);
|
||||
orr(EmitSize, Dest, Dest, T0);
|
||||
lsr(EmitSize, Input, Input, 1);
|
||||
and_(EmitSize, T0, Mask, T1);
|
||||
cbnz(EmitSize, T0, &NextBit);
|
||||
// Setup for first iteration
|
||||
neg(EmitSize, T0, Mask);
|
||||
and_(EmitSize, T0, T0, Mask);
|
||||
|
||||
// All done with nothing to do.
|
||||
Bind(&Done);
|
||||
// Main loop
|
||||
Bind(&NextBit);
|
||||
sbfx(EmitSize, T1, Input, 0, 1);
|
||||
eor(EmitSize, Mask, Mask, T0);
|
||||
and_(EmitSize, T0, T1, T0);
|
||||
neg(EmitSize, T1, Mask);
|
||||
orr(EmitSize, Dest, Dest, T0);
|
||||
lsr(EmitSize, Input, Input, 1);
|
||||
and_(EmitSize, T0, Mask, T1);
|
||||
cbnz(EmitSize, T0, &NextBit);
|
||||
|
||||
// All done with nothing to do.
|
||||
Bind(&Done);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(PExt) {
|
||||
@@ -769,35 +797,50 @@ DEF_OP(PExt) {
|
||||
const auto Mask = GetReg(Op->Mask.ID());
|
||||
const auto Dest = GetReg(Node);
|
||||
|
||||
const auto MaskReg = TMP1;
|
||||
const auto BitReg = TMP2;
|
||||
const auto ValueReg = TMP3;
|
||||
if (CTX->HostFeatures.SupportsSVEBitPerm) {
|
||||
// SVE added support for PEXT but it needs to be done in a vector register.
|
||||
if (EmitSize == ARMEmitter::Size::i32Bit) {
|
||||
fmov(ARMEmitter::Size::i32Bit, VTMP1.S(), Input.W());
|
||||
fmov(ARMEmitter::Size::i32Bit, VTMP2.S(), Mask.W());
|
||||
bext(ARMEmitter::SubRegSize::i32Bit, VTMP1.Z(), VTMP1.Z(), VTMP2.Z());
|
||||
umov<ARMEmitter::SubRegSize::i32Bit>(Dest, VTMP1, 0);
|
||||
} else {
|
||||
fmov(ARMEmitter::Size::i64Bit, VTMP1.D(), Input.X());
|
||||
fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), Mask.X());
|
||||
bext(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), VTMP1.Z(), VTMP2.Z());
|
||||
umov<ARMEmitter::SubRegSize::i64Bit>(Dest, VTMP1, 0);
|
||||
}
|
||||
} else {
|
||||
const auto MaskReg = TMP1;
|
||||
const auto BitReg = TMP2;
|
||||
const auto ValueReg = TMP3;
|
||||
|
||||
ARMEmitter::SingleUseForwardLabel EarlyExit;
|
||||
ARMEmitter::BackwardLabel NextBit;
|
||||
ARMEmitter::SingleUseForwardLabel Done;
|
||||
ARMEmitter::SingleUseForwardLabel EarlyExit;
|
||||
ARMEmitter::BackwardLabel NextBit;
|
||||
ARMEmitter::SingleUseForwardLabel Done;
|
||||
|
||||
cbz(EmitSize, Mask, &EarlyExit);
|
||||
mov(EmitSize, MaskReg, Mask);
|
||||
mov(EmitSize, ValueReg, Input);
|
||||
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
|
||||
cbz(EmitSize, Mask, &EarlyExit);
|
||||
mov(EmitSize, MaskReg, Mask);
|
||||
mov(EmitSize, ValueReg, Input);
|
||||
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
|
||||
|
||||
// Main loop
|
||||
Bind(&NextBit);
|
||||
cbz(EmitSize, MaskReg, &Done);
|
||||
clz(EmitSize, BitReg, MaskReg);
|
||||
lslv(EmitSize, ValueReg, ValueReg, BitReg);
|
||||
lslv(EmitSize, MaskReg, MaskReg, BitReg);
|
||||
extr(EmitSize, Dest, Dest, ValueReg, OpSizeBitsM1);
|
||||
bfc(EmitSize, MaskReg, OpSizeBitsM1, 1);
|
||||
b(&NextBit);
|
||||
// Main loop
|
||||
Bind(&NextBit);
|
||||
cbz(EmitSize, MaskReg, &Done);
|
||||
clz(EmitSize, BitReg, MaskReg);
|
||||
lslv(EmitSize, ValueReg, ValueReg, BitReg);
|
||||
lslv(EmitSize, MaskReg, MaskReg, BitReg);
|
||||
extr(EmitSize, Dest, Dest, ValueReg, OpSizeBitsM1);
|
||||
bfc(EmitSize, MaskReg, OpSizeBitsM1, 1);
|
||||
b(&NextBit);
|
||||
|
||||
// Early exit
|
||||
Bind(&EarlyExit);
|
||||
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
|
||||
// Early exit
|
||||
Bind(&EarlyExit);
|
||||
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
|
||||
|
||||
// All done with nothing to do.
|
||||
Bind(&Done);
|
||||
// All done with nothing to do.
|
||||
Bind(&Done);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LDiv) {
|
||||
|
||||
@@ -184,7 +184,7 @@ DEF_OP(Syscall) {
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
FillStaticRegs(true, GPRSpillMask, FPRSpillMask);
|
||||
FillStaticRegs(true, GPRSpillMask, FPRSpillMask, ARMEmitter::Reg::r1, ARMEmitter::Reg::r2);
|
||||
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
@@ -285,7 +285,7 @@ DEF_OP(InlineSyscall) {
|
||||
if ((Op->Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
// Now that we are done in the syscall we need to carefully peel back the state
|
||||
// First unspill the registers from before
|
||||
FillStaticRegs(false, SpillMask);
|
||||
FillStaticRegs(false, SpillMask, ~0U, ARMEmitter::Reg::r8, ARMEmitter::Reg::r1);
|
||||
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
|
||||
@@ -5,6 +5,7 @@ tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
@@ -355,6 +356,39 @@ DEF_OP(Vector_FToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VFCVTL2) {
|
||||
const auto Op = IROp->C<IR::IROp_VFCVTL2>();
|
||||
|
||||
const auto SubEmitSize = ConvertSubRegSize248(IROp);
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
|
||||
fcvtl2(SubEmitSize, Dst.D(), Vector.D());
|
||||
}
|
||||
|
||||
DEF_OP(VFCVTN2) {
|
||||
const auto Op = IROp->C<IR::IROp_VFCVTN2>();
|
||||
|
||||
const auto SubEmitSize = ConvertSubRegSize248(IROp);
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto VectorLower = GetVReg(Op->VectorLower.ID());
|
||||
const auto VectorUpper = GetVReg(Op->VectorUpper.ID());
|
||||
|
||||
auto Lower = VectorLower;
|
||||
if (Dst != VectorLower) {
|
||||
mov(VTMP1.Q(), VectorLower.Q());
|
||||
Lower = VTMP1;
|
||||
}
|
||||
|
||||
fcvtn2(SubEmitSize, Lower.Q(), VectorUpper.Q());
|
||||
|
||||
if (Dst != VectorLower) {
|
||||
mov(Dst.Q(), Lower.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToI) {
|
||||
const auto Op = IROp->C<IR::IROp_Vector_FToI>();
|
||||
const auto OpSize = IROp->Size;
|
||||
@@ -416,5 +450,63 @@ DEF_OP(Vector_FToI) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_F64ToI32) {
|
||||
const auto Op = IROp->C<IR::IROp_Vector_F64ToI32>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Round = Op->Round;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
if (HostSupportsSVE128 || HostSupportsSVE256) {
|
||||
const auto Mask = Is256Bit ? PRED_TMP_32B.Merging() : PRED_TMP_16B.Merging();
|
||||
// First step is to round the f64 values to integrals (frint*)
|
||||
// Then convert to integers using fcvtzs.
|
||||
auto CVTReg = Dst.Z();
|
||||
switch (Round) {
|
||||
case IR::Round_Nearest.Val: frintn(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
|
||||
case IR::Round_Negative_Infinity.Val: frintm(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
|
||||
case IR::Round_Positive_Infinity.Val: frintp(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
|
||||
case IR::Round_Towards_Zero.Val: CVTReg = Vector.Z(); break;
|
||||
case IR::Round_Host.Val: frinti(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
|
||||
}
|
||||
|
||||
fcvtzs(Dst.Z(), ARMEmitter::SubRegSize::i32Bit, Mask, CVTReg, ARMEmitter::SubRegSize::i64Bit);
|
||||
|
||||
///< Fixup format of register that fcvtzs returns.
|
||||
uzp1(ARMEmitter::SubRegSize::i32Bit, Dst.Z(), Dst.Z(), Dst.Z());
|
||||
if (Op->EnsureZeroUpperHalf) {
|
||||
///< Match CVTPD2DQ/CVTTPD2DQ behaviour if necessary by zeroing the upper bits here.
|
||||
if (Is256Bit) {
|
||||
mov(Dst.Q(), Dst.Q());
|
||||
} else {
|
||||
mov(Dst.D(), Dst.D());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This has a known precision issue that isn't easily resolvable without throwing away performance.
|
||||
// Doing the conversion in multi-stage steps has an issue that you can lose precision in the f32->i32 step if your source was f64.
|
||||
// To get around this with ASIMD FEX needs to use fcvtzs (Scalar, Integer, to GPR) for each F64 to be directly converted to i32.
|
||||
// This is a very costly transform that the SVE path doesn't need to do since it supports f64->i32 directly.
|
||||
// If this precision issue is necessary then we can add an option for it in the future.
|
||||
|
||||
///< Round float to integral depending on rounding mode.
|
||||
switch (Round) {
|
||||
case FEXCore::IR::Round_Nearest.Val: frintn(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
|
||||
case FEXCore::IR::Round_Negative_Infinity.Val: frintm(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
|
||||
case FEXCore::IR::Round_Positive_Infinity.Val: frintp(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
|
||||
case FEXCore::IR::Round_Towards_Zero.Val: frintz(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
|
||||
case FEXCore::IR::Round_Host.Val: frinti(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
|
||||
}
|
||||
|
||||
// Now narrow from f64 to f32.
|
||||
fcvtn(ARMEmitter::SubRegSize::i32Bit, Dst.Q(), Dst.Q());
|
||||
|
||||
///< Convert the two F32 integrals to real integers.
|
||||
fcvtzs(ARMEmitter::SubRegSize::i32Bit, Dst.D(), Dst.D());
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const* IROp, IR::NodeID Node)
|
||||
DEF_OP(GetHostFlag) {
|
||||
auto Op = IROp->C<IR::IROp_GetHostFlag>();
|
||||
ubfx(ARMEmitter::Size::i64Bit, GetReg(Node), GetReg(Op->Value.ID()), Op->Flag, 1);
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -47,8 +47,8 @@ static uint64_t LUDIV(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
|
||||
return Res;
|
||||
}
|
||||
|
||||
static int64_t LDIV(int64_t SrcHigh, int64_t SrcLow, int64_t Divisor) {
|
||||
__int128_t Source = (static_cast<__int128_t>(SrcHigh) << 64) | SrcLow;
|
||||
static int64_t LDIV(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
|
||||
__int128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__int128_t Res = Source / Divisor;
|
||||
return Res;
|
||||
}
|
||||
@@ -59,8 +59,8 @@ static uint64_t LUREM(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
|
||||
return Res;
|
||||
}
|
||||
|
||||
static int64_t LREM(int64_t SrcHigh, int64_t SrcLow, int64_t Divisor) {
|
||||
__int128_t Source = (static_cast<__int128_t>(SrcHigh) << 64) | SrcLow;
|
||||
static int64_t LREM(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
|
||||
__int128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__int128_t Res = Source % Divisor;
|
||||
return Res;
|
||||
}
|
||||
@@ -496,7 +496,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame* Fram
|
||||
uintptr_t branch = (uintptr_t)(Record)-8;
|
||||
|
||||
auto offset = HostCode / 4 - branch / 4;
|
||||
if (vixl::IsInt26(offset)) {
|
||||
if (ARMEmitter::Emitter::IsInt26(offset)) {
|
||||
// optimal case - can branch directly
|
||||
// patch the code
|
||||
ARMEmitter::Emitter emit((uint8_t*)(branch), 4);
|
||||
@@ -521,8 +521,9 @@ void Arm64JITCore::Op_NoOp(const IR::IROp_Header* IROp, IR::NodeID Node) {}
|
||||
Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread)
|
||||
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
|
||||
, Arm64Emitter(ctx)
|
||||
, HostSupportsSVE128 {ctx->HostFeatures.SupportsSVE}
|
||||
, HostSupportsSVE256 {ctx->HostFeatures.SupportsAVX}
|
||||
, HostSupportsSVE128 {ctx->HostFeatures.SupportsSVE128}
|
||||
, HostSupportsSVE256 {ctx->HostFeatures.SupportsSVE256}
|
||||
, HostSupportsAVX256 {ctx->HostFeatures.SupportsAVX && ctx->HostFeatures.SupportsSVE256}
|
||||
, HostSupportsRPRES {ctx->HostFeatures.SupportsRPRES}
|
||||
, HostSupportsAFP {ctx->HostFeatures.SupportsAFP}
|
||||
, CTX {ctx} {
|
||||
@@ -723,14 +724,12 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, const FEXCore
|
||||
offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState));
|
||||
}
|
||||
|
||||
// LOGMAN_THROW_A_FMT(RAData->HasFullRA(), "Arm64 JIT only works with RA");
|
||||
|
||||
SpillSlots = RAData->SpillSlots();
|
||||
|
||||
if (SpillSlots) {
|
||||
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
|
||||
|
||||
if (vixl::aarch64::Assembler::IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
if (ARMEmitter::IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
sub(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, TotalSpillSlotsSize);
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, TotalSpillSlotsSize);
|
||||
@@ -873,7 +872,7 @@ void Arm64JITCore::ResetStack() {
|
||||
|
||||
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
|
||||
|
||||
if (vixl::aarch64::Assembler::IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
if (ARMEmitter::IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, TotalSpillSlotsSize);
|
||||
} else {
|
||||
// Too big to fit in a 12bit immediate
|
||||
@@ -886,12 +885,4 @@ fextl::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::ContextImpl*
|
||||
return fextl::make_unique<Arm64JITCore>(ctx, Thread);
|
||||
}
|
||||
|
||||
CPUBackendFeatures GetArm64JITBackendFeatures() {
|
||||
return CPUBackendFeatures {
|
||||
.SupportsFlags = true,
|
||||
.SupportsSaturatingRoundingShifts = true,
|
||||
.SupportsVTBL2 = true,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -72,6 +72,7 @@ private:
|
||||
|
||||
const bool HostSupportsSVE128 {};
|
||||
const bool HostSupportsSVE256 {};
|
||||
const bool HostSupportsAVX256 {};
|
||||
const bool HostSupportsRPRES {};
|
||||
const bool HostSupportsAFP {};
|
||||
|
||||
@@ -344,6 +345,10 @@ private:
|
||||
uint32_t SpillSlots {};
|
||||
using OpType = void (Arm64JITCore::*)(const IR::IROp_Header* IROp, IR::NodeID Node);
|
||||
|
||||
using ScalarFMAOpCaller =
|
||||
std::function<void(ARMEmitter::VRegister Dst, ARMEmitter::VRegister Src1, ARMEmitter::VRegister Src2, ARMEmitter::VRegister Src3)>;
|
||||
void VFScalarFMAOperation(uint8_t OpSize, uint8_t ElementSize, ScalarFMAOpCaller ScalarEmit, ARMEmitter::VRegister Dst,
|
||||
ARMEmitter::VRegister Vector1, ARMEmitter::VRegister Vector2, ARMEmitter::VRegister Addend);
|
||||
using ScalarBinaryOpCaller = std::function<void(ARMEmitter::VRegister Dst, ARMEmitter::VRegister Src1, ARMEmitter::VRegister Src2)>;
|
||||
void VFScalarOperation(uint8_t OpSize, uint8_t ElementSize, bool ZeroUpperBits, ScalarBinaryOpCaller ScalarEmit,
|
||||
ARMEmitter::VRegister Dst, ARMEmitter::VRegister Vector1, ARMEmitter::VRegister Vector2);
|
||||
@@ -351,6 +356,10 @@ private:
|
||||
void VFScalarUnaryOperation(uint8_t OpSize, uint8_t ElementSize, bool ZeroUpperBits, ScalarUnaryOpCaller ScalarEmit, ARMEmitter::VRegister Dst,
|
||||
ARMEmitter::VRegister Vector1, std::variant<ARMEmitter::VRegister, ARMEmitter::Register> Vector2);
|
||||
|
||||
void Emulate128BitGather(size_t Size, size_t ElementSize, ARMEmitter::VRegister Dst, ARMEmitter::VRegister IncomingDst,
|
||||
std::optional<ARMEmitter::Register> BaseAddr, ARMEmitter::VRegister VectorIndexLow,
|
||||
std::optional<ARMEmitter::VRegister> VectorIndexHigh, ARMEmitter::VRegister MaskReg, size_t VectorIndexSize,
|
||||
size_t DataElementOffsetStart, size_t IndexElementOffsetStart, uint8_t OffsetScale);
|
||||
// Runtime selection;
|
||||
// Load and store TSO memory style
|
||||
OpType RT_LoadMemTSO;
|
||||
|
||||
@@ -52,7 +52,7 @@ DEF_OP(StoreContext) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
auto Src = GetReg(Op->Value.ID());
|
||||
auto Src = GetZeroableReg(Op->Value);
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: strb(Src, STATE, Op->Offset); break;
|
||||
@@ -99,7 +99,7 @@ DEF_OP(LoadRegister) {
|
||||
}
|
||||
}
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
const auto regSize = HostSupportsSVE256 ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
[[maybe_unused]] const auto regSize = HostSupportsAVX256 ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "out of range reg");
|
||||
LOGMAN_THROW_A_FMT(OpSize == regSize, "expected sized");
|
||||
|
||||
@@ -107,7 +107,7 @@ DEF_OP(LoadRegister) {
|
||||
const auto host = GetVReg(Node);
|
||||
|
||||
if (host.Idx() != guest.Idx()) {
|
||||
if (HostSupportsSVE256) {
|
||||
if (HostSupportsAVX256) {
|
||||
mov(ARMEmitter::SubRegSize::i64Bit, host.Z(), PRED_TMP_32B.Merging(), guest.Z());
|
||||
} else {
|
||||
mov(host.Q(), guest.Q());
|
||||
@@ -120,8 +120,6 @@ DEF_OP(LoadRegister) {
|
||||
|
||||
DEF_OP(StoreRegister) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
|
||||
if (Op->Class == IR::GPRClass) {
|
||||
unsigned Reg = Op->Reg == Core::CPUState::PF_AS_GREG ? (StaticRegisters.size() - 2) :
|
||||
@@ -137,15 +135,15 @@ DEF_OP(StoreRegister) {
|
||||
mov(ARMEmitter::Size::i64Bit, reg, Src);
|
||||
}
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
const auto regSize = HostSupportsSVE256 ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
[[maybe_unused]] const auto regSize = HostSupportsAVX256 ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "reg out of range");
|
||||
LOGMAN_THROW_A_FMT(OpSize == regSize, "expected sized");
|
||||
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
|
||||
|
||||
const auto guest = StaticFPRegisters[Op->Reg];
|
||||
const auto host = GetVReg(Op->Value.ID());
|
||||
|
||||
if (guest.Idx() != host.Idx()) {
|
||||
if (HostSupportsSVE256) {
|
||||
if (HostSupportsAVX256) {
|
||||
mov(ARMEmitter::SubRegSize::i64Bit, guest.Z(), PRED_TMP_32B.Merging(), host.Z());
|
||||
} else {
|
||||
mov(guest.Q(), host.Q());
|
||||
@@ -531,25 +529,6 @@ DEF_OP(LoadDF) {
|
||||
ldrsb(Dst.X(), STATE, offsetof(FEXCore::Core::CPUState, flags[Flag]));
|
||||
}
|
||||
|
||||
DEF_OP(LoadFlag) {
|
||||
auto Op = IROp->C<IR::IROp_LoadFlag>();
|
||||
auto Dst = GetReg(Node);
|
||||
|
||||
LOGMAN_THROW_A_FMT(Op->Flag != X86State::RFLAG_PF_RAW_LOC && Op->Flag != X86State::RFLAG_AF_RAW_LOC, "PF/AF must be accessed as "
|
||||
"registers");
|
||||
|
||||
ldrb(Dst, STATE, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag);
|
||||
}
|
||||
|
||||
DEF_OP(StoreFlag) {
|
||||
auto Op = IROp->C<IR::IROp_StoreFlag>();
|
||||
|
||||
LOGMAN_THROW_A_FMT(Op->Flag != X86State::RFLAG_PF_RAW_LOC && Op->Flag != X86State::RFLAG_AF_RAW_LOC, "PF/AF must be accessed as "
|
||||
"registers");
|
||||
|
||||
strb(GetReg(Op->Value.ID()), STATE, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag);
|
||||
}
|
||||
|
||||
ARMEmitter::ExtendedMemOperand Arm64JITCore::GenerateMemOperand(
|
||||
uint8_t AccessSize, ARMEmitter::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
if (Offset.IsInvalid()) {
|
||||
@@ -752,12 +731,14 @@ DEF_OP(LoadMemTSO) {
|
||||
}
|
||||
|
||||
DEF_OP(VLoadVectorMasked) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE support in order to use VLoadVectorMasked");
|
||||
|
||||
const auto Op = IROp->C<IR::IROp_VLoadVectorMasked>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
if (Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use VLoadVectorMasked with 256-bit operation");
|
||||
}
|
||||
const auto SubRegSize = ConvertSubRegSize8(IROp);
|
||||
|
||||
const auto CMPPredicate = ARMEmitter::PReg::p0;
|
||||
@@ -766,39 +747,95 @@ DEF_OP(VLoadVectorMasked) {
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto MaskReg = GetVReg(Op->Mask.ID());
|
||||
const auto MemReg = GetReg(Op->Addr.ID());
|
||||
const auto MemSrc = GenerateSVEMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
|
||||
// Check if the sign bit is set for the given element size.
|
||||
cmplt(SubRegSize, CMPPredicate, GoverningPredicate.Zeroing(), MaskReg.Z(), 0);
|
||||
if (HostSupportsSVE128 || HostSupportsSVE256) {
|
||||
const auto MemSrc = GenerateSVEMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: {
|
||||
ld1b<ARMEmitter::SubRegSize::i8Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ld1h<ARMEmitter::SubRegSize::i16Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ld1w<ARMEmitter::SubRegSize::i32Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
ld1d(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
// Check if the sign bit is set for the given element size.
|
||||
cmplt(SubRegSize, CMPPredicate, GoverningPredicate.Zeroing(), MaskReg.Z(), 0);
|
||||
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: {
|
||||
ld1b<ARMEmitter::SubRegSize::i8Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ld1h<ARMEmitter::SubRegSize::i16Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ld1w<ARMEmitter::SubRegSize::i32Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
ld1d(Dst.Z(), CMPPredicate.Zeroing(), MemSrc);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
const auto PerformMove = [this](size_t ElementSize, const ARMEmitter::Register Dst, const ARMEmitter::VRegister Vector, int index) {
|
||||
switch (ElementSize) {
|
||||
case 1: umov<ARMEmitter::SubRegSize::i8Bit>(Dst, Vector, index); break;
|
||||
case 2: umov<ARMEmitter::SubRegSize::i16Bit>(Dst, Vector, index); break;
|
||||
case 4: umov<ARMEmitter::SubRegSize::i32Bit>(Dst, Vector, index); break;
|
||||
case 8: umov<ARMEmitter::SubRegSize::i64Bit>(Dst, Vector, index); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ExtractElementSize: {}", ElementSize); break;
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare yourself adventurer. For a masked load without instructions that implement it.
|
||||
LOGMAN_THROW_A_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE, "Only supports 128-bit without SVE256");
|
||||
size_t NumElements = IROp->Size / IROp->ElementSize;
|
||||
|
||||
// Use VTMP1 as the temporary destination
|
||||
auto TempDst = VTMP1;
|
||||
auto WorkingReg = TMP1;
|
||||
auto TempMemReg = MemReg;
|
||||
movi(ARMEmitter::SubRegSize::i64Bit, TempDst.Q(), 0);
|
||||
LOGMAN_THROW_A_FMT(Op->Offset.IsInvalid(), "Complex addressing requested and not supported!");
|
||||
|
||||
const uint64_t ElementSizeInBits = IROp->ElementSize * 8;
|
||||
for (size_t i = 0; i < NumElements; ++i) {
|
||||
// Extract the mask element.
|
||||
PerformMove(IROp->ElementSize, WorkingReg, MaskReg, i);
|
||||
|
||||
// If the sign bit is zero then skip the load
|
||||
ARMEmitter::SingleUseForwardLabel Skip {};
|
||||
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
// Do the gather load for this element into the destination
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: ld1<ARMEmitter::SubRegSize::i8Bit>(TempDst.Q(), i, TempMemReg); break;
|
||||
case 2: ld1<ARMEmitter::SubRegSize::i16Bit>(TempDst.Q(), i, TempMemReg); break;
|
||||
case 4: ld1<ARMEmitter::SubRegSize::i32Bit>(TempDst.Q(), i, TempMemReg); break;
|
||||
case 8: ld1<ARMEmitter::SubRegSize::i64Bit>(TempDst.Q(), i, TempMemReg); break;
|
||||
case 16: ldr(TempDst.Q(), TempMemReg, 0); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, IROp->ElementSize); return;
|
||||
}
|
||||
|
||||
Bind(&Skip);
|
||||
|
||||
if ((i + 1) != NumElements) {
|
||||
// Handle register rename to save a move.
|
||||
auto WorkingReg = TempMemReg;
|
||||
TempMemReg = TMP2;
|
||||
add(ARMEmitter::Size::i64Bit, TempMemReg, WorkingReg, IROp->ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Move result.
|
||||
mov(Dst.Q(), TempDst.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VStoreVectorMasked) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE support in order to use VStoreVectorMasked");
|
||||
|
||||
const auto Op = IROp->C<IR::IROp_VStoreVectorMasked>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
if (Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use VStoreVectorMasked with 256-bit operation");
|
||||
}
|
||||
const auto SubRegSize = ConvertSubRegSize8(IROp);
|
||||
|
||||
const auto CMPPredicate = ARMEmitter::PReg::p0;
|
||||
@@ -807,29 +844,366 @@ DEF_OP(VStoreVectorMasked) {
|
||||
const auto RegData = GetVReg(Op->Data.ID());
|
||||
const auto MaskReg = GetVReg(Op->Mask.ID());
|
||||
const auto MemReg = GetReg(Op->Addr.ID());
|
||||
const auto MemDst = GenerateSVEMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
if (HostSupportsSVE128 || HostSupportsSVE256) {
|
||||
const auto MemDst = GenerateSVEMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
|
||||
// Check if the sign bit is set for the given element size.
|
||||
cmplt(SubRegSize, CMPPredicate, GoverningPredicate.Zeroing(), MaskReg.Z(), 0);
|
||||
// Check if the sign bit is set for the given element size.
|
||||
cmplt(SubRegSize, CMPPredicate, GoverningPredicate.Zeroing(), MaskReg.Z(), 0);
|
||||
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: {
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: {
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
st1h<ARMEmitter::SubRegSize::i16Bit>(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
st1w<ARMEmitter::SubRegSize::i32Bit>(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
st1d(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
const auto PerformMove = [this](size_t ElementSize, const ARMEmitter::Register Dst, const ARMEmitter::VRegister Vector, int index) {
|
||||
switch (ElementSize) {
|
||||
case 1: umov<ARMEmitter::SubRegSize::i8Bit>(Dst, Vector, index); break;
|
||||
case 2: umov<ARMEmitter::SubRegSize::i16Bit>(Dst, Vector, index); break;
|
||||
case 4: umov<ARMEmitter::SubRegSize::i32Bit>(Dst, Vector, index); break;
|
||||
case 8: umov<ARMEmitter::SubRegSize::i64Bit>(Dst, Vector, index); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ExtractElementSize: {}", ElementSize); break;
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare yourself adventurer. For a masked store without instructions that implement it.
|
||||
LOGMAN_THROW_A_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE, "Only supports 128-bit without SVE256");
|
||||
size_t NumElements = IROp->Size / IROp->ElementSize;
|
||||
|
||||
// Use VTMP1 as the temporary destination
|
||||
auto WorkingReg = TMP1;
|
||||
auto TempMemReg = MemReg;
|
||||
LOGMAN_THROW_A_FMT(Op->Offset.IsInvalid(), "Complex addressing requested and not supported!");
|
||||
|
||||
const uint64_t ElementSizeInBits = IROp->ElementSize * 8;
|
||||
for (size_t i = 0; i < NumElements; ++i) {
|
||||
// Extract the mask element.
|
||||
PerformMove(IROp->ElementSize, WorkingReg, MaskReg, i);
|
||||
|
||||
// If the sign bit is zero then skip the load
|
||||
ARMEmitter::SingleUseForwardLabel Skip {};
|
||||
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
// Do the gather load for this element into the destination
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: st1<ARMEmitter::SubRegSize::i8Bit>(RegData.Q(), i, TempMemReg); break;
|
||||
case 2: st1<ARMEmitter::SubRegSize::i16Bit>(RegData.Q(), i, TempMemReg); break;
|
||||
case 4: st1<ARMEmitter::SubRegSize::i32Bit>(RegData.Q(), i, TempMemReg); break;
|
||||
case 8: st1<ARMEmitter::SubRegSize::i64Bit>(RegData.Q(), i, TempMemReg); break;
|
||||
case 16: str(RegData.Q(), TempMemReg, 0); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, IROp->ElementSize); return;
|
||||
}
|
||||
|
||||
Bind(&Skip);
|
||||
|
||||
if ((i + 1) != NumElements) {
|
||||
// Handle register rename to save a move.
|
||||
auto WorkingReg = TempMemReg;
|
||||
TempMemReg = TMP2;
|
||||
add(ARMEmitter::Size::i64Bit, TempMemReg, WorkingReg, IROp->ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2: {
|
||||
st1h<ARMEmitter::SubRegSize::i16Bit>(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
|
||||
void Arm64JITCore::Emulate128BitGather(
|
||||
size_t Size, size_t ElementSize, ARMEmitter::VRegister Dst, ARMEmitter::VRegister IncomingDst,
|
||||
std::optional<ARMEmitter::Register> BaseAddr, ARMEmitter::VRegister VectorIndexLow, std::optional<ARMEmitter::VRegister> VectorIndexHigh,
|
||||
ARMEmitter::VRegister MaskReg, size_t VectorIndexSize, size_t DataElementOffsetStart, size_t IndexElementOffsetStart, uint8_t OffsetScale) {
|
||||
|
||||
const auto PerformSMove = [this](size_t ElementSize, const ARMEmitter::Register Dst, const ARMEmitter::VRegister Vector, int index) {
|
||||
switch (ElementSize) {
|
||||
case 1: smov<ARMEmitter::SubRegSize::i8Bit>(Dst.X(), Vector, index); break;
|
||||
case 2: smov<ARMEmitter::SubRegSize::i16Bit>(Dst.X(), Vector, index); break;
|
||||
case 4: smov<ARMEmitter::SubRegSize::i32Bit>(Dst.X(), Vector, index); break;
|
||||
case 8: umov<ARMEmitter::SubRegSize::i64Bit>(Dst.X(), Vector, index); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ExtractElementSize: {}", ElementSize); break;
|
||||
}
|
||||
};
|
||||
|
||||
const auto PerformMove = [this](size_t ElementSize, const ARMEmitter::Register Dst, const ARMEmitter::VRegister Vector, int index) {
|
||||
switch (ElementSize) {
|
||||
case 1: umov<ARMEmitter::SubRegSize::i8Bit>(Dst, Vector, index); break;
|
||||
case 2: umov<ARMEmitter::SubRegSize::i16Bit>(Dst, Vector, index); break;
|
||||
case 4: umov<ARMEmitter::SubRegSize::i32Bit>(Dst, Vector, index); break;
|
||||
case 8: umov<ARMEmitter::SubRegSize::i64Bit>(Dst, Vector, index); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ExtractElementSize: {}", ElementSize); break;
|
||||
}
|
||||
};
|
||||
|
||||
// FEX needs to use a temporary destination vector register in a couple of instances.
|
||||
// When Dst overlaps MaskReg, VectorIndexLow, or VectorIndexHigh
|
||||
// Due to x86 gather instruction limitations, it is highly likely that a destination temporary isn't required.
|
||||
const bool NeedsDestTmp = Dst == MaskReg || Dst == VectorIndexLow || (VectorIndexHigh.has_value() && Dst == *VectorIndexHigh);
|
||||
|
||||
// If the incoming destination isn't the destination then we need to move.
|
||||
const bool NeedsIncomingDestMove = Dst != IncomingDst || NeedsDestTmp;
|
||||
|
||||
///< Adventurers beware, emulated ASIMD style gather masked load operation.
|
||||
// Number of elements to load is calculated by the number of index elements available.
|
||||
size_t NumAddrElements = (VectorIndexHigh.has_value() ? 32 : 16) / VectorIndexSize;
|
||||
// The number of elements is clamped by the resulting register size.
|
||||
size_t NumDataElements = std::min<size_t>(Size / ElementSize, NumAddrElements);
|
||||
|
||||
size_t IndexElementsSizeBytes = NumAddrElements * VectorIndexSize;
|
||||
if (IndexElementsSizeBytes > 16) {
|
||||
// We must have a high register in this case.
|
||||
LOGMAN_THROW_A_FMT(VectorIndexHigh.has_value(), "Need High vector index register!");
|
||||
}
|
||||
case 4: {
|
||||
st1w<ARMEmitter::SubRegSize::i32Bit>(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
|
||||
auto ResultReg = Dst;
|
||||
if (NeedsDestTmp) {
|
||||
// Use VTMP1 as the temporary destination
|
||||
ResultReg = VTMP1;
|
||||
}
|
||||
case 8: {
|
||||
st1d(RegData.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
auto WorkingReg = TMP1;
|
||||
auto TempMemReg = TMP2;
|
||||
const uint64_t ElementSizeInBits = ElementSize * 8;
|
||||
|
||||
if (NeedsIncomingDestMove) {
|
||||
mov(ResultReg.Q(), IncomingDst.Q());
|
||||
}
|
||||
default: break;
|
||||
|
||||
for (size_t i = DataElementOffsetStart, IndexElement = IndexElementOffsetStart; i < NumDataElements; ++i, ++IndexElement) {
|
||||
ARMEmitter::SingleUseForwardLabel Skip {};
|
||||
// Extract mask element
|
||||
PerformMove(ElementSize, WorkingReg, MaskReg, i);
|
||||
|
||||
// Skip if the mask's sign bit isn't set
|
||||
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
|
||||
// Extract Index Element
|
||||
if ((IndexElement * VectorIndexSize) >= 16) {
|
||||
// Fetch from the high index register.
|
||||
PerformSMove(VectorIndexSize, WorkingReg, *VectorIndexHigh, IndexElement - (16 / VectorIndexSize));
|
||||
} else {
|
||||
// Fetch from the low index register.
|
||||
PerformSMove(VectorIndexSize, WorkingReg, VectorIndexLow, IndexElement);
|
||||
}
|
||||
|
||||
// Calculate memory position for this gather load
|
||||
if (BaseAddr.has_value()) {
|
||||
if (VectorIndexSize == 4) {
|
||||
add(ARMEmitter::Size::i64Bit, TempMemReg, *BaseAddr, WorkingReg, ARMEmitter::ExtendedType::SXTW, FEXCore::ilog2(OffsetScale));
|
||||
} else {
|
||||
add(ARMEmitter::Size::i64Bit, TempMemReg, *BaseAddr, WorkingReg, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(OffsetScale));
|
||||
}
|
||||
} else {
|
||||
///< In this case we have no base address, All addresses come from the vector register itself
|
||||
if (VectorIndexSize == 4) {
|
||||
// Sign extend and shift in to the 64-bit register
|
||||
sbfiz(ARMEmitter::Size::i64Bit, TempMemReg, WorkingReg, FEXCore::ilog2(OffsetScale), 32);
|
||||
} else {
|
||||
lsl(ARMEmitter::Size::i64Bit, TempMemReg, WorkingReg, FEXCore::ilog2(OffsetScale));
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the address is calculated. Do the load.
|
||||
switch (ElementSize) {
|
||||
case 1: ld1<ARMEmitter::SubRegSize::i8Bit>(ResultReg.Q(), i, TempMemReg); break;
|
||||
case 2: ld1<ARMEmitter::SubRegSize::i16Bit>(ResultReg.Q(), i, TempMemReg); break;
|
||||
case 4: ld1<ARMEmitter::SubRegSize::i32Bit>(ResultReg.Q(), i, TempMemReg); break;
|
||||
case 8: ld1<ARMEmitter::SubRegSize::i64Bit>(ResultReg.Q(), i, TempMemReg); break;
|
||||
case 16: ldr(ResultReg.Q(), TempMemReg, 0); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, ElementSize); FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
Bind(&Skip);
|
||||
}
|
||||
|
||||
if (NeedsDestTmp) {
|
||||
// Move result.
|
||||
mov(Dst.Q(), ResultReg.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VLoadVectorGatherMasked) {
|
||||
const auto Op = IROp->C<IR::IROp_VLoadVectorGatherMasked>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto VectorIndexSize = Op->VectorIndexElementSize;
|
||||
const auto OffsetScale = Op->OffsetScale;
|
||||
const auto DataElementOffsetStart = Op->DataElementOffsetStart;
|
||||
const auto IndexElementOffsetStart = Op->IndexElementOffsetStart;
|
||||
|
||||
///< This IR operation handles discontiguous masked gather loadstore instructions. Some things to note about its behaviour.
|
||||
/// - VSIB behaviour is mostly entirely exposed in the IR operation directly.
|
||||
/// - Displacement is the only value missing as that can be added directly to AddrBase.
|
||||
/// - VectorIndex{Low,High} contains the index offsets for each element getting loaded.
|
||||
/// - These element sizes are decoupled from the resulting element size. These can be 32-bit or 64-bit.
|
||||
/// - When the element size is 32-bit then the value is zero-extended to the full 64-bit address calculation
|
||||
/// - When loading a 128-bit result with 64-bit VectorIndex Elements, this requires the use of both VectorIndexLow and VectorIndexHigh
|
||||
/// to get enough pointers.
|
||||
/// - When VectorIndexElementSize and OffsetScale matches Arm64 SVE behaviour then the operation becomes more optimal
|
||||
/// - When the behaviour doesn't match then it gets decomposed to ASIMD style masked load.
|
||||
/// - AddrBase also doesn't need to exist
|
||||
/// - If the instruction is using 64-bit vector indexing or 32-bit addresses where the top-bit isn't set then this is valid!
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
if (Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use VStoreVectorMasked with 256-bit operation");
|
||||
}
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto IncomingDst = GetVReg(Op->Incoming.ID());
|
||||
|
||||
const auto MaskReg = GetVReg(Op->Mask.ID());
|
||||
std::optional<ARMEmitter::Register> BaseAddr = !Op->AddrBase.IsInvalid() ? std::make_optional(GetReg(Op->AddrBase.ID())) : std::nullopt;
|
||||
const auto VectorIndexLow = GetVReg(Op->VectorIndexLow.ID());
|
||||
std::optional<ARMEmitter::VRegister> VectorIndexHigh =
|
||||
!Op->VectorIndexHigh.IsInvalid() ? std::make_optional(GetVReg(Op->VectorIndexHigh.ID())) : std::nullopt;
|
||||
|
||||
///< If the host supports SVE and the offset scale matches SVE limitations then it can do an SVE style load.
|
||||
const bool SupportsSVELoad = (HostSupportsSVE128 || HostSupportsSVE256) && (OffsetScale == 1 || OffsetScale == VectorIndexSize) &&
|
||||
VectorIndexSize == IROp->ElementSize;
|
||||
|
||||
if (SupportsSVELoad) {
|
||||
uint8_t SVEScale = FEXCore::ilog2(OffsetScale);
|
||||
ARMEmitter::SVEModType ModType = ARMEmitter::SVEModType::MOD_NONE;
|
||||
if (VectorIndexSize == 4) {
|
||||
ModType = ARMEmitter::SVEModType::MOD_SXTW;
|
||||
} else if (VectorIndexSize == 8 && OffsetScale != 1) {
|
||||
ModType = ARMEmitter::SVEModType::MOD_LSL;
|
||||
}
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize8(IROp);
|
||||
|
||||
const auto CMPPredicate = ARMEmitter::PReg::p0;
|
||||
const auto GoverningPredicate = Is256Bit ? PRED_TMP_32B : PRED_TMP_16B;
|
||||
|
||||
// Check if the sign bit is set for the given element size.
|
||||
cmplt(SubRegSize, CMPPredicate, GoverningPredicate.Zeroing(), MaskReg.Z(), 0);
|
||||
auto TempDst = VTMP1;
|
||||
|
||||
// No need to load a temporary register in the case that we weren't provided a base address and there is no scaling.
|
||||
ARMEmitter::SVEMemOperand MemDst {ARMEmitter::SVEMemOperand(VectorIndexLow.Z(), 0)};
|
||||
if (BaseAddr.has_value() || OffsetScale != 1) {
|
||||
ARMEmitter::Register AddrReg = TMP1;
|
||||
if (BaseAddr.has_value()) {
|
||||
AddrReg = GetReg(Op->AddrBase.ID());
|
||||
} else {
|
||||
///< OpcodeDispatcher didn't provide a Base address while SVE requires one.
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, AddrReg, 0);
|
||||
}
|
||||
MemDst = ARMEmitter::SVEMemOperand(AddrReg.X(), VectorIndexLow.Z(), ModType, SVEScale);
|
||||
}
|
||||
|
||||
switch (IROp->ElementSize) {
|
||||
case 1: {
|
||||
ld1b<ARMEmitter::SubRegSize::i8Bit>(TempDst.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ld1h<ARMEmitter::SubRegSize::i16Bit>(TempDst.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ld1w<ARMEmitter::SubRegSize::i32Bit>(TempDst.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
ld1d(TempDst.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
///< Merge elements based on predicate.
|
||||
sel(SubRegSize, Dst.Z(), CMPPredicate, TempDst.Z(), IncomingDst.Z());
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(!Is256Bit, "Can't emulate this gather load in the backend! Programming error!");
|
||||
Emulate128BitGather(IROp->Size, IROp->ElementSize, Dst, IncomingDst, BaseAddr, VectorIndexLow, VectorIndexHigh, MaskReg,
|
||||
VectorIndexSize, DataElementOffsetStart, IndexElementOffsetStart, OffsetScale);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VLoadVectorGatherMaskedQPS) {
|
||||
const auto Op = IROp->C<IR::IROp_VLoadVectorGatherMaskedQPS>();
|
||||
|
||||
/// This instruction behaves similarly to the non-QPS version except for some STRICT limitations
|
||||
/// - Only supports 32-bit element data size!
|
||||
/// - Only supports 64-bit element address size!
|
||||
/// - Only masks elements based on 32-bit element data size! (NOT ADDR SIZE!)
|
||||
/// - Optimally uses SVE's `ld1w {zt.D}` variant instruction!
|
||||
/// - Only outputs a single 128-bit result, while consuming 128-bit or 256-bit of address indexes!
|
||||
/// - Matches VGATHERQPS/VPGATHERQD behaviour!
|
||||
const auto OffsetScale = Op->OffsetScale;
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto IncomingDst = GetVReg(Op->Incoming.ID());
|
||||
|
||||
const auto MaskReg = GetVReg(Op->MaskReg.ID());
|
||||
std::optional<ARMEmitter::Register> BaseAddr = !Op->AddrBase.IsInvalid() ? std::make_optional(GetReg(Op->AddrBase.ID())) : std::nullopt;
|
||||
const auto VectorIndexLow = GetVReg(Op->VectorIndexLow.ID());
|
||||
std::optional<ARMEmitter::VRegister> VectorIndexHigh =
|
||||
!Op->VectorIndexHigh.IsInvalid() ? std::make_optional(GetVReg(Op->VectorIndexHigh.ID())) : std::nullopt;
|
||||
|
||||
///< If the host supports SVE and the offset scale matches SVE limitations then it can do an SVE style load.
|
||||
if (HostSupportsSVE128 && (OffsetScale == 1 || OffsetScale == 4)) {
|
||||
ARMEmitter::SVEModType ModType = ARMEmitter::SVEModType::MOD_NONE;
|
||||
if (OffsetScale != 1) {
|
||||
ModType = ARMEmitter::SVEModType::MOD_LSL;
|
||||
}
|
||||
|
||||
const auto CMPPredicate = ARMEmitter::PReg::p0;
|
||||
const auto CMPPredicate2 = ARMEmitter::PReg::p1;
|
||||
|
||||
const auto GoverningPredicate = PRED_TMP_16B;
|
||||
|
||||
// Check if the sign bit is set for the given element size.
|
||||
// This will set the predicate bits for elements [0, 1, 2, 3]
|
||||
// We then use punpklo to extend the low results to be for 64-bit elements.
|
||||
cmplt(ARMEmitter::SubRegSize::i32Bit, CMPPredicate, GoverningPredicate.Zeroing(), MaskReg.Z(), 0);
|
||||
punpklo(CMPPredicate2, CMPPredicate);
|
||||
auto TempDst = VTMP1;
|
||||
|
||||
auto GatherExtend = [this](ARMEmitter::VRegister Dst, std::optional<ARMEmitter::Register> BaseAddr, ARMEmitter::VRegister VectorIndex,
|
||||
ARMEmitter::PRegister CMPPredicate, ARMEmitter::SVEModType ModType, uint8_t OffsetScale) {
|
||||
// No need to load a temporary register in the case that we weren't provided a base address and there is no scaling.
|
||||
uint8_t SVEScale = FEXCore::ilog2(OffsetScale);
|
||||
ARMEmitter::SVEMemOperand MemDst {ARMEmitter::SVEMemOperand(VectorIndex.Z(), 0)};
|
||||
if (BaseAddr.has_value() || OffsetScale != 1) {
|
||||
ARMEmitter::Register AddrReg = TMP1;
|
||||
if (BaseAddr.has_value()) {
|
||||
AddrReg = *BaseAddr;
|
||||
} else {
|
||||
///< OpcodeDispatcher didn't provide a Base address while SVE requires one.
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, AddrReg, 0);
|
||||
}
|
||||
MemDst = ARMEmitter::SVEMemOperand(AddrReg.X(), VectorIndex.Z(), ModType, SVEScale);
|
||||
}
|
||||
|
||||
ld1w<ARMEmitter::SubRegSize::i64Bit>(Dst.Z(), CMPPredicate.Zeroing(), MemDst);
|
||||
};
|
||||
|
||||
GatherExtend(TempDst, BaseAddr, VectorIndexLow, CMPPredicate2, ModType, OffsetScale);
|
||||
|
||||
if (VectorIndexHigh.has_value()) {
|
||||
punpkhi(CMPPredicate2, CMPPredicate);
|
||||
GatherExtend(VTMP2, BaseAddr, *VectorIndexHigh, CMPPredicate2, ModType, OffsetScale);
|
||||
// Move elements to the lower half.
|
||||
uzp1(ARMEmitter::SubRegSize::i32Bit, TempDst.Q(), TempDst.Q(), VTMP2.Q());
|
||||
///< Merge elements based on predicate.
|
||||
sel(ARMEmitter::SubRegSize::i32Bit, Dst.Z(), CMPPredicate, TempDst.Z(), IncomingDst.Z());
|
||||
} else {
|
||||
// Move elements to the lower half.
|
||||
xtn(ARMEmitter::SubRegSize::i32Bit, TempDst.Q(), TempDst.Q());
|
||||
///< Merge elements based on predicate.
|
||||
sel(ARMEmitter::SubRegSize::i32Bit, Dst.Z(), CMPPredicate, TempDst.Z(), IncomingDst.Z());
|
||||
}
|
||||
} else {
|
||||
Emulate128BitGather(16, 4, Dst, IncomingDst, BaseAddr, VectorIndexLow, VectorIndexHigh, MaskReg, 8, 0, 0, OffsetScale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1883,5 +2257,73 @@ DEF_OP(Prefetch) {
|
||||
prfm(PrefetchType[LUT], MemSrc);
|
||||
}
|
||||
|
||||
DEF_OP(VStoreNonTemporal) {
|
||||
const auto Op = IROp->C<IR::IROp_VStoreNonTemporal>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto Is128Bit = OpSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
const auto Value = GetVReg(Op->Value.ID());
|
||||
const auto MemReg = GetReg(Op->Addr.ID());
|
||||
const auto Offset = Op->Offset;
|
||||
|
||||
if (Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use VStoreNonTemporal with 256-bit operation");
|
||||
const auto GoverningPredicate = PRED_TMP_32B.Zeroing();
|
||||
const auto OffsetScaled = Offset / 32;
|
||||
stnt1b(Value.Z(), GoverningPredicate, MemReg, OffsetScaled);
|
||||
} else if (Is128Bit && HostSupportsSVE128) {
|
||||
const auto GoverningPredicate = PRED_TMP_16B.Zeroing();
|
||||
const auto OffsetScaled = Offset / 16;
|
||||
stnt1b(Value.Z(), GoverningPredicate, MemReg, OffsetScaled);
|
||||
} else {
|
||||
// Treat the non-temporal store as a regular vector store in this case for compatibility
|
||||
str(Value.Q(), MemReg, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VStoreNonTemporalPair) {
|
||||
const auto Op = IROp->C<IR::IROp_VStoreNonTemporalPair>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
[[maybe_unused]] const auto Is128Bit = OpSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
LOGMAN_THROW_A_FMT(Is128Bit, "This IR operation only operates at 128-bit wide");
|
||||
|
||||
const auto ValueLow = GetVReg(Op->ValueLow.ID());
|
||||
const auto ValueHigh = GetVReg(Op->ValueHigh.ID());
|
||||
|
||||
const auto MemReg = GetReg(Op->Addr.ID());
|
||||
const auto Offset = Op->Offset;
|
||||
|
||||
stnp(ValueLow.Q(), ValueHigh.Q(), MemReg, Offset);
|
||||
}
|
||||
|
||||
DEF_OP(VLoadNonTemporal) {
|
||||
const auto Op = IROp->C<IR::IROp_VLoadNonTemporal>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto Is128Bit = OpSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto MemReg = GetReg(Op->Addr.ID());
|
||||
const auto Offset = Op->Offset;
|
||||
|
||||
if (Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use VStoreNonTemporal with 256-bit operation");
|
||||
const auto GoverningPredicate = PRED_TMP_32B.Zeroing();
|
||||
const auto OffsetScaled = Offset / 32;
|
||||
ldnt1b(Dst.Z(), GoverningPredicate, MemReg, OffsetScaled);
|
||||
} else if (Is128Bit && HostSupportsSVE128) {
|
||||
const auto GoverningPredicate = PRED_TMP_16B.Zeroing();
|
||||
const auto OffsetScaled = Offset / 16;
|
||||
ldnt1b(Dst.Z(), GoverningPredicate, MemReg, OffsetScaled);
|
||||
} else {
|
||||
// Treat the non-temporal store as a regular vector store in this case for compatibility
|
||||
ldr(Dst.Q(), MemReg, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -98,6 +98,7 @@ DEF_OP(GetRoundingMode) {
|
||||
DEF_OP(SetRoundingMode) {
|
||||
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
|
||||
auto Src = GetReg(Op->RoundMode.ID());
|
||||
auto MXCSR = GetReg(Op->MXCSR.ID());
|
||||
|
||||
// As above, setup the rounding flags in [31:30]
|
||||
rbit(ARMEmitter::Size::i32Bit, TMP2, Src);
|
||||
@@ -116,10 +117,48 @@ DEF_OP(SetRoundingMode) {
|
||||
lsr(ARMEmitter::Size::i64Bit, TMP2, Src, 2);
|
||||
bfi(ARMEmitter::Size::i64Bit, TMP1, TMP2, 24, 1);
|
||||
|
||||
if (Op->SetDAZ && HostSupportsAFP) {
|
||||
// Extract DAZ from MXCSR and insert to in FPCR.FIZ
|
||||
bfxil(ARMEmitter::Size::i64Bit, TMP1, MXCSR, 6, 1);
|
||||
}
|
||||
|
||||
// Now save the new FPCR
|
||||
msr(ARMEmitter::SystemRegister::FPCR, TMP1);
|
||||
}
|
||||
|
||||
DEF_OP(PushRoundingMode) {
|
||||
auto Op = IROp->C<IR::IROp_PushRoundingMode>();
|
||||
auto Dest = GetReg(Node);
|
||||
|
||||
// Save the old rounding mode
|
||||
mrs(Dest, ARMEmitter::SystemRegister::FPCR);
|
||||
|
||||
// vixl simulator doesn't support anything beyond ties-to-even rounding
|
||||
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the rounding flags, reversing the mode bits as above
|
||||
if (Op->RoundMode == 3) {
|
||||
orr(ARMEmitter::Size::i64Bit, TMP1, Dest, 3 << 22);
|
||||
} else if (Op->RoundMode == 0) {
|
||||
and_(ARMEmitter::Size::i64Bit, TMP1, Dest, ~(3 << 22));
|
||||
} else {
|
||||
LOGMAN_THROW_AA_FMT(Op->RoundMode == 1 || Op->RoundMode == 2, "expect a valid round mode");
|
||||
|
||||
and_(ARMEmitter::Size::i64Bit, TMP1, Dest, ~(Op->RoundMode << 22));
|
||||
orr(ARMEmitter::Size::i64Bit, TMP1, TMP1, (Op->RoundMode == 2 ? 1 : 2) << 22);
|
||||
}
|
||||
|
||||
// Now save the new FPCR
|
||||
msr(ARMEmitter::SystemRegister::FPCR, TMP1);
|
||||
}
|
||||
|
||||
DEF_OP(PopRoundingMode) {
|
||||
auto Op = IROp->C<IR::IROp_PopRoundingMode>();
|
||||
msr(ARMEmitter::SystemRegister::FPCR, GetReg(Op->FPCR.ID()));
|
||||
}
|
||||
|
||||
DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
|
||||
@@ -194,7 +233,7 @@ DEF_OP(ProcessorID) {
|
||||
|
||||
// Now that we are done in the syscall we need to carefully peel back the state
|
||||
// First unspill the registers from before
|
||||
FillStaticRegs(false, SpillMask);
|
||||
FillStaticRegs(false, SpillMask, ~0U, ARMEmitter::Reg::r8, ARMEmitter::Reg::r2);
|
||||
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
|
||||
@@ -53,7 +53,7 @@ DEF_OP(Copy) {
|
||||
DEF_OP(Swap1) {
|
||||
auto Op = IROp->C<IR::IROp_Swap1>();
|
||||
auto A = GetReg(Op->A.ID()), B = GetReg(Op->B.ID());
|
||||
LOGMAN_THROW_AA_FMT(B == GetReg(Node), "Invariant");
|
||||
LOGMAN_THROW_A_FMT(B == GetReg(Node), "Invariant");
|
||||
|
||||
mov(ARMEmitter::Size::i64Bit, TMP1, A);
|
||||
mov(ARMEmitter::Size::i64Bit, A, B);
|
||||
|
||||
@@ -188,13 +188,37 @@ namespace FEXCore::CPU {
|
||||
VFScalarOperation(IROp->Size, ElementSize, Op->ZeroUpperBits, ScalarEmit, Dst, Vector1, Vector2); \
|
||||
}
|
||||
|
||||
#define DEF_FMAOP_SCALAR_INSERT(FEXOp, ARMOp) \
|
||||
DEF_OP(FEXOp) { \
|
||||
const auto Op = IROp->C<IR::IROp_##FEXOp>(); \
|
||||
const auto ElementSize = Op->Header.ElementSize; \
|
||||
\
|
||||
auto ScalarEmit = \
|
||||
[this, ElementSize](ARMEmitter::VRegister Dst, ARMEmitter::VRegister Src1, ARMEmitter::VRegister Src2, ARMEmitter::VRegister Src3) { \
|
||||
if (ElementSize == 2) { \
|
||||
ARMOp(Dst.H(), Src1.H(), Src2.H(), Src3.H()); \
|
||||
} else if (ElementSize == 4) { \
|
||||
ARMOp(Dst.S(), Src1.S(), Src2.S(), Src3.S()); \
|
||||
} else if (ElementSize == 8) { \
|
||||
ARMOp(Dst.D(), Src1.D(), Src2.D(), Src3.D()); \
|
||||
} \
|
||||
}; \
|
||||
\
|
||||
const auto Dst = GetVReg(Node); \
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID()); \
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID()); \
|
||||
const auto Addend = GetVReg(Op->Addend.ID()); \
|
||||
\
|
||||
VFScalarFMAOperation(IROp->Size, ElementSize, ScalarEmit, Dst, Vector1, Vector2, Addend); \
|
||||
}
|
||||
|
||||
DEF_UNOP(VAbs, abs, true)
|
||||
DEF_UNOP(VPopcount, cnt, true)
|
||||
DEF_UNOP(VNeg, neg, false)
|
||||
DEF_UNOP(VFNeg, fneg, false)
|
||||
|
||||
DEF_BITOP(VAnd, and_)
|
||||
DEF_BITOP(VBic, bic)
|
||||
DEF_BITOP(VAndn, bic)
|
||||
DEF_BITOP(VOr, orr)
|
||||
DEF_BITOP(VXor, eor)
|
||||
|
||||
@@ -224,16 +248,47 @@ DEF_FBINOP_SCALAR_INSERT(VFSubScalarInsert, fsub)
|
||||
DEF_FBINOP_SCALAR_INSERT(VFMulScalarInsert, fmul)
|
||||
DEF_FBINOP_SCALAR_INSERT(VFDivScalarInsert, fdiv)
|
||||
|
||||
DEF_FMAOP_SCALAR_INSERT(VFMLAScalarInsert, fmadd)
|
||||
DEF_FMAOP_SCALAR_INSERT(VFMLSScalarInsert, fnmsub)
|
||||
DEF_FMAOP_SCALAR_INSERT(VFNMLAScalarInsert, fmsub)
|
||||
DEF_FMAOP_SCALAR_INSERT(VFNMLSScalarInsert, fnmadd)
|
||||
|
||||
void Arm64JITCore::VFScalarFMAOperation(uint8_t OpSize, uint8_t ElementSize, ScalarFMAOpCaller ScalarEmit, ARMEmitter::VRegister Dst,
|
||||
ARMEmitter::VRegister Vector1, ARMEmitter::VRegister Vector2, ARMEmitter::VRegister Addend) {
|
||||
LOGMAN_THROW_A_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE, "256-bit unsupported", __func__);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize = ARMEmitter::ToVectorSizePair(ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ARMEmitter::SubRegSize::i64Bit);
|
||||
if (Dst != Vector1 && Dst != Vector2 && Dst != Addend && HostSupportsAFP) {
|
||||
// If destination doesnt overlap any incoming register then move the adder to the destination first.
|
||||
mov(Dst.Q(), Addend.Q());
|
||||
Dst = Addend;
|
||||
}
|
||||
|
||||
if (HostSupportsAFP && Dst == Addend) {
|
||||
///< Exactly matches ARM scalar FMA semantics
|
||||
// If the host CPU supports AFP then scalar does an insert without modifying upper bits.
|
||||
ScalarEmit(Dst, Vector1, Vector2, Addend);
|
||||
} else {
|
||||
// No overlap between addr and destination or host doesn't support AFP, need to emit in to a temporary then insert.
|
||||
ScalarEmit(VTMP1, Vector1, Vector2, Addend);
|
||||
ins(SubRegSize.Vector, Dst.Q(), 0, VTMP1.Q(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// VFScalarOperation performs the operation described through ScalarEmit between Vector1 and Vector2,
|
||||
// storing it into Dst. This is a scalar operation, so the only lowest element of each vector is used for the operation.
|
||||
// The result is stored into the destination. The untouched bits of the destination come from Vector1, unless it's a 256 vector
|
||||
// and ZeroUpperBits is true, in which case the upper bits are zero.
|
||||
void Arm64JITCore::VFScalarOperation(uint8_t OpSize, uint8_t ElementSize, bool ZeroUpperBits, ScalarBinaryOpCaller ScalarEmit,
|
||||
ARMEmitter::VRegister Dst, ARMEmitter::VRegister Vector1, ARMEmitter::VRegister Vector2) {
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
if (!Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(ZeroUpperBits == false, "128-bit operation doesn't support ZeroUpperBits in {}", __func__);
|
||||
}
|
||||
LOGMAN_THROW_A_FMT(Is256Bit || !ZeroUpperBits, "128-bit operation doesn't support ZeroUpperBits in {}", __func__);
|
||||
|
||||
// Bit of a tricky detail.
|
||||
// The upper bits of the destination comes from the first source.
|
||||
|
||||
// The upper bits of the destination comes from Vector1.
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize = ARMEmitter::ToVectorSizePair(ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
@@ -261,8 +316,8 @@ void Arm64JITCore::VFScalarOperation(uint8_t OpSize, uint8_t ElementSize, bool Z
|
||||
ins(SubRegSize.Vector, Dst.Q(), 0, VTMP1.Q(), 0);
|
||||
}
|
||||
}
|
||||
} else if (Dst != Vector2) {
|
||||
if (!ZeroUpperBits && Is256Bit) {
|
||||
} else if (Dst != Vector2) { // Dst different from both Vector1 and Vector2
|
||||
if (Is256Bit && !ZeroUpperBits) {
|
||||
mov(Dst.Z(), Vector1.Z());
|
||||
} else {
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
@@ -279,36 +334,30 @@ void Arm64JITCore::VFScalarOperation(uint8_t OpSize, uint8_t ElementSize, bool Z
|
||||
ins(SubRegSize.Vector, Dst.Q(), 0, VTMP1.Q(), 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Destination intersects Vector2, can't do anything optimal in this case.
|
||||
// Do the scalar operation first and then move and insert.
|
||||
} else { // Dst same as Vector2
|
||||
|
||||
ScalarEmit(VTMP1, Vector1, Vector2);
|
||||
|
||||
if (!ZeroUpperBits && Is256Bit) {
|
||||
mov(Dst.Z(), Vector1.Z());
|
||||
} else {
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
}
|
||||
|
||||
if (!ZeroUpperBits && Is256Bit) {
|
||||
ptrue(SubRegSize.Vector, Predicate, ARMEmitter::PredicatePattern::SVE_VL1);
|
||||
mov(SubRegSize.Vector, Dst.Z(), Predicate.Merging(), VTMP1.Z());
|
||||
} else {
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
ins(SubRegSize.Vector, Dst.Q(), 0, VTMP1.Q(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Similarly to VFScalarOperation it performs the operation described through ScalarEmit operating on Vector2.
|
||||
// However the result of the scalar operation is inserted into Vector1 and moved to Destination.
|
||||
// The untouched bits of the destination come from Vector1, unless it's a 256 vector
|
||||
// and ZeroUpperBits is true, in which case the upper bits are zero.
|
||||
void Arm64JITCore::VFScalarUnaryOperation(uint8_t OpSize, uint8_t ElementSize, bool ZeroUpperBits, ScalarUnaryOpCaller ScalarEmit,
|
||||
ARMEmitter::VRegister Dst, ARMEmitter::VRegister Vector1,
|
||||
std::variant<ARMEmitter::VRegister, ARMEmitter::Register> Vector2) {
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
if (!Is256Bit) {
|
||||
LOGMAN_THROW_A_FMT(ZeroUpperBits == false, "128-bit operation doesn't support ZeroUpperBits in {}", __func__);
|
||||
}
|
||||
|
||||
// Bit of a tricky detail.
|
||||
// The upper bits of the destination comes from the first source.
|
||||
LOGMAN_THROW_A_FMT(Is256Bit || !ZeroUpperBits, "128-bit operation doesn't support ZeroUpperBits in {}", __func__);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize = ARMEmitter::ToVectorSizePair(ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
@@ -327,7 +376,7 @@ void Arm64JITCore::VFScalarUnaryOperation(uint8_t OpSize, uint8_t ElementSize, b
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
}
|
||||
|
||||
if (HostSupportsAFP) {
|
||||
if (HostSupportsAFP) { // or Dst (here Dst == Vector1)
|
||||
// If the host CPU supports AFP then scalar does an insert without modifying upper bits.
|
||||
ScalarEmit(Dst, Vector2);
|
||||
} else {
|
||||
@@ -366,14 +415,10 @@ void Arm64JITCore::VFScalarUnaryOperation(uint8_t OpSize, uint8_t ElementSize, b
|
||||
|
||||
if (!ZeroUpperBits && Is256Bit) {
|
||||
mov(Dst.Z(), Vector1.Z());
|
||||
} else {
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
}
|
||||
|
||||
if (!ZeroUpperBits && Is256Bit) {
|
||||
ptrue(SubRegSize.Vector, Predicate, ARMEmitter::PredicatePattern::SVE_VL1);
|
||||
mov(SubRegSize.Vector, Dst.Z(), Predicate.Merging(), VTMP1.Z());
|
||||
} else {
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
ins(SubRegSize.Vector, Dst.Q(), 0, VTMP1.Q(), 0);
|
||||
}
|
||||
}
|
||||
@@ -457,12 +502,17 @@ DEF_OP(VFRSqrtScalarInsert) {
|
||||
|
||||
fmov(SubRegSize.Scalar, VTMP1.Q(), 1.0f);
|
||||
fsqrt(SubRegSize.Scalar, VTMP2, Src);
|
||||
fdiv(SubRegSize.Scalar, Dst, VTMP1, VTMP2);
|
||||
if (HostSupportsAFP) {
|
||||
fdiv(SubRegSize.Scalar, VTMP1, VTMP1, VTMP2);
|
||||
ins(SubRegSize.Vector, Dst, 0, VTMP1, 0);
|
||||
} else {
|
||||
fdiv(SubRegSize.Scalar, Dst, VTMP1, VTMP2);
|
||||
}
|
||||
};
|
||||
|
||||
auto ScalarEmitRPRES = [this, SubRegSize](ARMEmitter::VRegister Dst, std::variant<ARMEmitter::VRegister, ARMEmitter::Register> SrcVar) {
|
||||
auto Src = *std::get_if<ARMEmitter::VRegister>(&SrcVar);
|
||||
frsqrte(SubRegSize.Scalar, Dst.S(), Src.S());
|
||||
frsqrte(SubRegSize.Scalar, Dst.D(), Src.D());
|
||||
};
|
||||
|
||||
std::array<ScalarUnaryOpCaller, 2> Handlers = {
|
||||
@@ -590,7 +640,28 @@ DEF_OP(VSToFVectorInsert) {
|
||||
// Claim the element size is 8-bytes.
|
||||
// Might be scalar 8-byte (cvtsi2ss xmm0, rax)
|
||||
// Might be vector i32v2 (cvtpi2ps xmm0, mm0)
|
||||
VFScalarUnaryOperation(IROp->Size, ElementSize * (HasTwoElements ? 2 : 1), Op->ZeroUpperBits, ScalarEmit, Dst, Vector1, Vector2);
|
||||
if (!HasTwoElements) {
|
||||
VFScalarUnaryOperation(IROp->Size, ElementSize, Op->ZeroUpperBits, ScalarEmit, Dst, Vector1, Vector2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dealing with the odd case of this being actually a vector operation rather than scalar.
|
||||
const auto Is256Bit = IROp->Size == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
constexpr auto Predicate = ARMEmitter::PReg::p0;
|
||||
|
||||
ScalarEmit(VTMP1, Vector2);
|
||||
if (!Op->ZeroUpperBits && Is256Bit) {
|
||||
if (Dst != Vector1) {
|
||||
mov(Dst.Z(), Vector1.Z());
|
||||
}
|
||||
ptrue(ARMEmitter::SubRegSize::i64Bit, Predicate, ARMEmitter::PredicatePattern::SVE_VL1);
|
||||
mov(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Predicate.Merging(), VTMP1.Z());
|
||||
} else {
|
||||
if (Dst != Vector1) {
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
}
|
||||
ins(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), 0, VTMP1.Q(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VSToFGPRInsert) {
|
||||
@@ -679,11 +750,11 @@ DEF_OP(VFCMPScalarInsert) {
|
||||
auto ScalarEmitEQ = [this, SubRegSize](ARMEmitter::VRegister Dst, ARMEmitter::VRegister Src1, ARMEmitter::VRegister Src2) {
|
||||
switch (SubRegSize.Scalar) {
|
||||
case ARMEmitter::ScalarRegSize::i16Bit: {
|
||||
fcmeq(Dst.H(), Src1.H(), Src2.H());
|
||||
fcmeq(Dst.H(), Src2.H(), Src1.H());
|
||||
break;
|
||||
}
|
||||
case ARMEmitter::ScalarRegSize::i32Bit:
|
||||
case ARMEmitter::ScalarRegSize::i64Bit: fcmeq(SubRegSize.Scalar, Dst, Src1, Src2); break;
|
||||
case ARMEmitter::ScalarRegSize::i64Bit: fcmeq(SubRegSize.Scalar, Dst, Src2, Src1); break;
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
@@ -748,11 +819,11 @@ DEF_OP(VFCMPScalarInsert) {
|
||||
[this, SubRegSize, ZeroUpperBits, Is256Bit](ARMEmitter::VRegister Dst, ARMEmitter::VRegister Src1, ARMEmitter::VRegister Src2) {
|
||||
switch (SubRegSize.Scalar) {
|
||||
case ARMEmitter::ScalarRegSize::i16Bit: {
|
||||
fcmeq(VTMP1.H(), Src1.H(), Src2.H());
|
||||
fcmeq(VTMP1.H(), Src2.H(), Src1.H());
|
||||
break;
|
||||
}
|
||||
case ARMEmitter::ScalarRegSize::i32Bit:
|
||||
case ARMEmitter::ScalarRegSize::i64Bit: fcmeq(SubRegSize.Scalar, VTMP1, Src1, Src2); break;
|
||||
case ARMEmitter::ScalarRegSize::i64Bit: fcmeq(SubRegSize.Scalar, VTMP1, Src2, Src1); break;
|
||||
default: break;
|
||||
}
|
||||
// If the destination is a temporary then it is going to do an insert after the operation.
|
||||
@@ -1750,6 +1821,7 @@ DEF_OP(VBSL) {
|
||||
const auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto Is128Bit = OpSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto VectorFalse = GetVReg(Op->VectorFalse.ID());
|
||||
@@ -1770,6 +1842,11 @@ DEF_OP(VBSL) {
|
||||
bsl(VTMP1.Z(), VTMP1.Z(), VectorFalse.Z(), VectorMask.Z());
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
}
|
||||
} else if (!HostSupportsSVE256 && HostSupportsSVE128 && Is128Bit && Dst != VectorFalse && Dst != VectorTrue && Dst != VectorMask) {
|
||||
// Needs to move but SVE movprfx+bsl is slightly more efficient than ASIMD mov+bsl on CPUs that support
|
||||
// movprfx fusion and NOT zero-cycle vector register moves.
|
||||
movprfx(Dst.Z(), VectorTrue.Z());
|
||||
bsl(Dst.Z(), Dst.Z(), VectorFalse.Z(), VectorMask.Z());
|
||||
} else {
|
||||
if (VectorMask == Dst) {
|
||||
// Can use BSL without any moves.
|
||||
@@ -3141,6 +3218,48 @@ DEF_OP(VSXTL2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VSSHLL) {
|
||||
const auto Op = IROp->C<IR::IROp_VSSHLL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize248(IROp);
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
const auto BitShift = Op->BitShift;
|
||||
LOGMAN_THROW_A_FMT(BitShift < ((IROp->ElementSize >> 1) * 8), "Bitshift size too large for source element size: {} < {}", BitShift,
|
||||
(IROp->ElementSize >> 1) * 8);
|
||||
|
||||
if (Is256Bit) {
|
||||
sunpklo(SubRegSize, Dst.Z(), Vector.Z());
|
||||
lsl(SubRegSize, Dst.Z(), Dst.Z(), BitShift);
|
||||
} else {
|
||||
sshll(SubRegSize, Dst.D(), Vector.D(), BitShift);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VSSHLL2) {
|
||||
const auto Op = IROp->C<IR::IROp_VSSHLL2>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize248(IROp);
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
const auto BitShift = Op->BitShift;
|
||||
LOGMAN_THROW_A_FMT(BitShift < ((IROp->ElementSize >> 1) * 8), "Bitshift size too large for source element size: {} < {}", BitShift,
|
||||
(IROp->ElementSize >> 1) * 8);
|
||||
|
||||
if (Is256Bit) {
|
||||
sunpkhi(SubRegSize, Dst.Z(), Vector.Z());
|
||||
lsl(SubRegSize, Dst.Z(), Dst.Z(), BitShift);
|
||||
} else {
|
||||
sshll2(SubRegSize, Dst.Q(), Vector.Q(), BitShift);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUXTL) {
|
||||
const auto Op = IROp->C<IR::IROp_VUXTL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
@@ -3960,5 +4079,317 @@ DEF_OP(VFCADD) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VFMLA) {
|
||||
///< Dest = (Vector1 * Vector2) + Addend
|
||||
// Matches:
|
||||
// - SVE - FMLA
|
||||
// - ASIMD - FMLA
|
||||
// - Scalar - FMADD
|
||||
const auto Op = IROp->C<IR::IROp_VFMLA>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize248(IROp);
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
const auto VectorAddend = GetVReg(Op->Addend.ID());
|
||||
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Z(), VectorAddend.Z());
|
||||
}
|
||||
|
||||
fmla(SubRegSize, DestTmp.Z(), Mask, Vector1.Z(), Vector2.Z());
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Z(), DestTmp.Z());
|
||||
}
|
||||
} else {
|
||||
if (IROp->ElementSize == OpSize) {
|
||||
if (IROp->ElementSize == 2) {
|
||||
fmadd(Dst.H(), Vector1.H(), Vector2.H(), VectorAddend.H());
|
||||
} else if (IROp->ElementSize == 4) {
|
||||
fmadd(Dst.S(), Vector1.S(), Vector2.S(), VectorAddend.S());
|
||||
} else if (IROp->ElementSize == 8) {
|
||||
fmadd(Dst.D(), Vector1.D(), Vector2.D(), VectorAddend.D());
|
||||
}
|
||||
return;
|
||||
}
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Q(), VectorAddend.Q());
|
||||
}
|
||||
if (OpSize == 16) {
|
||||
fmla(SubRegSize, DestTmp.Q(), Vector1.Q(), Vector2.Q());
|
||||
} else {
|
||||
fmla(SubRegSize, DestTmp.D(), Vector1.D(), Vector2.D());
|
||||
}
|
||||
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Q(), DestTmp.Q());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VFMLS) {
|
||||
///< Dest = (Vector1 * Vector2) - Addend
|
||||
// Matches:
|
||||
// - SVE - FNMLS
|
||||
// - ASIMD - FMLA (With negated addend)
|
||||
// - Scalar - FNMSUB
|
||||
const auto Op = IROp->C<IR::IROp_VFMLS>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize248(IROp);
|
||||
const auto Is128Bit = OpSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
const auto VectorAddend = GetVReg(Op->Addend.ID());
|
||||
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Z(), VectorAddend.Z());
|
||||
}
|
||||
|
||||
fnmls(SubRegSize, DestTmp.Z(), Mask, Vector1.Z(), Vector2.Z());
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Z(), DestTmp.Z());
|
||||
}
|
||||
} else if (HostSupportsSVE128 && Is128Bit) {
|
||||
const auto Mask = PRED_TMP_16B.Merging();
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Z(), VectorAddend.Z());
|
||||
}
|
||||
|
||||
fnmls(SubRegSize, DestTmp.Z(), Mask, Vector1.Z(), Vector2.Z());
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Z(), DestTmp.Z());
|
||||
}
|
||||
} else {
|
||||
if (IROp->ElementSize == OpSize) {
|
||||
if (IROp->ElementSize == 2) {
|
||||
fnmsub(Dst.H(), Vector1.H(), Vector2.H(), VectorAddend.H());
|
||||
} else if (IROp->ElementSize == 4) {
|
||||
fnmsub(Dst.S(), Vector1.S(), Vector2.S(), VectorAddend.S());
|
||||
} else if (IROp->ElementSize == 8) {
|
||||
fnmsub(Dst.D(), Vector1.D(), Vector2.D(), VectorAddend.D());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Addend needs to get negated to match correct behaviour here.
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst == Vector1 || Dst == Vector2) {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
|
||||
if (Is128Bit) {
|
||||
fneg(SubRegSize, DestTmp.Q(), VectorAddend.Q());
|
||||
} else {
|
||||
fneg(SubRegSize, DestTmp.D(), VectorAddend.D());
|
||||
}
|
||||
|
||||
if (Is128Bit) {
|
||||
fmla(SubRegSize, DestTmp.Q(), Vector1.Q(), Vector2.Q());
|
||||
} else {
|
||||
fmla(SubRegSize, DestTmp.D(), Vector1.D(), Vector2.D());
|
||||
}
|
||||
|
||||
if (DestTmp != Dst) {
|
||||
if (Is128Bit) {
|
||||
mov(Dst.Q(), DestTmp.Q());
|
||||
} else {
|
||||
mov(Dst.D(), DestTmp.D());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VFNMLA) {
|
||||
///< Dest = (-Vector1 * Vector2) + Addend
|
||||
// Matches:
|
||||
// - SVE - FMLS
|
||||
// - ASIMD - FMLS
|
||||
// - Scalar - FMSUB
|
||||
const auto Op = IROp->C<IR::IROp_VFMLA>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize248(IROp);
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
const auto VectorAddend = GetVReg(Op->Addend.ID());
|
||||
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Z(), VectorAddend.Z());
|
||||
}
|
||||
|
||||
fmls(SubRegSize, DestTmp.Z(), Mask, Vector1.Z(), Vector2.Z());
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Z(), DestTmp.Z());
|
||||
}
|
||||
} else {
|
||||
if (IROp->ElementSize == OpSize) {
|
||||
if (IROp->ElementSize == 2) {
|
||||
fmsub(Dst.H(), Vector1.H(), Vector2.H(), VectorAddend.H());
|
||||
} else if (IROp->ElementSize == 4) {
|
||||
fmsub(Dst.S(), Vector1.S(), Vector2.S(), VectorAddend.S());
|
||||
} else if (IROp->ElementSize == 8) {
|
||||
fmsub(Dst.D(), Vector1.D(), Vector2.D(), VectorAddend.D());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Q(), VectorAddend.Q());
|
||||
}
|
||||
if (OpSize == 16) {
|
||||
fmls(SubRegSize, DestTmp.Q(), Vector1.Q(), Vector2.Q());
|
||||
} else {
|
||||
fmls(SubRegSize, DestTmp.D(), Vector1.D(), Vector2.D());
|
||||
}
|
||||
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Q(), DestTmp.Q());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VFNMLS) {
|
||||
///< Dest = (-Vector1 * Vector2) - Addend
|
||||
// Matches:
|
||||
// - SVE - FNMLA
|
||||
// - ASIMD - FMLS (With Negated addend)
|
||||
// - Scalar - FNMADD
|
||||
|
||||
const auto Op = IROp->C<IR::IROp_VFMLS>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto SubRegSize = ConvertSubRegSize248(IROp);
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto Is128Bit = OpSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
const auto VectorAddend = GetVReg(Op->Addend.ID());
|
||||
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Z(), VectorAddend.Z());
|
||||
}
|
||||
|
||||
fnmla(SubRegSize, DestTmp.Z(), Mask, Vector1.Z(), Vector2.Z());
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Z(), DestTmp.Z());
|
||||
}
|
||||
} else if (HostSupportsSVE128 && Is128Bit) {
|
||||
const auto Mask = PRED_TMP_16B.Merging();
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst != VectorAddend) {
|
||||
if (Dst != Vector1 && Dst != Vector2) {
|
||||
DestTmp = Dst;
|
||||
} else {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
mov(DestTmp.Z(), VectorAddend.Z());
|
||||
}
|
||||
|
||||
fnmla(SubRegSize, DestTmp.Z(), Mask, Vector1.Z(), Vector2.Z());
|
||||
if (Dst != DestTmp) {
|
||||
mov(Dst.Z(), DestTmp.Z());
|
||||
}
|
||||
} else {
|
||||
if (IROp->ElementSize == OpSize) {
|
||||
if (IROp->ElementSize == 2) {
|
||||
fnmadd(Dst.H(), Vector1.H(), Vector2.H(), VectorAddend.H());
|
||||
} else if (IROp->ElementSize == 4) {
|
||||
fnmadd(Dst.S(), Vector1.S(), Vector2.S(), VectorAddend.S());
|
||||
} else if (IROp->ElementSize == 8) {
|
||||
fnmadd(Dst.D(), Vector1.D(), Vector2.D(), VectorAddend.D());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Addend needs to get negated to match correct behaviour here.
|
||||
ARMEmitter::VRegister DestTmp = Dst;
|
||||
if (Dst == Vector1 || Dst == Vector2) {
|
||||
DestTmp = VTMP1;
|
||||
}
|
||||
|
||||
if (Is128Bit) {
|
||||
fneg(SubRegSize, DestTmp.Q(), VectorAddend.Q());
|
||||
} else {
|
||||
fneg(SubRegSize, DestTmp.D(), VectorAddend.D());
|
||||
}
|
||||
|
||||
if (Is128Bit) {
|
||||
fmls(SubRegSize, DestTmp.Q(), Vector1.Q(), Vector2.Q());
|
||||
} else {
|
||||
fmls(SubRegSize, DestTmp.D(), Vector1.D(), Vector2.D());
|
||||
}
|
||||
|
||||
if (DestTmp != Dst) {
|
||||
if (Is128Bit) {
|
||||
mov(Dst.Q(), DestTmp.Q());
|
||||
} else {
|
||||
mov(Dst.D(), DestTmp.D());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -17,6 +17,5 @@ class CPUBackend;
|
||||
|
||||
[[nodiscard]]
|
||||
fextl::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread);
|
||||
CPUBackendFeatures GetArm64JITBackendFeatures();
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -13,9 +13,6 @@
|
||||
#include <stddef.h>
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
#ifdef _M_ARM_64EC
|
||||
#include <winnt.h>
|
||||
#endif
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
@@ -70,24 +67,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _M_ARM_64EC
|
||||
bool CheckPageEC(uint64_t Address) {
|
||||
if (!RtlIsEcCode(Address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(WriteLock);
|
||||
|
||||
// Mark L2 entry for this page as EC by setting the LSB, this can then be
|
||||
// checked by the dispatcher to see if it needs to perform a call/return to
|
||||
// EC code.
|
||||
const auto PageIndex = (Address & (VirtualMemSize - 1)) >> 12;
|
||||
const auto Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
Pointers[PageIndex] |= 1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
fextl::map<uint64_t, fextl::vector<uint64_t>> CodePages;
|
||||
|
||||
// Appends Block {Address} to CodePages [Start, Start + Length)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -65,7 +65,7 @@ void OpDispatchBuilder::SHA1MSG2Op(OpcodeArgs) {
|
||||
// This instruction mostly matches ARMv8's SHA1SU1 instruction but one of the elements are flipped in an unexpected way.
|
||||
// Do all the work without it.
|
||||
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(OpSize::i32Bit, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
const auto ZeroRegister = LoadZeroVector(OpSize::i32Bit);
|
||||
|
||||
// Shift the incoming source left by a 32-bit element, inserting Zeros.
|
||||
// This could be slightly improved to use a VInsGPR with the zero register.
|
||||
@@ -90,8 +90,6 @@ void OpDispatchBuilder::SHA1MSG2Op(OpcodeArgs) {
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::SHA1RNDS4Op(OpcodeArgs) {
|
||||
LOGMAN_THROW_A_FMT(Op->Src[1].IsLiteral(), "Src1 needs to be literal here to indicate function and constants");
|
||||
|
||||
using FnType = Ref (*)(OpDispatchBuilder&, Ref, Ref, Ref);
|
||||
|
||||
const auto f0 = [](OpDispatchBuilder& Self, Ref B, Ref C, Ref D) -> Ref {
|
||||
@@ -121,7 +119,7 @@ void OpDispatchBuilder::SHA1RNDS4Op(OpcodeArgs) {
|
||||
f3,
|
||||
};
|
||||
|
||||
const uint64_t Imm8 = Op->Src[1].Data.Literal.Value & 0b11;
|
||||
const uint64_t Imm8 = Op->Src[1].Literal() & 0b11;
|
||||
const FnType Fn = fn_array[Imm8];
|
||||
auto K = _Constant(32, k_array[Imm8]);
|
||||
|
||||
@@ -312,8 +310,7 @@ void OpDispatchBuilder::AESImcOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESEncOp(OpcodeArgs) {
|
||||
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(16, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESEnc(16, Dest, Src, ZeroRegister);
|
||||
Ref Result = _VAESEnc(16, Dest, Src, LoadZeroVector(16));
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -326,8 +323,7 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
|
||||
|
||||
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(DstSize, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESEnc(DstSize, State, Key, ZeroRegister);
|
||||
Ref Result = _VAESEnc(DstSize, State, Key, LoadZeroVector(DstSize));
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
@@ -335,8 +331,7 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESEncLastOp(OpcodeArgs) {
|
||||
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(16, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESEncLast(16, Dest, Src, ZeroRegister);
|
||||
Ref Result = _VAESEncLast(16, Dest, Src, LoadZeroVector(16));
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -349,8 +344,7 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
|
||||
|
||||
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(DstSize, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESEncLast(DstSize, State, Key, ZeroRegister);
|
||||
Ref Result = _VAESEncLast(DstSize, State, Key, LoadZeroVector(DstSize));
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
@@ -358,8 +352,7 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESDecOp(OpcodeArgs) {
|
||||
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(16, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESDec(16, Dest, Src, ZeroRegister);
|
||||
Ref Result = _VAESDec(16, Dest, Src, LoadZeroVector(16));
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -372,8 +365,7 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
|
||||
|
||||
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(DstSize, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESDec(DstSize, State, Key, ZeroRegister);
|
||||
Ref Result = _VAESDec(DstSize, State, Key, LoadZeroVector(DstSize));
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
@@ -381,8 +373,7 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESDecLastOp(OpcodeArgs) {
|
||||
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(16, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESDecLast(16, Dest, Src, ZeroRegister);
|
||||
Ref Result = _VAESDecLast(16, Dest, Src, LoadZeroVector(16));
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -395,20 +386,17 @@ void OpDispatchBuilder::VAESDecLastOp(OpcodeArgs) {
|
||||
|
||||
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(DstSize, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
Ref Result = _VAESDecLast(DstSize, State, Key, ZeroRegister);
|
||||
Ref Result = _VAESDecLast(DstSize, State, Key, LoadZeroVector(DstSize));
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::AESKeyGenAssistImpl(OpcodeArgs) {
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
LOGMAN_THROW_A_FMT(Op->Src[1].IsLiteral(), "Src1 needs to be literal here");
|
||||
const uint64_t RCON = Op->Src[1].Data.Literal.Value;
|
||||
const uint64_t RCON = Op->Src[1].Literal();
|
||||
|
||||
auto KeyGenSwizzle = LoadAndCacheNamedVectorConstant(16, NAMED_VECTOR_AESKEYGENASSIST_SWIZZLE);
|
||||
const auto ZeroRegister = LoadAndCacheNamedVectorConstant(16, FEXCore::IR::NamedVectorConstant::NAMED_VECTOR_ZERO);
|
||||
return _VAESKeyGenAssist(Src, KeyGenSwizzle, ZeroRegister, RCON);
|
||||
return _VAESKeyGenAssist(Src, KeyGenSwizzle, LoadZeroVector(16), RCON);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::AESKeyGenAssist(OpcodeArgs) {
|
||||
@@ -417,26 +405,22 @@ void OpDispatchBuilder::AESKeyGenAssist(OpcodeArgs) {
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::PCLMULQDQOp(OpcodeArgs) {
|
||||
LOGMAN_THROW_A_FMT(Op->Src[1].IsLiteral(), "Selector needs to be literal here");
|
||||
|
||||
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
const auto Selector = static_cast<uint8_t>(Op->Src[1].Data.Literal.Value);
|
||||
const auto Selector = static_cast<uint8_t>(Op->Src[1].Literal());
|
||||
|
||||
auto Res = _PCLMUL(16, Dest, Src, Selector);
|
||||
auto Res = _PCLMUL(16, Dest, Src, Selector & 0b1'0001);
|
||||
StoreResult(FPRClass, Op, Res, -1);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::VPCLMULQDQOp(OpcodeArgs) {
|
||||
LOGMAN_THROW_A_FMT(Op->Src[2].IsLiteral(), "Selector needs to be literal here");
|
||||
|
||||
const auto DstSize = GetDstSize(Op);
|
||||
|
||||
Ref Src1 = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Ref Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
|
||||
const auto Selector = static_cast<uint8_t>(Op->Src[2].Data.Literal.Value);
|
||||
const auto Selector = static_cast<uint8_t>(Op->Src[2].Literal());
|
||||
|
||||
Ref Res = _PCLMUL(DstSize, Src1, Src2, Selector);
|
||||
Ref Res = _PCLMUL(DstSize, Src1, Src2, Selector & 0b1'0001);
|
||||
StoreResult(FPRClass, Op, Res, -1);
|
||||
}
|
||||
|
||||
|
||||
@@ -258,68 +258,7 @@ void OpDispatchBuilder::CalculateAF(Ref Src1, Ref Src2) {
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_AF_RAW_LOC>(XorRes);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateDeferredFlags(uint32_t FlagsToCalculateMask) {
|
||||
if (CurrentDeferredFlags.Type == FlagsGenerationType::TYPE_NONE) {
|
||||
// Nothing to do
|
||||
if (NZCVDirty && CachedNZCV) {
|
||||
_StoreNZCV(CachedNZCV);
|
||||
}
|
||||
|
||||
CachedNZCV = nullptr;
|
||||
NZCVDirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (CurrentDeferredFlags.Type) {
|
||||
case FlagsGenerationType::TYPE_SUB:
|
||||
CalculateFlags_SUB(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Sources.TwoSrcImmediate.Src1,
|
||||
CurrentDeferredFlags.Sources.TwoSrcImmediate.Src2, CurrentDeferredFlags.Sources.TwoSrcImmediate.UpdateCF);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_MUL:
|
||||
CalculateFlags_MUL(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res, CurrentDeferredFlags.Sources.OneSource.Src1);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_UMUL: CalculateFlags_UMUL(CurrentDeferredFlags.Res); break;
|
||||
case FlagsGenerationType::TYPE_LOGICAL:
|
||||
CalculateFlags_Logical(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res, CurrentDeferredFlags.Sources.TwoSource.Src1,
|
||||
CurrentDeferredFlags.Sources.TwoSource.Src2);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_LSHLI:
|
||||
CalculateFlags_ShiftLeftImmediate(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res,
|
||||
CurrentDeferredFlags.Sources.OneSrcImmediate.Src1, CurrentDeferredFlags.Sources.OneSrcImmediate.Imm);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_LSHRI:
|
||||
CalculateFlags_ShiftRightImmediate(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res,
|
||||
CurrentDeferredFlags.Sources.OneSrcImmediate.Src1, CurrentDeferredFlags.Sources.OneSrcImmediate.Imm);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_LSHRDI:
|
||||
CalculateFlags_ShiftRightDoubleImmediate(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res,
|
||||
CurrentDeferredFlags.Sources.OneSrcImmediate.Src1, CurrentDeferredFlags.Sources.OneSrcImmediate.Imm);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_ASHRI:
|
||||
CalculateFlags_SignShiftRightImmediate(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res,
|
||||
CurrentDeferredFlags.Sources.OneSrcImmediate.Src1, CurrentDeferredFlags.Sources.OneSrcImmediate.Imm);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_BEXTR: CalculateFlags_BEXTR(CurrentDeferredFlags.Res); break;
|
||||
case FlagsGenerationType::TYPE_BLSI: CalculateFlags_BLSI(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res); break;
|
||||
case FlagsGenerationType::TYPE_BLSMSK:
|
||||
CalculateFlags_BLSMSK(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res, CurrentDeferredFlags.Sources.OneSource.Src1);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_BLSR:
|
||||
CalculateFlags_BLSR(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res, CurrentDeferredFlags.Sources.OneSource.Src1);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_POPCOUNT: CalculateFlags_POPCOUNT(CurrentDeferredFlags.Res); break;
|
||||
case FlagsGenerationType::TYPE_BZHI:
|
||||
CalculateFlags_BZHI(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res, CurrentDeferredFlags.Sources.OneSource.Src1);
|
||||
break;
|
||||
case FlagsGenerationType::TYPE_ZCNT: CalculateFlags_ZCNT(CurrentDeferredFlags.SrcSize, CurrentDeferredFlags.Res); break;
|
||||
case FlagsGenerationType::TYPE_RDRAND: CalculateFlags_RDRAND(CurrentDeferredFlags.Res); break;
|
||||
case FlagsGenerationType::TYPE_NONE:
|
||||
default: ERROR_AND_DIE_FMT("Unhandled flags type {}", CurrentDeferredFlags.Type);
|
||||
}
|
||||
|
||||
// Done calculating
|
||||
CurrentDeferredFlags.Type = FlagsGenerationType::TYPE_NONE;
|
||||
|
||||
void OpDispatchBuilder::CalculateDeferredFlags() {
|
||||
if (NZCVDirty && CachedNZCV) {
|
||||
_StoreNZCV(CachedNZCV);
|
||||
}
|
||||
@@ -383,15 +322,14 @@ Ref OpDispatchBuilder::CalculateFlags_SBB(uint8_t SrcSize, Ref Src1, Ref Src2) {
|
||||
} else {
|
||||
// Zero extend for correct comparison behaviour with Src1 = 0xffff.
|
||||
Src1 = _Bfe(OpSize, SrcSize * 8, 0, Src1);
|
||||
Src2 = _Bfe(OpSize, SrcSize * 8, 0, Src2);
|
||||
|
||||
auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
|
||||
auto Src1MinusCF = _Sub(OpSize, Src1, CF);
|
||||
auto Src2PlusCF = _Adc(OpSize, _Constant(0), Src2);
|
||||
|
||||
Res = _Sub(OpSize, Src1MinusCF, Src2);
|
||||
Res = _Sub(OpSize, Src1, Src2PlusCF);
|
||||
Res = _Bfe(OpSize, SrcSize * 8, 0, Res);
|
||||
|
||||
// Need to zero-extend for correct comparisons below
|
||||
auto SelectCF = _Select(FEXCore::IR::COND_ULT, Src1MinusCF, Res, One, Zero);
|
||||
auto SelectCF = _Select(FEXCore::IR::COND_ULT, Src1, Src2PlusCF, One, Zero);
|
||||
|
||||
SetNZ_ZeroCV(SrcSize, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(SelectCF);
|
||||
@@ -459,65 +397,42 @@ Ref OpDispatchBuilder::CalculateFlags_ADD(uint8_t SrcSize, Ref Src1, Ref Src2, b
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_MUL(uint8_t SrcSize, Ref Res, Ref High) {
|
||||
HandleNZCVWrite();
|
||||
InvalidatePF_AF();
|
||||
|
||||
// PF/AF/ZF/SF
|
||||
// Undefined
|
||||
{
|
||||
_InvalidateFlags(1 << X86State::RFLAG_PF_RAW_LOC);
|
||||
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
|
||||
}
|
||||
// CF and OF are set if the result of the operation can't be fit in to the destination register
|
||||
// If the value can fit then the top bits will be zero
|
||||
auto SignBit = _Sbfe(OpSize::i64Bit, 1, SrcSize * 8 - 1, Res);
|
||||
_SubNZCV(OpSize::i64Bit, High, SignBit);
|
||||
|
||||
// CF/OF
|
||||
{
|
||||
// CF and OF are set if the result of the operation can't be fit in to the destination register
|
||||
// If the value can fit then the top bits will be zero
|
||||
auto SignBit = _Sbfe(OpSize::i64Bit, 1, SrcSize * 8 - 1, Res);
|
||||
_SubNZCV(OpSize::i64Bit, High, SignBit);
|
||||
|
||||
// If High = SignBit, then sets to nZcv. Else sets to nzCV. Since SF/ZF
|
||||
// undefined, this does what we need.
|
||||
auto Zero = _Constant(0);
|
||||
_CondAddNZCV(OpSize::i64Bit, Zero, Zero, CondClassType {COND_EQ}, 0x3 /* nzCV */);
|
||||
}
|
||||
// If High = SignBit, then sets to nZcv. Else sets to nzCV. Since SF/ZF
|
||||
// undefined, this does what we need.
|
||||
auto Zero = _Constant(0);
|
||||
_CondAddNZCV(OpSize::i64Bit, Zero, Zero, CondClassType {COND_EQ}, 0x3 /* nzCV */);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_UMUL(Ref High) {
|
||||
HandleNZCVWrite();
|
||||
InvalidatePF_AF();
|
||||
|
||||
auto Zero = _Constant(0);
|
||||
OpSize Size = IR::SizeToOpSize(GetOpSize(High));
|
||||
|
||||
// AF/SF/PF/ZF
|
||||
// Undefined
|
||||
{
|
||||
_InvalidateFlags(1 << X86State::RFLAG_PF_RAW_LOC);
|
||||
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
|
||||
}
|
||||
// CF and OF are set if the result of the operation can't be fit in to the destination register
|
||||
// The result register will be all zero if it can't fit due to how multiplication behaves
|
||||
_SubNZCV(Size, High, Zero);
|
||||
|
||||
// CF/OF
|
||||
{
|
||||
// CF and OF are set if the result of the operation can't be fit in to the destination register
|
||||
// The result register will be all zero if it can't fit due to how multiplication behaves
|
||||
_SubNZCV(Size, High, Zero);
|
||||
|
||||
// If High = 0, then sets to nZcv. Else sets to nzCV. Since SF/ZF undefined,
|
||||
// this does what we need.
|
||||
_CondAddNZCV(Size, Zero, Zero, CondClassType {COND_EQ}, 0x3 /* nzCV */);
|
||||
}
|
||||
// If High = 0, then sets to nZcv. Else sets to nzCV. Since SF/ZF undefined,
|
||||
// this does what we need.
|
||||
_CondAddNZCV(Size, Zero, Zero, CondClassType {COND_EQ}, 0x3 /* nzCV */);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_Logical(uint8_t SrcSize, Ref Res, Ref Src1, Ref Src2) {
|
||||
// AF
|
||||
// Undefined
|
||||
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
|
||||
InvalidateAF();
|
||||
|
||||
if (SrcSize >= 4) {
|
||||
HandleNZ00Write();
|
||||
CalculatePF(_AndWithFlags(IR::SizeToOpSize(SrcSize), Res, Res));
|
||||
} else {
|
||||
SetNZ_ZeroCV(SrcSize, Res);
|
||||
CalculatePF(Res);
|
||||
}
|
||||
CalculatePF(Res);
|
||||
|
||||
// SF/ZF/CF/OF
|
||||
SetNZ_ZeroCV(SrcSize, Res);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_ShiftLeftImmediate(uint8_t SrcSize, Ref UnmaskedRes, Ref Src1, uint64_t Shift) {
|
||||
@@ -532,19 +447,17 @@ void OpDispatchBuilder::CalculateFlags_ShiftLeftImmediate(uint8_t SrcSize, Ref U
|
||||
|
||||
// CF
|
||||
{
|
||||
// Extract the last bit shifted in to CF
|
||||
// Extract the last bit shifted in to CF. Shift is already masked, but for
|
||||
// 8/16-bit it might be >= SrcSizeBits, in which case CF is cleared. There's
|
||||
// nothing to do in that case since we already cleared CF above.
|
||||
auto SrcSizeBits = SrcSize * 8;
|
||||
if (SrcSizeBits < Shift) {
|
||||
Shift &= (SrcSizeBits - 1);
|
||||
if (Shift < SrcSizeBits) {
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(Src1, SrcSizeBits - Shift, true);
|
||||
}
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(Src1, SrcSizeBits - Shift, true);
|
||||
}
|
||||
|
||||
CalculatePF(UnmaskedRes);
|
||||
|
||||
// AF
|
||||
// Undefined
|
||||
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
|
||||
InvalidateAF();
|
||||
|
||||
// OF
|
||||
// In the case of left shift. OF is only set from the result of <Top Source Bit> XOR <Top Result Bit>
|
||||
@@ -571,10 +484,7 @@ void OpDispatchBuilder::CalculateFlags_SignShiftRightImmediate(uint8_t SrcSize,
|
||||
}
|
||||
|
||||
CalculatePF(Res);
|
||||
|
||||
// AF
|
||||
// Undefined
|
||||
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
|
||||
InvalidateAF();
|
||||
|
||||
// OF
|
||||
// Only defined when Shift is 1 else undefined. Only is set if the top bit was set to 1 when
|
||||
@@ -594,10 +504,7 @@ void OpDispatchBuilder::CalculateFlags_ShiftRightImmediateCommon(uint8_t SrcSize
|
||||
}
|
||||
|
||||
CalculatePF(Res);
|
||||
|
||||
// AF
|
||||
// Undefined
|
||||
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
|
||||
InvalidateAF();
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_ShiftRightImmediate(uint8_t SrcSize, Ref Res, Ref Src1, uint64_t Shift) {
|
||||
@@ -643,8 +550,7 @@ void OpDispatchBuilder::CalculateFlags_BEXTR(Ref Src) {
|
||||
// ZF is set properly. CF and OF are defined as being set to zero. SF, PF, and
|
||||
// AF are undefined.
|
||||
SetNZ_ZeroCV(GetOpSize(Src), Src);
|
||||
|
||||
_InvalidateFlags((1UL << X86State::RFLAG_PF_RAW_LOC) | (1UL << X86State::RFLAG_AF_RAW_LOC));
|
||||
InvalidatePF_AF();
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_BLSI(uint8_t SrcSize, Ref Result) {
|
||||
@@ -654,17 +560,14 @@ void OpDispatchBuilder::CalculateFlags_BLSI(uint8_t SrcSize, Ref Result) {
|
||||
//
|
||||
// ZF/SF/OF set as usual.
|
||||
SetNZ_ZeroCV(SrcSize, Result);
|
||||
InvalidatePF_AF();
|
||||
|
||||
auto CFOp = GetRFLAG(X86State::RFLAG_ZF_RAW_LOC, true /* Invert */);
|
||||
SetRFLAG<X86State::RFLAG_CF_RAW_LOC>(CFOp);
|
||||
|
||||
// PF/AF undefined
|
||||
_InvalidateFlags((1UL << X86State::RFLAG_PF_RAW_LOC) | (1UL << X86State::RFLAG_AF_RAW_LOC));
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_BLSMSK(uint8_t SrcSize, Ref Result, Ref Src) {
|
||||
// PF/AF undefined
|
||||
_InvalidateFlags((1UL << X86State::RFLAG_PF_RAW_LOC) | (1UL << X86State::RFLAG_AF_RAW_LOC));
|
||||
InvalidatePF_AF();
|
||||
|
||||
// CF set according to the Src
|
||||
auto Zero = _Constant(0);
|
||||
@@ -684,9 +587,7 @@ void OpDispatchBuilder::CalculateFlags_BLSR(uint8_t SrcSize, Ref Result, Ref Src
|
||||
|
||||
SetNZ_ZeroCV(SrcSize, Result);
|
||||
SetRFLAG<X86State::RFLAG_CF_RAW_LOC>(CFOp);
|
||||
|
||||
// PF/AF undefined
|
||||
_InvalidateFlags((1UL << X86State::RFLAG_PF_RAW_LOC) | (1UL << X86State::RFLAG_AF_RAW_LOC));
|
||||
InvalidatePF_AF();
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_POPCOUNT(Ref Result) {
|
||||
@@ -698,9 +599,7 @@ void OpDispatchBuilder::CalculateFlags_POPCOUNT(Ref Result) {
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CalculateFlags_BZHI(uint8_t SrcSize, Ref Result, Ref Src) {
|
||||
// PF/AF undefined
|
||||
_InvalidateFlags((1UL << X86State::RFLAG_PF_RAW_LOC) | (1UL << X86State::RFLAG_AF_RAW_LOC));
|
||||
|
||||
InvalidatePF_AF();
|
||||
SetNZ_ZeroCV(SrcSize, Result);
|
||||
SetRFLAG<X86State::RFLAG_CF_RAW_LOC>(Src);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -27,8 +27,8 @@ std::array<X86InstInfo, MAX_VEX_TABLE_SIZE> VEXTableOps = []() consteval {
|
||||
{OPD(1, 0b10, 0x11), 1, X86InstInfo{"VMOVSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x11), 1, X86InstInfo{"VMOVSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(1, 0b00, 0x12), 1, X86InstInfo{"VMOVLPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x12), 1, X86InstInfo{"VMOVLPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
|
||||
{OPD(1, 0b00, 0x12), 1, X86InstInfo{"VMOVLPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x12), 1, X86InstInfo{"VMOVLPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x12), 1, X86InstInfo{"VMOVSLDUP", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x12), 1, X86InstInfo{"VMOVDDUP", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
@@ -282,7 +282,7 @@ std::array<X86InstInfo, MAX_VEX_TABLE_SIZE> VEXTableOps = []() consteval {
|
||||
{OPD(2, 0b01, 0x0E), 1, X86InstInfo{"VTESTPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0F), 1, X86InstInfo{"VTESTPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x13), 1, X86InstInfo{"VCVTPH2PS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x13), 1, X86InstInfo{"VCVTPH2PS", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x16), 1, X86InstInfo{"VPERMPS", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x17), 1, X86InstInfo{"VPTEST", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
@@ -343,46 +343,46 @@ std::array<X86InstInfo, MAX_VEX_TABLE_SIZE> VEXTableOps = []() consteval {
|
||||
{OPD(2, 0b01, 0x8C), 1, X86InstInfo{"VPMASKMOV", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x8E), 1, X86InstInfo{"VPMASKMOV", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x90), 1, X86InstInfo{"VPGATHERDD/Q", TYPE_UNDEC, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x91), 1, X86InstInfo{"VPGATHERQD/Q", TYPE_UNDEC, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x92), 1, X86InstInfo{"VGATHERDPS/D", TYPE_UNDEC, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x93), 1, X86InstInfo{"VGATHERQPS/D", TYPE_UNDEC, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x90), 1, X86InstInfo{"VPGATHERDD/Q", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x91), 1, X86InstInfo{"VPGATHERQD/Q", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x92), 1, X86InstInfo{"VGATHERDPS/D", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x93), 1, X86InstInfo{"VGATHERQPS/D", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_2ND_SRC | FLAGS_VEX_VSIB | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x96), 1, X86InstInfo{"VFMADDSUB132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x97), 1, X86InstInfo{"VFMSUBADD132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x96), 1, X86InstInfo{"VFMADDSUB132", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x97), 1, X86InstInfo{"VFMSUBADD132", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x98), 1, X86InstInfo{"VFMADD132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x99), 1, X86InstInfo{"VFMADD132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9A), 1, X86InstInfo{"VFMSUB132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9B), 1, X86InstInfo{"VFMSUB132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9C), 1, X86InstInfo{"VFNMADD132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9D), 1, X86InstInfo{"VFNMADD132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9E), 1, X86InstInfo{"VFNMSUB132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9F), 1, X86InstInfo{"VFNMSUB132", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x98), 1, X86InstInfo{"VFMADD132", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x99), 1, X86InstInfo{"VFMADD132_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9A), 1, X86InstInfo{"VFMSUB132", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9B), 1, X86InstInfo{"VFMSUB132_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9C), 1, X86InstInfo{"VFNMADD132", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9D), 1, X86InstInfo{"VFNMADD132_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9E), 1, X86InstInfo{"VFNMSUB132", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x9F), 1, X86InstInfo{"VFNMSUB132_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0xA8), 1, X86InstInfo{"VFMADD213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xA9), 1, X86InstInfo{"VFMADD213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAA), 1, X86InstInfo{"VFMSUB213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAB), 1, X86InstInfo{"VFMSUB213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAC), 1, X86InstInfo{"VFNMADD213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAD), 1, X86InstInfo{"VFNMADD213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAE), 1, X86InstInfo{"VFNMSUB213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAF), 1, X86InstInfo{"VFNMSUB213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xA8), 1, X86InstInfo{"VFMADD213", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xA9), 1, X86InstInfo{"VFMADD213_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAA), 1, X86InstInfo{"VFMSUB213", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAB), 1, X86InstInfo{"VFMSUB213_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAC), 1, X86InstInfo{"VFNMADD213", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAD), 1, X86InstInfo{"VFNMADD213_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAE), 1, X86InstInfo{"VFNMSUB213", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xAF), 1, X86InstInfo{"VFNMSUB213_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0xB8), 1, X86InstInfo{"VFMADD231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xB9), 1, X86InstInfo{"VFMADD231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBA), 1, X86InstInfo{"VFMSUB231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBB), 1, X86InstInfo{"VFMSUB231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBC), 1, X86InstInfo{"VFNMADD231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBD), 1, X86InstInfo{"VFNMADD231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBE), 1, X86InstInfo{"VFNMSUB231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBF), 1, X86InstInfo{"VFNMSUB231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xB8), 1, X86InstInfo{"VFMADD231", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xB9), 1, X86InstInfo{"VFMADD231_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBA), 1, X86InstInfo{"VFMSUB231", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBB), 1, X86InstInfo{"VFMSUB231_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBC), 1, X86InstInfo{"VFNMADD231", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBD), 1, X86InstInfo{"VFNMADD231_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBE), 1, X86InstInfo{"VFNMSUB231", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xBF), 1, X86InstInfo{"VFNMSUB231_S", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0xA6), 1, X86InstInfo{"VFMADDSUB213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xA7), 1, X86InstInfo{"VFMSUBADD213", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xA6), 1, X86InstInfo{"VFMADDSUB213", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xA7), 1, X86InstInfo{"VFMSUBADD213", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0xB6), 1, X86InstInfo{"VFMADDSUB231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xB7), 1, X86InstInfo{"VFMSUBADD231", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xB6), 1, X86InstInfo{"VFMADDSUB231", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xB7), 1, X86InstInfo{"VFMSUBADD231", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0xDB), 1, X86InstInfo{"VAESIMC", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0xDC), 1, X86InstInfo{"VAESENC", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -433,7 +433,7 @@ std::array<X86InstInfo, MAX_VEX_TABLE_SIZE> VEXTableOps = []() consteval {
|
||||
|
||||
{OPD(3, 0b01, 0x18), 1, X86InstInfo{"VINSERTF128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x19), 1, X86InstInfo{"VEXTRACTF128", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_256BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x1D), 1, X86InstInfo{"VCVTPS2PH", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x1D), 1, X86InstInfo{"VCVTPS2PH", TYPE_INST, GenFlagsSizes(SIZE_64BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x20), 1, X86InstInfo{"VPINSRB", TYPE_INST, GenFlagsDstSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x21), 1, X86InstInfo{"VINSERTPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
@@ -452,33 +452,33 @@ std::array<X86InstInfo, MAX_VEX_TABLE_SIZE> VEXTableOps = []() consteval {
|
||||
{OPD(3, 0b01, 0x4B), 1, X86InstInfo{"VBLENDVPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x4C), 1, X86InstInfo{"VPBLENDVB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x5C), 1, X86InstInfo{"VFMADDSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x5D), 1, X86InstInfo{"VFMADDSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x5E), 1, X86InstInfo{"VFMSUBADDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x5F), 1, X86InstInfo{"VFMSUBADDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x5C), 1, X86InstInfo{"VFMADDSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x5D), 1, X86InstInfo{"VFMADDSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x5E), 1, X86InstInfo{"VFMSUBADDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x5F), 1, X86InstInfo{"VFMSUBADDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
|
||||
{OPD(3, 0b01, 0x60), 1, X86InstInfo{"VPCMPESTRM", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x61), 1, X86InstInfo{"VPCMPESTRI", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x62), 1, X86InstInfo{"VPCMPISTRM", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x63), 1, X86InstInfo{"VPCMPISTRI", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x68), 1, X86InstInfo{"VFMADDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x69), 1, X86InstInfo{"VFMADDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x6A), 1, X86InstInfo{"VFMADDSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x6B), 1, X86InstInfo{"VFMADDSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x6C), 1, X86InstInfo{"VFMSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x6D), 1, X86InstInfo{"VFMSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x6E), 1, X86InstInfo{"VFMSUBSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x6F), 1, X86InstInfo{"VFMSUBSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x68), 1, X86InstInfo{"VFMADDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x69), 1, X86InstInfo{"VFMADDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x6A), 1, X86InstInfo{"VFMADDSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x6B), 1, X86InstInfo{"VFMADDSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x6C), 1, X86InstInfo{"VFMSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x6D), 1, X86InstInfo{"VFMSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x6E), 1, X86InstInfo{"VFMSUBSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x6F), 1, X86InstInfo{"VFMSUBSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
|
||||
{OPD(3, 0b01, 0x78), 1, X86InstInfo{"VFNMADDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x79), 1, X86InstInfo{"VFNMADDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x7A), 1, X86InstInfo{"VFNMADDSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x7B), 1, X86InstInfo{"VFNMADDSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x7C), 1, X86InstInfo{"VFNMSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x7D), 1, X86InstInfo{"VFNMSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x7E), 1, X86InstInfo{"VFNMSUBSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x7F), 1, X86InstInfo{"VFNMSUBSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x78), 1, X86InstInfo{"VFNMADDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x79), 1, X86InstInfo{"VFNMADDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x7A), 1, X86InstInfo{"VFNMADDSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x7B), 1, X86InstInfo{"VFNMADDSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x7C), 1, X86InstInfo{"VFNMSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x7D), 1, X86InstInfo{"VFNMSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x7E), 1, X86InstInfo{"VFNMSUBSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
{OPD(3, 0b01, 0x7F), 1, X86InstInfo{"VFNMSUBSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, ///< FMA4
|
||||
|
||||
{OPD(3, 0b01, 0xDF), 1, X86InstInfo{"VAESKEYGENASSIST", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ constexpr uint32_t FLAG_LOCK = (1 << 2);
|
||||
constexpr uint32_t FLAG_LEGACY_PREFIX = (1 << 3);
|
||||
constexpr uint32_t FLAG_REX_PREFIX = (1 << 4);
|
||||
constexpr uint32_t FLAG_VSIB_BYTE = (1 << 5);
|
||||
// Hole where 1 << 6 is
|
||||
constexpr uint32_t FLAG_OPTION_AVX_W = (1 << 6);
|
||||
constexpr uint32_t FLAG_REX_WIDENING = (1 << 7);
|
||||
constexpr uint32_t FLAG_REX_XGPR_B = (1 << 8);
|
||||
constexpr uint32_t FLAG_REX_XGPR_X = (1 << 9);
|
||||
@@ -137,6 +137,10 @@ struct DecodedOperand {
|
||||
bool IsSIB() const {
|
||||
return Type == OpType::SIB;
|
||||
}
|
||||
uint64_t Literal() const {
|
||||
LOGMAN_THROW_A_FMT(IsLiteral(), "Precondition: must be a literal");
|
||||
return Data.Literal.Value;
|
||||
}
|
||||
|
||||
union TypeUnion {
|
||||
struct GPRType {
|
||||
|
||||
@@ -373,7 +373,7 @@ struct ThunkHandler_impl final : public ThunkHandler {
|
||||
Thread = _Thread;
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(const fextl::vector<FEXCore::IR::ThunkDefinition>& Definitions) override {
|
||||
void AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) override {
|
||||
for (auto& Definition : Definitions) {
|
||||
Thunks.emplace(Definition.Sum, Definition.ThunkFunction);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,6 @@ public:
|
||||
|
||||
static fextl::unique_ptr<ThunkHandler> Create();
|
||||
|
||||
virtual void AppendThunkDefinitions(const fextl::vector<FEXCore::IR::ThunkDefinition>& Definitions) = 0;
|
||||
virtual void AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) = 0;
|
||||
};
|
||||
}; // namespace FEXCore
|
||||
@@ -83,7 +83,7 @@ struct AOTIRCacheEntry {
|
||||
AOTIRInlineIndex* Array;
|
||||
void* FilePtr;
|
||||
size_t Size;
|
||||
std::unique_ptr<FEXCore::HLE::SourcecodeMap> SourcecodeMap;
|
||||
fextl::unique_ptr<FEXCore::HLE::SourcecodeMap> SourcecodeMap;
|
||||
fextl::string FileId;
|
||||
fextl::string Filename;
|
||||
bool ContainsCode;
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
"SwitchGen": false,
|
||||
"JITDispatchOverride": "NoOp"
|
||||
},
|
||||
"IRHeader SSA:$Blocks, u64:$OriginalRIP, u32:$BlockCount, u32:$NumHostInstructions": {
|
||||
"IRHeader SSA:$Blocks, u64:$OriginalRIP, u32:$BlockCount, u32:$NumHostInstructions, i1:$HasX87{false}": {
|
||||
"SwitchGen": false,
|
||||
"JITDispatchOverride": "NoOp"
|
||||
},
|
||||
@@ -226,11 +226,22 @@
|
||||
"DestSize": "4"
|
||||
},
|
||||
|
||||
"SetRoundingMode GPR:$RoundMode": {
|
||||
"SetRoundingMode GPR:$RoundMode, i1:$SetDAZ, GPR:$MXCSR": {
|
||||
"Desc": ["Sets the current rounding mode options for the thread"
|
||||
],
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"GPR = PushRoundingMode u8:$RoundMode": {
|
||||
"Desc": ["Override the current rounding mode options for the thread, returning old FPCR"
|
||||
],
|
||||
"DestSize": "8",
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"PopRoundingMode GPR:$FPCR": {
|
||||
"Desc": ["Resets rounding mode after PushRoundingMode operation"
|
||||
],
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"Print SSA:$Value": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Debug operation that prints an SSA value to the console",
|
||||
@@ -275,7 +286,7 @@
|
||||
"Desc": ["Exits the current JIT function with a target RIP"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "GetOpSize(_NewRIP)"
|
||||
"DestSize": "GetOpSize(NewRIP)"
|
||||
},
|
||||
"Break BreakDefinition:$Reason": {
|
||||
"HasSideEffects": true
|
||||
@@ -478,24 +489,6 @@
|
||||
"DestSize": "8"
|
||||
},
|
||||
|
||||
"GPR = LoadFlag u32:$Flag": {
|
||||
"Desc": ["Loads an x86-64 flag from the context object",
|
||||
"Specialized to allow flexible implementation of flag handling"
|
||||
],
|
||||
"DestSize": "1"
|
||||
},
|
||||
|
||||
"StoreFlag GPR:$Value, u32:$Flag": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Stores 1-bit of the flag in to the specified x86-64 flag",
|
||||
"Specialized to allow flexible implementation of flag handling"
|
||||
],
|
||||
"DestSize": "1"
|
||||
},
|
||||
|
||||
"GPR = GetHostFlag GPR:$Value, u8:$Flag": {
|
||||
},
|
||||
|
||||
"SSA = LoadMem RegisterClass:$Class, u8:#Size, GPR:$Addr, GPR:$Offset, u8:$Align, MemOffsetType:$OffsetType, u8:$OffsetScale": {
|
||||
"DestSize": "Size"
|
||||
},
|
||||
@@ -545,6 +538,36 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VLoadVectorGatherMasked u8:#RegisterSize, u8:#ElementSize, FPR:$Incoming, FPR:$Mask, GPR:$AddrBase, FPR:$VectorIndexLow, FPR:$VectorIndexHigh, u8:$VectorIndexElementSize, u8:$OffsetScale, u8:$DataElementOffsetStart, u8:$IndexElementOffsetStart": {
|
||||
"Desc": [
|
||||
"Does a masked load similar to VPGATHERD* where the upper bit of each element",
|
||||
"determines whether or not that element will be loaded from memory.",
|
||||
"Most of VSIB encoding is passed directly through to the IR operation."
|
||||
],
|
||||
"TiedSource": 0,
|
||||
"ImplicitFlagClobber": true,
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"EmitValidation": [
|
||||
"$VectorIndexElementSize == OpSize::i32Bit || $VectorIndexElementSize == OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"FPR = VLoadVectorGatherMaskedQPS u8:#RegisterSize, u8:#ElementSize, FPR:$Incoming, FPR:$MaskReg, GPR:$AddrBase, FPR:$VectorIndexLow, FPR:$VectorIndexHigh, u8:$OffsetScale": {
|
||||
"Desc": [
|
||||
"Does a masked load similar to VPGATHERQPS where the upper bit of each element",
|
||||
"determines whether or not that element will be loaded from memory.",
|
||||
"Most of VSIB encoding is passed directly through to the IR operation.",
|
||||
"Only supports the case of 32-bit data element sizes from 64-bit addresses"
|
||||
],
|
||||
"TiedSource": 0,
|
||||
"ImplicitFlagClobber": true,
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"EmitValidation": [
|
||||
"ElementSize == OpSize::i32Bit",
|
||||
"RegisterSize != FEXCore::IR::OpSize::i256Bit && \"What does 256-bit mean in this context?\""
|
||||
]
|
||||
},
|
||||
"FPR = VLoadVectorElement u8:#RegisterSize, u8:#ElementSize, FPR:$DstSrc, u8:$Index, GPR:$Addr": {
|
||||
"Desc": ["Does a memory load to a single element of a vector.",
|
||||
"Leaves the rest of the vector's data intact.",
|
||||
@@ -625,10 +648,46 @@
|
||||
"Desc": ["Does a cacheline prefetch operation"
|
||||
],
|
||||
"EmitValidation": [
|
||||
"_CacheLevel > 0 && _CacheLevel < 4"
|
||||
"CacheLevel > 0 && CacheLevel < 4"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "8"
|
||||
},
|
||||
"VStoreNonTemporal u8:#RegisterSize, FPR:$Value, GPR:$Addr, i8:$Offset": {
|
||||
"Desc": ["Does a non-temporal memory store of a vector.",
|
||||
"Matches arm64 SVE stnt1b semantics.",
|
||||
"Specifically weak-memory model ordered to match x86 non-temporal stores."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "RegisterSize",
|
||||
"EmitValidation": [
|
||||
"Offset % RegisterSize == 0",
|
||||
"RegisterSize == FEXCore::IR::OpSize::i128Bit || RegisterSize == FEXCore::IR::OpSize::i256Bit"
|
||||
]
|
||||
},
|
||||
"VStoreNonTemporalPair u8:#RegisterSize, FPR:$ValueLow, FPR:$ValueHigh, GPR:$Addr, i8:$Offset": {
|
||||
"Desc": ["Does a non-temporal memory store of two vector registers.",
|
||||
"Matches arm64 stnp semantics.",
|
||||
"Specifically weak-memory model ordered to match x86 non-temporal stores."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "RegisterSize",
|
||||
"EmitValidation": [
|
||||
"Offset % RegisterSize == 0",
|
||||
"RegisterSize == FEXCore::IR::OpSize::i128Bit"
|
||||
]
|
||||
},
|
||||
"FPR = VLoadNonTemporal u8:#RegisterSize, GPR:$Addr, i8:$Offset": {
|
||||
"Desc": ["Does a non-temporal memory load of a vector.",
|
||||
"Matches arm64 SVE ldnt1b semantics.",
|
||||
"Specifically weak-memory model ordered to match x86 non-temporal stores."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "RegisterSize",
|
||||
"EmitValidation": [
|
||||
"Offset % RegisterSize == 0",
|
||||
"RegisterSize == FEXCore::IR::OpSize::i128Bit || RegisterSize == FEXCore::IR::OpSize::i256Bit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Atomic": {
|
||||
@@ -1009,7 +1068,7 @@
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit",
|
||||
"_Shift != ShiftType::ROR"
|
||||
"Shift != ShiftType::ROR"
|
||||
]
|
||||
},
|
||||
"GPR = AddWithFlags OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
@@ -1109,7 +1168,7 @@
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit",
|
||||
"_Shift != ShiftType::ROR"
|
||||
"Shift != ShiftType::ROR"
|
||||
]
|
||||
},
|
||||
"GPR = SubWithFlags OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
@@ -1394,7 +1453,7 @@
|
||||
"DestSize": "ResultSize",
|
||||
"ImplicitFlagClobber": true,
|
||||
"EmitValidation": [
|
||||
"_CompareSize == FEXCore::IR::OpSize::i32Bit || _CompareSize == FEXCore::IR::OpSize::i64Bit || _CompareSize == FEXCore::IR::OpSize::i128Bit",
|
||||
"CompareSize == FEXCore::IR::OpSize::i32Bit || CompareSize == FEXCore::IR::OpSize::i64Bit || CompareSize == FEXCore::IR::OpSize::i128Bit",
|
||||
"ResultSize == FEXCore::IR::OpSize::i32Bit || ResultSize == FEXCore::IR::OpSize::i64Bit",
|
||||
"WalkFindRegClass($Cmp1) == WalkFindRegClass($Cmp2)"
|
||||
]
|
||||
@@ -1657,6 +1716,42 @@
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VFMLAScalarInsert u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (Vector1 * Vector2) + Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
},
|
||||
"FPR = VFMLSScalarInsert u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (Vector1 * Vector2) - Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
},
|
||||
"FPR = VFNMLAScalarInsert u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (-Vector1 * Vector2) + Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
},
|
||||
"FPR = VFNMLSScalarInsert u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (-Vector1 * Vector2) - Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
}
|
||||
},
|
||||
"Vector": {
|
||||
@@ -1829,6 +1924,18 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize << 1)"
|
||||
},
|
||||
"FPR = VSSHLL u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$BitShift{0}": {
|
||||
"Desc": "Sign extends elements from the source element size to the next size up",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize << 1)"
|
||||
},
|
||||
"FPR = VSSHLL2 u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$BitShift{0}": {
|
||||
"Desc": ["Sign extends elements from the source element size to the next size up",
|
||||
"Source elements come from the upper half of the register"
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize << 1)"
|
||||
},
|
||||
"FPR = VUXTL u8:#RegisterSize, u8:#ElementSize, FPR:$Vector": {
|
||||
"Desc": "Zero extends elements from the source element size to the next size up",
|
||||
"DestSize": "RegisterSize",
|
||||
@@ -1917,7 +2024,7 @@
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
"FPR = VBic u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
|
||||
"FPR = VAndn u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
@@ -2262,6 +2369,42 @@
|
||||
"FPR = VFCADD u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, u16:$Rotate": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VFMLA u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (Vector1 * Vector2) + Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
},
|
||||
"FPR = VFMLS u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (Vector1 * Vector2) - Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
},
|
||||
"FPR = VFNMLA u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (-Vector1 * Vector2) + Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
},
|
||||
"FPR = VFNMLS u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2, FPR:$Addend": {
|
||||
"Desc": [
|
||||
"Dest = (-Vector1 * Vector2) - Addend",
|
||||
"This explicitly matches x86 FMA semantics because ARM semantics are mind-bending."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"TiedSource": 2
|
||||
}
|
||||
},
|
||||
"Conv": {
|
||||
@@ -2314,12 +2457,45 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / DestElementSize"
|
||||
},
|
||||
|
||||
"FPR = VFCVTL2 u8:#RegisterSize, u8:#ElementSize, FPR:$Vector": {
|
||||
"Desc": [
|
||||
"Vector op: Converts float from source element size to destination size (fp32->fp64)",
|
||||
"Selecting from the high half of the register."
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize << 1)",
|
||||
"EmitValidation": [
|
||||
"RegisterSize != FEXCore::IR::OpSize::i256Bit && \"What does 256-bit mean in this context?\""
|
||||
]
|
||||
},
|
||||
"FPR = VFCVTN2 u8:#RegisterSize, u8:#ElementSize, FPR:$VectorLower, FPR:$VectorUpper": {
|
||||
"TiedSource": 0,
|
||||
"Desc": [
|
||||
"Vector op: Converts float from source element size and inserting in to the high bits.",
|
||||
"Bottom half is untouched",
|
||||
"Narrowing to the element size below what is passed in.",
|
||||
"F64->F32, F32->F16"
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize >> 1)",
|
||||
"EmitValidation": [
|
||||
"RegisterSize != FEXCore::IR::OpSize::i256Bit && \"What does 256-bit mean in this context?\""
|
||||
]
|
||||
},
|
||||
"FPR = Vector_FToI u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, RoundType:$Round": {
|
||||
"Desc": ["Vector op: Rounds float to integral",
|
||||
"Rounding mode determined by argument"
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = Vector_F64ToI32 u8:#RegisterSize, FPR:$Vector, RoundType:$Round, i1:$EnsureZeroUpperHalf": {
|
||||
"Desc": ["Vector op: Rounds 64-bit float to 32-bit integral with round mode",
|
||||
"Matches CVTPD2DQ/CVTTPD2DQ behaviour"
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / FEXCore::IR::OpSize::i32Bit"
|
||||
}
|
||||
},
|
||||
"Crypto": {
|
||||
@@ -2410,34 +2586,283 @@
|
||||
}
|
||||
},
|
||||
"F80": {
|
||||
"GPR = SyncStackToSlow": {
|
||||
"Desc": [
|
||||
"Synchronizes the virtual stack environment to the physical registers.",
|
||||
"Returns the current stack top."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true,
|
||||
"DestSize": 8
|
||||
},
|
||||
"StackForceSlow": {
|
||||
"Desc": [
|
||||
"Forces the slow path."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"InitStack": {
|
||||
"Desc": [
|
||||
"Initializes the stack by marking all tags as invalid and setting top to zero."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"IncStackTop": {
|
||||
"Desc": [
|
||||
"Increase stack top-pointer."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"DecStackTop": {
|
||||
"Desc": [
|
||||
"Decrease stack top-pointer."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"InvalidateStack u8:$StackLocation": {
|
||||
"Desc": [
|
||||
"Marks the value in TOP+$StackLocation as empty / invalid 0b11.",
|
||||
"If the StackLocation is 0xff, we invalidate all locations."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"PushStack FPR:$X80Src, SSA:$OriginalValue, u8:$LoadSize, i1:$Float": {
|
||||
"Desc": [
|
||||
"Pushes the provided X80Src source on to the x87 stack.",
|
||||
"Tracks OriginalValue as the original value of X80Src.",
|
||||
"Opsize is 128bit for F80 values, 64-bit for low precision.",
|
||||
"LoadSize the original load size, i.e. of size of OriginalValue.",
|
||||
"Float: 80-bit, 64-bit, 32-bit",
|
||||
"Int: 64-bit, 32-bit, 16-bit"
|
||||
],
|
||||
"EmitValidation": [
|
||||
"WalkFindRegClass($OriginalValue) == FPRClass || WalkFindRegClass($OriginalValue) == GPRClass"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"CopyPushStack u8:$StackLocation": {
|
||||
"Desc": [
|
||||
"Pushes an element already on the stack onto the top."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"StoreStackMemory GPR:$Addr, OpSize:$SourceSize, i1:$Float, u8:$StoreSize": {
|
||||
"Desc": [
|
||||
"Takes the top value off the x87 stack and stores it to memory.",
|
||||
"SourceSize is 128bit for F80 values, 64-bit for low precision.",
|
||||
"StoreSize is the store size for conversion:",
|
||||
"Float: 80-bit, 64-bit, or 32-bit",
|
||||
"Int: 64-bit, 32-bit, 16-bit"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"StoreStackToStack u8:$StackLocation": {
|
||||
"Desc": [
|
||||
"Takes the top value off the x87 stack and stores it to stack location TOP+StackLocation",
|
||||
"Float: 80-bit, 64-bit, or 32-bit",
|
||||
"Int: 64-bit, 32-bit, 16-bit"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"PopStackDestroy": {
|
||||
"Desc": [
|
||||
"Pops the top value off the stack but doesn't save it anywhere."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"FPR = ReadStackValue u8:$StackLocation": {
|
||||
"Desc": [
|
||||
"Reads a value off the stack at the offset"
|
||||
],
|
||||
"DestSize": "16",
|
||||
"X87": true
|
||||
},
|
||||
"GPR = StackValidTag u8:$StackLocation": {
|
||||
"Desc": [
|
||||
"Returns 1 if the value in location TOP+$StackLocation is valid, 0 otherwise."
|
||||
],
|
||||
"DestSize": 4,
|
||||
"X87": true
|
||||
},
|
||||
"F80AddStack u8:$SrcStack1, u8:$SrcStack2": {
|
||||
"Desc": [
|
||||
"Adds two stack locations together, storing the result in to the first stack location"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"F80AddValue u8:$SrcStack, FPR:$X80Src": {
|
||||
"Desc": [
|
||||
"Adds a operand value to a stack location. The result stored in to the stack location provided."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"FPR = F80Add FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80SubStack u8:$DstStack, u8:$SrcStack1, u8:$SrcStack2": {
|
||||
"Desc": [
|
||||
"Subtracts the value in stack location TOP+$SrcStack2 from the value in stack location TOP+$SrcStack1.",
|
||||
"The result is stored in stack location TOP+$DstStack."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"F80SubValue u8:$SrcStack, FPR:$X80Src": {
|
||||
"Desc": [
|
||||
"Subtracts the value $X80Src from the value in stack location TOP+$SrcStack.",
|
||||
"The result is stored in stack location TOP."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"F80SubRValue FPR:$X80Src, u8:$SrcStack": {
|
||||
"Desc": [
|
||||
"Subtracts the value in stack location TOP+$SrcStack from the value $X80Src.",
|
||||
"The result is stored in stack location TOP."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"FPR = F80Sub FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"Desc": [
|
||||
"Subtracts the value in $X80Src1 from the value in $X80Src2.",
|
||||
"The result is returned.",
|
||||
"`FPR = X80Src2 - X80Src1`"
|
||||
],
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80MulStack u8:$SrcStack1, u8:$SrcStack2": {
|
||||
"Desc": [
|
||||
"Multiplies two stack locations together, storing the result in to the first stack location"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"F80MulValue u8:$SrcStack, FPR:$X80Src": {
|
||||
"Desc": [
|
||||
"Multiplies a operand value to a stack location. The result stored in to the stack location provided."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"FPR = F80Mul FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80DivStack u8:$DstStack, u8:$SrcStack1, u8:$SrcStack2": {
|
||||
"Desc": [
|
||||
"Divides the value in stack location TOP+$SrcStack1 by the value in stack location TOP+$SrcStack2.",
|
||||
"The result is stored in stack location TOP+$DstStack.",
|
||||
"`FPR|Stack[TOP+DstStack] = Stack[TOP+SrcStack1] / Stack[TOP+SrcStack2]`"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"F80DivValue u8:$SrcStack, FPR:$X80Src": {
|
||||
"Desc": [
|
||||
"Divides the value in stack location TOP+$SrcStack by the value $X80Src.",
|
||||
"The result is stored in stack location TOP and returned.",
|
||||
"`FPR|Stack[TOP] = Stack[TOP+SrcStack] / X80Src`"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"F80DivRValue FPR:$X80Src, u8:$SrcStack": {
|
||||
"Desc": [
|
||||
"Divides the value X80Src by the value in stack location TOP+$SrcStack.",
|
||||
"The result is stored in stack location TOP.",
|
||||
"`FPR|Stack[TOP] = X80Src / Stack[TOP+SrcStack]`"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"FPR = F80Div FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"Desc": [
|
||||
"Divides the value in $X80Src1 by the value in $X80Src2.",
|
||||
"The result is returned.",
|
||||
"`FPR = X80Src1 / X80Src2`"
|
||||
],
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80StackXchange u8:$SrcStack": {
|
||||
"Desc": [
|
||||
"Exchanges the value at the top of the stack with the value at TOP+$SrcStack."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80StackChangeSign": {
|
||||
"Desc": [
|
||||
"Complements the sign bit of the value at the top of the stack.",
|
||||
"Returns the new value at the top of the stack."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "16",
|
||||
"X87": true
|
||||
},
|
||||
"FPR = F80StackAbs": {
|
||||
"Desc": [
|
||||
"Clears the sign bit of the value at the top of the stack.",
|
||||
"Returns the new value at the top of the stack."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "16",
|
||||
"X87": true
|
||||
},
|
||||
"F80PTANStack": {
|
||||
"Desc": [
|
||||
"Computes the approximate tangent of the source operand in register ST(0), stores the result in ST(0), and pushes a 1.0 onto the FPU register stack."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80ATANStack": {
|
||||
"Desc": [
|
||||
"Computes arctan(st1/st0) and stores it in st0. Then pops the stack."
|
||||
],
|
||||
"DestSize": "16",
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80ATAN FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80FPREMStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80FPREM FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80FPREM1Stack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80FPREM1 FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80SCALEStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80SCALE FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
@@ -2458,10 +2883,21 @@
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80RoundStack": {
|
||||
"Desc": [
|
||||
"Replaces the value at the top of the stack with its nearest integral value."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80Round FPR:$X80Src": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80F2XM1Stack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80F2XM1 FPR:$X80Src": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
@@ -2470,18 +2906,38 @@
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80SINStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80SIN FPR:$X80Src": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80COSStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80COS FPR:$X80Src": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80SINCOSStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"F80SQRTStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80SQRT FPR:$X80Src": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80XTRACTStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"FPR = F80XTRACT_EXP FPR:$X80Src": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
@@ -2490,8 +2946,32 @@
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"GPR = F80Cmp FPR:$X80Src1, FPR:$X80Src2, u32:$Flags": {
|
||||
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
|
||||
"GPR = F80StackTest u8:$SrcStack": {
|
||||
"Desc": [
|
||||
"Does comparison between value in stack at TOP + SrcStack"
|
||||
],
|
||||
"DestSize": "4",
|
||||
"X87": true
|
||||
},
|
||||
"GPR = F80CmpStack u8:$SrcStack": {
|
||||
"Desc": [
|
||||
"Does a scalar unordered compare between the value at the top of the stack and the value in stack position TOP+$SrcStack and stores the flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"DestSize": "4",
|
||||
"X87": true
|
||||
},
|
||||
"GPR = F80CmpValue FPR:$X80Src": {
|
||||
"Desc": [
|
||||
"Does a scalar unordered compare between the value at the top of the stack and $X80Src and stores the asked for flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"DestSize": "4",
|
||||
"HasSideEffects": true,
|
||||
"X87": true
|
||||
},
|
||||
"GPR = F80Cmp FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"Desc": ["Does a scalar unordered compare and stores the flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"DestSize": "4",
|
||||
@@ -2505,10 +2985,29 @@
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
|
||||
"FPR = F80FYL2XStack": {
|
||||
"Desc": [
|
||||
"Computes ST1 * log2(ST0)",
|
||||
"Stores the result in ST1, and pops the top of the stack.",
|
||||
"Returns the new value at the top of the stack, i.e. the result of the operation."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "16",
|
||||
"X87": true
|
||||
},
|
||||
"FPR = F80FYL2X FPR:$X80Src1, FPR:$X80Src2": {
|
||||
"DestSize": "16",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80VBSLStack u8:#RegisterSize, FPR:$VectorMask, u8:$SrcStack1, u8:$SrcStack2": {
|
||||
"Desc": [
|
||||
"Does a vector bitwise select.",
|
||||
"If the bit in the field is 1 then the corresponding bit is pulled from VectorTrue",
|
||||
"If the bit in the field is 0 then the corresponding bit is pulled from VectorFalse",
|
||||
"Writes the result to the top of the stack."
|
||||
],
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
}
|
||||
},
|
||||
"Backend": {
|
||||
|
||||
@@ -183,6 +183,14 @@ static void PrintArg(fextl::stringstream* out, [[maybe_unused]] const IRListView
|
||||
return "addsubpd_invert";
|
||||
case NamedVectorConstant::NAMED_VECTOR_PADDSUBPD_INVERT_UPPER:
|
||||
return "addsubpd_invert_upper";
|
||||
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPS_INVERT:
|
||||
return "subaddps_invert";
|
||||
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPS_INVERT_UPPER:
|
||||
return "subaddps_invert_upper";
|
||||
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPD_INVERT:
|
||||
return "subaddpd_invert";
|
||||
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPD_INVERT_UPPER:
|
||||
return "subaddpd_invert_upper";
|
||||
case NamedVectorConstant::NAMED_VECTOR_MOVMSKPS_SHIFT:
|
||||
return "movmskps_shift";
|
||||
case NamedVectorConstant::NAMED_VECTOR_AESKEYGENASSIST_SWIZZLE:
|
||||
|
||||
@@ -343,9 +343,9 @@ protected:
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
virtual void SaveNZCV(IROps Op) {
|
||||
// Overriden by dispatcher, stubbed for IR tests
|
||||
}
|
||||
// Overriden by dispatcher, stubbed for IR tests
|
||||
virtual void RecordX87Use() {}
|
||||
virtual void SaveNZCV(IROps Op) {}
|
||||
|
||||
Ref CurrentWriteCursor = nullptr;
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl* ctx) {
|
||||
FEX_CONFIG_OPT(DisablePasses, O0);
|
||||
|
||||
if (!DisablePasses()) {
|
||||
InsertPass(CreateContextLoadStoreElimination(ctx->HostFeatures.SupportsAVX));
|
||||
InsertPass(CreateX87StackOptimizationPass());
|
||||
InsertPass(CreateDeadStoreElimination());
|
||||
InsertPass(CreateConstProp(ctx->HostFeatures.SupportsTSOImm9, &ctx->CPUID));
|
||||
InsertPass(CreateDeadFlagCalculationEliminination());
|
||||
|
||||
Loaded 100 of 528 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user