Compare commits
3155
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b59a54c61b | ||
|
|
42011f23b4 | ||
|
|
7e6d31627e | ||
|
|
a39ab80a77 | ||
|
|
18adf0ab32 | ||
|
|
3ebfb353e1 | ||
|
|
7769b0e0eb | ||
|
|
b549aa6f53 | ||
|
|
bd4103a2ed | ||
|
|
2e07f0be92 | ||
|
|
87eddd70af | ||
|
|
7034bc6fb3 | ||
|
|
28cfdf8c23 | ||
|
|
00f24b8841 | ||
|
|
8b706be4ba | ||
|
|
eb5d271dc4 | ||
|
|
fe107331fe | ||
|
|
51bac6418f | ||
|
|
598344fb62 | ||
|
|
c1690cc3a0 | ||
|
|
d6ea4109ec | ||
|
|
9f634046e5 | ||
|
|
50af7630e9 | ||
|
|
b30ff1db0e | ||
|
|
a18662ebce | ||
|
|
3cd6fe4e34 | ||
|
|
0b9ac05fbe | ||
|
|
7b7028be6a | ||
|
|
6f8e28bda8 | ||
|
|
11938e343b | ||
|
|
471d7981c4 | ||
|
|
a729279224 | ||
|
|
c0f7780709 | ||
|
|
38cbdac05a | ||
|
|
128da9b687 | ||
|
|
5bf5172310 | ||
|
|
f422a81d46 | ||
|
|
6b0e55d42f | ||
|
|
38ce112e5c | ||
|
|
d9c7f55270 | ||
|
|
2b03311fe9 | ||
|
|
361621a46a | ||
|
|
d8f75f5cf4 | ||
|
|
36349c2770 | ||
|
|
2c0d131cab | ||
|
|
9feda4d22d | ||
|
|
03264e760f | ||
|
|
224e3b25e5 | ||
|
|
fa20199ead | ||
|
|
03c624a77b | ||
|
|
1ed76de58a | ||
|
|
36566091aa | ||
|
|
1a1466ec67 | ||
|
|
5f40ff5a03 | ||
|
|
b1baa90b19 | ||
|
|
f0579de244 | ||
|
|
044836b026 | ||
|
|
268bce79bd | ||
|
|
19c1d2438e | ||
|
|
2dcb35da21 | ||
|
|
7747af9e9f | ||
|
|
c2a761c895 | ||
|
|
6bdd2b2397 | ||
|
|
e95db0c7e8 | ||
|
|
efcd8dacfe | ||
|
|
06124deab9 | ||
|
|
0f0195d35b | ||
|
|
0695806ee9 | ||
|
|
a1c34cab1a | ||
|
|
0fa702c471 | ||
|
|
70f41cf221 | ||
|
|
78547ba931 | ||
|
|
49de45d736 | ||
|
|
7085ac3392 | ||
|
|
fce86135c0 | ||
|
|
d9052fff97 | ||
|
|
03fff05f6c | ||
|
|
1a91adc5c1 | ||
|
|
f626e9e06b | ||
|
|
983974821c | ||
|
|
f4af9fd394 | ||
|
|
b34dd7745b | ||
|
|
2728fbdd46 | ||
|
|
6bfb448de1 | ||
|
|
b8839896c6 | ||
|
|
df66651356 | ||
|
|
6653b5ab6c | ||
|
|
7373ebe0b2 | ||
|
|
5ac2dc29b1 | ||
|
|
8bed3915e4 | ||
|
|
296cec0b0c | ||
|
|
8a93a79fc7 | ||
|
|
0df7c2a1e2 | ||
|
|
5d2ea9d7a3 | ||
|
|
bd653785c2 | ||
|
|
e7b9a28d70 | ||
|
|
e4aab9128e | ||
|
|
a69e03310e | ||
|
|
eb9fdf1fdc | ||
|
|
63ca42d12c | ||
|
|
a502633f76 | ||
|
|
23b194eaa8 | ||
|
|
ce92b30119 | ||
|
|
2adffd32ea | ||
|
|
94a5c1fe31 | ||
|
|
33c9184fed | ||
|
|
7d12ceae74 | ||
|
|
12ec3d11a2 | ||
|
|
b1c94a0885 | ||
|
|
3614867f30 | ||
|
|
ae19383ee0 | ||
|
|
75e367372c | ||
|
|
620f5a40d4 | ||
|
|
a834bc6c05 | ||
|
|
80b35cdefb | ||
|
|
196501ae66 | ||
|
|
76503edfeb | ||
|
|
b1d8930b5a | ||
|
|
0ae6f8fa16 | ||
|
|
9ce917feee | ||
|
|
7becd7a3d2 | ||
|
|
acbcdedfa0 | ||
|
|
0ab8fa25e2 | ||
|
|
f5b96eead2 | ||
|
|
fcdceb53d7 | ||
|
|
2ddb1ab56c | ||
|
|
0ab0ced3d3 | ||
|
|
47dcf7d1a8 | ||
|
|
51f2dd6275 | ||
|
|
0b077a9a8d | ||
|
|
5ab1a65bab | ||
|
|
1d7851d32f | ||
|
|
489b4157d2 | ||
|
|
2d8d14dcc5 | ||
|
|
1c8a0006f0 | ||
|
|
c722523d40 | ||
|
|
b8434e0d1e | ||
|
|
3ff9a75a8f | ||
|
|
e2944412b4 | ||
|
|
e7ff735e93 | ||
|
|
da0b714c92 | ||
|
|
37146b4c3d | ||
|
|
b56a7bd4ba | ||
|
|
1c52de67d5 | ||
|
|
e42e029d61 | ||
|
|
6db017ff1a | ||
|
|
b5a5c7d4f2 | ||
|
|
d79c59f6f3 | ||
|
|
40a7c08851 | ||
|
|
07c090d40d | ||
|
|
9bc010f670 | ||
|
|
11f7ecba5f | ||
|
|
6d458f2960 | ||
|
|
aad83a3a9b | ||
|
|
d1ba4c61d7 | ||
|
|
f255374a9d | ||
|
|
635481ea90 | ||
|
|
290deef444 | ||
|
|
baefb4d0b0 | ||
|
|
0ce8c55ab3 | ||
|
|
e064e4d13d | ||
|
|
bb26a33889 | ||
|
|
4c15826848 | ||
|
|
91301ffdcb | ||
|
|
baed5fb5de | ||
|
|
31122a6f34 | ||
|
|
95ac0796f8 | ||
|
|
df33fbcdd7 | ||
|
|
ac7e298c42 | ||
|
|
9fd20af939 | ||
|
|
8960804f1c | ||
|
|
6437c5a861 | ||
|
|
600e824c63 | ||
|
|
5977574270 | ||
|
|
1234511304 | ||
|
|
7784bc6d0d | ||
|
|
638b3757c8 | ||
|
|
9474142aca | ||
|
|
cf391fb346 | ||
|
|
9e684cded1 | ||
|
|
b642bfb984 | ||
|
|
31e76f8b5f | ||
|
|
2e35a72dec | ||
|
|
0d4ad260f0 | ||
|
|
1e66305ce8 | ||
|
|
b7a5d715c0 | ||
|
|
20537807e1 | ||
|
|
e63c6cfd1e | ||
|
|
ed527a35e2 | ||
|
|
3a82442c86 | ||
|
|
98dde2f077 | ||
|
|
9322a3b1d0 | ||
|
|
0f0bef2a04 | ||
|
|
b13f104fde | ||
|
|
e9d91386d7 | ||
|
|
c2c60f2c69 | ||
|
|
836395a6af | ||
|
|
a7094537cb | ||
|
|
d02a097384 | ||
|
|
a34540f27f | ||
|
|
454311fd28 | ||
|
|
a260c390ad | ||
|
|
4de86a1bad | ||
|
|
59b1d66c39 | ||
|
|
b5521eb709 | ||
|
|
72f193f336 | ||
|
|
751c72f93c | ||
|
|
5d77754695 | ||
|
|
e9b2e17b69 | ||
|
|
c40d9f3ec4 | ||
|
|
14d2a6f7b6 | ||
|
|
65c8bcf3bd | ||
|
|
d7fe799097 | ||
|
|
52a6812805 | ||
|
|
96312999c0 | ||
|
|
3fe45c532d | ||
|
|
a7ff925038 | ||
|
|
1d4d1a1400 | ||
|
|
4ec735afe1 | ||
|
|
05086d83f0 | ||
|
|
965411519c | ||
|
|
d328adea5d | ||
|
|
fb804ea9dd | ||
|
|
7df37d50f4 | ||
|
|
c36b076b01 | ||
|
|
ee3a26d8fb | ||
|
|
6f86ef2896 | ||
|
|
ead9e96017 | ||
|
|
2b5b7c6292 | ||
|
|
1ee40b5579 | ||
|
|
cc6cb99ac5 | ||
|
|
d9babadd54 | ||
|
|
14f725a53f | ||
|
|
dc465733c7 | ||
|
|
58e12d620f | ||
|
|
0ef5a80d84 | ||
|
|
4587f6391a | ||
|
|
280df5d3d6 | ||
|
|
9cd8d2fb4e | ||
|
|
f384cf0b86 | ||
|
|
d1d61119ae | ||
|
|
96eecfe5a0 | ||
|
|
e0b58b4da0 | ||
|
|
021db03eb9 | ||
|
|
854ce92d83 | ||
|
|
0bef27cdf9 | ||
|
|
2037420013 | ||
|
|
01c3205d5d | ||
|
|
b9aee1d53e | ||
|
|
feb93360a4 | ||
|
|
afd0613199 | ||
|
|
71e9e040ec | ||
|
|
d54319cd55 | ||
|
|
0ce790f1bc | ||
|
|
4cb83de7b3 | ||
|
|
6ebc6412e4 | ||
|
|
d2f996ebaf | ||
|
|
325eac5b6d | ||
|
|
888d840198 | ||
|
|
72d5fbe641 | ||
|
|
9eb34006c2 | ||
|
|
1ccd50fffa | ||
|
|
3a698aab1a | ||
|
|
b257768f4c | ||
|
|
b81652bfdd | ||
|
|
4f2ffcdea5 | ||
|
|
2c8c6028f8 | ||
|
|
30d64ad538 | ||
|
|
df888048c4 | ||
|
|
d3427a3074 | ||
|
|
6378980f0d | ||
|
|
31244a5ce1 | ||
|
|
517de5d1fe | ||
|
|
4294536055 | ||
|
|
c7526928b7 | ||
|
|
f967babe29 | ||
|
|
41f42c0cb0 | ||
|
|
57388b78c5 | ||
|
|
b589fba6b8 | ||
|
|
f8e63c8a60 | ||
|
|
9169c66d03 | ||
|
|
32310c7e39 | ||
|
|
f8b1d1838c | ||
|
|
7877585135 | ||
|
|
a9e80bf4c4 | ||
|
|
d932d29720 | ||
|
|
0985f5f317 | ||
|
|
dcdee7c7f2 | ||
|
|
e0215220b4 | ||
|
|
5cbac8c415 | ||
|
|
9f781930ee | ||
|
|
43928bbbd2 | ||
|
|
c7d304349d | ||
|
|
50b3e1ed28 | ||
|
|
6ff76802eb | ||
|
|
4be2c84df0 | ||
|
|
2ff37018f9 | ||
|
|
293f91988c | ||
|
|
32f6309163 | ||
|
|
57ef2acd9c | ||
|
|
d0159f9b8d | ||
|
|
9178eb5686 | ||
|
|
fed354f24f | ||
|
|
8d201d451b | ||
|
|
1e4380f184 | ||
|
|
09e97b07f4 | ||
|
|
bbc81e3cc8 | ||
|
|
e68d69a38c | ||
|
|
d02555b80b | ||
|
|
2cb338d6f7 | ||
|
|
dadde36ff6 | ||
|
|
1a28886ce2 | ||
|
|
0919cc458b | ||
|
|
c3c200c295 | ||
|
|
2e0010ee4e | ||
|
|
653b9ffaa0 | ||
|
|
84ddae95ba | ||
|
|
0503841939 | ||
|
|
dd6dc4d982 | ||
|
|
e8c1afc860 | ||
|
|
aa08c1adef | ||
|
|
1bbfdf6a78 | ||
|
|
c3fcece75a | ||
|
|
f907577094 | ||
|
|
980fbd9b10 | ||
|
|
9902cac3ee | ||
|
|
7cf1e8f15f | ||
|
|
a107496f76 | ||
|
|
ef7935e2af | ||
|
|
9bfabe3ffd | ||
|
|
28703bc038 | ||
|
|
f48ce0d94a | ||
|
|
418d265f51 | ||
|
|
2c60a4c353 | ||
|
|
099b1ab81e | ||
|
|
ae7553ba30 | ||
|
|
b14b01d5c9 | ||
|
|
6ebf6a2797 | ||
|
|
7b813e142b | ||
|
|
d7d610bb99 | ||
|
|
5b9ef80f61 | ||
|
|
0ec286b9f4 | ||
|
|
0f3be26c42 | ||
|
|
163122afb1 | ||
|
|
0b9d56912d | ||
|
|
721401f85a | ||
|
|
a4ac85be85 | ||
|
|
8ca0e5f112 | ||
|
|
a99c3b7ae9 | ||
|
|
6406eb5ba6 | ||
|
|
5d2aebc269 | ||
|
|
c0fe43ff0c | ||
|
|
cbb590b4d7 | ||
|
|
7067d22da0 | ||
|
|
683737a6c7 | ||
|
|
ca302493f7 | ||
|
|
6cdce0c95d | ||
|
|
9894198028 | ||
|
|
b221d22e38 | ||
|
|
b56ce88c27 | ||
|
|
a7f1ff24cd | ||
|
|
1c2e404853 | ||
|
|
33bb40765c | ||
|
|
1987a18e68 | ||
|
|
62e1109cd4 | ||
|
|
3701f7cf9c | ||
|
|
e14e4b51c7 | ||
|
|
efd7cd155e | ||
|
|
cef448ca23 | ||
|
|
f51cd6977a | ||
|
|
7b7d93a2d6 | ||
|
|
d37d97c5fa | ||
|
|
53aeda3c6b | ||
|
|
f793c4b57f | ||
|
|
b72c997789 | ||
|
|
636feddeb3 | ||
|
|
a726646ff3 | ||
|
|
e2e1839829 | ||
|
|
2f22f28a4d | ||
|
|
541f532988 | ||
|
|
2fb572ed4c | ||
|
|
f938d566f7 | ||
|
|
d3df8708af | ||
|
|
2b3fefc988 | ||
|
|
568012589d | ||
|
|
c949589bea | ||
|
|
893de2e047 | ||
|
|
b4dcc7da7d | ||
|
|
3c361654f7 | ||
|
|
29a8075d90 | ||
|
|
4cfeef6300 | ||
|
|
b2d4039a9f | ||
|
|
8ede41fde8 | ||
|
|
c0a3c4d144 | ||
|
|
37b1ad80e3 | ||
|
|
4a2f338c85 | ||
|
|
910f2d3b8c | ||
|
|
ce25dd54b5 | ||
|
|
56935ef0c2 | ||
|
|
010cbd68fd | ||
|
|
2ec90922a2 | ||
|
|
9dac5f6dab | ||
|
|
e5263121eb | ||
|
|
b1270fd54d | ||
|
|
648720c8ac | ||
|
|
526b254345 | ||
|
|
806d8b5e42 | ||
|
|
62b24e9fd4 | ||
|
|
4ca1d40bcc | ||
|
|
939c92f1c1 | ||
|
|
0000db2f64 | ||
|
|
18352ff6d8 | ||
|
|
6661d98e5f | ||
|
|
60ff34d7de | ||
|
|
a5b317d552 | ||
|
|
7b18694599 | ||
|
|
e6b4f00cb2 | ||
|
|
0c87e207d5 | ||
|
|
1fc4437054 | ||
|
|
5792331c10 | ||
|
|
c06078e4cc | ||
|
|
d71d9becd8 | ||
|
|
01ea47f59e | ||
|
|
a81cbb9813 | ||
|
|
47d3f0e624 | ||
|
|
43c718af37 | ||
|
|
4e90b9260b | ||
|
|
d8282e7b7f | ||
|
|
a66b329255 | ||
|
|
d138a593a6 | ||
|
|
19eda24e57 | ||
|
|
cbd54274f2 | ||
|
|
4181b1dff6 | ||
|
|
e5a088aee9 | ||
|
|
6c6e44ec64 | ||
|
|
5e1d1f5205 | ||
|
|
db5298a2fc | ||
|
|
7c13b74636 | ||
|
|
fc2bd63d91 | ||
|
|
b9bdfbf790 | ||
|
|
f54055e69b | ||
|
|
d8e7ed1f1f | ||
|
|
702e800d3f | ||
|
|
4168dbddbb | ||
|
|
77ba719284 | ||
|
|
acd23c1002 | ||
|
|
4729ca1c40 | ||
|
|
f2691b4673 | ||
|
|
db8bc50c17 | ||
|
|
9f1a1cb661 | ||
|
|
c04fd47637 | ||
|
|
4bdcd5b9d5 | ||
|
|
b08bacede5 | ||
|
|
d89ffbcc0f | ||
|
|
5ff3973ebb | ||
|
|
7331e43207 | ||
|
|
0d7473d104 | ||
|
|
9c46306b07 | ||
|
|
63517e3de3 | ||
|
|
78566f067d | ||
|
|
c48ab0f163 | ||
|
|
19a6da0ae3 | ||
|
|
a560c691ca | ||
|
|
2306d4b59b | ||
|
|
1a836031a7 | ||
|
|
26a741e3fe | ||
|
|
0511c22e6f | ||
|
|
81120cddce | ||
|
|
bbe28d6d8f | ||
|
|
caaf218dd4 | ||
|
|
ded46e6197 | ||
|
|
198cd1a68a | ||
|
|
d19a455e65 | ||
|
|
3f51b4389b | ||
|
|
55af517a57 | ||
|
|
0c209f2439 | ||
|
|
27699e91ea | ||
|
|
eadf16dd14 | ||
|
|
10ea091471 | ||
|
|
ff32db6e12 | ||
|
|
ee908baa7a | ||
|
|
694e7490a6 | ||
|
|
9d4cdbcc32 | ||
|
|
eefa41a58e | ||
|
|
3b76c4b620 | ||
|
|
cedcccb18e | ||
|
|
0076d1e87a | ||
|
|
b428a5fd1b | ||
|
|
20f8e910de | ||
|
|
ae2eb13623 | ||
|
|
2aca4dc0c7 | ||
|
|
de2a73149b | ||
|
|
981885dafb | ||
|
|
b11348d3e3 | ||
|
|
f8b3e9cfcb | ||
|
|
d9c2add062 | ||
|
|
7777946979 | ||
|
|
3b929b3687 | ||
|
|
e1f3447a8b | ||
|
|
ac1bb9efbf | ||
|
|
cbf02a7741 | ||
|
|
802514db04 | ||
|
|
84ac034fa9 | ||
|
|
6c837f6fb6 | ||
|
|
f7f0e02688 | ||
|
|
be3b8cd23e | ||
|
|
868f0acb18 | ||
|
|
579e7261a3 | ||
|
|
9dc14bda3a | ||
|
|
080ae52bce | ||
|
|
170d4bd57b | ||
|
|
a541144d4a | ||
|
|
bcd5d9aa8b | ||
|
|
66c9b05d3d | ||
|
|
8a410913f3 | ||
|
|
474ed722f6 | ||
|
|
bbd03114ee | ||
|
|
b04335f16e | ||
|
|
353337be24 | ||
|
|
ed87d759e0 | ||
|
|
c6824a6748 | ||
|
|
3615dd43d6 | ||
|
|
865c3cfaec | ||
|
|
db3435356c | ||
|
|
5c2adca262 | ||
|
|
801d1a3056 | ||
|
|
1eea46654b | ||
|
|
c44f78d303 | ||
|
|
d5587d51c1 | ||
|
|
bccad86f50 | ||
|
|
4d6b5437c8 | ||
|
|
ab9aba1de6 | ||
|
|
2a8b54c91b | ||
|
|
2dedbc25f6 | ||
|
|
7401ddca43 | ||
|
|
af470dc7b2 | ||
|
|
7e2469b1f7 | ||
|
|
ddf21ea753 | ||
|
|
8c451f6fee | ||
|
|
dbb58f44e5 | ||
|
|
d1981fd566 | ||
|
|
73eafbe57c | ||
|
|
99087629f2 | ||
|
|
f217914a47 | ||
|
|
8034e55050 | ||
|
|
db0ffa3ed5 | ||
|
|
4318923ad5 | ||
|
|
78cf0d8f9d | ||
|
|
8524f3367c | ||
|
|
5b18c9837a | ||
|
|
fa07c4d659 | ||
|
|
ebcb3df9b4 | ||
|
|
9f76ede41e | ||
|
|
69d62b7b12 | ||
|
|
7ea55e2d29 | ||
|
|
febbc4f2cd | ||
|
|
7480105a06 | ||
|
|
6d604aed28 | ||
|
|
73d57220f0 | ||
|
|
de4ee28ccf | ||
|
|
3bbe4842b7 | ||
|
|
66198e0159 | ||
|
|
70a479836d | ||
|
|
de4c90b790 | ||
|
|
c8cc80a253 | ||
|
|
cd991ca5bb | ||
|
|
8b6ce4e113 | ||
|
|
b356b72781 | ||
|
|
58d3d66515 | ||
|
|
94855fbaae | ||
|
|
a406b21401 | ||
|
|
60018b5561 | ||
|
|
25a5c39252 | ||
|
|
8939e2601d | ||
|
|
fec04089c0 | ||
|
|
8dea244d1b | ||
|
|
b96dc05a45 | ||
|
|
1ed3cf28b0 | ||
|
|
661d4ee8e1 | ||
|
|
c711bf42c9 | ||
|
|
1afb5e5219 | ||
|
|
98eaaae30e | ||
|
|
10e303b0a8 | ||
|
|
63391d3763 | ||
|
|
8a3d767c08 | ||
|
|
d3b786d725 | ||
|
|
ceff2e9be6 | ||
|
|
31dabda1db | ||
|
|
72322e5f42 | ||
|
|
15f97ab8a4 | ||
|
|
95f797fcda | ||
|
|
a150512122 | ||
|
|
1b58bdc903 | ||
|
|
095fd0b015 | ||
|
|
1abf62e929 | ||
|
|
00f9eaf67f | ||
|
|
3193081d0e | ||
|
|
ae7296c586 | ||
|
|
51efdb32e5 | ||
|
|
50e9721a0d | ||
|
|
2305a3210e | ||
|
|
d947f5b4c1 | ||
|
|
18163ec6d7 | ||
|
|
efaf1eb2a1 | ||
|
|
d6476fde67 | ||
|
|
c99f2c20b1 | ||
|
|
989e1a3f56 | ||
|
|
e2f311f9e0 | ||
|
|
45b59fc81e | ||
|
|
02fb70e2ae | ||
|
|
53dfa07237 | ||
|
|
bc2b85df0f | ||
|
|
b21586a698 | ||
|
|
3797b02143 | ||
|
|
796be8d0ff | ||
|
|
e394056c36 | ||
|
|
8863b39aa9 | ||
|
|
9c61691632 | ||
|
|
1e71c73a61 | ||
|
|
4349b6b9cb | ||
|
|
793f3c23a6 | ||
|
|
5987fd8fda | ||
|
|
c55fb60cc8 | ||
|
|
9fcf4548df | ||
|
|
11ea49fd15 | ||
|
|
d09ae5ef5b | ||
|
|
78b1476c87 | ||
|
|
a2e12767ce | ||
|
|
e2ec2ff4de | ||
|
|
a5dcea17b6 | ||
|
|
f803128b16 | ||
|
|
777629d8c3 | ||
|
|
3de5a2d1d1 | ||
|
|
48c3081fdc | ||
|
|
00e3f48329 | ||
|
|
39744885f2 | ||
|
|
c6451208ad | ||
|
|
0774274e9c | ||
|
|
2dc3ca5f01 | ||
|
|
174a7b5d84 | ||
|
|
cd57ebd763 | ||
|
|
b14d1f1e0b | ||
|
|
3f3df9346f | ||
|
|
b0535da97b | ||
|
|
cdc25fddc8 | ||
|
|
351f043131 | ||
|
|
5f7632decf | ||
|
|
235f830621 | ||
|
|
d49b5af91a | ||
|
|
d245c6eca7 | ||
|
|
f02bd5513e | ||
|
|
88dc60648e | ||
|
|
63d6987b93 | ||
|
|
2386877394 | ||
|
|
65188a6652 | ||
|
|
35e1c3370d | ||
|
|
7904740024 | ||
|
|
8af0d2c14c | ||
|
|
bfd19512b1 | ||
|
|
b777e82890 | ||
|
|
8cc09712bc | ||
|
|
21a1158798 | ||
|
|
207741651a | ||
|
|
098f5c9086 | ||
|
|
919623157b | ||
|
|
2d1f3b90e4 | ||
|
|
67fb508e8d | ||
|
|
ec121f5ac7 | ||
|
|
73dc9a794f | ||
|
|
3e563df999 | ||
|
|
7ad25c5f19 | ||
|
|
3bae721fa8 | ||
|
|
e0ae6aa85c | ||
|
|
36508c24e3 | ||
|
|
201ead11ea | ||
|
|
05354a3352 | ||
|
|
ed6b6404a3 | ||
|
|
e7c68fbeea | ||
|
|
9c00b4c32d | ||
|
|
bab0f05c3e | ||
|
|
bc62f0632a | ||
|
|
f5c5978f9d | ||
|
|
8eaa1f6130 | ||
|
|
9a84d9f1f8 | ||
|
|
4b44af7cf1 | ||
|
|
b16472a379 | ||
|
|
8f7d51e57c | ||
|
|
3cc8f8650d | ||
|
|
3162930634 | ||
|
|
45a8f61266 | ||
|
|
2f66ba0126 | ||
|
|
90bdd24de2 | ||
|
|
8527d71fa8 | ||
|
|
aba60ffd74 | ||
|
|
3e455ee8ee | ||
|
|
5f297136b8 | ||
|
|
2b6c05839e | ||
|
|
aa5b480a44 | ||
|
|
ba38b69782 | ||
|
|
8cf889c390 | ||
|
|
398d62e2a6 | ||
|
|
c923c6c96a | ||
|
|
983410c953 | ||
|
|
4260527170 | ||
|
|
34603b9128 | ||
|
|
a4213d0316 | ||
|
|
3f63d89099 | ||
|
|
83b6b841c6 | ||
|
|
f84ac6febe | ||
|
|
89443dfe30 | ||
|
|
df33acdd01 | ||
|
|
e0579225f3 | ||
|
|
4f64ba6e79 | ||
|
|
fd3efc5469 | ||
|
|
64afc7a737 | ||
|
|
e5de2a0165 | ||
|
|
09a2deb6de | ||
|
|
ef1917dc6b | ||
|
|
94fbd49f40 | ||
|
|
9c00eedfaf | ||
|
|
444e42517f | ||
|
|
33921e79f4 | ||
|
|
bdafaa014c | ||
|
|
dfe9815837 | ||
|
|
db5b728a97 | ||
|
|
10c0c8afab | ||
|
|
f14591033d | ||
|
|
4e76751372 | ||
|
|
7e8cda4a96 | ||
|
|
cebbcbaf7c | ||
|
|
55e90733ec | ||
|
|
fd95e6a075 | ||
|
|
fb9212a8c6 | ||
|
|
a19260080d | ||
|
|
fe8b8d798c | ||
|
|
2b988d404f | ||
|
|
439a2811b1 | ||
|
|
097b70f069 | ||
|
|
f557ae8bd6 | ||
|
|
4dd80ff328 | ||
|
|
a011b4e3ee | ||
|
|
69d8dafc8f | ||
|
|
10028a4769 | ||
|
|
86712703d7 | ||
|
|
230962c8c2 | ||
|
|
a3dfa2a930 | ||
|
|
4ce0a6c63e | ||
|
|
25b13a89d4 | ||
|
|
c2611a465a | ||
|
|
bb6e58b5ce | ||
|
|
7aa641bbfd | ||
|
|
3d3ebdefd6 | ||
|
|
ad4be98c8a | ||
|
|
86a172800d | ||
|
|
e1cfbd25fa | ||
|
|
0a40a28c3d | ||
|
|
ff90a678f1 | ||
|
|
edb7678675 | ||
|
|
d19e3b69fc | ||
|
|
70428d2309 | ||
|
|
b14b913d0a | ||
|
|
f0df97589f | ||
|
|
58d0b5ffc6 | ||
|
|
fd1ea7ab0a | ||
|
|
2036dfd83b | ||
|
|
fa9582cc72 | ||
|
|
4b7a2848e5 | ||
|
|
9c16798fdb | ||
|
|
0d3d958b46 | ||
|
|
a899923067 | ||
|
|
6e0519bbbc | ||
|
|
26ef5b0dad | ||
|
|
127a7e5ae7 | ||
|
|
7320fdc340 | ||
|
|
867bc8197b | ||
|
|
db7ab2d33f | ||
|
|
f56de827b5 | ||
|
|
a9cb23be79 | ||
|
|
07287d056c | ||
|
|
f3c44b48b9 | ||
|
|
5ab348bf30 | ||
|
|
2715699648 | ||
|
|
355dc07401 | ||
|
|
7c7e820a94 | ||
|
|
2fd8532be0 | ||
|
|
83091413e7 | ||
|
|
14fff2e3a3 | ||
|
|
ba26fc8226 | ||
|
|
85d68a7b7b | ||
|
|
2885dbfb12 | ||
|
|
9e5341537a | ||
|
|
cedf7beaa1 | ||
|
|
dd84250c8a | ||
|
|
fe3a7525b3 | ||
|
|
5380ce85d9 | ||
|
|
cc043cb4c8 | ||
|
|
db6cd6b510 | ||
|
|
6670b6f611 | ||
|
|
2d5cf03ceb | ||
|
|
51631dcb40 | ||
|
|
ff620c0218 | ||
|
|
7cc3f6af7f | ||
|
|
56007eda9c | ||
|
|
9b4d275d51 | ||
|
|
405693e1ed | ||
|
|
269dbc6fef | ||
|
|
8346d59a42 | ||
|
|
4e34b549b5 | ||
|
|
fe598266e3 | ||
|
|
d6579a9e6d | ||
|
|
c057803a17 | ||
|
|
0bcd840f60 | ||
|
|
c7425a02ac | ||
|
|
0f7c91e894 | ||
|
|
e9f7f50088 | ||
|
|
b8540f9e21 | ||
|
|
2196905023 | ||
|
|
bc874e5605 | ||
|
|
f84a78c4e6 | ||
|
|
db836ae315 | ||
|
|
b328b98a2d | ||
|
|
66bfff4f84 | ||
|
|
32bc9979a0 | ||
|
|
55f2841c33 | ||
|
|
127e2bcf27 | ||
|
|
e84e186df0 | ||
|
|
d6ad6f85ea | ||
|
|
f8515f697e | ||
|
|
89856bf5b2 | ||
|
|
3743303748 | ||
|
|
6b5517ee2c | ||
|
|
de8ffbfd60 | ||
|
|
ade0526311 | ||
|
|
713fe463b0 | ||
|
|
c18695d194 | ||
|
|
4cacac86cd | ||
|
|
719c70a432 | ||
|
|
db567a3d80 | ||
|
|
61bcab727f | ||
|
|
2512a9bc87 | ||
|
|
a51540fe06 | ||
|
|
26b8b126d2 | ||
|
|
37ed39840f | ||
|
|
6fa1ec8a07 | ||
|
|
723f5a97f2 | ||
|
|
88761ce4a4 | ||
|
|
07e1c9e993 | ||
|
|
c3c5a38360 | ||
|
|
b36b2379b2 | ||
|
|
fdef0127fa | ||
|
|
56c4d72318 | ||
|
|
e64fc61fac | ||
|
|
c5ccb2cd2c | ||
|
|
90eda3b79a | ||
|
|
dd8ac9f75c | ||
|
|
4d37cfba2a | ||
|
|
8fac3fa367 | ||
|
|
fc658f3fe9 | ||
|
|
4c7a204151 | ||
|
|
fa09ee3c42 | ||
|
|
031c1017c1 | ||
|
|
e3a4a85833 | ||
|
|
bf0d4077b0 | ||
|
|
0c5c12adb9 | ||
|
|
ba27cd1c6c | ||
|
|
0bc865395e | ||
|
|
f78d5fffdc | ||
|
|
0801c18e22 | ||
|
|
fe3bc867bc | ||
|
|
23dd5aa1d9 | ||
|
|
52b8a34535 | ||
|
|
4f5d49292c | ||
|
|
c991abc64d | ||
|
|
ca7b397e21 | ||
|
|
6b44984549 | ||
|
|
06ae053783 | ||
|
|
178db4baae | ||
|
|
0ffff2751e | ||
|
|
9121ec9527 | ||
|
|
8f251485a3 | ||
|
|
5ea1e2bd71 | ||
|
|
21d1bd3033 | ||
|
|
a79e78644d | ||
|
|
782e3172ad | ||
|
|
5fbc1ae911 | ||
|
|
8c8a597559 | ||
|
|
f3f5f364d3 | ||
|
|
e70342fc19 | ||
|
|
f168c2f8c4 | ||
|
|
89e5ed191c | ||
|
|
e02ad7883c | ||
|
|
5bb613dda1 | ||
|
|
37b3519a51 | ||
|
|
95ed1712bf | ||
|
|
1d5502a17e | ||
|
|
eee6e4e020 | ||
|
|
7cdacc7bdd | ||
|
|
64d447298b | ||
|
|
5433a9d31d | ||
|
|
d30934fa4d | ||
|
|
eed42d9b18 | ||
|
|
44c016fd2c | ||
|
|
f0f1a16982 | ||
|
|
4b6587e8ba | ||
|
|
f427dce73b | ||
|
|
a4ddbff64d | ||
|
|
aa937f2f7b | ||
|
|
bc1cde262c | ||
|
|
56014403b2 | ||
|
|
48566ea8eb | ||
|
|
e44bec1b8c | ||
|
|
a8bfd89b01 | ||
|
|
0f245f52b1 | ||
|
|
ae8629ecba | ||
|
|
f0f69e873a | ||
|
|
e9c35d269d | ||
|
|
ee8f937866 | ||
|
|
1a0bb18de7 | ||
|
|
f4fa1fbd8c | ||
|
|
50593dbfc2 | ||
|
|
65b5415216 | ||
|
|
bbe9d29d5c | ||
|
|
b77673550e | ||
|
|
d82acd060c | ||
|
|
3f139d83ca | ||
|
|
a8743d50b8 | ||
|
|
9c105db8da | ||
|
|
25bcd2b866 | ||
|
|
31726b05e6 | ||
|
|
2f1e591d06 | ||
|
|
8cb4349950 | ||
|
|
38653dd22f | ||
|
|
e6e179453c | ||
|
|
849cfbfbfa | ||
|
|
d9b04cd7d8 | ||
|
|
678e2f604c | ||
|
|
7bc804bccb | ||
|
|
56177e06bf | ||
|
|
2b1b1fd882 | ||
|
|
3e976415f0 | ||
|
|
f5ee615b2d | ||
|
|
beaf44040d | ||
|
|
807eff3168 | ||
|
|
ba394303b1 | ||
|
|
9bbd33ad48 | ||
|
|
77ff2b986d | ||
|
|
b8e9cd0a42 | ||
|
|
d6d9839985 | ||
|
|
68e0f40b86 | ||
|
|
3085e49e87 | ||
|
|
f733bf140e | ||
|
|
ef94e1051f | ||
|
|
7687e6effc | ||
|
|
d705bd3fb7 | ||
|
|
ea484e4aec | ||
|
|
a77aa11e36 | ||
|
|
b1f6263537 | ||
|
|
4c38961efa | ||
|
|
dc6904fad2 | ||
|
|
e9b1329f15 | ||
|
|
bd989239ea | ||
|
|
3dd0313d53 | ||
|
|
b91f2e91f1 | ||
|
|
dd267a725b | ||
|
|
7a5c255956 | ||
|
|
a33b225c0b | ||
|
|
dfdea068ef | ||
|
|
0c0f52c38c | ||
|
|
f017db6be0 | ||
|
|
2b0e3104a6 | ||
|
|
f7dad2c580 | ||
|
|
087fbca227 | ||
|
|
6024405de4 | ||
|
|
574ff2cb84 | ||
|
|
823b0eeaa2 | ||
|
|
fa21a9621e | ||
|
|
1f8203b155 | ||
|
|
2c55e28ea5 | ||
|
|
878b1d60da | ||
|
|
87a14fa263 | ||
|
|
32a35eb2e7 | ||
|
|
81c1f2d1a8 | ||
|
|
2cfc1a7980 | ||
|
|
248e47118f | ||
|
|
e18d973d5e | ||
|
|
da3ffafc90 | ||
|
|
cf32ce90b6 | ||
|
|
970657eb74 | ||
|
|
65ab0c21d4 | ||
|
|
4633a55c97 | ||
|
|
a27642faee | ||
|
|
8368c668df | ||
|
|
b932edb983 | ||
|
|
1cf4db9148 | ||
|
|
d12320bbb5 | ||
|
|
213b3124fa | ||
|
|
10a306110b | ||
|
|
da9b6f80f1 | ||
|
|
ca9f2aa9c0 | ||
|
|
d88ececbea | ||
|
|
01a325736d | ||
|
|
bec1420591 | ||
|
|
a1a644aa48 | ||
|
|
ec68b885d7 | ||
|
|
57d01415e2 | ||
|
|
8a3b934518 | ||
|
|
f775bb54a5 | ||
|
|
9cc96f9d70 | ||
|
|
93da9867b4 | ||
|
|
9ed6a0af1e | ||
|
|
d97f86b3f6 | ||
|
|
871fa6bb67 | ||
|
|
d7f31cfea0 | ||
|
|
6182fd9488 | ||
|
|
dad904967e | ||
|
|
2186fc6ce0 | ||
|
|
de8c933f1c | ||
|
|
fda002c736 | ||
|
|
9c34bb0ae7 | ||
|
|
5c7d12b60d | ||
|
|
7ae685565a | ||
|
|
4fbfb19b21 | ||
|
|
dd8aca5773 | ||
|
|
b903dc1205 | ||
|
|
3ca07376ac | ||
|
|
49518b753e | ||
|
|
413f236a7d | ||
|
|
64a5a4920b | ||
|
|
d2b0fc3b5e | ||
|
|
aa95507478 | ||
|
|
b0aba7f4a4 | ||
|
|
7341f77fe1 | ||
|
|
76460b9d9c | ||
|
|
2ba291516e | ||
|
|
30d1747d43 | ||
|
|
113dd4aeae | ||
|
|
17f79b0260 | ||
|
|
2526a99726 | ||
|
|
21ec8ea1a4 | ||
|
|
9cac882840 | ||
|
|
02dfced6c0 | ||
|
|
9f2e80fc3c | ||
|
|
57a097ea52 | ||
|
|
e5d8a2e8a9 | ||
|
|
579b66e1c0 | ||
|
|
2e3c4a45b0 | ||
|
|
7f5033f14a | ||
|
|
084574f2f2 | ||
|
|
2d5cdd3f19 | ||
|
|
64897ba6d6 | ||
|
|
87887aa3a0 | ||
|
|
8e67e96e6f | ||
|
|
14f030e9a8 | ||
|
|
93dbe7eecb | ||
|
|
dd8f25d43c | ||
|
|
7f25117c3e | ||
|
|
01525e294f | ||
|
|
07a6ea240b | ||
|
|
2e658ac546 | ||
|
|
930fdc84ec | ||
|
|
a42342a4b8 | ||
|
|
75309ff21b | ||
|
|
0c58f66fd8 | ||
|
|
e03d13c2b8 | ||
|
|
fba943470d | ||
|
|
595972629d | ||
|
|
4fb43b798d | ||
|
|
87c499d8c7 | ||
|
|
265e52caab | ||
|
|
1445a75183 | ||
|
|
cc2434fd69 | ||
|
|
936b39f7a3 | ||
|
|
b8a86ca405 | ||
|
|
773df88224 | ||
|
|
e7a2c83aa5 | ||
|
|
dc1ae2125c | ||
|
|
8e9036029a | ||
|
|
b14558765a | ||
|
|
131b28ff9f | ||
|
|
0e1a8b3623 | ||
|
|
ffb8b281d8 | ||
|
|
07aeb0d94b | ||
|
|
725640379e | ||
|
|
340dffab23 | ||
|
|
c1e5cadcc7 | ||
|
|
898d1e7fc7 | ||
|
|
f5058fb5be | ||
|
|
fbd051f947 | ||
|
|
dc846fc487 | ||
|
|
402cd310d9 | ||
|
|
2ee68da944 | ||
|
|
780308fee0 | ||
|
|
dc256a03b4 | ||
|
|
4e5576351c | ||
|
|
33957b5f60 | ||
|
|
c492622102 | ||
|
|
f7494fedd5 | ||
|
|
b02d4365ac | ||
|
|
ba1486f763 | ||
|
|
a495482fc1 | ||
|
|
39a5da5036 | ||
|
|
504f15db58 | ||
|
|
54116b3d3f | ||
|
|
6c9c648af1 | ||
|
|
5da992b63b | ||
|
|
9bb5e95f54 | ||
|
|
bad4180e63 | ||
|
|
084e364a8a | ||
|
|
2cbc4e580d | ||
|
|
a2e6ec4f5b | ||
|
|
231a7f1366 | ||
|
|
bee77ec2ad | ||
|
|
11cc7e63b7 | ||
|
|
2083daf4c3 | ||
|
|
816e6a0287 | ||
|
|
6d9541c135 | ||
|
|
0f208617bd | ||
|
|
e8bbac82f4 | ||
|
|
d657afe18b | ||
|
|
96f3ac9d9e | ||
|
|
9290744fca | ||
|
|
f08f91a9fe | ||
|
|
22e20ed282 | ||
|
|
be2952db5d | ||
|
|
ff4a27de0e | ||
|
|
991e1a2844 | ||
|
|
a2cbec78f9 | ||
|
|
d1b3cd4994 | ||
|
|
431c2c71f5 | ||
|
|
5fd6437c34 | ||
|
|
47c429c792 | ||
|
|
b69b94f26f | ||
|
|
b9bb2e5663 | ||
|
|
488aecf95c | ||
|
|
d80a02fe42 | ||
|
|
8dff1abc54 | ||
|
|
06d85ac65a | ||
|
|
b4d49c565f | ||
|
|
da5136c7c1 | ||
|
|
32c3b7761a | ||
|
|
5445be6645 | ||
|
|
7b89f41b77 | ||
|
|
56e1b50260 | ||
|
|
9fdd7de5b1 | ||
|
|
960679a3fd | ||
|
|
e428fdc89a | ||
|
|
fd514d4e1d | ||
|
|
cec6dfc131 | ||
|
|
20319ad43c | ||
|
|
774bfc7959 | ||
|
|
3db5f64b5e | ||
|
|
ddc97b662c | ||
|
|
75ad25f961 | ||
|
|
e604884bd7 | ||
|
|
65d487c60b | ||
|
|
c489cfb657 | ||
|
|
1608b8388b | ||
|
|
f706548779 | ||
|
|
67bb704a66 | ||
|
|
516cd19d68 | ||
|
|
94d883e22e | ||
|
|
d3fb8e9bac | ||
|
|
98fbd3fa65 | ||
|
|
db96d717ce | ||
|
|
6b904b10f5 | ||
|
|
134b28f2d2 | ||
|
|
c66497a1d6 | ||
|
|
5e625e64ce | ||
|
|
c11b6f9fda | ||
|
|
76fc3c1da7 | ||
|
|
07fc47d995 | ||
|
|
5b264a35bf | ||
|
|
ff58d21051 | ||
|
|
77583d44ed | ||
|
|
b3ff5e30aa | ||
|
|
c0a69065ea | ||
|
|
077ee0bc12 | ||
|
|
303f6cb1ce | ||
|
|
e458c10aac | ||
|
|
85d58d22b7 | ||
|
|
22445958c1 | ||
|
|
0fc7d2124a | ||
|
|
3f99d9aae9 | ||
|
|
b357a8f503 | ||
|
|
f3e184caf1 | ||
|
|
f41fda74b5 | ||
|
|
ec6e8fa3d5 | ||
|
|
2f0f11976d | ||
|
|
82ad5a91ab | ||
|
|
cd8f43fa47 | ||
|
|
69a593e09d | ||
|
|
1173ea3301 | ||
|
|
6a1398b788 | ||
|
|
2fcb5d628e | ||
|
|
5d902b8507 | ||
|
|
d3edc19de9 | ||
|
|
5bad795c60 | ||
|
|
9b8374665a | ||
|
|
0479a3e723 | ||
|
|
b54ed5f6e5 | ||
|
|
a127088db1 | ||
|
|
015f583908 | ||
|
|
b6d1e67cf1 | ||
|
|
9afd00dda7 | ||
|
|
95379f534d | ||
|
|
e67d04c694 | ||
|
|
027016e8a3 | ||
|
|
4f30029884 | ||
|
|
8e48cd1b25 | ||
|
|
6fcc40748c | ||
|
|
2bed437313 | ||
|
|
88aafd9df2 | ||
|
|
76bb99422a | ||
|
|
b3e3947fc0 | ||
|
|
3ed1e5b8b0 | ||
|
|
6783ef7ff7 | ||
|
|
199fc184b7 | ||
|
|
4db0b89e92 | ||
|
|
103bb7e58d | ||
|
|
4a9b206d01 | ||
|
|
3cd66e75e7 | ||
|
|
e9c43843b3 | ||
|
|
577c6f194f | ||
|
|
8b7c171a67 | ||
|
|
77b37d6579 | ||
|
|
5ffdadfc41 | ||
|
|
d9648a4b41 | ||
|
|
ba059ec1a6 | ||
|
|
3790782ec7 | ||
|
|
f0b7899cf6 | ||
|
|
af9ba7ef41 | ||
|
|
840aff38f2 | ||
|
|
7ebbc35f52 | ||
|
|
c3e2a71724 | ||
|
|
69e5b7c199 | ||
|
|
5e46b9de60 | ||
|
|
ed67c8fd56 | ||
|
|
e36302ea57 | ||
|
|
f28355bd82 | ||
|
|
5f76010bdc | ||
|
|
14a1d7782c | ||
|
|
275403d9fc | ||
|
|
ea787467ed | ||
|
|
d0738121be | ||
|
|
1c4817cb08 | ||
|
|
aeb31aaabf | ||
|
|
6dc304d445 | ||
|
|
7961428b7c | ||
|
|
61229fdaa4 | ||
|
|
ed8b4c4b6e | ||
|
|
ba1b4e868c | ||
|
|
eda3820e66 | ||
|
|
dc1dd3e04e | ||
|
|
bb6632831c | ||
|
|
d996bc7793 | ||
|
|
ea0babc5c0 | ||
|
|
702dd09d3f | ||
|
|
56cd789eea | ||
|
|
d8e6e17ee5 | ||
|
|
70e66b9fed | ||
|
|
d81e484aa7 | ||
|
|
1feeb18a49 | ||
|
|
5d7d99fa3d | ||
|
|
b5cd4ab8cd | ||
|
|
d607d7bd7c | ||
|
|
4d3187cc45 | ||
|
|
a5a919a29e | ||
|
|
d0df038ad8 | ||
|
|
f866b86397 | ||
|
|
eba4601c73 | ||
|
|
8c3bf47a88 | ||
|
|
baaae908bb | ||
|
|
d6a2fe0993 | ||
|
|
5ceb4d9be6 | ||
|
|
56c91eba23 | ||
|
|
2a3dfbde83 | ||
|
|
caf4fd036e | ||
|
|
7732dd218f | ||
|
|
a3008b7188 | ||
|
|
464141837c | ||
|
|
f10bc1838e | ||
|
|
3a236ca2ad | ||
|
|
6b7fe5ac25 | ||
|
|
73acf71f9c | ||
|
|
ad36471d97 | ||
|
|
a40f9171a3 | ||
|
|
80bf58eb00 | ||
|
|
d0874b2d33 | ||
|
|
68461d05d2 | ||
|
|
0e421f0468 | ||
|
|
7b27bfc907 | ||
|
|
a73059a76a | ||
|
|
81b9a1e0a2 | ||
|
|
bacc793bc7 | ||
|
|
f83079d2fb | ||
|
|
a115963ba2 | ||
|
|
5e3309c216 | ||
|
|
ca4bb191a4 | ||
|
|
799ed82532 | ||
|
|
0e55d4018a | ||
|
|
9fb5cdbe99 | ||
|
|
1cd3b43646 | ||
|
|
a680bf196d | ||
|
|
43ae24be29 | ||
|
|
05b397e8d6 | ||
|
|
1e5d809740 | ||
|
|
0764c335b3 | ||
|
|
138feecefc | ||
|
|
2e53406b07 | ||
|
|
bec5bb12c5 | ||
|
|
469f4bd223 | ||
|
|
0309477c9d | ||
|
|
622dce1b1a | ||
|
|
bce066f3d0 | ||
|
|
a0bc9e119c | ||
|
|
5cc5fe9e55 | ||
|
|
ae7454f2d4 | ||
|
|
afb7ffc6d0 | ||
|
|
3fc0deb0bc | ||
|
|
d81f378271 | ||
|
|
423797e414 | ||
|
|
898c54f6f0 | ||
|
|
bb94c8f359 | ||
|
|
76233104d1 | ||
|
|
d5eeb33212 | ||
|
|
57fd3e5aa5 | ||
|
|
dc9887e1bf | ||
|
|
e5b73c45e2 | ||
|
|
ac6daeef76 | ||
|
|
eebf21369c | ||
|
|
e06bc2a3b3 | ||
|
|
f829265f75 | ||
|
|
b995a764c4 | ||
|
|
f1c3b49933 | ||
|
|
fc6428a0f7 | ||
|
|
52f7da69ad | ||
|
|
7af93a3251 | ||
|
|
fcb4fbfb44 | ||
|
|
60ad8b12cc | ||
|
|
4c2443ff69 | ||
|
|
9a1b334731 | ||
|
|
07104e60f4 | ||
|
|
34fca9f5a4 | ||
|
|
42d0ec2a49 | ||
|
|
18b0e12840 | ||
|
|
074e297850 | ||
|
|
997727f370 | ||
|
|
d57bca137d | ||
|
|
f8185069a0 | ||
|
|
1ddf5cf81e | ||
|
|
d2aa9faa7b | ||
|
|
de5155a3c2 | ||
|
|
9338c3bfe3 | ||
|
|
b9cf085411 | ||
|
|
e859324b71 | ||
|
|
f11460c42a | ||
|
|
fa27a01dc4 | ||
|
|
77da44cb23 | ||
|
|
f8e9c34353 | ||
|
|
33ad483b0b | ||
|
|
7c36b670f3 | ||
|
|
b57a150033 | ||
|
|
a804ee0868 | ||
|
|
584de3737c | ||
|
|
0d159674fa | ||
|
|
6c40c0eda0 | ||
|
|
8a8324c841 | ||
|
|
fff0adff15 | ||
|
|
28bee7e619 | ||
|
|
cd7698ffba | ||
|
|
8f508b6cd8 | ||
|
|
e2c3dbe9ce | ||
|
|
2299159722 | ||
|
|
cc82da4179 | ||
|
|
4f0b5cb0cf | ||
|
|
2de193b68a | ||
|
|
3080d3dc3e | ||
|
|
e17e47b438 | ||
|
|
e08b6af500 | ||
|
|
b8fdb05f35 | ||
|
|
36b7bc0ed6 | ||
|
|
79f46931f4 | ||
|
|
cd78d6f0c7 | ||
|
|
7393e752ca | ||
|
|
83de4399fd | ||
|
|
4c815bd3bf | ||
|
|
0545bffacf | ||
|
|
c009866158 | ||
|
|
d0ee4d73f9 | ||
|
|
d97815620f | ||
|
|
588e52a673 | ||
|
|
b83016f492 | ||
|
|
8a0129df6a | ||
|
|
470f1fbc73 | ||
|
|
d5b0d77b9f | ||
|
|
cf130189a2 | ||
|
|
c56b5fc1a6 | ||
|
|
53e9303321 | ||
|
|
e6d90a7d0e | ||
|
|
06b84f3ba3 | ||
|
|
11d9df3f0b | ||
|
|
5a19c26500 | ||
|
|
1b293981fa | ||
|
|
a9759f2740 | ||
|
|
a0cefa9dea | ||
|
|
879a13d622 | ||
|
|
13ccdcfd23 | ||
|
|
de23737563 | ||
|
|
34d3e5b760 | ||
|
|
ea2de6b4e2 | ||
|
|
d2540ba060 | ||
|
|
b8a30cef57 | ||
|
|
ae71aa6511 | ||
|
|
8a476b9218 | ||
|
|
14e91dda44 | ||
|
|
271b8f3a2f | ||
|
|
861356870c | ||
|
|
36b42cce02 | ||
|
|
461ca659f8 | ||
|
|
7e81337298 | ||
|
|
80168e59c1 | ||
|
|
ab7291bd33 | ||
|
|
040e969799 | ||
|
|
c965c41c3a | ||
|
|
1d9138075a | ||
|
|
46738de038 | ||
|
|
dc49e1cbaf | ||
|
|
d4c36c64fc | ||
|
|
122bd69069 | ||
|
|
27b77c2db4 | ||
|
|
6333fc73e9 | ||
|
|
63d4ae5206 | ||
|
|
9fc4f81fef | ||
|
|
9fa9261580 | ||
|
|
2f98c5457d | ||
|
|
f7bca6f868 | ||
|
|
426dd1489a | ||
|
|
740eb5d860 | ||
|
|
5100443457 | ||
|
|
b5c284f526 | ||
|
|
8c0fa813fe | ||
|
|
ab5aa30068 | ||
|
|
525ab05549 | ||
|
|
f7c831f9d1 | ||
|
|
a195f807d0 | ||
|
|
dde1229e54 | ||
|
|
88c4517b72 | ||
|
|
f47eed9413 | ||
|
|
fe35d5ab55 | ||
|
|
0e17013255 | ||
|
|
a44e3515a2 | ||
|
|
001cd91b62 | ||
|
|
f88931445a | ||
|
|
947e6f943f | ||
|
|
1cba35d7aa | ||
|
|
675e0f8c4f | ||
|
|
30e504810b | ||
|
|
185198593d | ||
|
|
34cdf39c6b | ||
|
|
4f8736300a | ||
|
|
e40e1c42c3 | ||
|
|
a04d7c6b23 | ||
|
|
40b37e9c31 | ||
|
|
c49bb1bbc6 | ||
|
|
ff0f0ee347 | ||
|
|
e3eb49e4a2 | ||
|
|
42e463e529 | ||
|
|
f781532e1b | ||
|
|
ba8958b27a | ||
|
|
d082bfd8ec | ||
|
|
3216b2515d | ||
|
|
078f198d81 | ||
|
|
6162d285c9 | ||
|
|
56b1ead206 | ||
|
|
5f11d93d42 | ||
|
|
78a62ba84a | ||
|
|
ad5f288374 | ||
|
|
2aa6f1b8f5 | ||
|
|
25a5ac2a2d | ||
|
|
8a9f44ebaf | ||
|
|
e6b77081f7 | ||
|
|
9808d123cd | ||
|
|
4213bffab0 | ||
|
|
516f0c700f | ||
|
|
b03939d753 | ||
|
|
0786e3dcf2 | ||
|
|
fe4397d746 | ||
|
|
b4c52190e2 | ||
|
|
723491fd86 | ||
|
|
a1b087938e | ||
|
|
c13cb0b8d9 | ||
|
|
c1703de590 | ||
|
|
e08f03cf57 | ||
|
|
5b3daac4c8 | ||
|
|
c1ce93b6b0 | ||
|
|
5da1e8bb80 | ||
|
|
48a62eeee4 | ||
|
|
f9788a21f6 | ||
|
|
db425035a4 | ||
|
|
ed7aa81527 | ||
|
|
8108dec7e3 | ||
|
|
f9363e71d9 | ||
|
|
98a7e35c55 | ||
|
|
c998bd9c05 | ||
|
|
7194f44eca | ||
|
|
4654070f9a | ||
|
|
16db0ba290 | ||
|
|
58bf1572de | ||
|
|
4af35d623c | ||
|
|
3bbafe8fc1 | ||
|
|
068afd7eb4 | ||
|
|
f98209a32e | ||
|
|
d62a654a2d | ||
|
|
509341569e | ||
|
|
20b82c1f2a | ||
|
|
54170bece7 | ||
|
|
0a7578472e | ||
|
|
65cf58dfab | ||
|
|
d95828451e | ||
|
|
e21182ccfc | ||
|
|
ba818244df | ||
|
|
c667fedc1a | ||
|
|
a7d6c0994f | ||
|
|
d601975597 | ||
|
|
21b57c1009 | ||
|
|
16800a2730 | ||
|
|
36448930d6 | ||
|
|
740c39c3c4 | ||
|
|
397efe5479 | ||
|
|
f6c9b22adc | ||
|
|
ffa053c695 | ||
|
|
5c4fee6cdf | ||
|
|
38335e857a | ||
|
|
9f16fa3e17 | ||
|
|
d0ad91f277 | ||
|
|
e1d98ea45c | ||
|
|
cedab33c13 | ||
|
|
212667a34f | ||
|
|
cf5ee4cf27 | ||
|
|
6953e494cb | ||
|
|
daad7627b8 | ||
|
|
0bfed31cae | ||
|
|
9660128123 | ||
|
|
c927d333bd | ||
|
|
75637e361c | ||
|
|
cbcfe9d3e9 | ||
|
|
7ea07ec3b0 | ||
|
|
df842b981c | ||
|
|
c98781403d | ||
|
|
651e5a4924 | ||
|
|
9eec9f7e52 | ||
|
|
41244ae522 | ||
|
|
f1d0e2834e | ||
|
|
7f98bac3ae | ||
|
|
9d8fcfeeb6 | ||
|
|
28847e4adf | ||
|
|
ee2d078957 | ||
|
|
e51bb250e2 | ||
|
|
e93ea48b96 | ||
|
|
5efe6364ca | ||
|
|
c6574ee800 | ||
|
|
df8c39425a | ||
|
|
0e49632006 | ||
|
|
9c60a9298d | ||
|
|
a4792f3058 | ||
|
|
05b65dd7f6 | ||
|
|
9657dc0f1b | ||
|
|
d5ff7f1274 | ||
|
|
5b59e9e740 | ||
|
|
567b61133e | ||
|
|
3427fc3275 | ||
|
|
f3b96b5050 | ||
|
|
3a3eb2790d | ||
|
|
d5225537c4 | ||
|
|
fac56d9469 | ||
|
|
cc73aa18c6 | ||
|
|
ba2127b00e | ||
|
|
4e966aa44a | ||
|
|
23fe7755f8 | ||
|
|
f81d33acdf | ||
|
|
305037cae2 | ||
|
|
2909b68872 | ||
|
|
bf9cd35e5c | ||
|
|
30b7fc2f2b | ||
|
|
4213e6a965 | ||
|
|
b868ff35c2 | ||
|
|
ccb116e910 | ||
|
|
0412e8fd8e | ||
|
|
3f68006e3c | ||
|
|
4ed7a63806 | ||
|
|
4d56c1559f | ||
|
|
ae99bcfca2 | ||
|
|
8f8ec6ef5d | ||
|
|
05faa2cbf8 | ||
|
|
56b942289f | ||
|
|
b095ac7620 | ||
|
|
bc7520f45a | ||
|
|
82f9de3114 | ||
|
|
a5de6eccd5 | ||
|
|
7c645c1623 | ||
|
|
540d99924e | ||
|
|
68681a34f2 | ||
|
|
d56df7e0fc | ||
|
|
eb4eef5260 | ||
|
|
7b62c14a8e | ||
|
|
0ecc23e52d | ||
|
|
fdefb1829b | ||
|
|
b775095b67 | ||
|
|
b87074c5f6 | ||
|
|
2be07aee63 | ||
|
|
c45a792ef9 | ||
|
|
1b027e169a | ||
|
|
06bcd3ffca | ||
|
|
401597d309 | ||
|
|
e3773172f3 | ||
|
|
bc37b4c817 | ||
|
|
df031d1b88 | ||
|
|
1dc83a0c3c | ||
|
|
1883178c55 | ||
|
|
e0a74be510 | ||
|
|
2725e590ea | ||
|
|
30aa9b2bc1 | ||
|
|
fe591b6620 | ||
|
|
6d8ed5897f | ||
|
|
ece9eb3504 | ||
|
|
c6de3040fb | ||
|
|
5f1dfecd18 | ||
|
|
76d22deddd | ||
|
|
b92fddeabc | ||
|
|
2dddc81713 | ||
|
|
0fb2992b49 | ||
|
|
8464554a71 | ||
|
|
4034a8227f | ||
|
|
ff9d2b0cdc | ||
|
|
95cdc2c5a6 | ||
|
|
f8083e10eb | ||
|
|
8c6322c04d | ||
|
|
ef58f61d8e | ||
|
|
fae42eecbc | ||
|
|
a81ea3c2cc | ||
|
|
661984b620 | ||
|
|
f6b571e022 | ||
|
|
1ca2a8865f | ||
|
|
a55dacdcc6 | ||
|
|
d20ebed618 | ||
|
|
5fdc20f997 | ||
|
|
968445c12c | ||
|
|
45965414b1 | ||
|
|
f92034225d | ||
|
|
6949efbb7e | ||
|
|
6e085e302d | ||
|
|
642b196130 | ||
|
|
c84ad4aabb | ||
|
|
93bb7273c2 | ||
|
|
dfca69d59c | ||
|
|
9655c33f48 | ||
|
|
0c408d09fd | ||
|
|
5f6b9ff87c | ||
|
|
1da7a2d401 | ||
|
|
a3c9812f67 | ||
|
|
03fd761179 | ||
|
|
89a64267fa | ||
|
|
4c25b7c26a | ||
|
|
97c8d06e33 | ||
|
|
9747d0bb20 | ||
|
|
3fe3f512e3 | ||
|
|
b4d4377938 | ||
|
|
f5bf80cc63 | ||
|
|
3e9f9129d8 | ||
|
|
34dd04b9ef | ||
|
|
d286bb43dd | ||
|
|
cab06f92d6 | ||
|
|
4297c1b80d | ||
|
|
bb6df8b4b8 | ||
|
|
577380da3b | ||
|
|
2f843feabf | ||
|
|
9440aa62ff | ||
|
|
0daf5e0da9 | ||
|
|
64c4ad4852 | ||
|
|
cf68f56497 | ||
|
|
405ac6f85b | ||
|
|
72768e7d60 | ||
|
|
cd3bd8713a | ||
|
|
399dbcaee9 | ||
|
|
3f2889a644 | ||
|
|
533d2429b3 | ||
|
|
2c0129be47 | ||
|
|
cb0da0d737 | ||
|
|
ce9ab6e6c6 | ||
|
|
373adf473a | ||
|
|
1cc461cc60 | ||
|
|
89a4a19eed | ||
|
|
5ee5eac998 | ||
|
|
999e5618d7 | ||
|
|
056f7a32e8 | ||
|
|
d1a3549ebc | ||
|
|
8c58359aac | ||
|
|
0dca45ca6c | ||
|
|
11039a4ce6 | ||
|
|
e16bc759b0 | ||
|
|
c745a534de | ||
|
|
ed24674b0c | ||
|
|
91f8705844 | ||
|
|
e24fe7a239 | ||
|
|
970f948844 | ||
|
|
85c5851539 | ||
|
|
52c35ea0c5 | ||
|
|
14cc43dd6a | ||
|
|
699da62ab1 | ||
|
|
4be125d3da | ||
|
|
23631e4a0f | ||
|
|
49c0e04f13 | ||
|
|
16709f5f8e | ||
|
|
b4ccca9892 | ||
|
|
4e9086b369 | ||
|
|
8114e25056 | ||
|
|
7c6e36f8c5 | ||
|
|
1a13b83fd7 | ||
|
|
0729065d16 | ||
|
|
e6be07af1f | ||
|
|
47aa62092e | ||
|
|
ce6596edde | ||
|
|
0bdf1ca87a | ||
|
|
cbc39ba4f9 | ||
|
|
77b3609cd8 | ||
|
|
c41bf8708b | ||
|
|
54cc730414 | ||
|
|
d138cee30c | ||
|
|
56182a1ef5 | ||
|
|
1f7487ae37 | ||
|
|
2db1dcf79f | ||
|
|
82ac0d3ab0 | ||
|
|
c0d1214032 | ||
|
|
d0c909c51d | ||
|
|
2587911936 | ||
|
|
42bc7e3c17 | ||
|
|
16ea4c1393 | ||
|
|
9b6995e267 | ||
|
|
4117c109d8 | ||
|
|
2923020273 | ||
|
|
ab47582f8a | ||
|
|
4041a93ae5 | ||
|
|
9cf3eb1631 | ||
|
|
56fba6725e | ||
|
|
3c4e552517 | ||
|
|
cdc284f009 | ||
|
|
961fe4aad4 | ||
|
|
67eb7bbff8 | ||
|
|
5c82e674cc | ||
|
|
dc5a9bb856 | ||
|
|
28d58b9828 | ||
|
|
a187b82887 | ||
|
|
2bf60a9a26 | ||
|
|
d2c1158d57 | ||
|
|
c1305904e5 | ||
|
|
094cf21c4c | ||
|
|
dc7a23db65 | ||
|
|
9297620b6a | ||
|
|
28019b1088 | ||
|
|
18714ac542 | ||
|
|
1724765feb | ||
|
|
97ee57ff9c | ||
|
|
4bc1224d0c | ||
|
|
169ee01338 | ||
|
|
d335898584 | ||
|
|
4269ccdee4 | ||
|
|
f12622f1e3 | ||
|
|
866d4f6b1d | ||
|
|
a344a5efc5 | ||
|
|
e272885f26 | ||
|
|
a6c28245be | ||
|
|
13e8367106 | ||
|
|
925180c10f | ||
|
|
69844884b7 | ||
|
|
e07efa1c21 | ||
|
|
87c5336cf4 | ||
|
|
113d418144 | ||
|
|
87bc2f1b64 | ||
|
|
d522dc4acc | ||
|
|
ef40f4a0ca | ||
|
|
8b1d30b319 | ||
|
|
3bb18a1bc7 | ||
|
|
65793db889 | ||
|
|
862a4437e9 | ||
|
|
58b3ab8185 | ||
|
|
fa3a9c4be8 | ||
|
|
1db698961c | ||
|
|
c9bc9ce38c | ||
|
|
8ab836c081 | ||
|
|
b703428ee3 | ||
|
|
00a4c30979 | ||
|
|
3b080f4563 | ||
|
|
d0f0e97254 | ||
|
|
85f39b5310 | ||
|
|
b5f980a4ff | ||
|
|
1d0032561e | ||
|
|
dfc6ffe2ba | ||
|
|
8c379f56cd | ||
|
|
0a54114db0 | ||
|
|
fcb37a75da | ||
|
|
95382298ce | ||
|
|
51cf4b7b6f | ||
|
|
77c40d8aa1 | ||
|
|
af7abea5e1 | ||
|
|
32a1a7ee3f | ||
|
|
2d43624a87 | ||
|
|
df6ef9752a | ||
|
|
8b9efaa179 | ||
|
|
2ffb37e8ec | ||
|
|
3397bf912d | ||
|
|
448e1b3a75 | ||
|
|
2b32605fde | ||
|
|
cdf90723c3 | ||
|
|
9e9e989ec1 | ||
|
|
589c16e1fc | ||
|
|
8cd79199e8 | ||
|
|
364d2a042b | ||
|
|
b0b7bf5e14 | ||
|
|
d9d766c098 | ||
|
|
1f1d6df342 | ||
|
|
1a1e2f0c78 | ||
|
|
e01ee77a52 | ||
|
|
2521d06233 | ||
|
|
4200e263ed | ||
|
|
f3dfda530e | ||
|
|
d0e04ca603 | ||
|
|
f45f733ff4 | ||
|
|
951bf35db6 | ||
|
|
eaea684e71 | ||
|
|
0261fa3077 | ||
|
|
8c6414ce5a | ||
|
|
c7262e257d | ||
|
|
c5df712c7d | ||
|
|
8ed7bd0034 | ||
|
|
e7a0ac104a | ||
|
|
e21b83b1ec | ||
|
|
31b8038aa0 | ||
|
|
dcb262f192 | ||
|
|
47dad991b9 | ||
|
|
21cf80dd58 | ||
|
|
5a4c9f4633 | ||
|
|
c25db4d9be | ||
|
|
457f73bc0f | ||
|
|
1488457211 | ||
|
|
33ddb1fa4f | ||
|
|
57b5614124 | ||
|
|
7e813768ed | ||
|
|
eb2f64801e | ||
|
|
1396d6b06b | ||
|
|
8ce2ad0d39 | ||
|
|
a699be42c7 | ||
|
|
05009b9c82 | ||
|
|
39cf5d5f70 | ||
|
|
836658d5d5 | ||
|
|
1b5cd02289 | ||
|
|
9180f27315 | ||
|
|
aa6a011bef | ||
|
|
32ee5a8a61 | ||
|
|
1950b0956e | ||
|
|
542d5a4127 | ||
|
|
b94b3c9d17 | ||
|
|
dab48b4183 | ||
|
|
10f0c9c0d8 | ||
|
|
61bd811dfd | ||
|
|
ce4a89ed96 | ||
|
|
db4e94652d | ||
|
|
34196dc989 | ||
|
|
a8a71cc70c | ||
|
|
f95c751ff2 | ||
|
|
eed32527cd | ||
|
|
1134bbf672 | ||
|
|
2635dab82f | ||
|
|
57b6495e06 | ||
|
|
bc356ceac9 | ||
|
|
8aeedadbcf | ||
|
|
1e3459c588 | ||
|
|
8dda416592 | ||
|
|
39a5b43e30 | ||
|
|
9bdcfc34b5 | ||
|
|
04b76339cb | ||
|
|
2e80a9ffe9 | ||
|
|
900d4fe433 | ||
|
|
76e9b8a599 | ||
|
|
b2fc9cc141 | ||
|
|
f280748d71 | ||
|
|
8a19bda170 | ||
|
|
6f1fcd3e10 | ||
|
|
9c5e591038 | ||
|
|
0790919125 | ||
|
|
0840e5be3f | ||
|
|
4894d42e67 | ||
|
|
664128cc18 | ||
|
|
3809f06ae8 | ||
|
|
b7347fd371 | ||
|
|
189b91be57 | ||
|
|
105d57c78d | ||
|
|
eaed388d33 | ||
|
|
564e8f6df4 | ||
|
|
6059753e73 | ||
|
|
d82886c517 | ||
|
|
23734480cc | ||
|
|
50871fa9b5 | ||
|
|
39a11a4196 | ||
|
|
80cd5ea6b4 | ||
|
|
a197a85930 | ||
|
|
aa4bf55574 | ||
|
|
0651a744fb | ||
|
|
3e4cdd4cf1 | ||
|
|
29b1409122 | ||
|
|
e26c65bffb | ||
|
|
69bed9dcb6 | ||
|
|
a49081a473 | ||
|
|
1aa49108d0 | ||
|
|
0dbc68a7df | ||
|
|
5fd25a76ad | ||
|
|
f4535b6465 | ||
|
|
ac35961a15 | ||
|
|
b2c4ca99c2 | ||
|
|
ce8cf450b7 | ||
|
|
112839a489 | ||
|
|
60041b03f7 | ||
|
|
67df9e0a50 | ||
|
|
5113387134 | ||
|
|
3f267034a8 | ||
|
|
dec8a16921 | ||
|
|
e3a9fdff22 | ||
|
|
0bf25fad84 | ||
|
|
2e09d88999 | ||
|
|
6df8f5b35c | ||
|
|
bce621d647 | ||
|
|
01e51767e7 | ||
|
|
9a24ed2a09 | ||
|
|
a5f756eeb0 | ||
|
|
e92c3d4d79 | ||
|
|
ca4471ea6a | ||
|
|
ecdbba2d28 | ||
|
|
057fb30b39 | ||
|
|
678ddd781c | ||
|
|
98111fe114 | ||
|
|
b47f5ab235 | ||
|
|
b909fe8ea9 | ||
|
|
276ef2a150 | ||
|
|
72540a8a0d | ||
|
|
0ce6cb4cee | ||
|
|
8b81f4cef2 | ||
|
|
d04d278f0e | ||
|
|
23f839dc19 | ||
|
|
6eca8150f1 | ||
|
|
521c9f686f | ||
|
|
37da18cdcb | ||
|
|
cdc5460fd6 | ||
|
|
8aa6f62b2d | ||
|
|
5c9b9b6295 | ||
|
|
2e70119e68 | ||
|
|
1c5a312867 | ||
|
|
f2e0b4dbc0 | ||
|
|
15fd488e0d | ||
|
|
04c0dad598 | ||
|
|
0acb817e38 | ||
|
|
fd00a08fa2 | ||
|
|
0bc529ef5c | ||
|
|
2e8f06286d | ||
|
|
52c1a950ba | ||
|
|
e946c93fe8 | ||
|
|
220e996531 | ||
|
|
05a0085e22 | ||
|
|
27074cb8d2 | ||
|
|
1fb126b4d3 | ||
|
|
b5ba82e52b | ||
|
|
a71139749f | ||
|
|
dd6332b715 | ||
|
|
936e461812 | ||
|
|
8c2367d0e7 | ||
|
|
eb3c68df88 | ||
|
|
d9123c50d6 | ||
|
|
43dfab7974 | ||
|
|
4d5b993490 | ||
|
|
44ab751fcb | ||
|
|
96fcf2f668 | ||
|
|
4db7992542 | ||
|
|
5a3893b46d | ||
|
|
9f4f728541 | ||
|
|
ba20020929 | ||
|
|
a3f224da6e | ||
|
|
9e7d4b3c22 | ||
|
|
56e5242201 | ||
|
|
38c1462fef | ||
|
|
156173b3b9 | ||
|
|
b6a8f96e02 | ||
|
|
6bb9c88a1f | ||
|
|
00ea95ee2b | ||
|
|
859e08d628 | ||
|
|
7c24fcfecb | ||
|
|
1c17d82836 | ||
|
|
2d822ded27 | ||
|
|
143c8bd436 | ||
|
|
2d166a35d0 | ||
|
|
eb02f15a5d | ||
|
|
a8559c3c29 | ||
|
|
bdb175f84b | ||
|
|
7122c7cd5a | ||
|
|
b9f5fd21e7 | ||
|
|
69f75a9191 | ||
|
|
b753c5ab2d | ||
|
|
7389537c63 | ||
|
|
00096d340f | ||
|
|
1b3977436d | ||
|
|
0631468f0d | ||
|
|
a57cb9eb6a | ||
|
|
ed4c4cb311 | ||
|
|
094373d89f | ||
|
|
a6d08a12b5 | ||
|
|
55c6aa78e5 | ||
|
|
f95d0a1d34 | ||
|
|
73b84a0e02 | ||
|
|
7704bf7c90 | ||
|
|
a76ebe3600 | ||
|
|
f8c8fedbe8 | ||
|
|
6a21729ff9 | ||
|
|
9839d7ca3a | ||
|
|
82ad38e7f4 | ||
|
|
654c98f2ee | ||
|
|
febadb6c08 | ||
|
|
b9f4eaa413 | ||
|
|
ddf1db3d3d | ||
|
|
987253afcb | ||
|
|
0a717e0081 | ||
|
|
ae0bce61f6 | ||
|
|
a0ee75ebf4 | ||
|
|
1665d9aaa3 | ||
|
|
be43f4d528 | ||
|
|
6bcd2024a0 | ||
|
|
f2b21c6055 | ||
|
|
afb96c60cb | ||
|
|
e5159e13dd | ||
|
|
fb622eda2a | ||
|
|
5565edfcb4 | ||
|
|
9b141b9f9f | ||
|
|
6c885bff1e | ||
|
|
9e575d89db | ||
|
|
9e266c7a41 | ||
|
|
b08c175bfc | ||
|
|
97c01c5951 | ||
|
|
519e23ec36 | ||
|
|
3f4d399afd | ||
|
|
efaded5016 | ||
|
|
2410bbfe4c | ||
|
|
9637ae563c | ||
|
|
f71631e732 | ||
|
|
cca082b091 | ||
|
|
cf8c38d08e | ||
|
|
4254e47bf4 | ||
|
|
2b32aee35d | ||
|
|
adfc22e377 | ||
|
|
949d1a81ac | ||
|
|
09a7999e4e | ||
|
|
351583ecf7 | ||
|
|
3afd3aab8f | ||
|
|
4bae40170f | ||
|
|
09cb9831ef | ||
|
|
3864dc5833 | ||
|
|
60d7b0272e | ||
|
|
fec72732e5 | ||
|
|
1571b11b0b | ||
|
|
8d168076ef | ||
|
|
9c6669fd5c | ||
|
|
e2dedfff00 | ||
|
|
a07c619947 | ||
|
|
c5ee27c1b4 | ||
|
|
2530667b86 | ||
|
|
83f8591afa | ||
|
|
78213a7ed5 | ||
|
|
08ff64da12 | ||
|
|
57650dc955 | ||
|
|
9e8c1d694a | ||
|
|
1dd7d63380 | ||
|
|
6a8ed13a99 | ||
|
|
d6c5066bfd | ||
|
|
60783d7109 | ||
|
|
db525f15da | ||
|
|
f33daf6bee | ||
|
|
d13928b394 | ||
|
|
5613a0d173 | ||
|
|
aa38586f82 | ||
|
|
d2b1ceb641 | ||
|
|
9dd7e9a25f | ||
|
|
1648273ddd | ||
|
|
3f90e4f12a | ||
|
|
2b9d565f0c | ||
|
|
70af63cc94 | ||
|
|
64b92881f9 | ||
|
|
be5ea069ad | ||
|
|
71e9357114 | ||
|
|
e28f86ac32 | ||
|
|
f97fe4bd65 | ||
|
|
3d0ac03f06 | ||
|
|
094132b7d3 | ||
|
|
51b9c9ae5a | ||
|
|
d31941ef13 | ||
|
|
48db826142 | ||
|
|
32b62d19ff | ||
|
|
e4f2961129 | ||
|
|
eba09dfad2 | ||
|
|
d9b72fcae6 | ||
|
|
6f17e7f735 | ||
|
|
047b82acc0 | ||
|
|
5347039d75 | ||
|
|
edc8095577 | ||
|
|
06f69723c2 | ||
|
|
8218fd4f57 | ||
|
|
6ee22edac5 | ||
|
|
37215ef1f4 | ||
|
|
a2eb2222b9 | ||
|
|
d63b4d9463 | ||
|
|
3bd85c20bc | ||
|
|
f70063c7d8 | ||
|
|
a2f7dd7408 | ||
|
|
147014d43a | ||
|
|
9460d1e9a1 | ||
|
|
b3a9a6b59a | ||
|
|
69f7d669c8 | ||
|
|
74a68b1462 | ||
|
|
006ea90493 | ||
|
|
aede063266 | ||
|
|
5aaeff4c5d | ||
|
|
5f80d6c18d | ||
|
|
7c59c9e6ec | ||
|
|
aae83b3235 | ||
|
|
520165128a | ||
|
|
556ced63cf | ||
|
|
063bf830ff | ||
|
|
7fd3bd412c | ||
|
|
47b9508af2 | ||
|
|
417c44aad0 | ||
|
|
1fefa0a99a | ||
|
|
06db4d2b62 | ||
|
|
bf7fc12a8d | ||
|
|
395daf57fa | ||
|
|
97219b7a84 | ||
|
|
0d5d677eff | ||
|
|
3bd6c99a3b | ||
|
|
1d2d8d58db | ||
|
|
9909b002fe | ||
|
|
ccda1ef104 | ||
|
|
52a882bda2 | ||
|
|
177c60e857 | ||
|
|
0566b96738 | ||
|
|
f72c179f56 | ||
|
|
0da963c794 | ||
|
|
7d885f7100 | ||
|
|
c1c2f794e3 | ||
|
|
8bb1c7a032 | ||
|
|
804123afc1 | ||
|
|
8e4f278ce4 | ||
|
|
3193241b11 | ||
|
|
edb44fa6a2 | ||
|
|
66e9ac3657 | ||
|
|
87e0796bf4 | ||
|
|
6bc6db3665 | ||
|
|
2a4aa51373 | ||
|
|
e15b052570 | ||
|
|
629e249a12 | ||
|
|
86a260bb89 | ||
|
|
6ab2de2787 | ||
|
|
8effdc1615 | ||
|
|
561fc2d01a | ||
|
|
3fd9cf573b | ||
|
|
d7c0bed4eb | ||
|
|
afd1f595e1 | ||
|
|
f47a43df2f | ||
|
|
4385327101 | ||
|
|
4e73c05ef9 | ||
|
|
c01ef035a5 | ||
|
|
b59c080943 | ||
|
|
814c65b290 | ||
|
|
9f1c1c3f18 | ||
|
|
e0df7dfba5 | ||
|
|
220db7212f | ||
|
|
d59f45a26a | ||
|
|
f88d92ecee | ||
|
|
c28188b6d8 | ||
|
|
80419748db | ||
|
|
874e6cd5af | ||
|
|
cd500fc344 | ||
|
|
ae7afc5749 | ||
|
|
b0fa94c781 | ||
|
|
9bf717fae9 | ||
|
|
859c1020e6 | ||
|
|
9108d64820 | ||
|
|
e9d3c1a421 | ||
|
|
f36e069f40 | ||
|
|
1f5c7b1d12 | ||
|
|
94ecb9e634 | ||
|
|
b9db67bc08 | ||
|
|
c152799295 | ||
|
|
f91165857d | ||
|
|
29b93f02f1 | ||
|
|
f9a6452528 | ||
|
|
e7e51957e9 | ||
|
|
f8903e7ce7 | ||
|
|
d3548a4dfb | ||
|
|
eca72c6dc6 | ||
|
|
6e378f4de6 | ||
|
|
94ac52d792 | ||
|
|
1f276a7eb6 | ||
|
|
a28a590078 | ||
|
|
91d052d25e | ||
|
|
2f6ded132d | ||
|
|
51cdc1ce90 | ||
|
|
494fe0b1e7 | ||
|
|
eac5310f5e | ||
|
|
7f4a2f9943 | ||
|
|
28a31301d4 | ||
|
|
1c5e575718 | ||
|
|
4a2d6b2241 | ||
|
|
001baec444 | ||
|
|
1b35d4f03b | ||
|
|
75fc8cb766 | ||
|
|
28bef8c54f | ||
|
|
787973a3d5 | ||
|
|
f0e3ca7c1f | ||
|
|
1b837a81ef | ||
|
|
b74dd85ef2 | ||
|
|
fc9cc62b12 | ||
|
|
e665edb652 | ||
|
|
e81cde8753 | ||
|
|
4e995436c5 | ||
|
|
c57154a8e5 | ||
|
|
31fbba057a | ||
|
|
46fa6e7ba4 | ||
|
|
bbb0379813 | ||
|
|
156b63440e | ||
|
|
20e062b031 | ||
|
|
c98ebc9f35 | ||
|
|
c6001c7bf6 | ||
|
|
2d0945264f | ||
|
|
3d0981cf7b | ||
|
|
cce581fa51 | ||
|
|
2446b804c7 | ||
|
|
20cdb1c5ec | ||
|
|
212b184320 | ||
|
|
37e98f342c | ||
|
|
578f5b85dd | ||
|
|
99b1e3f969 | ||
|
|
8c9ef0e8f3 | ||
|
|
f91d3ccf24 | ||
|
|
691063983e | ||
|
|
b912047f68 | ||
|
|
f2ddbf1e68 | ||
|
|
5cd36e55ab | ||
|
|
1cb590ebb8 | ||
|
|
280e937844 | ||
|
|
5afb55547d | ||
|
|
e3c91629db | ||
|
|
1b0496b1e7 | ||
|
|
b7369b40cb | ||
|
|
6567313de6 | ||
|
|
5ecf3cc36c | ||
|
|
71e3d89181 | ||
|
|
a284ece8e4 | ||
|
|
e73e75e740 | ||
|
|
9bfd61fabc | ||
|
|
a2aeaa51e8 | ||
|
|
b558c3db98 | ||
|
|
0f70bcbf28 | ||
|
|
f7c4600007 | ||
|
|
8910f073e5 | ||
|
|
ea18be1866 | ||
|
|
0bd687eb15 | ||
|
|
25b74bf3dd | ||
|
|
e4f9ca3f3c | ||
|
|
38a3f403bd | ||
|
|
51dd2030a0 | ||
|
|
7e31067e6a | ||
|
|
9bf06ff30b | ||
|
|
85aa726bb7 | ||
|
|
3a67ff3afa | ||
|
|
0a022a4348 | ||
|
|
ad24beea5b | ||
|
|
9026ae7a16 | ||
|
|
2fee9b861f | ||
|
|
b7e2bcf02e | ||
|
|
d548848b18 | ||
|
|
4c628ea74f | ||
|
|
d3ef97f637 | ||
|
|
02bd819f7e | ||
|
|
ea3b4ddeb0 | ||
|
|
ab3ea7475f | ||
|
|
b9b97d0a56 | ||
|
|
e19db23bb5 | ||
|
|
5ec1ca46e5 | ||
|
|
c2c918fdda | ||
|
|
4627989401 | ||
|
|
9552e09471 | ||
|
|
c8fb25066b | ||
|
|
b1cb6d42f4 | ||
|
|
caf7e4a1cf | ||
|
|
6a5c0223c4 | ||
|
|
9ff2d66079 | ||
|
|
2db2b9309d | ||
|
|
7a38c15d59 | ||
|
|
cb7375f873 | ||
|
|
41e52f9e88 | ||
|
|
ce7e30a752 | ||
|
|
3214811fcb | ||
|
|
edda2a5399 | ||
|
|
926bc8e72c | ||
|
|
eadffb6be3 | ||
|
|
cf04e8c23c | ||
|
|
ec5840f840 | ||
|
|
7dc4a7177d | ||
|
|
3d91de8178 | ||
|
|
6ea1abfedc | ||
|
|
5251482dd7 | ||
|
|
dd73787c8b | ||
|
|
340450a67e | ||
|
|
0d40989f4c | ||
|
|
c640096d9c | ||
|
|
76081869e3 | ||
|
|
9156c1b850 | ||
|
|
d78718d8f4 | ||
|
|
eef1e986a6 | ||
|
|
8fa99c34be | ||
|
|
a35fbdd1b5 | ||
|
|
0efef78684 | ||
|
|
62ce17cb4c | ||
|
|
6112be232a | ||
|
|
c1a9c58eb5 | ||
|
|
0393588fe8 | ||
|
|
b7ff0325ea | ||
|
|
c4d39eae25 | ||
|
|
0e88981425 | ||
|
|
ed7fcae33b | ||
|
|
8b967fb3b7 | ||
|
|
4d68a5dc2b | ||
|
|
ecd6ef40ef | ||
|
|
0c9dc33ff0 | ||
|
|
98c27fe8ef | ||
|
|
3d490f52b2 | ||
|
|
c78c17d953 | ||
|
|
ee5d667a24 | ||
|
|
b9653b4ff8 | ||
|
|
78bcfa8cfa | ||
|
|
1f1130efda | ||
|
|
3d7b05d633 | ||
|
|
343bbd8a1a | ||
|
|
f702df48f5 | ||
|
|
bc382366a1 | ||
|
|
c158144b7b | ||
|
|
d4337fbc56 | ||
|
|
e822d8e7d1 | ||
|
|
dc95b2e8d7 | ||
|
|
d226ad38ff | ||
|
|
2a160c5247 | ||
|
|
641a9dc4e2 | ||
|
|
7e71af8d25 | ||
|
|
8e33d2e1b2 | ||
|
|
61118b348c | ||
|
|
84faff82a2 | ||
|
|
d6cf7cc06f | ||
|
|
0688b94729 | ||
|
|
695f53194d | ||
|
|
1b963c9439 | ||
|
|
b50d6dd389 | ||
|
|
2afad9490e | ||
|
|
812a275add | ||
|
|
90ccf7699b | ||
|
|
31ed12d88a | ||
|
|
5a4e44d064 | ||
|
|
515c67fa3a | ||
|
|
81d60165a3 | ||
|
|
cc0fac3546 | ||
|
|
d71ad13fcf | ||
|
|
0d13811305 | ||
|
|
b69cc4e34a | ||
|
|
caf8f383ad | ||
|
|
7db160a8e2 | ||
|
|
202a3d1fb1 | ||
|
|
332135fc6f | ||
|
|
7e895f82b2 | ||
|
|
c5c61f7692 | ||
|
|
bd8a392882 | ||
|
|
36183e6dc8 | ||
|
|
5ae4af54da | ||
|
|
620bdd1419 | ||
|
|
c18eacbb81 | ||
|
|
bb8acc5f51 | ||
|
|
d41f4810d7 | ||
|
|
387c4edb3b | ||
|
|
c3fd7c1a7a | ||
|
|
94f1ec18de | ||
|
|
e00f8f6cee | ||
|
|
79b0a9c833 | ||
|
|
5e8e2f4b3a | ||
|
|
f0158dd915 | ||
|
|
10cf251b46 | ||
|
|
72231e5658 | ||
|
|
c510ff3b1c | ||
|
|
936e138fee | ||
|
|
8325696b55 | ||
|
|
d63f517ae5 | ||
|
|
e74df8db90 | ||
|
|
1a0bf67452 | ||
|
|
f0df9b399b | ||
|
|
f9345308ff | ||
|
|
6ed7c75448 | ||
|
|
cb71167fb7 | ||
|
|
d4292987cc | ||
|
|
5da24a18dc | ||
|
|
1cf7b3e697 | ||
|
|
3f3672497b | ||
|
|
7714304c7a | ||
|
|
ff640639b2 | ||
|
|
0965603c8f | ||
|
|
b8412cec96 | ||
|
|
86b7e192b2 | ||
|
|
32b9e6094b | ||
|
|
3ff7f3158b | ||
|
|
bd83ba8ca9 | ||
|
|
785881c90e | ||
|
|
567ccf8a59 | ||
|
|
11423a6d78 | ||
|
|
ab6b716e78 | ||
|
|
7b1be2e34f | ||
|
|
19d0d247b4 | ||
|
|
1074a9e1d2 | ||
|
|
0648f384bb | ||
|
|
58d72204cc | ||
|
|
067b4c260f | ||
|
|
9e7e4d5fe7 | ||
|
|
7d9609be76 | ||
|
|
7e510f8bc5 | ||
|
|
a16e009370 | ||
|
|
a021c497b6 | ||
|
|
e77159cb58 | ||
|
|
36ac353f1f | ||
|
|
58122bf023 | ||
|
|
c5da23bb02 | ||
|
|
fd45391616 | ||
|
|
11dd80e4e7 | ||
|
|
298f8ef842 | ||
|
|
4c45b9fb85 | ||
|
|
599cea3238 | ||
|
|
0c5685e9eb | ||
|
|
760b82fd63 | ||
|
|
340034adf7 | ||
|
|
d978bfd0db | ||
|
|
de9f9722ac | ||
|
|
4dd9dc03fe | ||
|
|
47fc4aa39a | ||
|
|
84ad608d70 | ||
|
|
d4dd0c9992 | ||
|
|
ac93d027af | ||
|
|
190a95584c | ||
|
|
9ad368e420 | ||
|
|
a524215479 | ||
|
|
6104c36455 | ||
|
|
a04287c9f6 | ||
|
|
99876e3f73 | ||
|
|
f16f498f27 | ||
|
|
4f9b5923bd | ||
|
|
84476cd51f | ||
|
|
bf9a6bde82 | ||
|
|
a6f8f57725 | ||
|
|
02f4c334f0 | ||
|
|
9884b294c6 | ||
|
|
49f22b5790 | ||
|
|
841270e824 | ||
|
|
630ce57303 | ||
|
|
94aed4d1d1 | ||
|
|
cdc0ec96a9 | ||
|
|
751188bcd8 | ||
|
|
c0c44d46bc | ||
|
|
19d8749abf | ||
|
|
c6f9fb2ac2 | ||
|
|
b1cfdfbc14 | ||
|
|
360df11aa6 | ||
|
|
d88cc3251f | ||
|
|
cd7079da92 | ||
|
|
77a0e8213b | ||
|
|
5efa778509 | ||
|
|
02e6463fae | ||
|
|
b411787fe7 | ||
|
|
8fd91194a7 | ||
|
|
4f85e1d603 | ||
|
|
fae0aeb442 | ||
|
|
ec77824a96 | ||
|
|
4127936eef | ||
|
|
ae829375a0 | ||
|
|
a1afcbc855 | ||
|
|
e1e3aa899d | ||
|
|
2644a3bf6e | ||
|
|
6b4e81e1ec | ||
|
|
c705a0f35a | ||
|
|
b1b3102b31 | ||
|
|
2d823d34f9 | ||
|
|
1bb9fb2c67 | ||
|
|
b90e3ce9da | ||
|
|
e04a074a79 | ||
|
|
f98c3311af | ||
|
|
1a8c44af27 | ||
|
|
a57089c1a9 | ||
|
|
87001f7e78 | ||
|
|
1fbb05c938 | ||
|
|
3eef9d3b62 | ||
|
|
af2adf0134 | ||
|
|
bbdfee8d33 | ||
|
|
2c4da83502 | ||
|
|
75f2868971 | ||
|
|
87d9ecc3c8 | ||
|
|
b637943bd9 | ||
|
|
0514475155 | ||
|
|
8cfb39e035 | ||
|
|
a9a7cb7be0 | ||
|
|
f8649a24aa | ||
|
|
ee5a860e07 | ||
|
|
e6f03c1136 | ||
|
|
780162f516 | ||
|
|
a6d32d834b | ||
|
|
cef3c7f032 | ||
|
|
07f8a532f9 | ||
|
|
352adf526a | ||
|
|
df48f9caf9 | ||
|
|
f611053d4f | ||
|
|
39011cd93b | ||
|
|
562cb8cebd | ||
|
|
89ae721421 | ||
|
|
f65f4b32e2 | ||
|
|
303fca6bcc | ||
|
|
4aba159ee8 | ||
|
|
5838e60bf6 | ||
|
|
527e436d57 | ||
|
|
60ede73ff8 | ||
|
|
298d5e95fc | ||
|
|
e9a68f0b7d | ||
|
|
d613cf4d4c | ||
|
|
3487fae95d | ||
|
|
9ee47a071b | ||
|
|
3579fc3dea | ||
|
|
921bff19f1 | ||
|
|
d07420eaeb | ||
|
|
8028db77d8 | ||
|
|
e49a059783 | ||
|
|
482b6e6844 | ||
|
|
aa70635919 | ||
|
|
c331b0fa89 | ||
|
|
fe55744de2 | ||
|
|
15e9d85a14 | ||
|
|
51043b61a1 | ||
|
|
6620d5fb26 | ||
|
|
f547020fc5 | ||
|
|
89ea473c3b | ||
|
|
b60d9d0be7 | ||
|
|
4fdb2174f0 | ||
|
|
06a48584d6 | ||
|
|
e9f9c1009d | ||
|
|
b66ceda525 | ||
|
|
7c9c28f026 | ||
|
|
72f0552ef1 | ||
|
|
877a1f15a9 | ||
|
|
efa2a68805 | ||
|
|
023209bb76 | ||
|
|
da5dcde6ee | ||
|
|
e92c5e1bf9 | ||
|
|
2b7c4ae76c | ||
|
|
ea26db2cbd | ||
|
|
b9e3747f99 | ||
|
|
4d608b7f37 | ||
|
|
9c42b77879 | ||
|
|
a2d0ea9d11 | ||
|
|
298b4d23e8 | ||
|
|
875adb17b1 | ||
|
|
9eb68bf052 | ||
|
|
8cec2dab82 | ||
|
|
5f58e7b343 | ||
|
|
28bb88e9b4 | ||
|
|
04455c9625 | ||
|
|
9853150e57 | ||
|
|
85c7216eaa | ||
|
|
a54384a579 | ||
|
|
19f1d8b847 | ||
|
|
8270b64624 | ||
|
|
81e3810c32 | ||
|
|
c266151071 | ||
|
|
27d55260e8 | ||
|
|
233583657e | ||
|
|
7bb1eed249 | ||
|
|
aa85fc3787 | ||
|
|
c9a88617b1 | ||
|
|
2f899fdfcc | ||
|
|
cdd2b6560e | ||
|
|
60f29edbca | ||
|
|
60cffe1ed1 | ||
|
|
e77e7d1643 | ||
|
|
5dc22f502e | ||
|
|
7a309c1175 | ||
|
|
9ce5baad9e | ||
|
|
a4aa9a76f2 | ||
|
|
befa25de5c | ||
|
|
83175d5c09 | ||
|
|
d8df8e8778 | ||
|
|
09957d0279 | ||
|
|
9e44985f56 | ||
|
|
b657a84699 | ||
|
|
5ec7835556 | ||
|
|
03a864156d | ||
|
|
c823bf3320 | ||
|
|
4f4d2f1f4c | ||
|
|
1bb389052d | ||
|
|
31fdc5bbc1 | ||
|
|
db3833430d | ||
|
|
6d2212d259 | ||
|
|
912c0e7fc4 | ||
|
|
43d232819a | ||
|
|
302eaf60b5 | ||
|
|
84535faa90 | ||
|
|
8c55d3c705 | ||
|
|
ec11b9268f | ||
|
|
a45af4a80c | ||
|
|
df627b6830 | ||
|
|
dfbeaf30ad | ||
|
|
a03f74b125 | ||
|
|
1a01f1299c | ||
|
|
91f0cdbaca | ||
|
|
e50bd4125d | ||
|
|
ded2c8a7c8 | ||
|
|
88ce9fc0a6 | ||
|
|
3c713248b3 | ||
|
|
75f3edcd92 | ||
|
|
081a7779a6 | ||
|
|
6e995207a6 | ||
|
|
3492b77aa0 | ||
|
|
e864901035 | ||
|
|
7ee6de6b6a | ||
|
|
553b8ccf6b | ||
|
|
bceef0b861 | ||
|
|
53cce6c6bb | ||
|
|
b95047d09f | ||
|
|
c6cb4375f5 | ||
|
|
a07e2b8eda | ||
|
|
98e7002167 | ||
|
|
3428dde3c6 | ||
|
|
13d48f982b | ||
|
|
b2e4dc27e4 | ||
|
|
deddeab049 | ||
|
|
c3a6410822 | ||
|
|
c5915091aa | ||
|
|
4401f0659e | ||
|
|
a0c72d32e8 | ||
|
|
1493045ec4 | ||
|
|
210efb2be7 | ||
|
|
a02fc1de63 | ||
|
|
7755bfc861 | ||
|
|
01172bca77 | ||
|
|
143cbc8587 | ||
|
|
b65f655a8a | ||
|
|
ed04c86369 | ||
|
|
c0eaa17eb3 | ||
|
|
6383a55038 | ||
|
|
574df60d58 | ||
|
|
61ee575cd3 | ||
|
|
bdd963081e | ||
|
|
6b908d7a14 | ||
|
|
7d0d70c094 | ||
|
|
e75c5a9248 | ||
|
|
b8ed5f4f1a | ||
|
|
660d7060e4 | ||
|
|
79c5f57714 | ||
|
|
606d8fbe55 | ||
|
|
edfa116a40 | ||
|
|
55ca0c712f | ||
|
|
16bef488ee | ||
|
|
82d784d9f8 | ||
|
|
103fb2dfc1 | ||
|
|
bba3e1810b | ||
|
|
af5333f63e | ||
|
|
ea77a5fb53 | ||
|
|
a4aef0894b | ||
|
|
bab7972518 | ||
|
|
88853a7aee | ||
|
|
d3339df853 | ||
|
|
385614b448 | ||
|
|
c47ba80f5d | ||
|
|
6de0700f49 | ||
|
|
d9fe52b1a1 | ||
|
|
ccfc476ec0 | ||
|
|
f518b3c48b | ||
|
|
81d52db722 | ||
|
|
df9973da99 | ||
|
|
ca4c779c66 | ||
|
|
7897352175 | ||
|
|
e0e75eac33 | ||
|
|
31d83dd26e | ||
|
|
698a0b811f | ||
|
|
f69609b403 | ||
|
|
56259e3476 | ||
|
|
f3dfa53f31 | ||
|
|
e10d60c08e | ||
|
|
ad4037351f | ||
|
|
f9e1da8c9f | ||
|
|
2f05137af7 | ||
|
|
c305e31c2e | ||
|
|
a55fabe4f9 | ||
|
|
c58848b298 | ||
|
|
ede644d3c4 | ||
|
|
4ff98e62a9 | ||
|
|
e87fd2fff6 | ||
|
|
33e8b10579 | ||
|
|
d11780f62d | ||
|
|
7706aa0bf0 | ||
|
|
cf96e10552 | ||
|
|
1d01e1e6cd | ||
|
|
a5c4759662 | ||
|
|
e7ff1d693b | ||
|
|
9b4354a057 | ||
|
|
51a46ba92c | ||
|
|
31c4e0d59f | ||
|
|
6b246548c3 | ||
|
|
57dda3843a | ||
|
|
b04ac14751 | ||
|
|
5ad8497db2 | ||
|
|
672f07a5ee | ||
|
|
aceffafc4e | ||
|
|
891b3183ed | ||
|
|
26dd066280 | ||
|
|
157ebdb5fe | ||
|
|
1a18839f82 | ||
|
|
e7f8e7628f | ||
|
|
07190571a1 | ||
|
|
f04d922fca | ||
|
|
7fa382d4e9 | ||
|
|
97766710c0 | ||
|
|
994ee060a7 | ||
|
|
589c27fdcf | ||
|
|
37a018fda4 | ||
|
|
f49381d1c5 | ||
|
|
573095bbad | ||
|
|
0fdcc8e288 | ||
|
|
03d7e26c3d | ||
|
|
3a5cd835ad | ||
|
|
47a8f081e9 | ||
|
|
ad8d691c11 | ||
|
|
8186696e62 | ||
|
|
183ec22923 | ||
|
|
5376995350 | ||
|
|
acb6e30fad | ||
|
|
fe0dabb177 | ||
|
|
194ae46ce8 | ||
|
|
2e9f1bfe90 | ||
|
|
a82adc4948 | ||
|
|
6440c70a38 | ||
|
|
748eb07c9a | ||
|
|
5be2f591d2 | ||
|
|
f2e4950e89 | ||
|
|
7444c5b826 | ||
|
|
5f6cca68cc | ||
|
|
3d33e5351b | ||
|
|
c6e245450d | ||
|
|
36854a16b7 | ||
|
|
b4ee8229ee | ||
|
|
cf0a3841e8 | ||
|
|
3ebd2257f8 | ||
|
|
844d1967cc | ||
|
|
dac37f8938 | ||
|
|
dd74bc7c2b | ||
|
|
7eec58a834 | ||
|
|
da81998c71 | ||
|
|
d1552e7b71 | ||
|
|
c64e10083b | ||
|
|
43e2be58da | ||
|
|
63b60d2546 | ||
|
|
91ddc709eb | ||
|
|
1bcb67ffb9 | ||
|
|
976177c244 | ||
|
|
a37c81a142 | ||
|
|
58de3a35a0 | ||
|
|
44b8a8a0d8 | ||
|
|
b0f5f4338f | ||
|
|
6457847633 | ||
|
|
69d47a5e53 | ||
|
|
02177a7794 | ||
|
|
65c15126f1 | ||
|
|
76c78b8e63 | ||
|
|
f63342ff0f | ||
|
|
e3410cf34c | ||
|
|
7a129047e7 | ||
|
|
343716b7da | ||
|
|
1687fb9120 | ||
|
|
3e89057c23 | ||
|
|
774587efa6 | ||
|
|
2dc8bf321e | ||
|
|
992813d770 | ||
|
|
46882dee93 | ||
|
|
8bbb7b540f | ||
|
|
f5f1ab036d | ||
|
|
f15d556c05 | ||
|
|
570a664db8 | ||
|
|
52f69324d9 | ||
|
|
ea6016b1ba | ||
|
|
30fcffd63c | ||
|
|
50446bbde4 | ||
|
|
b307033aab | ||
|
|
3e5985833c | ||
|
|
8421f02aab | ||
|
|
b6daafec17 | ||
|
|
fc058b669e | ||
|
|
99214b98b9 | ||
|
|
ab35a44ed9 | ||
|
|
125b82a5bb | ||
|
|
f294ea157f | ||
|
|
486ae052ef | ||
|
|
53b5442dad | ||
|
|
507c5f75bd | ||
|
|
0fb72c579e | ||
|
|
c5d6ac63c5 | ||
|
|
a934c76263 | ||
|
|
0098c3bbc0 | ||
|
|
f3eedc03e6 | ||
|
|
17c71a2528 | ||
|
|
d2ecbd7c6e | ||
|
|
2ce7dc0c37 | ||
|
|
b648424c18 | ||
|
|
301754c857 | ||
|
|
c9a3c4c9b8 | ||
|
|
ce0c8b363a | ||
|
|
9d6c38004c | ||
|
|
19a58cc8c6 | ||
|
|
d855c39da2 | ||
|
|
03f4bfecc1 | ||
|
|
c8d9e4c7b3 | ||
|
|
cb2b8f2e7c | ||
|
|
0272dd4c24 | ||
|
|
3b327937c2 | ||
|
|
f8f2f17184 | ||
|
|
2efaebcf89 | ||
|
|
150c695979 | ||
|
|
6fe87ab05a | ||
|
|
f6613a5555 | ||
|
|
947d5c2739 | ||
|
|
1544706ff4 | ||
|
|
e121d1a8a1 | ||
|
|
badbdbf03f | ||
|
|
3b5495b53a | ||
|
|
545027ae64 | ||
|
|
91896433f8 | ||
|
|
a60074fc8a | ||
|
|
eb82aa14bb | ||
|
|
cb4b640730 | ||
|
|
266d9a3c71 | ||
|
|
7a5a704a15 | ||
|
|
29ed8de935 | ||
|
|
3418cab5de | ||
|
|
e2ce015d49 | ||
|
|
0d0ab3f414 | ||
|
|
22b5bcc7e1 | ||
|
|
34d5d73d7e | ||
|
|
7ae4441731 | ||
|
|
d0b7f6203c | ||
|
|
3757f3ae57 | ||
|
|
8f9366da5a | ||
|
|
b8e55f87a9 | ||
|
|
b469f70e02 | ||
|
|
6df69b24db | ||
|
|
0133a5eb23 | ||
|
|
86712c2c48 | ||
|
|
5195caa3c6 | ||
|
|
684ad38d4b | ||
|
|
c6d292a3d7 | ||
|
|
246414a735 | ||
|
|
429bcffaf8 | ||
|
|
d92e7b138c | ||
|
|
cd51ad2a25 | ||
|
|
8732825404 | ||
|
|
fc37cdcc95 | ||
|
|
2435ec9515 | ||
|
|
623fab8ea8 | ||
|
|
e063345c4b | ||
|
|
27533a1dcd | ||
|
|
d232e0e2c6 | ||
|
|
0475489e24 | ||
|
|
34b35db27f | ||
|
|
b47bc12e48 | ||
|
|
4d102dcf68 | ||
|
|
a8c9663200 | ||
|
|
57660cc5af | ||
|
|
c459e918af | ||
|
|
962abba1b2 | ||
|
|
5297d18f92 | ||
|
|
7853e48a6f | ||
|
|
303d87f69d | ||
|
|
9dcdc9500d | ||
|
|
7723bafff6 | ||
|
|
d74a89c6c1 | ||
|
|
cce443be0b | ||
|
|
81fa3d27bf | ||
|
|
493091cd66 | ||
|
|
c75f5ee961 | ||
|
|
083e0ca2e0 | ||
|
|
79bd89016c | ||
|
|
eca7e38763 | ||
|
|
b5cbfb970f | ||
|
|
74e1cafd9d | ||
|
|
ee7381b67d | ||
|
|
7f93bc305d | ||
|
|
c7931ca69c | ||
|
|
90f06a3289 | ||
|
|
0e344a7e02 | ||
|
|
3f91a4e908 | ||
|
|
fb443c58ab | ||
|
|
b233514b58 | ||
|
|
11615171d1 | ||
|
|
2df4738af2 | ||
|
|
9e62f5bd52 | ||
|
|
80349ae8ee | ||
|
|
06d51f2648 | ||
|
|
fcc810f703 | ||
|
|
d6d70e1525 | ||
|
|
e5f79e061c | ||
|
|
ad3c0a1ffd | ||
|
|
2ef935198a | ||
|
|
dc3b9c648a | ||
|
|
2d610d40b6 | ||
|
|
d2c686f48b | ||
|
|
5cafec3262 | ||
|
|
580c42d167 | ||
|
|
dcfec35e57 | ||
|
|
701bac6e11 | ||
|
|
0c46d593d8 | ||
|
|
b867de0667 | ||
|
|
e207b75c7a | ||
|
|
fc3df5bfe4 | ||
|
|
dfb05b9d39 | ||
|
|
19702daecd | ||
|
|
dde4b4df3b | ||
|
|
c070930a8c | ||
|
|
d36b5f7c59 | ||
|
|
0e517907ce | ||
|
|
739e9716ab | ||
|
|
0e5972d895 | ||
|
|
71975658ca | ||
|
|
6572e29a64 | ||
|
|
41b21c2a0c | ||
|
|
2b19d2e6de | ||
|
|
f99aa3459c | ||
|
|
5bce9d5abf | ||
|
|
abe102649e | ||
|
|
424fcdc63a | ||
|
|
a3fd451c74 | ||
|
|
731fd113d2 | ||
|
|
c9ca1bda5c | ||
|
|
58ea822050 | ||
|
|
671484d6f9 | ||
|
|
e6e74194bc | ||
|
|
04432c8368 | ||
|
|
6782876c0c | ||
|
|
4b09e92d7c | ||
|
|
3266f64fb9 | ||
|
|
2a9886e698 | ||
|
|
1319e3a811 | ||
|
|
6cbb40f874 | ||
|
|
07fd6a3ad4 | ||
|
|
8891869013 | ||
|
|
04fc735e12 | ||
|
|
7b9860f1be | ||
|
|
38f26a2bd8 | ||
|
|
a9750f8cff | ||
|
|
ad86e7f34a | ||
|
|
496435ef3b | ||
|
|
5275b31e7e | ||
|
|
e341991703 | ||
|
|
fb65496507 | ||
|
|
abe52ba440 | ||
|
|
ee5d097201 | ||
|
|
0ea182d04f | ||
|
|
8d737c96c9 | ||
|
|
5c93b90c2e | ||
|
|
41bee850da | ||
|
|
416c2a9e9d | ||
|
|
8fa9fccc79 | ||
|
|
c5a4071a06 | ||
|
|
930e48ad39 | ||
|
|
868678f321 | ||
|
|
8530b6512c | ||
|
|
2a5f487e5f | ||
|
|
141f6c51dc | ||
|
|
1fc9492b30 | ||
|
|
65782d3d75 | ||
|
|
b5cb6dfb9d | ||
|
|
6667e4b8bc | ||
|
|
5e2492a930 | ||
|
|
4f07372e0f | ||
|
|
80a1a97083 | ||
|
|
204476c55d | ||
|
|
8fab258778 | ||
|
|
12059955e5 | ||
|
|
3ca1f957ba | ||
|
|
b11362aec1 | ||
|
|
75630df10e | ||
|
|
4d93abb7ac | ||
|
|
731943f980 | ||
|
|
265dc1f296 | ||
|
|
c449ffcc98 | ||
|
|
34500d32d0 | ||
|
|
d3e5dfc346 | ||
|
|
8ed11107eb | ||
|
|
2f51108783 | ||
|
|
2667a66419 | ||
|
|
0fd073f2f0 | ||
|
|
840cf4e00a | ||
|
|
d5d99f94b7 | ||
|
|
dea19e4416 | ||
|
|
f7612d7398 | ||
|
|
e39182607e | ||
|
|
79f1febbce | ||
|
|
120def9676 | ||
|
|
82562bc20d | ||
|
|
1c51361120 | ||
|
|
c07e79deb7 | ||
|
|
81cb1111b4 | ||
|
|
369307181e | ||
|
|
7881e04e6b | ||
|
|
66a72eb1be | ||
|
|
ad77f11859 | ||
|
|
96d177876f | ||
|
|
dc7392f86b | ||
|
|
6f6252b6f8 | ||
|
|
20b32df2c6 | ||
|
|
d413d02bf7 | ||
|
|
426c0366cc | ||
|
|
d3d3655580 | ||
|
|
d4a011ee6d | ||
|
|
b08118734e | ||
|
|
41ee6de784 | ||
|
|
139d2e6be8 | ||
|
|
a866461b0a | ||
|
|
6f61331d46 | ||
|
|
7731ca9fb2 | ||
|
|
3de0c4f5d8 | ||
|
|
2e602f8861 | ||
|
|
1a7d92a082 | ||
|
|
5aa2a0c821 | ||
|
|
4f2328b8dc | ||
|
|
e929e524a3 | ||
|
|
e6b9f18d3d | ||
|
|
7f197ef84e | ||
|
|
a9d7ca3332 | ||
|
|
a65e586de5 | ||
|
|
7072a50060 | ||
|
|
fc1a367c2e | ||
|
|
3b376d85d4 | ||
|
|
9aca8438a2 | ||
|
|
c2d37dfb61 | ||
|
|
3c3bd0f0ea | ||
|
|
958db336b7 | ||
|
|
342d570b3b | ||
|
|
67457c25df | ||
|
|
a98d46f8a8 | ||
|
|
b2fca84b15 | ||
|
|
2e31169738 | ||
|
|
4cda2c8093 | ||
|
|
73b4e8a988 | ||
|
|
82dbfb4379 | ||
|
|
0e2ff2390a | ||
|
|
220d5db739 | ||
|
|
212d475f50 | ||
|
|
b7fbd0433f | ||
|
|
d28dd96aa0 | ||
|
|
0e46f91c51 | ||
|
|
d6dabacb05 | ||
|
|
4363473955 | ||
|
|
7e5fd4e290 | ||
|
|
c30a0cd4f1 | ||
|
|
98d56a38f5 | ||
|
|
f26e95e7c9 | ||
|
|
f39e1a9b70 | ||
|
|
1d5b531ba3 | ||
|
|
a90bd08405 | ||
|
|
1cbb726cab | ||
|
|
ce61d00dcf | ||
|
|
2d92b798c6 | ||
|
|
cc62164a7c | ||
|
|
af510bce93 | ||
|
|
4ba60bedcd | ||
|
|
583bf26129 | ||
|
|
d8c3623141 | ||
|
|
2cd0875da6 | ||
|
|
73d0848f7e | ||
|
|
d757e82e3f | ||
|
|
007238e7dd | ||
|
|
944e019020 | ||
|
|
634f8897b4 | ||
|
|
7651d31c50 | ||
|
|
1702873ab1 | ||
|
|
44e7d5249c | ||
|
|
8ab37040db | ||
|
|
ac203b0ccb | ||
|
|
557546c9c3 | ||
|
|
52a94dbe78 | ||
|
|
f4ffc19981 | ||
|
|
ac3c117b03 | ||
|
|
1ac008ab02 | ||
|
|
58ac86e3bf | ||
|
|
1daf06858b | ||
|
|
a4e8ce7f01 | ||
|
|
ed6e16cdce | ||
|
|
57bb92a36c | ||
|
|
142540e263 | ||
|
|
5a17430747 | ||
|
|
23c84a9e96 | ||
|
|
6bb9dcc216 | ||
|
|
1901e4dbdc | ||
|
|
1657f769da | ||
|
|
c82eb489fe | ||
|
|
9f05befd70 | ||
|
|
f274ed3fd8 | ||
|
|
f6e027abcb | ||
|
|
a92114a854 | ||
|
|
ffb84705d3 | ||
|
|
2f8366b6f9 | ||
|
|
a6f062c126 | ||
|
|
41c24f3bc9 | ||
|
|
af40bdb11b | ||
|
|
00a69e6aed | ||
|
|
04b2d784ce | ||
|
|
a5de51b039 | ||
|
|
4a80e9f20f | ||
|
|
8676769a8f | ||
|
|
2f8c7a81ca | ||
|
|
ac11af6048 | ||
|
|
7c558802a4 | ||
|
|
5a516aa1ad | ||
|
|
4d1b6ed974 | ||
|
|
e8e6bb0499 | ||
|
|
58f94666d1 | ||
|
|
085efaa605 | ||
|
|
6028e392f0 | ||
|
|
fa67dd74a0 | ||
|
|
cb734b50c3 | ||
|
|
087e58b88b | ||
|
|
78b9d67aaa | ||
|
|
a6015e4647 | ||
|
|
fcbe95447a | ||
|
|
760fe392a8 | ||
|
|
5dab606980 | ||
|
|
c265575dfd | ||
|
|
4a1f526ac6 | ||
|
|
497b633661 | ||
|
|
c7f54a98e0 | ||
|
|
7da8c20409 | ||
|
|
116328c7bf | ||
|
|
bd68fef4a2 | ||
|
|
7759d56ad3 | ||
|
|
9c7020a369 | ||
|
|
f8c1903316 | ||
|
|
652423ad14 |
No files matched your search
@@ -4,6 +4,7 @@
|
||||
*.plg
|
||||
*.suo
|
||||
*.vcproj.*.*.user
|
||||
*.kate-swp
|
||||
Win32_LIB_ASM_Debug
|
||||
Win32_LIB_ASM_Release
|
||||
*.bsc
|
||||
|
||||
@@ -42,4 +42,3 @@ compversion() {
|
||||
|
||||
[ -f "$path/comptime.c" ] && compversion "$@"
|
||||
exit 2
|
||||
|
||||
+5
-1
@@ -31,4 +31,8 @@ and don't forget to build r_opengl.dll for srb2dd.
|
||||
|
||||
Updated 2/17/2024 (v1.6 Saturn)
|
||||
No i threw everthing into dll-binaries
|
||||
theres no reason at all to have stuff spread across multiple directories
|
||||
theres no reason at all to have stuff spread across multiple directories
|
||||
|
||||
Updated 3/19/2025 (v1.6; Neptune)
|
||||
We're using some C++ code now, so we need to have a few more DLLs.
|
||||
Otherwise, it's about the same as prior.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,191 +0,0 @@
|
||||
/* include/curl/curlbuild.h. Generated from curlbuild.h.in by configure. */
|
||||
#ifndef __CURL_CURLBUILD_H
|
||||
#define __CURL_CURLBUILD_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* ================================================================ */
|
||||
/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */
|
||||
/* ================================================================ */
|
||||
|
||||
/*
|
||||
* NOTE 1:
|
||||
* -------
|
||||
*
|
||||
* Nothing in this file is intended to be modified or adjusted by the
|
||||
* curl library user nor by the curl library builder.
|
||||
*
|
||||
* If you think that something actually needs to be changed, adjusted
|
||||
* or fixed in this file, then, report it on the libcurl development
|
||||
* mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/
|
||||
*
|
||||
* This header file shall only export symbols which are 'curl' or 'CURL'
|
||||
* prefixed, otherwise public name space would be polluted.
|
||||
*
|
||||
* NOTE 2:
|
||||
* -------
|
||||
*
|
||||
* Right now you might be staring at file include/curl/curlbuild.h.in or
|
||||
* at file include/curl/curlbuild.h, this is due to the following reason:
|
||||
*
|
||||
* On systems capable of running the configure script, the configure process
|
||||
* will overwrite the distributed include/curl/curlbuild.h file with one that
|
||||
* is suitable and specific to the library being configured and built, which
|
||||
* is generated from the include/curl/curlbuild.h.in template file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* ================================================================ */
|
||||
/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */
|
||||
/* ================================================================ */
|
||||
|
||||
#ifdef CURL_SIZEOF_LONG
|
||||
# error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_TYPEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SIZEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_TYPEOF_CURL_OFF_T
|
||||
# error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_FORMAT_CURL_OFF_T
|
||||
# error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_FORMAT_CURL_OFF_TU
|
||||
# error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_FORMAT_OFF_T
|
||||
# error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SIZEOF_CURL_OFF_T
|
||||
# error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SUFFIX_CURL_OFF_T
|
||||
# error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined
|
||||
#endif
|
||||
|
||||
#ifdef CURL_SUFFIX_CURL_OFF_TU
|
||||
# error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined
|
||||
#endif
|
||||
|
||||
/* ================================================================ */
|
||||
/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */
|
||||
/* ================================================================ */
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file ws2tcpip.h must be included by the external interface. */
|
||||
#define CURL_PULL_WS2TCPIP_H 1
|
||||
#ifdef CURL_PULL_WS2TCPIP_H
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file sys/types.h must be included by the external interface. */
|
||||
#define CURL_PULL_SYS_TYPES_H 1
|
||||
#ifdef CURL_PULL_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file stdint.h must be included by the external interface. */
|
||||
#define CURL_PULL_STDINT_H 1
|
||||
#ifdef CURL_PULL_STDINT_H
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file inttypes.h must be included by the external interface. */
|
||||
#define CURL_PULL_INTTYPES_H 1
|
||||
#ifdef CURL_PULL_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
|
||||
/* Configure process defines this to 1 when it finds out that system */
|
||||
/* header file sys/socket.h must be included by the external interface. */
|
||||
/* #undef CURL_PULL_SYS_SOCKET_H */
|
||||
#ifdef CURL_PULL_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define CURL_SIZEOF_LONG 4
|
||||
|
||||
/* Integral data type used for curl_socklen_t. */
|
||||
#define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
|
||||
|
||||
/* The size of `curl_socklen_t', as computed by sizeof. */
|
||||
#define CURL_SIZEOF_CURL_SOCKLEN_T 4
|
||||
|
||||
/* Data type definition of curl_socklen_t. */
|
||||
typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t;
|
||||
|
||||
/* Signed integral data type used for curl_off_t. */
|
||||
#define CURL_TYPEOF_CURL_OFF_T int64_t
|
||||
|
||||
/* Data type definition of curl_off_t. */
|
||||
typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
|
||||
|
||||
/* curl_off_t formatting string directive without "%" conversion specifier. */
|
||||
#define CURL_FORMAT_CURL_OFF_T "I64d"
|
||||
|
||||
/* unsigned curl_off_t formatting string without "%" conversion specifier. */
|
||||
#define CURL_FORMAT_CURL_OFF_TU "I64u"
|
||||
|
||||
/* curl_off_t formatting string directive with "%" conversion specifier. */
|
||||
#define CURL_FORMAT_OFF_T "%I64d"
|
||||
|
||||
/* The size of `curl_off_t', as computed by sizeof. */
|
||||
#define CURL_SIZEOF_CURL_OFF_T 8
|
||||
|
||||
/* curl_off_t constant suffix. */
|
||||
#define CURL_SUFFIX_CURL_OFF_T LL
|
||||
|
||||
/* unsigned curl_off_t constant suffix. */
|
||||
#define CURL_SUFFIX_CURL_OFF_TU ULL
|
||||
|
||||
#endif /* __CURL_CURLBUILD_H */
|
||||
@@ -1,261 +0,0 @@
|
||||
#ifndef __CURL_CURLRULES_H
|
||||
#define __CURL_CURLRULES_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* ================================================================ */
|
||||
/* COMPILE TIME SANITY CHECKS */
|
||||
/* ================================================================ */
|
||||
|
||||
/*
|
||||
* NOTE 1:
|
||||
* -------
|
||||
*
|
||||
* All checks done in this file are intentionally placed in a public
|
||||
* header file which is pulled by curl/curl.h when an application is
|
||||
* being built using an already built libcurl library. Additionally
|
||||
* this file is also included and used when building the library.
|
||||
*
|
||||
* If compilation fails on this file it is certainly sure that the
|
||||
* problem is elsewhere. It could be a problem in the curlbuild.h
|
||||
* header file, or simply that you are using different compilation
|
||||
* settings than those used to build the library.
|
||||
*
|
||||
* Nothing in this file is intended to be modified or adjusted by the
|
||||
* curl library user nor by the curl library builder.
|
||||
*
|
||||
* Do not deactivate any check, these are done to make sure that the
|
||||
* library is properly built and used.
|
||||
*
|
||||
* You can find further help on the libcurl development mailing list:
|
||||
* http://cool.haxx.se/mailman/listinfo/curl-library/
|
||||
*
|
||||
* NOTE 2
|
||||
* ------
|
||||
*
|
||||
* Some of the following compile time checks are based on the fact
|
||||
* that the dimension of a constant array can not be a negative one.
|
||||
* In this way if the compile time verification fails, the compilation
|
||||
* will fail issuing an error. The error description wording is compiler
|
||||
* dependent but it will be quite similar to one of the following:
|
||||
*
|
||||
* "negative subscript or subscript is too large"
|
||||
* "array must have at least one element"
|
||||
* "-1 is an illegal array size"
|
||||
* "size of array is negative"
|
||||
*
|
||||
* If you are building an application which tries to use an already
|
||||
* built libcurl library and you are getting this kind of errors on
|
||||
* this file, it is a clear indication that there is a mismatch between
|
||||
* how the library was built and how you are trying to use it for your
|
||||
* application. Your already compiled or binary library provider is the
|
||||
* only one who can give you the details you need to properly use it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Verify that some macros are actually defined.
|
||||
*/
|
||||
|
||||
#ifndef CURL_SIZEOF_LONG
|
||||
# error "CURL_SIZEOF_LONG definition is missing!"
|
||||
Error Compilation_aborted_CURL_SIZEOF_LONG_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_TYPEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_TYPEOF_CURL_SOCKLEN_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SIZEOF_CURL_SOCKLEN_T
|
||||
# error "CURL_SIZEOF_CURL_SOCKLEN_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_TYPEOF_CURL_OFF_T
|
||||
# error "CURL_TYPEOF_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_FORMAT_CURL_OFF_T
|
||||
# error "CURL_FORMAT_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_FORMAT_CURL_OFF_TU
|
||||
# error "CURL_FORMAT_CURL_OFF_TU definition is missing!"
|
||||
Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_FORMAT_OFF_T
|
||||
# error "CURL_FORMAT_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_FORMAT_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SIZEOF_CURL_OFF_T
|
||||
# error "CURL_SIZEOF_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SUFFIX_CURL_OFF_T
|
||||
# error "CURL_SUFFIX_CURL_OFF_T definition is missing!"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_is_missing
|
||||
#endif
|
||||
|
||||
#ifndef CURL_SUFFIX_CURL_OFF_TU
|
||||
# error "CURL_SUFFIX_CURL_OFF_TU definition is missing!"
|
||||
Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_is_missing
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros private to this header file.
|
||||
*/
|
||||
|
||||
#define CurlchkszEQ(t, s) sizeof(t) == s ? 1 : -1
|
||||
|
||||
#define CurlchkszGE(t1, t2) sizeof(t1) >= sizeof(t2) ? 1 : -1
|
||||
|
||||
/*
|
||||
* Verify that the size previously defined and expected for long
|
||||
* is the same as the one reported by sizeof() at compile time.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_01__
|
||||
[CurlchkszEQ(long, CURL_SIZEOF_LONG)];
|
||||
|
||||
/*
|
||||
* Verify that the size previously defined and expected for
|
||||
* curl_off_t is actually the the same as the one reported
|
||||
* by sizeof() at compile time.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_02__
|
||||
[CurlchkszEQ(curl_off_t, CURL_SIZEOF_CURL_OFF_T)];
|
||||
|
||||
/*
|
||||
* Verify at compile time that the size of curl_off_t as reported
|
||||
* by sizeof() is greater or equal than the one reported for long
|
||||
* for the current compilation.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_03__
|
||||
[CurlchkszGE(curl_off_t, long)];
|
||||
|
||||
/*
|
||||
* Verify that the size previously defined and expected for
|
||||
* curl_socklen_t is actually the the same as the one reported
|
||||
* by sizeof() at compile time.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_04__
|
||||
[CurlchkszEQ(curl_socklen_t, CURL_SIZEOF_CURL_SOCKLEN_T)];
|
||||
|
||||
/*
|
||||
* Verify at compile time that the size of curl_socklen_t as reported
|
||||
* by sizeof() is greater or equal than the one reported for int for
|
||||
* the current compilation.
|
||||
*/
|
||||
|
||||
typedef char
|
||||
__curl_rule_05__
|
||||
[CurlchkszGE(curl_socklen_t, int)];
|
||||
|
||||
/* ================================================================ */
|
||||
/* EXTERNALLY AND INTERNALLY VISIBLE DEFINITIONS */
|
||||
/* ================================================================ */
|
||||
|
||||
/*
|
||||
* CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow
|
||||
* these to be visible and exported by the external libcurl interface API,
|
||||
* while also making them visible to the library internals, simply including
|
||||
* setup.h, without actually needing to include curl.h internally.
|
||||
* If some day this section would grow big enough, all this should be moved
|
||||
* to its own header file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Figure out if we can use the ## preprocessor operator, which is supported
|
||||
* by ISO/ANSI C and C++. Some compilers support it without setting __STDC__
|
||||
* or __cplusplus so we need to carefully check for them too.
|
||||
*/
|
||||
|
||||
#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
|
||||
defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
|
||||
defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \
|
||||
defined(__ILEC400__)
|
||||
/* This compiler is believed to have an ISO compatible preprocessor */
|
||||
#define CURL_ISOCPP
|
||||
#else
|
||||
/* This compiler is believed NOT to have an ISO compatible preprocessor */
|
||||
#undef CURL_ISOCPP
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros for minimum-width signed and unsigned curl_off_t integer constants.
|
||||
*/
|
||||
|
||||
#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
|
||||
# define __CURL_OFF_T_C_HLPR2(x) x
|
||||
# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)
|
||||
# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
|
||||
__CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
|
||||
# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
|
||||
__CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
|
||||
#else
|
||||
# ifdef CURL_ISOCPP
|
||||
# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
|
||||
# else
|
||||
# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
|
||||
# endif
|
||||
# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)
|
||||
# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
|
||||
# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get rid of macros private to this header file.
|
||||
*/
|
||||
|
||||
#undef CurlchkszEQ
|
||||
#undef CurlchkszGE
|
||||
|
||||
/*
|
||||
* Get rid of macros not intended to exist beyond this point.
|
||||
*/
|
||||
|
||||
#undef CURL_PULL_WS2TCPIP_H
|
||||
#undef CURL_PULL_SYS_TYPES_H
|
||||
#undef CURL_PULL_SYS_SOCKET_H
|
||||
#undef CURL_PULL_STDINT_H
|
||||
#undef CURL_PULL_INTTYPES_H
|
||||
|
||||
#undef CURL_TYPEOF_CURL_SOCKLEN_T
|
||||
#undef CURL_TYPEOF_CURL_OFF_T
|
||||
|
||||
#ifdef CURL_NO_OLDIES
|
||||
#undef CURL_FORMAT_OFF_T /* not required since 7.19.0 - obsoleted in 7.20.0 */
|
||||
#endif
|
||||
|
||||
#endif /* __CURL_CURLRULES_H */
|
||||
@@ -1 +0,0 @@
|
||||
/* not used */
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+94
-94
@@ -1,94 +1,94 @@
|
||||
# CMake project definition file.
|
||||
project(libgme)
|
||||
|
||||
include (CheckCXXCompilerFlag)
|
||||
|
||||
# When version is changed, also change the one in gme/gme.h to match
|
||||
set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version")
|
||||
|
||||
# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
|
||||
# Of course, 2.4 might work, in which case you're welcome to drop
|
||||
# down the requirement, but I can't test that.
|
||||
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
|
||||
|
||||
# Default emulators to build (all of them! ;)
|
||||
if (NOT DEFINED USE_GME_AY)
|
||||
SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GBS)
|
||||
SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GYM)
|
||||
SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_HES)
|
||||
SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_KSS)
|
||||
SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSF)
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSFE)
|
||||
SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SAP)
|
||||
SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SPC)
|
||||
SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_VGM)
|
||||
SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation")
|
||||
endif()
|
||||
|
||||
if (USE_GME_NSFE AND NOT USE_GME_NSF)
|
||||
MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --")
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
|
||||
|
||||
# Check for GCC "visibility" support.
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY)
|
||||
set (ENABLE_VISIBILITY OFF)
|
||||
if (__LIBGME_TEST_VISIBILITY)
|
||||
# get the gcc version
|
||||
exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info)
|
||||
string (REGEX MATCH "[3-9]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
|
||||
|
||||
# gcc <4.1 had poor support for symbol visibility
|
||||
if ((${_gcc_version} VERSION_GREATER "4.1") OR (${_gcc_version} VERSION_EQUAL "4.1"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
|
||||
set (ENABLE_VISIBILITY ON)
|
||||
add_definitions (-DLIBGME_VISIBILITY)
|
||||
|
||||
# GCC >= 4.2 also correctly supports making inline members have hidden
|
||||
# visibility by default.
|
||||
if ((${_gcc_version} VERSION_GREATER "4.2") OR (${_gcc_version} VERSION_EQUAL "4.2"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
|
||||
endif()
|
||||
endif()
|
||||
endif() # test visibility
|
||||
|
||||
# Cache this result
|
||||
set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
|
||||
endif (CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
# Shared library defined here
|
||||
add_subdirectory(gme)
|
||||
|
||||
# EXCLUDE_FROM_ALL adds build rules but keeps it out of default build
|
||||
add_subdirectory(player EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(demo EXCLUDE_FROM_ALL)
|
||||
# CMake project definition file.
|
||||
project(libgme)
|
||||
|
||||
include (CheckCXXCompilerFlag)
|
||||
|
||||
# When version is changed, also change the one in gme/gme.h to match
|
||||
set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version")
|
||||
|
||||
# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
|
||||
# Of course, 2.4 might work, in which case you're welcome to drop
|
||||
# down the requirement, but I can't test that.
|
||||
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
|
||||
|
||||
# Default emulators to build (all of them! ;)
|
||||
if (NOT DEFINED USE_GME_AY)
|
||||
SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GBS)
|
||||
SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_GYM)
|
||||
SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_HES)
|
||||
SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_KSS)
|
||||
SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSF)
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_NSFE)
|
||||
SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SAP)
|
||||
SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_SPC)
|
||||
SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED USE_GME_VGM)
|
||||
SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation")
|
||||
endif()
|
||||
|
||||
if (USE_GME_NSFE AND NOT USE_GME_NSF)
|
||||
MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --")
|
||||
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
|
||||
|
||||
# Check for GCC "visibility" support.
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY)
|
||||
set (ENABLE_VISIBILITY OFF)
|
||||
if (__LIBGME_TEST_VISIBILITY)
|
||||
# get the gcc version
|
||||
exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info)
|
||||
string (REGEX MATCH "[3-9]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
|
||||
|
||||
# gcc <4.1 had poor support for symbol visibility
|
||||
if ((${_gcc_version} VERSION_GREATER "4.1") OR (${_gcc_version} VERSION_EQUAL "4.1"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
|
||||
set (ENABLE_VISIBILITY ON)
|
||||
add_definitions (-DLIBGME_VISIBILITY)
|
||||
|
||||
# GCC >= 4.2 also correctly supports making inline members have hidden
|
||||
# visibility by default.
|
||||
if ((${_gcc_version} VERSION_GREATER "4.2") OR (${_gcc_version} VERSION_EQUAL "4.2"))
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
|
||||
endif()
|
||||
endif()
|
||||
endif() # test visibility
|
||||
|
||||
# Cache this result
|
||||
set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
|
||||
endif (CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
# Shared library defined here
|
||||
add_subdirectory(gme)
|
||||
|
||||
# EXCLUDE_FROM_ALL adds build rules but keeps it out of default build
|
||||
add_subdirectory(player EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(demo EXCLUDE_FROM_ALL)
|
||||
@@ -1,5 +1,5 @@
|
||||
Game_Music_Emu Change Log
|
||||
-------------------------
|
||||
|
||||
Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
|
||||
for the accurate change log.
|
||||
Game_Music_Emu Change Log
|
||||
-------------------------
|
||||
|
||||
Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
|
||||
for the accurate change log.
|
||||
+194
-194
@@ -1,194 +1,194 @@
|
||||
Game_Music_Emu 0.6.0 Design
|
||||
---------------------------
|
||||
This might be slightly out-of-date at times, but will be a big help in
|
||||
understanding the library implementation.
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
The library is essentially a bunch of independent game music file
|
||||
emulators unified with a common interface.
|
||||
|
||||
Gme_File and Music_Emu provide a common interface to the emulators. The
|
||||
virtual functions are protected rather than public to allow pre- and
|
||||
post-processing of arguments and data in one place. This allows the
|
||||
emulator classes to assume that everything is set up properly when
|
||||
starting a track and playing samples.
|
||||
|
||||
All file input is done with the Data_Reader interface. Many derived
|
||||
classes are present, for the usual disk-based file and block of memory,
|
||||
to specialized adaptors for things like reading a subset of data or
|
||||
combining a block of memory with a Data_Reader to the remaining data.
|
||||
This makes the library much more flexible with regard to the source of
|
||||
game music file data. I still added a specialized load_mem() function to
|
||||
have the emulator keep a pointer to data already read in memory, for
|
||||
those formats whose files can be absolutely huge (GYM, some VGMs). This
|
||||
is important if for some reason the caller must load the data ahead of
|
||||
time, but doesn't want the emulator needlessly making a copy.
|
||||
|
||||
Since silence checking and fading are relatively complex, they are kept
|
||||
separate from basic file loading and track information, which are
|
||||
handled in the base class Gme_File. My original intent was to use
|
||||
Gme_File as the common base class for full emulators and track
|
||||
information-only readers, but implementing the C interface was much
|
||||
simpler if both derived from Music_Emu. User C++ code can still benefit
|
||||
from static checking by using Gme_File where only track information will
|
||||
be accessed.
|
||||
|
||||
Each emulator generally has three components: main emulator, CPU
|
||||
emulator, and sound chip emulator(s). Each component has minimal
|
||||
coupling, so use in a full emulator or stand alone is fairly easy. This
|
||||
modularity really helps reduce complexity. Blip_Buffer helps a lot with
|
||||
simplifying the APU interfaces and implementation.
|
||||
|
||||
The "classic" emulators derive from Classic_Emu, which handles
|
||||
Blip_Buffer filling and multiple channels. It uses Multi_Buffer for
|
||||
output, allowing you to derive a custom buffer that could output each
|
||||
voice to a separate sound channel and do different processing on each.
|
||||
At some point I'm going to implement a better Effects_Buffer that allows
|
||||
individual control of every channel.
|
||||
|
||||
In implementing the C interface, I wanted a way to specify an emulator
|
||||
type that didn't require linking in all the emulators. For each emulator
|
||||
type there is a global object with pointers to functions to create the
|
||||
emulator or a track information reader. The emulator type is thus a
|
||||
pointer to this, which conveniently allows for a NULL value. The user
|
||||
referencing this emulator type object is what ultimately links the
|
||||
emulator in (unless new Foo_Emu is used in C++, of course). This type
|
||||
also serves as a useful substitute for RTTI on older C++ compilers.
|
||||
|
||||
Addendum: I have since added gme_type_list(), which causes all listed
|
||||
emulators to be linked in. To avoid this, I make the list itself
|
||||
editable in blargg_config.h. Having a built-in list allows
|
||||
gme_load_file() to take a path and give back an emulator with the file
|
||||
loaded, which is extremely useful for new users.
|
||||
|
||||
|
||||
Interface conventions
|
||||
----------------------
|
||||
If a function retains a pointer to or replaces the value of an object
|
||||
passed, it takes a pointer so that it will be clear in the caller's
|
||||
source code that care is required.
|
||||
|
||||
Multi-word names have an underscore '_' separator between individual
|
||||
words.
|
||||
|
||||
Functions are named with lowercase words. Functions which perform an
|
||||
action with side-effects are named with a verb phrase (i.e. load, move,
|
||||
run). Functions which return the value of a piece of state are named
|
||||
using a noun phrase (i.e. loaded, moved, running).
|
||||
|
||||
Classes are named with capitalized words. Only the first letter of an
|
||||
acronym is capitalized. Class names are nouns, sometimes suggestive of
|
||||
what they do (i.e. File_Scanner).
|
||||
|
||||
Structure, enumeration, and typedefs to these and built-in types are
|
||||
named using lowercase words with a _t suffix.
|
||||
|
||||
Macros are named with all-uppercase words.
|
||||
|
||||
Internal names which can't be hidden due to technical reasons have an
|
||||
underscore '_' suffix.
|
||||
|
||||
|
||||
Managing Complexity
|
||||
-------------------
|
||||
Complexity has been a factor in most library decisions. Many features
|
||||
have been passed by due to the complexity they would add. Once
|
||||
complexity goes past a certain level, it mentally grasping the library
|
||||
in its entirety, at which point more defects will occur and be hard to
|
||||
find.
|
||||
|
||||
I chose 16-bit signed samples because it seems to be the most common
|
||||
format. Supporting multiple formats would add too much complexity to be
|
||||
worth it. Other formats can be obtained via conversion.
|
||||
|
||||
I've kept interfaces fairly lean, leaving many possible features
|
||||
untapped but easy to add if necessary. For example the classic emulators
|
||||
could have volume and frequency equalization adjusted separately for
|
||||
each channel, since they each have an associated Blip_Synth.
|
||||
|
||||
Source files of 400 lines or less seem to be the best size to limit
|
||||
complexity. In a few cases there is no reasonable way to split longer
|
||||
files, or there is benefit from having the source together in one file.
|
||||
|
||||
|
||||
Preventing Bugs
|
||||
---------------
|
||||
I've done many things to reduce the opportunity for defects. A general
|
||||
principle is to write code so that defects will be as visible as
|
||||
possible. I've used several techniques to achieve this.
|
||||
|
||||
I put assertions at key points where defects seem likely or where
|
||||
corruption due to a defect is likely to be visible. I've also put
|
||||
assertions where violations of the interface are likely. In emulators
|
||||
where I am unsure of exact hardware operation in a particular case, I
|
||||
output a debug-only message noting that this has occurred; many times I
|
||||
haven't implemented a hardware feature because nothing uses it. I've
|
||||
made code brittle where there is no clear reason flexibility; code
|
||||
written to handle every possibility sacrifices quality and reliability
|
||||
to handle vaguely defined situations.
|
||||
|
||||
|
||||
Flexibility through indirection
|
||||
-------------------------------
|
||||
I've tried to allow the most flexibility of modules by using indirection
|
||||
to allow extension by the user. This keeps each module simpler and more
|
||||
focused on its unique task.
|
||||
|
||||
The classic emulators use Multi_Buffer, which potentially allows a
|
||||
separate Blip_Buffer for each channel. This keeps emulators free of
|
||||
typical code to allow output in mono, stereo, panning, etc.
|
||||
|
||||
All emulators use a reader object to access file data, allowing it to be
|
||||
stored in a regular file, compressed archive, memory, or generated
|
||||
on-the-fly. Again, the library can be kept free of the particulars of
|
||||
file access and changes required to support new formats.
|
||||
|
||||
|
||||
Emulators in general
|
||||
--------------------
|
||||
When I wrote the first NES sound emulator, I stored most of the state in
|
||||
an emulator-specific format, with significant redundancy. In the
|
||||
register write function I decoded everything into named variables. I
|
||||
became tired of the verbosity and wanted to more closely model the
|
||||
hardware, so I moved to a style of storing the last written value to
|
||||
each register, along with as little other state as possible, mostly the
|
||||
internal hardware registers. While this involves slightly more
|
||||
recalculation, in most cases the emulation code is of comparable size.
|
||||
It also makes state save/restore (for use in a full emulator) much
|
||||
simpler. Finally, it makes debugging easier since the hardware registers
|
||||
used in emulation are obvious.
|
||||
|
||||
|
||||
CPU Cores
|
||||
---------
|
||||
I've spent lots of time coming up with techniques to optimize the CPU
|
||||
cores. Some of the most important: execute multiple instructions during
|
||||
an emulation call, keep state in local variables to allow register
|
||||
assignment, optimize state representation for most common instructions,
|
||||
defer status flag calculation until actually needed, read program code
|
||||
directly without a call to the memory read function, always pre-fetch
|
||||
the operand byte before decoding instruction, and emulate instructions
|
||||
using common blocks of code.
|
||||
|
||||
I've successfully used Nes_Cpu in a fairly complete NES emulator, and
|
||||
I'd like to make all the CPU emulators suitable for use in emulators. It
|
||||
seems a waste for them to be used only for the small amount of emulation
|
||||
necessary for game music files.
|
||||
|
||||
I debugged the CPU cores by writing a test shell that ran them in
|
||||
parallel with other CPU cores and compared all memory accesses and
|
||||
processor states at each step. This provided good value at little cost.
|
||||
|
||||
The CPU mapping page size is adjustable to allow the best tradeoff
|
||||
between memory/cache usage and handler granularity. The interface allows
|
||||
code to be somewhat independent of the page size.
|
||||
|
||||
I optimize program memory accesses to direct reads rather than calls to
|
||||
the memory read function. My assumption is that it would be difficult to
|
||||
get useful code out of hardware I/O addresses, so no software will
|
||||
intentionally execute out of I/O space. Since the page size can be
|
||||
changed easily, most program memory mapping schemes can be accommodated.
|
||||
This greatly reduces memory access function calls.
|
||||
|
||||
Game_Music_Emu 0.6.0 Design
|
||||
---------------------------
|
||||
This might be slightly out-of-date at times, but will be a big help in
|
||||
understanding the library implementation.
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
The library is essentially a bunch of independent game music file
|
||||
emulators unified with a common interface.
|
||||
|
||||
Gme_File and Music_Emu provide a common interface to the emulators. The
|
||||
virtual functions are protected rather than public to allow pre- and
|
||||
post-processing of arguments and data in one place. This allows the
|
||||
emulator classes to assume that everything is set up properly when
|
||||
starting a track and playing samples.
|
||||
|
||||
All file input is done with the Data_Reader interface. Many derived
|
||||
classes are present, for the usual disk-based file and block of memory,
|
||||
to specialized adaptors for things like reading a subset of data or
|
||||
combining a block of memory with a Data_Reader to the remaining data.
|
||||
This makes the library much more flexible with regard to the source of
|
||||
game music file data. I still added a specialized load_mem() function to
|
||||
have the emulator keep a pointer to data already read in memory, for
|
||||
those formats whose files can be absolutely huge (GYM, some VGMs). This
|
||||
is important if for some reason the caller must load the data ahead of
|
||||
time, but doesn't want the emulator needlessly making a copy.
|
||||
|
||||
Since silence checking and fading are relatively complex, they are kept
|
||||
separate from basic file loading and track information, which are
|
||||
handled in the base class Gme_File. My original intent was to use
|
||||
Gme_File as the common base class for full emulators and track
|
||||
information-only readers, but implementing the C interface was much
|
||||
simpler if both derived from Music_Emu. User C++ code can still benefit
|
||||
from static checking by using Gme_File where only track information will
|
||||
be accessed.
|
||||
|
||||
Each emulator generally has three components: main emulator, CPU
|
||||
emulator, and sound chip emulator(s). Each component has minimal
|
||||
coupling, so use in a full emulator or stand alone is fairly easy. This
|
||||
modularity really helps reduce complexity. Blip_Buffer helps a lot with
|
||||
simplifying the APU interfaces and implementation.
|
||||
|
||||
The "classic" emulators derive from Classic_Emu, which handles
|
||||
Blip_Buffer filling and multiple channels. It uses Multi_Buffer for
|
||||
output, allowing you to derive a custom buffer that could output each
|
||||
voice to a separate sound channel and do different processing on each.
|
||||
At some point I'm going to implement a better Effects_Buffer that allows
|
||||
individual control of every channel.
|
||||
|
||||
In implementing the C interface, I wanted a way to specify an emulator
|
||||
type that didn't require linking in all the emulators. For each emulator
|
||||
type there is a global object with pointers to functions to create the
|
||||
emulator or a track information reader. The emulator type is thus a
|
||||
pointer to this, which conveniently allows for a NULL value. The user
|
||||
referencing this emulator type object is what ultimately links the
|
||||
emulator in (unless new Foo_Emu is used in C++, of course). This type
|
||||
also serves as a useful substitute for RTTI on older C++ compilers.
|
||||
|
||||
Addendum: I have since added gme_type_list(), which causes all listed
|
||||
emulators to be linked in. To avoid this, I make the list itself
|
||||
editable in blargg_config.h. Having a built-in list allows
|
||||
gme_load_file() to take a path and give back an emulator with the file
|
||||
loaded, which is extremely useful for new users.
|
||||
|
||||
|
||||
Interface conventions
|
||||
----------------------
|
||||
If a function retains a pointer to or replaces the value of an object
|
||||
passed, it takes a pointer so that it will be clear in the caller's
|
||||
source code that care is required.
|
||||
|
||||
Multi-word names have an underscore '_' separator between individual
|
||||
words.
|
||||
|
||||
Functions are named with lowercase words. Functions which perform an
|
||||
action with side-effects are named with a verb phrase (i.e. load, move,
|
||||
run). Functions which return the value of a piece of state are named
|
||||
using a noun phrase (i.e. loaded, moved, running).
|
||||
|
||||
Classes are named with capitalized words. Only the first letter of an
|
||||
acronym is capitalized. Class names are nouns, sometimes suggestive of
|
||||
what they do (i.e. File_Scanner).
|
||||
|
||||
Structure, enumeration, and typedefs to these and built-in types are
|
||||
named using lowercase words with a _t suffix.
|
||||
|
||||
Macros are named with all-uppercase words.
|
||||
|
||||
Internal names which can't be hidden due to technical reasons have an
|
||||
underscore '_' suffix.
|
||||
|
||||
|
||||
Managing Complexity
|
||||
-------------------
|
||||
Complexity has been a factor in most library decisions. Many features
|
||||
have been passed by due to the complexity they would add. Once
|
||||
complexity goes past a certain level, it mentally grasping the library
|
||||
in its entirety, at which point more defects will occur and be hard to
|
||||
find.
|
||||
|
||||
I chose 16-bit signed samples because it seems to be the most common
|
||||
format. Supporting multiple formats would add too much complexity to be
|
||||
worth it. Other formats can be obtained via conversion.
|
||||
|
||||
I've kept interfaces fairly lean, leaving many possible features
|
||||
untapped but easy to add if necessary. For example the classic emulators
|
||||
could have volume and frequency equalization adjusted separately for
|
||||
each channel, since they each have an associated Blip_Synth.
|
||||
|
||||
Source files of 400 lines or less seem to be the best size to limit
|
||||
complexity. In a few cases there is no reasonable way to split longer
|
||||
files, or there is benefit from having the source together in one file.
|
||||
|
||||
|
||||
Preventing Bugs
|
||||
---------------
|
||||
I've done many things to reduce the opportunity for defects. A general
|
||||
principle is to write code so that defects will be as visible as
|
||||
possible. I've used several techniques to achieve this.
|
||||
|
||||
I put assertions at key points where defects seem likely or where
|
||||
corruption due to a defect is likely to be visible. I've also put
|
||||
assertions where violations of the interface are likely. In emulators
|
||||
where I am unsure of exact hardware operation in a particular case, I
|
||||
output a debug-only message noting that this has occurred; many times I
|
||||
haven't implemented a hardware feature because nothing uses it. I've
|
||||
made code brittle where there is no clear reason flexibility; code
|
||||
written to handle every possibility sacrifices quality and reliability
|
||||
to handle vaguely defined situations.
|
||||
|
||||
|
||||
Flexibility through indirection
|
||||
-------------------------------
|
||||
I've tried to allow the most flexibility of modules by using indirection
|
||||
to allow extension by the user. This keeps each module simpler and more
|
||||
focused on its unique task.
|
||||
|
||||
The classic emulators use Multi_Buffer, which potentially allows a
|
||||
separate Blip_Buffer for each channel. This keeps emulators free of
|
||||
typical code to allow output in mono, stereo, panning, etc.
|
||||
|
||||
All emulators use a reader object to access file data, allowing it to be
|
||||
stored in a regular file, compressed archive, memory, or generated
|
||||
on-the-fly. Again, the library can be kept free of the particulars of
|
||||
file access and changes required to support new formats.
|
||||
|
||||
|
||||
Emulators in general
|
||||
--------------------
|
||||
When I wrote the first NES sound emulator, I stored most of the state in
|
||||
an emulator-specific format, with significant redundancy. In the
|
||||
register write function I decoded everything into named variables. I
|
||||
became tired of the verbosity and wanted to more closely model the
|
||||
hardware, so I moved to a style of storing the last written value to
|
||||
each register, along with as little other state as possible, mostly the
|
||||
internal hardware registers. While this involves slightly more
|
||||
recalculation, in most cases the emulation code is of comparable size.
|
||||
It also makes state save/restore (for use in a full emulator) much
|
||||
simpler. Finally, it makes debugging easier since the hardware registers
|
||||
used in emulation are obvious.
|
||||
|
||||
|
||||
CPU Cores
|
||||
---------
|
||||
I've spent lots of time coming up with techniques to optimize the CPU
|
||||
cores. Some of the most important: execute multiple instructions during
|
||||
an emulation call, keep state in local variables to allow register
|
||||
assignment, optimize state representation for most common instructions,
|
||||
defer status flag calculation until actually needed, read program code
|
||||
directly without a call to the memory read function, always pre-fetch
|
||||
the operand byte before decoding instruction, and emulate instructions
|
||||
using common blocks of code.
|
||||
|
||||
I've successfully used Nes_Cpu in a fairly complete NES emulator, and
|
||||
I'd like to make all the CPU emulators suitable for use in emulators. It
|
||||
seems a waste for them to be used only for the small amount of emulation
|
||||
necessary for game music files.
|
||||
|
||||
I debugged the CPU cores by writing a test shell that ran them in
|
||||
parallel with other CPU cores and compared all memory accesses and
|
||||
processor states at each step. This provided good value at little cost.
|
||||
|
||||
The CPU mapping page size is adjustable to allow the best tradeoff
|
||||
between memory/cache usage and handler granularity. The interface allows
|
||||
code to be somewhat independent of the page size.
|
||||
|
||||
I optimize program memory accesses to direct reads rather than calls to
|
||||
the memory read function. My assumption is that it would be difficult to
|
||||
get useful code out of hardware I/O addresses, so no software will
|
||||
intentionally execute out of I/O space. Since the page size can be
|
||||
changed easily, most program memory mapping schemes can be accommodated.
|
||||
This greatly reduces memory access function calls.
|
||||
|
||||
+376
-376
@@ -1,376 +1,376 @@
|
||||
Game_Music_Emu 0.6.2
|
||||
--------------------
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Maintainer : Michael Pyne <mpyne@purinchu.net>
|
||||
Website : https://bitbucket.org/mpyne/game-music-emu/
|
||||
Source : https://bitbucket.org/mpyne/game-music-emu/
|
||||
License : GNU Lesser General Public License (LGPL), see LICENSE.txt
|
||||
|
||||
Contents
|
||||
--------
|
||||
* Overview
|
||||
* Error handling
|
||||
* Emulator types
|
||||
* M3U playlist support
|
||||
* Information fields
|
||||
* Track length
|
||||
* Loading file data
|
||||
* Sound parameters
|
||||
* VGM/GYM YM2413 & YM2612 FM sound
|
||||
* Modular construction
|
||||
* Obscure features
|
||||
* Solving problems
|
||||
* Thanks
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
This library can open game music files, play tracks, and read game and
|
||||
track information tags. To play a game music file, do the following:
|
||||
|
||||
* Open the file with gme_open_file()
|
||||
* Start a track with gme_start_track();
|
||||
* Generate samples as needed with gme_play()
|
||||
* Play samples through speaker using your operating system
|
||||
* Delete emulator when done with gme_delete()
|
||||
|
||||
Your code must arrange for the generated samples to be played through
|
||||
the computer's speaker using whatever method your operating system
|
||||
requires.
|
||||
|
||||
There are many additional features available; you can:
|
||||
|
||||
* Determine of the type of a music file without opening it with
|
||||
gme_identify_*()
|
||||
* Load just the file's information tags with gme_info_only
|
||||
* Load from a block of memory rather than a file with gme_load_data()
|
||||
* Arrange for a fade-out at a particular time with gme_set_fade
|
||||
* Find when a track has ended with gme_track_ended()
|
||||
* Seek to a new time in the track with gme_seek()
|
||||
* Load an extended m3u playlist with gme_load_m3u()
|
||||
* Get a list of the voices (channels) and mute them individually with
|
||||
gme_voice_names() and gme_mute_voice()
|
||||
* Change the playback tempo without affecting pitch with gme_set_tempo()
|
||||
* Adjust treble/bass equalization with gme_set_equalizer()
|
||||
* Associate your own data with an emulator and later get it back with
|
||||
gme_set_user_data()
|
||||
* Register a function of yours to be called back when the emulator is
|
||||
deleted with gme_set_user_cleanup()
|
||||
|
||||
Refer to gme.h for a comprehensive summary of features.
|
||||
|
||||
|
||||
Error handling
|
||||
--------------
|
||||
Functions which can fail have a return type of gme_err_t, which is a
|
||||
pointer to an error string (const char*). If a function is successful it
|
||||
returns NULL. Errors that you can easily avoid are checked with debug
|
||||
assertions; gme_err_t return values are only used for genuine run-time
|
||||
errors that can't be easily predicted in advance (out of memory, I/O
|
||||
errors, incompatible file data). Your code should check all error
|
||||
values.
|
||||
|
||||
When loading a music file in the wrong emulator or trying to load a
|
||||
non-music file, gme_wrong_file_type is returned. You can check for this
|
||||
error in C++ like this:
|
||||
|
||||
gme_err_t err = gme_open_file( path, &emu );
|
||||
if ( err == gme_wrong_file_type )
|
||||
...
|
||||
|
||||
To check for minor problems, call gme_warning() to get a string
|
||||
describing the last warning. Your player should allow the user some way
|
||||
of knowing when this is the case, since these minor errors could affect
|
||||
playback. Without this information the user can't solve problems as
|
||||
well. When playing a track, gme_warning() returns minor playback-related
|
||||
problems (major playback problems end the track immediately and set the
|
||||
warning string).
|
||||
|
||||
|
||||
Emulator types
|
||||
--------------
|
||||
The library includes several game music emulators that each support a
|
||||
different file type. Each is identified by a gme_type_t constant defined
|
||||
in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use
|
||||
gme_open_file() or gme_open_data(), the library does the work of
|
||||
determining the file type and creating an appropriate emulator. If you
|
||||
want more control over this process, read on.
|
||||
|
||||
There are two basic ways to identify a game music file's type: look at
|
||||
its file extension, or read the header data. The library includes
|
||||
functions to help with both methods. The first is preferable because it
|
||||
is fast and the most common way to identify files. Sometimes the
|
||||
extension is lost or wrong, so the header must be read.
|
||||
|
||||
Use gme_identify_extension() to find the correct game music type based
|
||||
on a filename. To identify a file based on its extension and header
|
||||
contents, use gme_identify_file(). If you read the header data yourself,
|
||||
use gme_identify_header().
|
||||
|
||||
If you want to remove support for some music types to reduce your
|
||||
executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to
|
||||
support just NSF and GBS, use this:
|
||||
|
||||
#define GME_TYPE_LIST \
|
||||
gme_nsf_type,\
|
||||
gme_gbs_type
|
||||
|
||||
|
||||
M3U playlist support
|
||||
--------------------
|
||||
The library supports playlists in an extended m3u format with
|
||||
gme_load_m3u() to give track names and times to multi-song formats: AY,
|
||||
GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format
|
||||
itself is not well-defined so some m3u files won't work properly
|
||||
(particularly those provided with KSS files). Only m3u files referencing
|
||||
a single file are supported; your code must handle m3u files covering
|
||||
more than one game music file, though it can use the built-in m3u
|
||||
parsing provided by the library.
|
||||
|
||||
|
||||
Information fields
|
||||
------------------
|
||||
Support is provided for the various text fields and length information
|
||||
in a file with gme_track_info(). If you just need track information for
|
||||
a file (for example, building a playlist), use gme_new_info() in place
|
||||
of gme_new_emu(), load the file normally, then you can access the track
|
||||
count and info, but nothing else.
|
||||
|
||||
M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS
|
||||
-------------------------------------------------------
|
||||
Track Count | * * * * * * * * *
|
||||
|
|
||||
System | * * * * * * * * * *
|
||||
|
|
||||
Game | * * * * * * *
|
||||
|
|
||||
Song | * * * * * * *
|
||||
|
|
||||
Author | * * * * * * * *
|
||||
|
|
||||
Copyright | * * * * * * * *
|
||||
|
|
||||
Comment | * * * *
|
||||
|
|
||||
Dumper | * * * *
|
||||
|
|
||||
Length | * * * * * *
|
||||
|
|
||||
Intro Length| * * *
|
||||
|
|
||||
Loop Length | * * *
|
||||
|
||||
As listed above, the HES and KSS file formats don't include a track
|
||||
count, and tracks are often scattered over the 0-255 range, so an m3u
|
||||
playlist for these is a must.
|
||||
|
||||
Unavailable text fields are set to an empty string and times to -1. Your
|
||||
code should be prepared for any combination of available and unavailable
|
||||
fields, as a particular music file might not use all of the supported
|
||||
fields listed above.
|
||||
|
||||
Currently text fields are truncated to 255 characters. Obscure fields of
|
||||
some formats are not currently decoded; contact me if you want one
|
||||
added.
|
||||
|
||||
|
||||
Track length
|
||||
------------
|
||||
The library leaves it up to you as to when to stop playing a track. You
|
||||
can ask for available length information and then tell the library what
|
||||
time it should start fading the track with gme_set_fade(). By default it
|
||||
also continually checks for 6 or more seconds of silence to mark the end
|
||||
of a track. Here is a reasonable algorithm you can use to decide how
|
||||
long to play a track:
|
||||
|
||||
* If the track length is > 0, use it
|
||||
* If the loop length > 0, play for intro + loop * 2
|
||||
* Otherwise, default to 2.5 minutes (150000 msec)
|
||||
|
||||
If you want to play a track longer than normal, be sure the loop length
|
||||
isn't zero. See Music_Player.cpp around line 145 for example code.
|
||||
|
||||
By default, the library skips silence at the beginning of a track. It
|
||||
also continually checks for the end of a non-looping track by watching
|
||||
for 6 seconds of unbroken silence. When doing this is scans *ahead* by
|
||||
several seconds so it can report the end of the track after only one
|
||||
second of silence has actually played. This feature can be disabled with
|
||||
gme_ignore_silence().
|
||||
|
||||
|
||||
Loading file data
|
||||
-----------------
|
||||
The library allows file data to be loaded in many different ways. All
|
||||
load functions return an error which you should check. The following
|
||||
examples assume these variables:
|
||||
|
||||
Music_Emu* emu;
|
||||
gme_err_t error;
|
||||
|
||||
If you're letting the library determine a file's type, you can use
|
||||
either gme_open_file() or gme_open_data():
|
||||
|
||||
error = gme_open_file( pathname, &emu );
|
||||
error = gme_open_data( pointer, size, &emu );
|
||||
|
||||
If you're manually determining file type and using used gme_new_emu() to
|
||||
create an emulator, you can use the following methods of loading:
|
||||
|
||||
* From a block of memory:
|
||||
|
||||
error = gme_load_data( emu, pointer, size );
|
||||
|
||||
* Have library call your function to read data:
|
||||
|
||||
gme_err_t my_read( void* my_data, void* out, long count )
|
||||
{
|
||||
// code that reads 'count' bytes into 'out' buffer
|
||||
// and return 0 if no error
|
||||
}
|
||||
|
||||
error = gme_load_custom( emu, my_read, file_size, my_data );
|
||||
|
||||
|
||||
Sound parameters
|
||||
----------------
|
||||
All emulators support an arbitrary output sampling rate. A rate of 44100
|
||||
Hz should work well on most systems. Since band-limited synthesis is
|
||||
used, a sampling rate above 48000 Hz is not necessary and will actually
|
||||
reduce sound quality and performance.
|
||||
|
||||
All emulators also support adjustable gain, mainly for the purpose of
|
||||
getting consistent volume between different music formats and avoiding
|
||||
excessive modulation. The gain can only be set *before* setting the
|
||||
emulator's sampling rate, so it's not useful as a general volume
|
||||
control. The default gains of emulators are set so that they give
|
||||
generally similar volumes, though some soundtracks are significantly
|
||||
louder or quieter than normal.
|
||||
|
||||
Some emulators support adjustable treble and bass frequency equalization
|
||||
(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer().
|
||||
Parameters are specified using gme_equalizer_t eq = { treble_dB,
|
||||
bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives
|
||||
normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble
|
||||
for an extra crisp sound. Bass_freq sets the frequency where bass
|
||||
response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass,
|
||||
and 15000 Hz removes all bass. For example, the following makes the
|
||||
sound extra-crisp but lacking bass:
|
||||
|
||||
gme_equalizer_t eq = { 5.0, 1000 };
|
||||
gme_set_equalizer( music_emu, &eq );
|
||||
|
||||
Each emulator's equalization defaults to approximate the particular
|
||||
console's sound quality; this default can be determined by calling
|
||||
equalizer() just after creating the emulator. The Music_Emu::tv_eq
|
||||
profile gives sound as if coming from a TV speaker, and some emulators
|
||||
include other profiles for different versions of the system. For
|
||||
example, to use Famicom sound equalization with the NSF emulator, do the
|
||||
following:
|
||||
|
||||
music_emu->set_equalizer( Nsf_Emu::famicom_eq );
|
||||
|
||||
|
||||
VGM/GYM YM2413 & YM2612 FM sound
|
||||
--------------------------------
|
||||
The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound
|
||||
chip emulator based on the Gens project. Because this has some
|
||||
inaccuracies, other YM2612 emulators can be used in its place by
|
||||
re-implementing the interface in YM2612_Emu.h. Available on my website
|
||||
is a modified version of MAME's YM2612 emulator, which sounds better in
|
||||
some ways and whose author is still making improvements.
|
||||
|
||||
VGM music files using the YM2413 FM sound chip are also supported, but a
|
||||
YM2413 emulator isn't included with the library due to technical
|
||||
reasons. I have put one of the available YM2413 emulators on my website
|
||||
that can be used directly.
|
||||
|
||||
|
||||
Modular construction
|
||||
--------------------
|
||||
The library is made of many fairly independent modules. If you're using
|
||||
only one music file emulator, you can eliminate many of the library
|
||||
sources from your program. Refer to the files list in readme.txt to get
|
||||
a general idea of what can be removed, and be sure to edit GME_TYPE_LIST
|
||||
(see "Emulator types" above). Post to the forum if you'd like me to put
|
||||
together a smaller version for a particular use, as this only takes me a
|
||||
few minutes to do.
|
||||
|
||||
If you want to use one of the individual sound chip emulators (or CPU
|
||||
cores) in your own console emulator, first check the libraries page on
|
||||
my website since I have released several of them as stand alone
|
||||
libraries with included documentation and examples on their use. If you
|
||||
don't find it as a standalone library, contact me and I'll consider
|
||||
separating it.
|
||||
|
||||
The "classic" sound chips use my Blip_Buffer library, which greatly
|
||||
simplifies their implementation and efficiently handles band-limited
|
||||
synthesis. It is also available as a stand alone library with
|
||||
documentation and many examples.
|
||||
|
||||
|
||||
Obscure features
|
||||
----------------
|
||||
The library's flexibility allows many possibilities. Contact me if you
|
||||
want help implementing ideas or removing limitations.
|
||||
|
||||
* Uses no global/static variables, allowing multiple instances of any
|
||||
emulator. This is useful in a music player if you want to allow
|
||||
simultaneous recording or scanning of other tracks while one is already
|
||||
playing. This will also be useful if your platform disallows global
|
||||
data.
|
||||
|
||||
* Emulators that support a custom sound buffer can have *every* voice
|
||||
routed to a different Blip_Buffer, allowing custom processing on each
|
||||
voice. For example you could record a Game Boy track as a 4-channel
|
||||
sound file.
|
||||
|
||||
* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive
|
||||
synthesis on "classic" emulators, which might help on some really old
|
||||
processors. This significantly lowers sound quality and prevents treble
|
||||
equalization. Try this if your platform's processor isn't fast enough
|
||||
for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces
|
||||
processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth
|
||||
the quality loss.
|
||||
|
||||
|
||||
Solving problems
|
||||
----------------
|
||||
If you're having problems, try the following:
|
||||
|
||||
* If you're getting garbled sound, try this simple siren generator in
|
||||
place of your call to play(). This will quickly tell whether the problem
|
||||
is in the library or in your code.
|
||||
|
||||
static void play_siren( long count, short* out )
|
||||
{
|
||||
static double a, a2;
|
||||
while ( count-- )
|
||||
*out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) );
|
||||
}
|
||||
|
||||
* Enable debugging support in your environment. This enables assertions
|
||||
and other run-time checks.
|
||||
|
||||
* Turn the compiler's optimizer is off. Sometimes an optimizer generates
|
||||
bad code.
|
||||
|
||||
* If multiple threads are being used, ensure that only one at a time is
|
||||
accessing a given set of objects from the library. This library is not
|
||||
in general thread-safe, though independent objects can be used in
|
||||
separate threads.
|
||||
|
||||
* If all else fails, see if the demos work.
|
||||
|
||||
|
||||
Thanks
|
||||
------
|
||||
Big thanks to Chris Moeller (kode54) for help with library testing and
|
||||
feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and
|
||||
for original work on openspc++ that was used when developing Spc_Emu.
|
||||
Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the
|
||||
start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz,
|
||||
nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using,
|
||||
and giving feedback for the library in their respective game music
|
||||
players. More recently, Lucas Paul and Michael Pyne have helped nudge the
|
||||
library into a public repository and get its interface more stable for use
|
||||
in shared libraries.
|
||||
Game_Music_Emu 0.6.2
|
||||
--------------------
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Maintainer : Michael Pyne <mpyne@purinchu.net>
|
||||
Website : https://bitbucket.org/mpyne/game-music-emu/
|
||||
Source : https://bitbucket.org/mpyne/game-music-emu/
|
||||
License : GNU Lesser General Public License (LGPL), see LICENSE.txt
|
||||
|
||||
Contents
|
||||
--------
|
||||
* Overview
|
||||
* Error handling
|
||||
* Emulator types
|
||||
* M3U playlist support
|
||||
* Information fields
|
||||
* Track length
|
||||
* Loading file data
|
||||
* Sound parameters
|
||||
* VGM/GYM YM2413 & YM2612 FM sound
|
||||
* Modular construction
|
||||
* Obscure features
|
||||
* Solving problems
|
||||
* Thanks
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
This library can open game music files, play tracks, and read game and
|
||||
track information tags. To play a game music file, do the following:
|
||||
|
||||
* Open the file with gme_open_file()
|
||||
* Start a track with gme_start_track();
|
||||
* Generate samples as needed with gme_play()
|
||||
* Play samples through speaker using your operating system
|
||||
* Delete emulator when done with gme_delete()
|
||||
|
||||
Your code must arrange for the generated samples to be played through
|
||||
the computer's speaker using whatever method your operating system
|
||||
requires.
|
||||
|
||||
There are many additional features available; you can:
|
||||
|
||||
* Determine of the type of a music file without opening it with
|
||||
gme_identify_*()
|
||||
* Load just the file's information tags with gme_info_only
|
||||
* Load from a block of memory rather than a file with gme_load_data()
|
||||
* Arrange for a fade-out at a particular time with gme_set_fade
|
||||
* Find when a track has ended with gme_track_ended()
|
||||
* Seek to a new time in the track with gme_seek()
|
||||
* Load an extended m3u playlist with gme_load_m3u()
|
||||
* Get a list of the voices (channels) and mute them individually with
|
||||
gme_voice_names() and gme_mute_voice()
|
||||
* Change the playback tempo without affecting pitch with gme_set_tempo()
|
||||
* Adjust treble/bass equalization with gme_set_equalizer()
|
||||
* Associate your own data with an emulator and later get it back with
|
||||
gme_set_user_data()
|
||||
* Register a function of yours to be called back when the emulator is
|
||||
deleted with gme_set_user_cleanup()
|
||||
|
||||
Refer to gme.h for a comprehensive summary of features.
|
||||
|
||||
|
||||
Error handling
|
||||
--------------
|
||||
Functions which can fail have a return type of gme_err_t, which is a
|
||||
pointer to an error string (const char*). If a function is successful it
|
||||
returns NULL. Errors that you can easily avoid are checked with debug
|
||||
assertions; gme_err_t return values are only used for genuine run-time
|
||||
errors that can't be easily predicted in advance (out of memory, I/O
|
||||
errors, incompatible file data). Your code should check all error
|
||||
values.
|
||||
|
||||
When loading a music file in the wrong emulator or trying to load a
|
||||
non-music file, gme_wrong_file_type is returned. You can check for this
|
||||
error in C++ like this:
|
||||
|
||||
gme_err_t err = gme_open_file( path, &emu );
|
||||
if ( err == gme_wrong_file_type )
|
||||
...
|
||||
|
||||
To check for minor problems, call gme_warning() to get a string
|
||||
describing the last warning. Your player should allow the user some way
|
||||
of knowing when this is the case, since these minor errors could affect
|
||||
playback. Without this information the user can't solve problems as
|
||||
well. When playing a track, gme_warning() returns minor playback-related
|
||||
problems (major playback problems end the track immediately and set the
|
||||
warning string).
|
||||
|
||||
|
||||
Emulator types
|
||||
--------------
|
||||
The library includes several game music emulators that each support a
|
||||
different file type. Each is identified by a gme_type_t constant defined
|
||||
in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use
|
||||
gme_open_file() or gme_open_data(), the library does the work of
|
||||
determining the file type and creating an appropriate emulator. If you
|
||||
want more control over this process, read on.
|
||||
|
||||
There are two basic ways to identify a game music file's type: look at
|
||||
its file extension, or read the header data. The library includes
|
||||
functions to help with both methods. The first is preferable because it
|
||||
is fast and the most common way to identify files. Sometimes the
|
||||
extension is lost or wrong, so the header must be read.
|
||||
|
||||
Use gme_identify_extension() to find the correct game music type based
|
||||
on a filename. To identify a file based on its extension and header
|
||||
contents, use gme_identify_file(). If you read the header data yourself,
|
||||
use gme_identify_header().
|
||||
|
||||
If you want to remove support for some music types to reduce your
|
||||
executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to
|
||||
support just NSF and GBS, use this:
|
||||
|
||||
#define GME_TYPE_LIST \
|
||||
gme_nsf_type,\
|
||||
gme_gbs_type
|
||||
|
||||
|
||||
M3U playlist support
|
||||
--------------------
|
||||
The library supports playlists in an extended m3u format with
|
||||
gme_load_m3u() to give track names and times to multi-song formats: AY,
|
||||
GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format
|
||||
itself is not well-defined so some m3u files won't work properly
|
||||
(particularly those provided with KSS files). Only m3u files referencing
|
||||
a single file are supported; your code must handle m3u files covering
|
||||
more than one game music file, though it can use the built-in m3u
|
||||
parsing provided by the library.
|
||||
|
||||
|
||||
Information fields
|
||||
------------------
|
||||
Support is provided for the various text fields and length information
|
||||
in a file with gme_track_info(). If you just need track information for
|
||||
a file (for example, building a playlist), use gme_new_info() in place
|
||||
of gme_new_emu(), load the file normally, then you can access the track
|
||||
count and info, but nothing else.
|
||||
|
||||
M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS
|
||||
-------------------------------------------------------
|
||||
Track Count | * * * * * * * * *
|
||||
|
|
||||
System | * * * * * * * * * *
|
||||
|
|
||||
Game | * * * * * * *
|
||||
|
|
||||
Song | * * * * * * *
|
||||
|
|
||||
Author | * * * * * * * *
|
||||
|
|
||||
Copyright | * * * * * * * *
|
||||
|
|
||||
Comment | * * * *
|
||||
|
|
||||
Dumper | * * * *
|
||||
|
|
||||
Length | * * * * * *
|
||||
|
|
||||
Intro Length| * * *
|
||||
|
|
||||
Loop Length | * * *
|
||||
|
||||
As listed above, the HES and KSS file formats don't include a track
|
||||
count, and tracks are often scattered over the 0-255 range, so an m3u
|
||||
playlist for these is a must.
|
||||
|
||||
Unavailable text fields are set to an empty string and times to -1. Your
|
||||
code should be prepared for any combination of available and unavailable
|
||||
fields, as a particular music file might not use all of the supported
|
||||
fields listed above.
|
||||
|
||||
Currently text fields are truncated to 255 characters. Obscure fields of
|
||||
some formats are not currently decoded; contact me if you want one
|
||||
added.
|
||||
|
||||
|
||||
Track length
|
||||
------------
|
||||
The library leaves it up to you as to when to stop playing a track. You
|
||||
can ask for available length information and then tell the library what
|
||||
time it should start fading the track with gme_set_fade(). By default it
|
||||
also continually checks for 6 or more seconds of silence to mark the end
|
||||
of a track. Here is a reasonable algorithm you can use to decide how
|
||||
long to play a track:
|
||||
|
||||
* If the track length is > 0, use it
|
||||
* If the loop length > 0, play for intro + loop * 2
|
||||
* Otherwise, default to 2.5 minutes (150000 msec)
|
||||
|
||||
If you want to play a track longer than normal, be sure the loop length
|
||||
isn't zero. See Music_Player.cpp around line 145 for example code.
|
||||
|
||||
By default, the library skips silence at the beginning of a track. It
|
||||
also continually checks for the end of a non-looping track by watching
|
||||
for 6 seconds of unbroken silence. When doing this is scans *ahead* by
|
||||
several seconds so it can report the end of the track after only one
|
||||
second of silence has actually played. This feature can be disabled with
|
||||
gme_ignore_silence().
|
||||
|
||||
|
||||
Loading file data
|
||||
-----------------
|
||||
The library allows file data to be loaded in many different ways. All
|
||||
load functions return an error which you should check. The following
|
||||
examples assume these variables:
|
||||
|
||||
Music_Emu* emu;
|
||||
gme_err_t error;
|
||||
|
||||
If you're letting the library determine a file's type, you can use
|
||||
either gme_open_file() or gme_open_data():
|
||||
|
||||
error = gme_open_file( pathname, &emu );
|
||||
error = gme_open_data( pointer, size, &emu );
|
||||
|
||||
If you're manually determining file type and using used gme_new_emu() to
|
||||
create an emulator, you can use the following methods of loading:
|
||||
|
||||
* From a block of memory:
|
||||
|
||||
error = gme_load_data( emu, pointer, size );
|
||||
|
||||
* Have library call your function to read data:
|
||||
|
||||
gme_err_t my_read( void* my_data, void* out, long count )
|
||||
{
|
||||
// code that reads 'count' bytes into 'out' buffer
|
||||
// and return 0 if no error
|
||||
}
|
||||
|
||||
error = gme_load_custom( emu, my_read, file_size, my_data );
|
||||
|
||||
|
||||
Sound parameters
|
||||
----------------
|
||||
All emulators support an arbitrary output sampling rate. A rate of 44100
|
||||
Hz should work well on most systems. Since band-limited synthesis is
|
||||
used, a sampling rate above 48000 Hz is not necessary and will actually
|
||||
reduce sound quality and performance.
|
||||
|
||||
All emulators also support adjustable gain, mainly for the purpose of
|
||||
getting consistent volume between different music formats and avoiding
|
||||
excessive modulation. The gain can only be set *before* setting the
|
||||
emulator's sampling rate, so it's not useful as a general volume
|
||||
control. The default gains of emulators are set so that they give
|
||||
generally similar volumes, though some soundtracks are significantly
|
||||
louder or quieter than normal.
|
||||
|
||||
Some emulators support adjustable treble and bass frequency equalization
|
||||
(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer().
|
||||
Parameters are specified using gme_equalizer_t eq = { treble_dB,
|
||||
bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives
|
||||
normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble
|
||||
for an extra crisp sound. Bass_freq sets the frequency where bass
|
||||
response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass,
|
||||
and 15000 Hz removes all bass. For example, the following makes the
|
||||
sound extra-crisp but lacking bass:
|
||||
|
||||
gme_equalizer_t eq = { 5.0, 1000 };
|
||||
gme_set_equalizer( music_emu, &eq );
|
||||
|
||||
Each emulator's equalization defaults to approximate the particular
|
||||
console's sound quality; this default can be determined by calling
|
||||
equalizer() just after creating the emulator. The Music_Emu::tv_eq
|
||||
profile gives sound as if coming from a TV speaker, and some emulators
|
||||
include other profiles for different versions of the system. For
|
||||
example, to use Famicom sound equalization with the NSF emulator, do the
|
||||
following:
|
||||
|
||||
music_emu->set_equalizer( Nsf_Emu::famicom_eq );
|
||||
|
||||
|
||||
VGM/GYM YM2413 & YM2612 FM sound
|
||||
--------------------------------
|
||||
The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound
|
||||
chip emulator based on the Gens project. Because this has some
|
||||
inaccuracies, other YM2612 emulators can be used in its place by
|
||||
re-implementing the interface in YM2612_Emu.h. Available on my website
|
||||
is a modified version of MAME's YM2612 emulator, which sounds better in
|
||||
some ways and whose author is still making improvements.
|
||||
|
||||
VGM music files using the YM2413 FM sound chip are also supported, but a
|
||||
YM2413 emulator isn't included with the library due to technical
|
||||
reasons. I have put one of the available YM2413 emulators on my website
|
||||
that can be used directly.
|
||||
|
||||
|
||||
Modular construction
|
||||
--------------------
|
||||
The library is made of many fairly independent modules. If you're using
|
||||
only one music file emulator, you can eliminate many of the library
|
||||
sources from your program. Refer to the files list in readme.txt to get
|
||||
a general idea of what can be removed, and be sure to edit GME_TYPE_LIST
|
||||
(see "Emulator types" above). Post to the forum if you'd like me to put
|
||||
together a smaller version for a particular use, as this only takes me a
|
||||
few minutes to do.
|
||||
|
||||
If you want to use one of the individual sound chip emulators (or CPU
|
||||
cores) in your own console emulator, first check the libraries page on
|
||||
my website since I have released several of them as stand alone
|
||||
libraries with included documentation and examples on their use. If you
|
||||
don't find it as a standalone library, contact me and I'll consider
|
||||
separating it.
|
||||
|
||||
The "classic" sound chips use my Blip_Buffer library, which greatly
|
||||
simplifies their implementation and efficiently handles band-limited
|
||||
synthesis. It is also available as a stand alone library with
|
||||
documentation and many examples.
|
||||
|
||||
|
||||
Obscure features
|
||||
----------------
|
||||
The library's flexibility allows many possibilities. Contact me if you
|
||||
want help implementing ideas or removing limitations.
|
||||
|
||||
* Uses no global/static variables, allowing multiple instances of any
|
||||
emulator. This is useful in a music player if you want to allow
|
||||
simultaneous recording or scanning of other tracks while one is already
|
||||
playing. This will also be useful if your platform disallows global
|
||||
data.
|
||||
|
||||
* Emulators that support a custom sound buffer can have *every* voice
|
||||
routed to a different Blip_Buffer, allowing custom processing on each
|
||||
voice. For example you could record a Game Boy track as a 4-channel
|
||||
sound file.
|
||||
|
||||
* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive
|
||||
synthesis on "classic" emulators, which might help on some really old
|
||||
processors. This significantly lowers sound quality and prevents treble
|
||||
equalization. Try this if your platform's processor isn't fast enough
|
||||
for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces
|
||||
processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth
|
||||
the quality loss.
|
||||
|
||||
|
||||
Solving problems
|
||||
----------------
|
||||
If you're having problems, try the following:
|
||||
|
||||
* If you're getting garbled sound, try this simple siren generator in
|
||||
place of your call to play(). This will quickly tell whether the problem
|
||||
is in the library or in your code.
|
||||
|
||||
static void play_siren( long count, short* out )
|
||||
{
|
||||
static double a, a2;
|
||||
while ( count-- )
|
||||
*out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) );
|
||||
}
|
||||
|
||||
* Enable debugging support in your environment. This enables assertions
|
||||
and other run-time checks.
|
||||
|
||||
* Turn the compiler's optimizer is off. Sometimes an optimizer generates
|
||||
bad code.
|
||||
|
||||
* If multiple threads are being used, ensure that only one at a time is
|
||||
accessing a given set of objects from the library. This library is not
|
||||
in general thread-safe, though independent objects can be used in
|
||||
separate threads.
|
||||
|
||||
* If all else fails, see if the demos work.
|
||||
|
||||
|
||||
Thanks
|
||||
------
|
||||
Big thanks to Chris Moeller (kode54) for help with library testing and
|
||||
feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and
|
||||
for original work on openspc++ that was used when developing Spc_Emu.
|
||||
Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the
|
||||
start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz,
|
||||
nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using,
|
||||
and giving feedback for the library in their respective game music
|
||||
players. More recently, Lucas Paul and Michael Pyne have helped nudge the
|
||||
library into a public repository and get its interface more stable for use
|
||||
in shared libraries.
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Included at the beginning of library source files, after all other #include lines.
|
||||
* Sets up helpful macros and services used in my source code. They don't need
|
||||
* module an annoying module prefix on their names since they are defined after
|
||||
* all other #include lines. */
|
||||
|
||||
#ifndef BLARGG_SOURCE_H
|
||||
#define BLARGG_SOURCE_H
|
||||
|
||||
/* If debugging is enabled, abort program if expr is false. Meant for checking
|
||||
* internal state and consistency. A failed assertion indicates a bug in the module.
|
||||
* void assert( bool expr ); */
|
||||
#include <assert.h>
|
||||
|
||||
/* If debugging is enabled and expr is false, abort program. Meant for checking
|
||||
* caller-supplied parameters and operations that are outside the control of the
|
||||
* module. A failed requirement indicates a bug outside the module.
|
||||
* void require( bool expr ); */
|
||||
#undef require
|
||||
#define require( expr ) assert( expr )
|
||||
|
||||
/* Use to provide hints to compiler for optimized code layout in situations where we
|
||||
* can almost always expect a conditional to go one way or the other. Should only be
|
||||
* used in situations where an unexpected branch is truly exceptional though! */
|
||||
#undef likely
|
||||
#undef unlikely
|
||||
#ifdef __GNUC__
|
||||
#define likely( x ) __builtin_expect(x, 1)
|
||||
#define unlikely( x ) __builtin_expect(x, 0)
|
||||
#else
|
||||
#define likely( x ) (x)
|
||||
#define unlikely( x ) (x)
|
||||
#endif
|
||||
|
||||
/* Like printf() except output goes to debug log file. Might be defined to do
|
||||
* nothing (not even evaluate its arguments).
|
||||
* void debug_printf( const char* format, ... ); */
|
||||
#if defined(__cplusplus)
|
||||
static inline void blargg_dprintf_( const char* fmt_str, ... ) { (void) fmt_str; }
|
||||
#undef debug_printf
|
||||
#define debug_printf (1) ? (void) 0 : blargg_dprintf_
|
||||
#endif
|
||||
|
||||
/* If enabled, evaluate expr and if false, make debug log entry with source file
|
||||
* and line. Meant for finding situations that should be examined further, but that
|
||||
* don't indicate a problem. In all cases, execution continues normally. */
|
||||
#undef check
|
||||
#define check( expr ) ((void) 0)
|
||||
|
||||
/* If expr yields error string, return it from current function, otherwise continue. */
|
||||
#undef RETURN_ERR
|
||||
#define RETURN_ERR( expr ) do { \
|
||||
blargg_err_t blargg_return_err_ = (expr); \
|
||||
if ( blargg_return_err_ ) return blargg_return_err_; \
|
||||
} while ( 0 )
|
||||
|
||||
/* If ptr is 0, return out of memory error string. */
|
||||
#undef CHECK_ALLOC
|
||||
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
|
||||
|
||||
/* TODO: good idea? bad idea? */
|
||||
#undef byte
|
||||
#define byte byte_
|
||||
typedef unsigned char byte;
|
||||
|
||||
/* Setup compiler defines useful for exporting required public API symbols in gme.cpp */
|
||||
#ifndef BLARGG_EXPORT
|
||||
#if defined (_WIN32)
|
||||
#if defined(BLARGG_BUILD_DLL)
|
||||
#define BLARGG_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define BLARGG_EXPORT /* Leave blank: friendly with both static and shared linking */
|
||||
#endif
|
||||
#elif defined (LIBGME_VISIBILITY) && defined(__cplusplus)
|
||||
#define BLARGG_EXPORT __attribute__((visibility ("default")))
|
||||
#else
|
||||
#define BLARGG_EXPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* deprecated */
|
||||
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
|
||||
#define BLARGG_RETURN_ERR RETURN_ERR
|
||||
|
||||
/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check */
|
||||
#ifdef BLARGG_SOURCE_BEGIN
|
||||
#include BLARGG_SOURCE_BEGIN
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
+504
-504
File diff suppressed because it is too large.
Load diff
+231
-231
@@ -1,231 +1,231 @@
|
||||
Game_Music_Emu 0.6.2: Game Music Emulators
|
||||
------------------------------------------
|
||||
Game_Music_Emu is a collection of video game music file emulators that
|
||||
support the following formats and systems:
|
||||
|
||||
AY ZX Spectrum/Amstrad CPC
|
||||
GBS Nintendo Game Boy
|
||||
GYM Sega Genesis/Mega Drive
|
||||
HES NEC TurboGrafx-16/PC Engine
|
||||
KSS MSX Home Computer/other Z80 systems (doesn't support FM sound)
|
||||
NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound)
|
||||
SAP Atari systems using POKEY sound chip
|
||||
SPC Super Nintendo/Super Famicom
|
||||
VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro
|
||||
|
||||
Features:
|
||||
* C interface for use in C, C++, and other compatible languages
|
||||
* High emphasis has been placed on making the library very easy to use
|
||||
* One set of common functions work with all emulators the same way
|
||||
* Several code examples, including music player using SDL
|
||||
* Portable code for use on any system with modern or older C++ compilers
|
||||
* Adjustable output sample rate using quality band-limited resampling
|
||||
* Uniform access to text information fields and track timing information
|
||||
* End-of-track fading and automatic look ahead silence detection
|
||||
* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM
|
||||
* Tempo can be adjusted and individual voices can be muted while playing
|
||||
* Can read music data from file, memory, or custom reader function/class
|
||||
* Can access track information without having to load into full emulator
|
||||
* M3U track listing support for multi-track formats
|
||||
* Modular design allows elimination of unneeded emulators/features
|
||||
|
||||
This library has been used in game music players for Windows, Linux on
|
||||
several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
|
||||
GP2X, and Nintendo DS.
|
||||
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
|
||||
License: GNU Lesser General Public License (LGPL)
|
||||
|
||||
Current Maintainer: Michael Pyne <mpyne@purinchu.net>
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and
|
||||
all source files in gme/.
|
||||
|
||||
Or, if you have CMake 2.6 or later, execute at a command prompt (from the
|
||||
extracted source directory):
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ # <-- Pass any needed CMake flags here
|
||||
make # To build the library
|
||||
cd demo
|
||||
make # To build the demo itself
|
||||
|
||||
Be sure "test.nsf" is in the same directory as the demo program. Running it
|
||||
should generate the recording "out.wav".
|
||||
|
||||
You can use "make install" to install the library. To choose where to install
|
||||
the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local"
|
||||
(and replace /usr/local with the base path you wish to use). Alternately, you
|
||||
can specify the base path to install to when you run "make install" by passing
|
||||
'DESTDIR=/usr/local' on the make install command line (again, replace
|
||||
/usr/local as appropriate).
|
||||
|
||||
To build a static library instead of shared (the default), pass
|
||||
-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake.
|
||||
|
||||
A slightly more extensive demo application is available in the player/
|
||||
directory. It requires SDL to build.
|
||||
|
||||
Read gme.txt for more information. Post to the discussion forum for
|
||||
assistance.
|
||||
|
||||
Files
|
||||
-----
|
||||
gme.txt General notes about the library
|
||||
changes.txt Changes made since previous releases
|
||||
design.txt Library design notes
|
||||
license.txt GNU Lesser General Public License
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
test.nsf Test file for NSF emulator
|
||||
test.m3u Test m3u playlist for features.c demo
|
||||
|
||||
demo/
|
||||
basics.c Records NSF file to wave sound file
|
||||
features.c Demonstrates many additional features
|
||||
Wave_Writer.h WAVE sound file writer used for demo output
|
||||
Wave_Writer.cpp
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
player/ Player using the SDL multimedia library
|
||||
player.cpp Simple music player with waveform display
|
||||
Music_Player.cpp Stand alone player for background music
|
||||
Music_Player.h
|
||||
Audio_Scope.cpp Audio waveform scope
|
||||
Audio_Scope.h
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
gme/
|
||||
blargg_config.h Library configuration (modify this file as needed)
|
||||
|
||||
gme.h Library interface header file
|
||||
gme.cpp
|
||||
|
||||
Ay_Emu.h ZX Spectrum AY emulator
|
||||
Ay_Emu.cpp
|
||||
Ay_Apu.cpp
|
||||
Ay_Apu.h
|
||||
Ay_Cpu.cpp
|
||||
Ay_Cpu.h
|
||||
|
||||
Gbs_Emu.h Nintendo Game Boy GBS emulator
|
||||
Gbs_Emu.cpp
|
||||
Gb_Apu.cpp
|
||||
Gb_Apu.h
|
||||
Gb_Cpu.cpp
|
||||
Gb_Cpu.h
|
||||
gb_cpu_io.h
|
||||
Gb_Oscs.cpp
|
||||
Gb_Oscs.h
|
||||
|
||||
Hes_Emu.h TurboGrafx-16/PC Engine HES emulator
|
||||
Hes_Apu.cpp
|
||||
Hes_Apu.h
|
||||
Hes_Cpu.cpp
|
||||
Hes_Cpu.h
|
||||
hes_cpu_io.h
|
||||
Hes_Emu.cpp
|
||||
|
||||
Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator
|
||||
Kss_Emu.cpp
|
||||
Kss_Cpu.cpp
|
||||
Kss_Cpu.h
|
||||
Kss_Scc_Apu.cpp
|
||||
Kss_Scc_Apu.h
|
||||
Ay_Apu.h
|
||||
Ay_Apu.cpp
|
||||
Sms_Apu.h
|
||||
Sms_Apu.cpp
|
||||
Sms_Oscs.h
|
||||
|
||||
Nsf_Emu.h Nintendo NES NSF/NSFE emulator
|
||||
Nsf_Emu.cpp
|
||||
Nes_Apu.cpp
|
||||
Nes_Apu.h
|
||||
Nes_Cpu.cpp
|
||||
Nes_Cpu.h
|
||||
nes_cpu_io.h
|
||||
Nes_Oscs.cpp
|
||||
Nes_Oscs.h
|
||||
Nes_Fme7_Apu.cpp
|
||||
Nes_Fme7_Apu.h
|
||||
Nes_Namco_Apu.cpp
|
||||
Nes_Namco_Apu.h
|
||||
Nes_Vrc6_Apu.cpp
|
||||
Nes_Vrc6_Apu.h
|
||||
Nsfe_Emu.h NSFE support
|
||||
Nsfe_Emu.cpp
|
||||
|
||||
Spc_Emu.h Super Nintendo SPC emulator
|
||||
Spc_Emu.cpp
|
||||
Snes_Spc.cpp
|
||||
Snes_Spc.h
|
||||
Spc_Cpu.cpp
|
||||
Spc_Cpu.h
|
||||
Spc_Dsp.cpp
|
||||
Spc_Dsp.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
Sap_Emu.h Atari SAP emulator
|
||||
Sap_Emu.cpp
|
||||
Sap_Apu.cpp
|
||||
Sap_Apu.h
|
||||
Sap_Cpu.cpp
|
||||
Sap_Cpu.h
|
||||
sap_cpu_io.h
|
||||
|
||||
Vgm_Emu.h Sega VGM emulator
|
||||
Vgm_Emu_Impl.cpp
|
||||
Vgm_Emu_Impl.h
|
||||
Vgm_Emu.cpp
|
||||
Ym2413_Emu.cpp
|
||||
Ym2413_Emu.h
|
||||
Gym_Emu.h Sega Genesis GYM emulator
|
||||
Gym_Emu.cpp
|
||||
Sms_Apu.cpp Common Sega emulator files
|
||||
Sms_Apu.h
|
||||
Sms_Oscs.h
|
||||
Ym2612_Emu.cpp
|
||||
Ym2612_Emu.h
|
||||
Dual_Resampler.cpp
|
||||
Dual_Resampler.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
M3u_Playlist.h M3U playlist support
|
||||
M3u_Playlist.cpp
|
||||
|
||||
Effects_Buffer.h Sound buffer with stereo echo and panning
|
||||
Effects_Buffer.cpp
|
||||
|
||||
blargg_common.h Common files needed by all emulators
|
||||
blargg_endian.h
|
||||
blargg_source.h
|
||||
Blip_Buffer.cpp
|
||||
Blip_Buffer.h
|
||||
Gme_File.h
|
||||
Gme_File.cpp
|
||||
Music_Emu.h
|
||||
Music_Emu.cpp
|
||||
Classic_Emu.h
|
||||
Classic_Emu.cpp
|
||||
Multi_Buffer.h
|
||||
Multi_Buffer.cpp
|
||||
Data_Reader.h
|
||||
Data_Reader.cpp
|
||||
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
|
||||
Legal
|
||||
-----
|
||||
Game_Music_Emu library copyright (C) 2003-2009 Shay Green.
|
||||
Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville.
|
||||
|
||||
--
|
||||
Shay Green <gblargg@gmail.com>
|
||||
Game_Music_Emu 0.6.2: Game Music Emulators
|
||||
------------------------------------------
|
||||
Game_Music_Emu is a collection of video game music file emulators that
|
||||
support the following formats and systems:
|
||||
|
||||
AY ZX Spectrum/Amstrad CPC
|
||||
GBS Nintendo Game Boy
|
||||
GYM Sega Genesis/Mega Drive
|
||||
HES NEC TurboGrafx-16/PC Engine
|
||||
KSS MSX Home Computer/other Z80 systems (doesn't support FM sound)
|
||||
NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound)
|
||||
SAP Atari systems using POKEY sound chip
|
||||
SPC Super Nintendo/Super Famicom
|
||||
VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro
|
||||
|
||||
Features:
|
||||
* C interface for use in C, C++, and other compatible languages
|
||||
* High emphasis has been placed on making the library very easy to use
|
||||
* One set of common functions work with all emulators the same way
|
||||
* Several code examples, including music player using SDL
|
||||
* Portable code for use on any system with modern or older C++ compilers
|
||||
* Adjustable output sample rate using quality band-limited resampling
|
||||
* Uniform access to text information fields and track timing information
|
||||
* End-of-track fading and automatic look ahead silence detection
|
||||
* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM
|
||||
* Tempo can be adjusted and individual voices can be muted while playing
|
||||
* Can read music data from file, memory, or custom reader function/class
|
||||
* Can access track information without having to load into full emulator
|
||||
* M3U track listing support for multi-track formats
|
||||
* Modular design allows elimination of unneeded emulators/features
|
||||
|
||||
This library has been used in game music players for Windows, Linux on
|
||||
several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
|
||||
GP2X, and Nintendo DS.
|
||||
|
||||
Author : Shay Green <gblargg@gmail.com>
|
||||
Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
|
||||
License: GNU Lesser General Public License (LGPL)
|
||||
|
||||
Current Maintainer: Michael Pyne <mpyne@purinchu.net>
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and
|
||||
all source files in gme/.
|
||||
|
||||
Or, if you have CMake 2.6 or later, execute at a command prompt (from the
|
||||
extracted source directory):
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ # <-- Pass any needed CMake flags here
|
||||
make # To build the library
|
||||
cd demo
|
||||
make # To build the demo itself
|
||||
|
||||
Be sure "test.nsf" is in the same directory as the demo program. Running it
|
||||
should generate the recording "out.wav".
|
||||
|
||||
You can use "make install" to install the library. To choose where to install
|
||||
the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local"
|
||||
(and replace /usr/local with the base path you wish to use). Alternately, you
|
||||
can specify the base path to install to when you run "make install" by passing
|
||||
'DESTDIR=/usr/local' on the make install command line (again, replace
|
||||
/usr/local as appropriate).
|
||||
|
||||
To build a static library instead of shared (the default), pass
|
||||
-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake.
|
||||
|
||||
A slightly more extensive demo application is available in the player/
|
||||
directory. It requires SDL to build.
|
||||
|
||||
Read gme.txt for more information. Post to the discussion forum for
|
||||
assistance.
|
||||
|
||||
Files
|
||||
-----
|
||||
gme.txt General notes about the library
|
||||
changes.txt Changes made since previous releases
|
||||
design.txt Library design notes
|
||||
license.txt GNU Lesser General Public License
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
test.nsf Test file for NSF emulator
|
||||
test.m3u Test m3u playlist for features.c demo
|
||||
|
||||
demo/
|
||||
basics.c Records NSF file to wave sound file
|
||||
features.c Demonstrates many additional features
|
||||
Wave_Writer.h WAVE sound file writer used for demo output
|
||||
Wave_Writer.cpp
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
player/ Player using the SDL multimedia library
|
||||
player.cpp Simple music player with waveform display
|
||||
Music_Player.cpp Stand alone player for background music
|
||||
Music_Player.h
|
||||
Audio_Scope.cpp Audio waveform scope
|
||||
Audio_Scope.h
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
gme/
|
||||
blargg_config.h Library configuration (modify this file as needed)
|
||||
|
||||
gme.h Library interface header file
|
||||
gme.cpp
|
||||
|
||||
Ay_Emu.h ZX Spectrum AY emulator
|
||||
Ay_Emu.cpp
|
||||
Ay_Apu.cpp
|
||||
Ay_Apu.h
|
||||
Ay_Cpu.cpp
|
||||
Ay_Cpu.h
|
||||
|
||||
Gbs_Emu.h Nintendo Game Boy GBS emulator
|
||||
Gbs_Emu.cpp
|
||||
Gb_Apu.cpp
|
||||
Gb_Apu.h
|
||||
Gb_Cpu.cpp
|
||||
Gb_Cpu.h
|
||||
gb_cpu_io.h
|
||||
Gb_Oscs.cpp
|
||||
Gb_Oscs.h
|
||||
|
||||
Hes_Emu.h TurboGrafx-16/PC Engine HES emulator
|
||||
Hes_Apu.cpp
|
||||
Hes_Apu.h
|
||||
Hes_Cpu.cpp
|
||||
Hes_Cpu.h
|
||||
hes_cpu_io.h
|
||||
Hes_Emu.cpp
|
||||
|
||||
Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator
|
||||
Kss_Emu.cpp
|
||||
Kss_Cpu.cpp
|
||||
Kss_Cpu.h
|
||||
Kss_Scc_Apu.cpp
|
||||
Kss_Scc_Apu.h
|
||||
Ay_Apu.h
|
||||
Ay_Apu.cpp
|
||||
Sms_Apu.h
|
||||
Sms_Apu.cpp
|
||||
Sms_Oscs.h
|
||||
|
||||
Nsf_Emu.h Nintendo NES NSF/NSFE emulator
|
||||
Nsf_Emu.cpp
|
||||
Nes_Apu.cpp
|
||||
Nes_Apu.h
|
||||
Nes_Cpu.cpp
|
||||
Nes_Cpu.h
|
||||
nes_cpu_io.h
|
||||
Nes_Oscs.cpp
|
||||
Nes_Oscs.h
|
||||
Nes_Fme7_Apu.cpp
|
||||
Nes_Fme7_Apu.h
|
||||
Nes_Namco_Apu.cpp
|
||||
Nes_Namco_Apu.h
|
||||
Nes_Vrc6_Apu.cpp
|
||||
Nes_Vrc6_Apu.h
|
||||
Nsfe_Emu.h NSFE support
|
||||
Nsfe_Emu.cpp
|
||||
|
||||
Spc_Emu.h Super Nintendo SPC emulator
|
||||
Spc_Emu.cpp
|
||||
Snes_Spc.cpp
|
||||
Snes_Spc.h
|
||||
Spc_Cpu.cpp
|
||||
Spc_Cpu.h
|
||||
Spc_Dsp.cpp
|
||||
Spc_Dsp.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
Sap_Emu.h Atari SAP emulator
|
||||
Sap_Emu.cpp
|
||||
Sap_Apu.cpp
|
||||
Sap_Apu.h
|
||||
Sap_Cpu.cpp
|
||||
Sap_Cpu.h
|
||||
sap_cpu_io.h
|
||||
|
||||
Vgm_Emu.h Sega VGM emulator
|
||||
Vgm_Emu_Impl.cpp
|
||||
Vgm_Emu_Impl.h
|
||||
Vgm_Emu.cpp
|
||||
Ym2413_Emu.cpp
|
||||
Ym2413_Emu.h
|
||||
Gym_Emu.h Sega Genesis GYM emulator
|
||||
Gym_Emu.cpp
|
||||
Sms_Apu.cpp Common Sega emulator files
|
||||
Sms_Apu.h
|
||||
Sms_Oscs.h
|
||||
Ym2612_Emu.cpp
|
||||
Ym2612_Emu.h
|
||||
Dual_Resampler.cpp
|
||||
Dual_Resampler.h
|
||||
Fir_Resampler.cpp
|
||||
Fir_Resampler.h
|
||||
|
||||
M3u_Playlist.h M3U playlist support
|
||||
M3u_Playlist.cpp
|
||||
|
||||
Effects_Buffer.h Sound buffer with stereo echo and panning
|
||||
Effects_Buffer.cpp
|
||||
|
||||
blargg_common.h Common files needed by all emulators
|
||||
blargg_endian.h
|
||||
blargg_source.h
|
||||
Blip_Buffer.cpp
|
||||
Blip_Buffer.h
|
||||
Gme_File.h
|
||||
Gme_File.cpp
|
||||
Music_Emu.h
|
||||
Music_Emu.cpp
|
||||
Classic_Emu.h
|
||||
Classic_Emu.cpp
|
||||
Multi_Buffer.h
|
||||
Multi_Buffer.cpp
|
||||
Data_Reader.h
|
||||
Data_Reader.cpp
|
||||
|
||||
CMakeLists.txt CMake build rules
|
||||
|
||||
|
||||
Legal
|
||||
-----
|
||||
Game_Music_Emu library copyright (C) 2003-2009 Shay Green.
|
||||
Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville.
|
||||
|
||||
--
|
||||
Shay Green <gblargg@gmail.com>
|
||||
@@ -1,5 +1,5 @@
|
||||
/* backtrace.h -- Public header file for stack backtrace library.
|
||||
Copyright (C) 2012-2020 Free Software Foundation, Inc.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -54,13 +54,14 @@ struct backtrace_state;
|
||||
invalid after this function returns.
|
||||
|
||||
As a special case, the ERRNUM argument will be passed as -1 if no
|
||||
debug info can be found for the executable, but the function
|
||||
requires debug info (e.g., backtrace_full, backtrace_pcinfo). The
|
||||
MSG in this case will be something along the lines of "no debug
|
||||
info". Similarly, ERRNUM will be passed as -1 if there is no
|
||||
symbol table, but the function requires a symbol table (e.g.,
|
||||
backtrace_syminfo). This may be used as a signal that some other
|
||||
approach should be tried. */
|
||||
debug info can be found for the executable, or if the debug info
|
||||
exists but has an unsupported version, but the function requires
|
||||
debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in
|
||||
this case will be something along the lines of "no debug info".
|
||||
Similarly, ERRNUM will be passed as -1 if there is no symbol table,
|
||||
but the function requires a symbol table (e.g., backtrace_syminfo).
|
||||
This may be used as a signal that some other approach should be
|
||||
tried. */
|
||||
|
||||
typedef void (*backtrace_error_callback) (void *data, const char *msg,
|
||||
int errnum);
|
||||
|
||||
Binary file not shown.
@@ -3,6 +3,9 @@ SRB2.res
|
||||
depend.dep
|
||||
depend.ped
|
||||
*.o
|
||||
buildvm*
|
||||
minilua*
|
||||
lj_vm.S
|
||||
#VC9 folder only
|
||||
/VC9/Win32
|
||||
/VC9/x64
|
||||
@@ -1,4 +1,3 @@
|
||||
/cscope.out
|
||||
/srb2wii
|
||||
*.json
|
||||
*.idx
|
||||
+39
-25
@@ -68,7 +68,6 @@ ALL_SYSTEMS=\
|
||||
LINUX64\
|
||||
MINGW64\
|
||||
HAIKU\
|
||||
DUMMY\
|
||||
MINGW\
|
||||
UNIX\
|
||||
LINUX\
|
||||
@@ -158,17 +157,6 @@ endif
|
||||
|
||||
include Makefile.cfg
|
||||
|
||||
ifdef DUMMY
|
||||
NOPNG=1
|
||||
NOZLIB=1
|
||||
NONET=1
|
||||
NOHW=1
|
||||
NOIPX=1
|
||||
EXENAME?=srb2dummy
|
||||
OBJS=$(OBJDIR)/i_video.o
|
||||
LIBS=-lm
|
||||
endif
|
||||
|
||||
ifdef HAIKU
|
||||
NOIPX=1
|
||||
ifndef NONET
|
||||
@@ -245,6 +233,10 @@ endif
|
||||
|
||||
MSGFMT?=msgfmt
|
||||
|
||||
ifndef NOBLUAJIT
|
||||
include bluajit/Makefile.cfg
|
||||
endif
|
||||
|
||||
ifndef ECHO
|
||||
REMOVE:=@$(REMOVE)
|
||||
CC:=@$(CC)
|
||||
@@ -292,15 +284,10 @@ else
|
||||
endif
|
||||
else
|
||||
ifdef X86_64
|
||||
ARCHOPTS?=-march=nocona
|
||||
ARCHOPTS?=-march=native
|
||||
endif
|
||||
endif
|
||||
|
||||
# apparently MingW just completely ignores everything so just always force this shit
|
||||
# FIXME: we should port our code to c24
|
||||
CFLAGS+=-std=gnu17
|
||||
CPPFLAGS+=-std=gnu17
|
||||
|
||||
ifndef NOPNG
|
||||
OPTS+=-DHAVE_PNG
|
||||
|
||||
@@ -414,7 +401,10 @@ CFLAGS+=-DHAVE_DISCORDRPC -DUSE_STUN
|
||||
OBJS+=$(OBJDIR)/discord.o $(OBJDIR)/stun.o
|
||||
endif
|
||||
|
||||
ifdef NOBLUAJIT
|
||||
OPTS+=-DNOBLUAJIT
|
||||
include blua/Makefile.cfg
|
||||
endif
|
||||
|
||||
ifdef NOMD5
|
||||
OPTS+=-DNOMD5
|
||||
@@ -610,10 +600,6 @@ ifdef SDL
|
||||
all: pre-build $(BIN)/$(EXENAME)
|
||||
endif
|
||||
|
||||
ifdef DUMMY
|
||||
all: $(BIN)/$(EXENAME)
|
||||
endif
|
||||
|
||||
cleandep:
|
||||
$(REMOVE) $(OBJDIR)/depend.dep
|
||||
$(REMOVE) comptime.h
|
||||
@@ -628,6 +614,13 @@ endif
|
||||
clean:
|
||||
$(REMOVE) *~ *.flc
|
||||
$(REMOVE) $(OBJDIR)/*.o
|
||||
ifndef NOBLUAJIT
|
||||
$(REMOVE) $(OBJDIR)/buildvm* $(OBJDIR)/minilua* $(OBJDIR)/lj_vm* $(ALL_HDRGEN)
|
||||
|
||||
cleanjit:
|
||||
$(REMOVE) $(LJVMCORE_O) $(ALL_GEN)
|
||||
$(REMOVE) $(OBJDIR)/buildvm* $(OBJDIR)/minilua* $(OBJDIR)/lj_vm* $(ALL_HDRGEN)
|
||||
endif
|
||||
|
||||
ifdef MINGW
|
||||
$(REMOVE) $(OBJDIR)/*.res
|
||||
@@ -643,7 +636,7 @@ endif
|
||||
$(BIN)/$(EXENAME): $(POS) $(OBJS)
|
||||
-$(MKDIR) $(BIN)
|
||||
@echo Linking $(EXENAME)...
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $(BIN)/$(EXENAME) $(LIBS)
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $(BIN)/$(EXENAME) $(LIBS) -lstdc++
|
||||
ifndef VALGRIND
|
||||
ifndef NOOBJDUMP
|
||||
@echo Dumping debugging info
|
||||
@@ -734,21 +727,34 @@ endif
|
||||
|
||||
#dependecy made by gcc itself !
|
||||
$(OBJS):
|
||||
ifndef DUMMY
|
||||
-include $(OBJDIR)/depend.dep
|
||||
endif
|
||||
|
||||
$(OBJDIR)/depend.dep:
|
||||
@echo "Creating dependency file, depend.dep"
|
||||
@echo > comptime.h
|
||||
-$(MKDIR) $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -MM *.c > $(OBJDIR)/depend.ped
|
||||
$(CXX) $(CXXFLAGS) -MM *.cpp >> $(OBJDIR)/depend.ped
|
||||
$(CC) $(CFLAGS) -MM $(INTERFACE)/*.c >> $(OBJDIR)/depend.ped
|
||||
ifndef NOHW
|
||||
$(CC) $(CFLAGS) -MM hardware/*.c >> $(OBJDIR)/depend.ped
|
||||
endif
|
||||
|
||||
ifdef NOBLUAJIT
|
||||
$(CC) $(CFLAGS) -MM blua/*.c >> $(OBJDIR)/depend.ped
|
||||
else
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -f $$file || touch $$file; \
|
||||
done
|
||||
@$(HOST_CC) $(HOST_ACFLAGS) -MM bluajit/*.c bluajit/host/*.c | \
|
||||
sed -e "s| [^ ]*/dasm_\S*\.h||g" \
|
||||
-e "s| lj_target_\S*\.h| lj_target_*.h|g" \
|
||||
-e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \
|
||||
-e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >>$(OBJDIR)/depend.ped
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -s $$file || $(HOST_RM) $$file; \
|
||||
done
|
||||
endif
|
||||
|
||||
@sed -e 's,\(.*\)\.o: ,$(subst /,\/,$(OBJDIR))\/&,g' < $(OBJDIR)/depend.ped > $(OBJDIR)/depend.dep
|
||||
$(REMOVE) $(OBJDIR)/depend.ped
|
||||
@@ -780,8 +786,16 @@ endif
|
||||
$(OBJDIR)/%.o: hardware/%.c
|
||||
$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
ifdef NOBLUAJIT
|
||||
$(OBJDIR)/%.o: blua/%.c
|
||||
$(CC) $(CFLAGS) $(LUA_CFLAGS) $(WFLAGS) -c $< -o $@
|
||||
else
|
||||
include bluajit/Makefile.cfg2
|
||||
endif
|
||||
|
||||
# C++
|
||||
$(OBJDIR)/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(WFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJDIR)/%.o: %.s
|
||||
$(CC) $(OPTS) -x assembler-with-cpp -c $< -o $@
|
||||
|
||||
@@ -489,7 +489,6 @@ ifndef CYGWIN32
|
||||
ifndef MINGW
|
||||
ifndef MINGW64
|
||||
ifndef SDL
|
||||
ifndef DUMMY
|
||||
$(error No interface or platform flag defined)
|
||||
endif
|
||||
endif
|
||||
@@ -497,7 +496,6 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#determine the interface directory (where you put all i_*.c)
|
||||
i_net_o=$(OBJDIR)/i_net.o
|
||||
@@ -522,11 +520,6 @@ UPX_OPTS+=-q
|
||||
endif
|
||||
|
||||
#Interface Setup
|
||||
ifdef DUMMY
|
||||
INTERFACE=dummy
|
||||
OBJDIR:=$(OBJDIR)/dummy
|
||||
BIN:=$(BIN)/dummy
|
||||
else
|
||||
ifdef LINUX
|
||||
SDL=1
|
||||
ifdef LINUX64
|
||||
@@ -573,7 +566,6 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef ARCHNAME
|
||||
OBJDIR:=$(OBJDIR)/$(ARCHNAME)
|
||||
@@ -588,7 +580,6 @@ ifdef SDL
|
||||
OBJDIR:=$(OBJDIR)/SDL
|
||||
endif
|
||||
|
||||
ifndef DUMMY
|
||||
ifdef DEBUGMODE
|
||||
OBJDIR:=$(OBJDIR)/Debug
|
||||
BIN:=$(BIN)/Debug
|
||||
@@ -596,4 +587,3 @@ else
|
||||
OBJDIR:=$(OBJDIR)/Release
|
||||
BIN:=$(BIN)/Release
|
||||
endif
|
||||
endif
|
||||
+4
-3
@@ -312,12 +312,13 @@ static void AM_initVariables(void)
|
||||
m_h = FTOM(f_h);
|
||||
|
||||
// find player to center on initially
|
||||
if (!playeringame[pnum = consoleplayer])
|
||||
if (!players[pnum = consoleplayer].ingame)
|
||||
for (pnum = 0; pnum < MAXPLAYERS; pnum++)
|
||||
if (playeringame[pnum])
|
||||
if (players[pnum].ingame)
|
||||
break;
|
||||
|
||||
plr = &players[pnum];
|
||||
|
||||
if (plr != NULL && plr->mo != NULL)
|
||||
{
|
||||
m_x = (plr->mo->x >> FRACTOMAPBITS) - m_w/2;
|
||||
@@ -1227,7 +1228,7 @@ static inline void AM_drawPlayers(void)
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
if (!players[i].ingame || players[i].spectator)
|
||||
continue;
|
||||
|
||||
if (!players[i].mo)
|
||||
|
||||
@@ -49,7 +49,9 @@ OBJS:=$(OBJS) \
|
||||
$(OBJDIR)/lua_skinlib.o \
|
||||
$(OBJDIR)/lua_thinkerlib.o \
|
||||
$(OBJDIR)/lua_maplib.o \
|
||||
$(OBJDIR)/hashtable.o \
|
||||
$(OBJDIR)/lua_hudlib.o \
|
||||
$(OBJDIR)/lua_hudlib_drawlist.o \
|
||||
$(OBJDIR)/lua_udatalib.o \
|
||||
$(OBJDIR)/lua_glib.o
|
||||
|
||||
+5
-70
@@ -17,27 +17,14 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "../i_system.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../d_main.h"
|
||||
#include "../m_misc.h"
|
||||
|
||||
|
||||
|
||||
#define IO_INPUT 1
|
||||
#define IO_OUTPUT 2
|
||||
|
||||
#define FILELIMIT 1024*1024 // Size limit for reading/writing files
|
||||
|
||||
|
||||
static const char *const fnames[] = {"input", "output"};
|
||||
static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder
|
||||
".txt",
|
||||
".sav2",
|
||||
".cfg",
|
||||
".png",
|
||||
".bmp"
|
||||
};
|
||||
|
||||
|
||||
static int pushresult (lua_State *L, int i, const char *filename) {
|
||||
@@ -159,59 +146,12 @@ static int io_tostring (lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int StartsWith(const char *a, const char *b) // this is wolfs being lazy yet again
|
||||
{
|
||||
if(strncmp(a, b, strlen(b)) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int io_open (lua_State *L) {
|
||||
FILE **pf;
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
int pass = 0;
|
||||
size_t i;
|
||||
int length = strlen(filename);
|
||||
char *splitter, *forward, *backward;
|
||||
char *destFilename;
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
|
||||
for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++)
|
||||
{
|
||||
if (!stricmp(&filename[length - strlen(whitelist[i])], whitelist[i]))
|
||||
{
|
||||
pass = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strstr(filename, "..") || strchr(filename, ':') || StartsWith(filename, "\\")
|
||||
|| StartsWith(filename, "/") || !pass)
|
||||
{
|
||||
luaL_error(L,"access denied to %s", filename);
|
||||
return pushresult(L,0,filename);
|
||||
}
|
||||
|
||||
destFilename = va("%s"PATHSEP"luafiles"PATHSEP"%s", srb2home, filename);
|
||||
|
||||
// Make directories as needed
|
||||
splitter = destFilename;
|
||||
|
||||
forward = strchr(splitter, '/');
|
||||
backward = strchr(splitter, '\\');
|
||||
while ((splitter = (forward && backward) ? min(forward, backward) : (forward ?: backward)))
|
||||
{
|
||||
*splitter = 0;
|
||||
I_mkdir(destFilename, 0755);
|
||||
*splitter = '/';
|
||||
splitter++;
|
||||
|
||||
forward = strchr(splitter, '/');
|
||||
backward = strchr(splitter, '\\');
|
||||
}
|
||||
|
||||
pf = newfile(L);
|
||||
*pf = fopen(destFilename, mode);
|
||||
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
FILE **pf = newfile(L);
|
||||
*pf = fopen(filename, mode);
|
||||
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -454,11 +394,6 @@ static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
else {
|
||||
size_t l;
|
||||
const char *s = luaL_checklstring(L, arg, &l);
|
||||
if (ftell(f) + l > FILELIMIT)
|
||||
{
|
||||
luaL_error(L,"write limit bypassed in file. Changes have been discarded.");
|
||||
break;
|
||||
}
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
luajit
|
||||
luajit.h
|
||||
luajit_relver.txt
|
||||
lj_bcdef.h
|
||||
lj_ffdef.h
|
||||
lj_libdef.h
|
||||
lj_recdef.h
|
||||
lj_folddef.h
|
||||
lj_vm.[sS]
|
||||
@@ -0,0 +1,408 @@
|
||||
##############################################################################
|
||||
# LuaJIT Makefile. Requires GNU Make.
|
||||
#
|
||||
# Please read doc/install.html before changing any variables!
|
||||
#
|
||||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
||||
# Also works with MinGW and Cygwin on Windows.
|
||||
# Please check msvcbuild.bat for building with MSVC on Windows.
|
||||
#
|
||||
# Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
##############################################################################
|
||||
|
||||
# this is a very dumbed down version of the LuaJIT makefile.
|
||||
# to get the original makefile, visit
|
||||
# https://github.com/GenericHeroGuy/bluajit
|
||||
|
||||
XCFLAGS=
|
||||
XCFLAGS+= -DLUAJIT_DISABLE_FFI
|
||||
#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT
|
||||
#XCFLAGS+= -DLUAJIT_DISABLE_JIT
|
||||
#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
#XCFLAGS+= -DLUAJIT_USE_VALGRIND
|
||||
#XCFLAGS+= -DLUAJIT_USE_GDBJIT
|
||||
#XCFLAGS+= -DLUA_USE_APICHECK
|
||||
#XCFLAGS+= -DLUA_USE_ASSERT
|
||||
XCFLAGS+= -DLUAJIT_DISABLE_BYTECODE
|
||||
|
||||
# load standard libraries in debug mode, so jit.dump etc works
|
||||
ifndef DEBUGMODE
|
||||
XCFLAGS+= -DLUAJIT_SRB2LIB -DLUAJIT_DISABLE_BUFFER
|
||||
endif
|
||||
# additional functions
|
||||
#XCFLAGS+= -DLUAJIT_SRB2LIB_OS
|
||||
XCFLAGS+= -DLUAJIT_SRB2LIB_DEBUG
|
||||
XCFLAGS+= -DLUAJIT_SRB2LIB_IO
|
||||
|
||||
# don't touch these unless you wanna have a bad time
|
||||
XCFLAGS+= -DLUAJIT_NUMMODE=2
|
||||
XCFLAGS+= -DLUAJIT_INTONLY
|
||||
# needed for the headers
|
||||
OPTS+=-DLUAJIT_INTONLY
|
||||
|
||||
ifdef MINGW
|
||||
TARGET_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
|
||||
ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM))
|
||||
HOST_SYS= Windows
|
||||
else
|
||||
HOST_SYS:= $(shell uname -s)
|
||||
ifneq (,$(findstring MINGW,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
ifneq (,$(findstring MSYS,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
ifneq (,$(findstring CYGWIN,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= cygwin
|
||||
endif
|
||||
endif
|
||||
|
||||
ASOPTIONS= $(CCOPT) $(XCFLAGS) $(CFLAGS)
|
||||
CCOPTIONS= $(ASOPTIONS)
|
||||
|
||||
HOST_CC= $(CC)
|
||||
HOST_RM?= rm -f
|
||||
|
||||
HOST_XCFLAGS= -Ibluajit
|
||||
HOST_XLIBS=
|
||||
HOST_ACFLAGS= $(XCFLAGS) $(HOST_XCFLAGS) $(TARGET_ARCH)
|
||||
|
||||
TARGET_CC= $(CROSS)$(CC)
|
||||
|
||||
TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE
|
||||
TARGET_XLIBS= -lm
|
||||
TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS)
|
||||
TARGET_ASFLAGS= $(ASOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS)
|
||||
TARGET_ACFLAGS+= $(LUA_CFLAGS)
|
||||
|
||||
TARGET_TESTARCH:=$(shell $(TARGET_CC) $(TARGET_ACFLAGS) -E bluajit/lj_arch.h -dM)
|
||||
ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x64
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x86
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= arm
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_ARM64 ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring __AARCH64EB__ ,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -D__AARCH64EB__=1
|
||||
endif
|
||||
TARGET_LJARCH= arm64
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_LE
|
||||
else
|
||||
TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_BE
|
||||
endif
|
||||
TARGET_LJARCH= ppc
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -D__MIPSEL__=1
|
||||
endif
|
||||
ifneq (,$(findstring LJ_TARGET_MIPS64 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= mips64
|
||||
else
|
||||
TARGET_LJARCH= mips
|
||||
endif
|
||||
else
|
||||
$(error Unsupported target architecture)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH)))
|
||||
TARGET_SYS= PS3
|
||||
TARGET_ARCH+= -D__CELLOS_LV2__
|
||||
TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
TARGET_XLIBS+= -lpthread
|
||||
endif
|
||||
|
||||
TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH))
|
||||
|
||||
ifneq (,$(PREFIX))
|
||||
ifneq (/usr/local,$(PREFIX))
|
||||
TARGET_XCFLAGS+= -DLUA_ROOT=\"$(PREFIX)\"
|
||||
endif
|
||||
endif
|
||||
ifneq (,$(MULTILIB))
|
||||
TARGET_XCFLAGS+= -DLUA_MULTILIB=\"$(MULTILIB)\"
|
||||
endif
|
||||
ifneq (,$(LMULTILIB))
|
||||
TARGET_XCFLAGS+= -DLUA_LMULTILIB=\"$(LMULTILIB)\"
|
||||
endif
|
||||
ifneq (,$(INSTALL_LJLIBD))
|
||||
TARGET_XCFLAGS+= -DLUA_LJDIR=\"$(INSTALL_LJLIBD)\"
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# Target system detection.
|
||||
##############################################################################
|
||||
|
||||
TARGET_SYS?= $(HOST_SYS)
|
||||
ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector 2>/dev/null || echo 1))
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
endif
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
ifeq (,$(MACOSX_DEPLOYMENT_TARGET))
|
||||
$(error missing: export MACOSX_DEPLOYMENT_TARGET=XX.YY)
|
||||
endif
|
||||
TARGET_XCFLAGS+= -DLUAJIT_UNWIND_EXTERNAL
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
ifeq (arm64,$(TARGET_LJARCH))
|
||||
TARGET_XCFLAGS+= -fno-omit-frame-pointer
|
||||
endif
|
||||
else
|
||||
ifeq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH)))
|
||||
# Find out whether the target toolchain always generates unwind tables.
|
||||
TARGET_TESTUNWIND=$(shell exec 2>/dev/null; echo 'extern void b(void);int a(void){b();return 0;}' | $(TARGET_CC) -c -x c - -o tmpunwind.o && { grep -qa -e eh_frame -e __unwind_info tmpunwind.o || grep -qU -e eh_frame -e __unwind_info tmpunwind.o; } && echo E; rm -f tmpunwind.o)
|
||||
ifneq (,$(findstring E,$(TARGET_TESTUNWIND)))
|
||||
TARGET_XCFLAGS+= -DLUAJIT_UNWIND_EXTERNAL
|
||||
endif
|
||||
endif
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
ifeq (GNU/kFreeBSD,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(HOST_SYS),$(TARGET_SYS))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS
|
||||
else
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX -DTARGET_OS_IPHONE=1
|
||||
else
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# Files and pathnames.
|
||||
##############################################################################
|
||||
|
||||
MINILUA_O= $(OBJDIR)/minilua.o
|
||||
MINILUA_LIBS= -lm
|
||||
MINILUA_T= $(OBJDIR)/minilua
|
||||
MINILUA_X= $(MINILUA_T)
|
||||
MINILUA_DEP=
|
||||
|
||||
HOST_LUA= $(MINILUA_X)
|
||||
MINILUA_DEP= $(MINILUA_T)
|
||||
|
||||
DASM_DIR= bluajit/dynasm
|
||||
DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua
|
||||
DASM_XFLAGS=
|
||||
DASM_AFLAGS=
|
||||
DASM_ARCH= $(TARGET_LJARCH)
|
||||
|
||||
ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D ENDIAN_LE
|
||||
else
|
||||
DASM_AFLAGS+= -D ENDIAN_BE
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D P64
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D JIT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FFI
|
||||
endif
|
||||
ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D DUALNUM
|
||||
endif
|
||||
ifneq (,$(findstring LJ_INTONLY 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D INTONLY
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FPU
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=1
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=0
|
||||
endif
|
||||
ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D HFABI
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=0
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=1
|
||||
endif
|
||||
ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D NO_UNWIND
|
||||
TARGET_ARCH+= -DLUAJIT_NO_UNWIND
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ABI_PAUTH 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D PAUTH
|
||||
TARGET_ARCH+= -DLJ_ABI_PAUTH=1
|
||||
endif
|
||||
DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH))))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D WIN
|
||||
endif
|
||||
ifeq (x64,$(TARGET_LJARCH))
|
||||
ifeq (,$(findstring LJ_FR2 1,$(TARGET_TESTARCH)))
|
||||
DASM_ARCH= x86
|
||||
endif
|
||||
else
|
||||
ifeq (arm,$(TARGET_LJARCH))
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D IOS
|
||||
endif
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_MIPSR6 ,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D MIPSR6
|
||||
endif
|
||||
ifeq (ppc,$(TARGET_LJARCH))
|
||||
ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D SQRT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D ROUND
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_PPC32ON64 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D GPR64
|
||||
endif
|
||||
ifeq (PS3,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D PPE -D TOC
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS)
|
||||
DASM_DASC= bluajit/vm_$(DASM_ARCH).dasc
|
||||
|
||||
GIT= git
|
||||
ifeq (Windows,$(HOST_SYS)$(HOST_MSYS))
|
||||
GIT_RELVER= if exist ..\.git ( $(GIT) show -s --format=%%ct >bluajit/luajit_relver.txt ) else ( type ..\.relver >bluajit/luajit_relver.txt )
|
||||
else
|
||||
GIT_RELVER= [ -e ../.git ] && $(GIT) show -s --format=%ct >bluajit/luajit_relver.txt 2>/dev/null || cat ../.relver >bluajit/luajit_relver.txt 2>/dev/null || :
|
||||
endif
|
||||
GIT_DEP= $(wildcard ../.git/HEAD ../.git/refs/heads/*)
|
||||
|
||||
BUILDVM_O= $(OBJDIR)/buildvm.o $(OBJDIR)/buildvm_asm.o $(OBJDIR)/buildvm_peobj.o \
|
||||
$(OBJDIR)/buildvm_lib.o $(OBJDIR)/buildvm_fold.o
|
||||
BUILDVM_T= $(OBJDIR)/buildvm
|
||||
BUILDVM_X= $(BUILDVM_T)
|
||||
|
||||
HOST_O= $(MINILUA_O) $(BUILDVM_O)
|
||||
HOST_T= $(MINILUA_T) $(BUILDVM_T)
|
||||
|
||||
LJVM_S= $(OBJDIR)/lj_vm.S
|
||||
LJVM_O= $(OBJDIR)/lj_vm.o
|
||||
LJVM_BOUT= $(LJVM_S)
|
||||
LJVM_MODE= elfasm
|
||||
|
||||
LJLIB_O= lib_base.o lib_string.o lib_table.o lib_io.o lib_jit.o
|
||||
ifeq (,$(filter -DLUAJIT_SRB2LIB,$(XCFLAGS)))
|
||||
LJLIB_O+= lib_math.o lib_bit.o lib_os.o lib_package.o lib_debug.o lib_ffi.o lib_buffer.o
|
||||
else
|
||||
ifneq (,$(filter -DLUAJIT_SRB2LIB_OS,$(XCFLAGS)))
|
||||
LJLIB_O+= lib_os.o
|
||||
endif
|
||||
ifneq (,$(filter -DLUAJIT_SRB2LIB_DEBUG,$(XCFLAGS)))
|
||||
LJLIB_O+= lib_debug.o
|
||||
endif
|
||||
endif
|
||||
LJLIB_C= $(addprefix bluajit/,$(LJLIB_O:.o=.c))
|
||||
|
||||
LJCORE_O= lj_assert.o lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \
|
||||
lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
|
||||
lj_prng.o lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o \
|
||||
lj_strscan.o lj_strfmt.o lj_strfmt_num.o lj_serialize.o \
|
||||
lj_api.o lj_profile.o \
|
||||
lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
|
||||
lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
|
||||
lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
|
||||
lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
|
||||
lj_asm.o lj_trace.o lj_gdbjit.o \
|
||||
lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \
|
||||
lj_carith.o lj_clib.o lj_cparse.o \
|
||||
lj_lib.o lj_alloc.o lib_aux.o \
|
||||
$(LJLIB_O) lib_init.o
|
||||
|
||||
LJCORE_O:= $(addprefix $(OBJDIR)/,$(LJCORE_O))
|
||||
LJVMCORE_O= $(LJVM_O) $(LJCORE_O)
|
||||
|
||||
LIB_VMDEF= bluajit/jit/vmdef.lua
|
||||
LIB_VMDEFP= $(LIB_VMDEF)
|
||||
|
||||
ALL_T= $(HOST_T)
|
||||
ALL_HDRGEN:= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \
|
||||
host/buildvm_arch.h luajit.h
|
||||
ALL_HDRGEN:= $(addprefix bluajit/,$(ALL_HDRGEN))
|
||||
ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) bluajit/luajit_relver.txt $(LIB_VMDEFP)
|
||||
|
||||
##############################################################################
|
||||
# Build mode handling.
|
||||
##############################################################################
|
||||
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
LJVM_MODE= peobj
|
||||
LJVM_BOUT= $(LJVM_O)
|
||||
endif
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
MINILUA_T= $(OBJDIR)/minilua.exe
|
||||
BUILDVM_T= $(OBJDIR)/buildvm.exe
|
||||
ifeq (,$(HOST_MSYS))
|
||||
MINILUA_X= $(OBJDIR)\minilua
|
||||
BUILDVM_X= $(OBJDIR)\buildvm
|
||||
HOST_RM= del
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
OBJS:=$(OBJS) \
|
||||
$(LJVMCORE_O) \
|
||||
$(OBJDIR)/lua_script.o \
|
||||
$(OBJDIR)/lua_baselib.o \
|
||||
$(OBJDIR)/lua_blockmaplib.o \
|
||||
$(OBJDIR)/lua_mathlib.o \
|
||||
$(OBJDIR)/lua_hooklib.o \
|
||||
$(OBJDIR)/lua_consolelib.o \
|
||||
$(OBJDIR)/lua_infolib.o \
|
||||
$(OBJDIR)/lua_mobjlib.o \
|
||||
$(OBJDIR)/lua_playerlib.o \
|
||||
$(OBJDIR)/lua_skinlib.o \
|
||||
$(OBJDIR)/lua_thinkerlib.o \
|
||||
$(OBJDIR)/lua_maplib.o \
|
||||
$(OBJDIR)/hashtable.o \
|
||||
$(OBJDIR)/lua_hudlib.o \
|
||||
$(OBJDIR)/lua_hudlib_drawlist.o \
|
||||
$(OBJDIR)/lua_udatalib.o \
|
||||
$(OBJDIR)/lua_glib.o
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
Q= @
|
||||
E= @true
|
||||
#Q=
|
||||
#E= @:
|
||||
|
||||
$(MINILUA_T): $(MINILUA_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(HOST_CC) -o $@ $(MINILUA_O) $(MINILUA_LIBS)
|
||||
|
||||
bluajit/luajit.h: $(MINILUA_DEP) $(GIT_DEP) bluajit/luajit_rolling.h
|
||||
$(E) "VERSION $@"
|
||||
$(Q)$(GIT_RELVER)
|
||||
$(Q)cd bluajit && ../$(HOST_LUA) host/genversion.lua
|
||||
|
||||
bluajit/host/buildvm_arch.h: $(DASM_DASC) $(MINILUA_DEP) bluajit/lj_arch.h bluajit/lua.h bluajit/luaconf.h
|
||||
$(E) "DYNASM $@"
|
||||
$(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC)
|
||||
|
||||
$(OBJDIR)/buildvm.o: $(DASM_DIR)/dasm_*.h
|
||||
|
||||
$(BUILDVM_T): $(BUILDVM_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(HOST_CC) -o $@ $(BUILDVM_O)
|
||||
|
||||
$(LJVM_BOUT): $(BUILDVM_T)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@
|
||||
|
||||
bluajit/lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C)
|
||||
|
||||
bluajit/lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C)
|
||||
|
||||
bluajit/lj_libdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C)
|
||||
|
||||
bluajit/lj_recdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C)
|
||||
|
||||
$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C)
|
||||
|
||||
bluajit/lj_folddef.h: $(BUILDVM_T) bluajit/lj_opt_fold.c
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m folddef -o $@ bluajit/lj_opt_fold.c
|
||||
|
||||
$(OBJDIR)/%.o: bluajit/%.c
|
||||
$(E) "CC $@"
|
||||
$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: $(OBJDIR)/lj_vm.S
|
||||
$(E) "ASM $@"
|
||||
$(TARGET_CC) $(TARGET_ASFLAGS) -c -o $@ $<
|
||||
|
||||
$(HOST_O): $(OBJDIR)/%.o: bluajit/host/%.c
|
||||
$(E) "HOSTCC $@"
|
||||
$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $<
|
||||
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
** DynASM ARM encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "arm"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC,
|
||||
DASM_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12, DASM_IMMV8,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
static int dasm_imm12(unsigned int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30))
|
||||
if (n <= 255) return (int)(n + (i << 8));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
case DASM_IMM16:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
if ((ins & 0x8000))
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMV8:
|
||||
CK((n & 3) == 0, RANGE_I);
|
||||
n >>= 2;
|
||||
/* fallthrough */
|
||||
case DASM_IMML8:
|
||||
case DASM_IMML12:
|
||||
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) :
|
||||
(((-n)>>((ins>>5)&31)) == 0), RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMM12: case DASM_IMM16:
|
||||
case DASM_IMML8: case DASM_IMML12: case DASM_IMMV8: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
n = (int)((ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp - 4);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) - 4;
|
||||
patchrel:
|
||||
if ((ins & 0x800) == 0) {
|
||||
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n >> 2) & 0x00ffffff);
|
||||
} else if ((ins & 0x1000)) {
|
||||
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL);
|
||||
goto patchimml8;
|
||||
} else if ((ins & 0x2000) == 0) {
|
||||
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL);
|
||||
goto patchimml;
|
||||
} else {
|
||||
CK((n & 3) == 0 && -1020 <= n && n <= 1020, RANGE_REL);
|
||||
n >>= 2;
|
||||
goto patchimml;
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
cp[-1] |= dasm_imm12((unsigned int)n);
|
||||
break;
|
||||
case DASM_IMM16:
|
||||
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff);
|
||||
break;
|
||||
case DASM_IMML8: patchimml8:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) :
|
||||
((-n & 0x0f) | ((-n & 0xf0) << 4));
|
||||
break;
|
||||
case DASM_IMML12: case DASM_IMMV8: patchimml:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
** DynASM ARM64 encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "arm64"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_REL_A,
|
||||
DASM_IMM, DASM_IMM6, DASM_IMM12, DASM_IMM13W, DASM_IMM13X, DASM_IMML,
|
||||
DASM_IMMV, DASM_VREG,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_RANGE_VREG 0x16000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
static int dasm_imm12(unsigned int n)
|
||||
{
|
||||
if ((n >> 12) == 0)
|
||||
return n;
|
||||
else if ((n & 0xff000fff) == 0)
|
||||
return (n >> 12) | 0x1000;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int dasm_ffs(unsigned long long x)
|
||||
{
|
||||
int n = -1;
|
||||
while (x) { x >>= 1; n++; }
|
||||
return n;
|
||||
}
|
||||
|
||||
static int dasm_imm13(int lo, int hi)
|
||||
{
|
||||
int inv = 0, w = 64, s = 0xfff, xa, xb;
|
||||
unsigned long long n = (((unsigned long long)hi) << 32) | (unsigned int)lo;
|
||||
unsigned long long m = 1ULL, a, b, c;
|
||||
if (n & 1) { n = ~n; inv = 1; }
|
||||
a = n & (unsigned long long)-(long long)n;
|
||||
b = (n+a)&(unsigned long long)-(long long)(n+a);
|
||||
c = (n+a-b)&(unsigned long long)-(long long)(n+a-b);
|
||||
xa = dasm_ffs(a); xb = dasm_ffs(b);
|
||||
if (c) {
|
||||
w = dasm_ffs(c) - xa;
|
||||
if (w == 32) m = 0x0000000100000001UL;
|
||||
else if (w == 16) m = 0x0001000100010001UL;
|
||||
else if (w == 8) m = 0x0101010101010101UL;
|
||||
else if (w == 4) m = 0x1111111111111111UL;
|
||||
else if (w == 2) m = 0x5555555555555555UL;
|
||||
else return -1;
|
||||
s = (-2*w & 0x3f) - 1;
|
||||
} else if (!a) {
|
||||
return -1;
|
||||
} else if (xb == -1) {
|
||||
xb = 64;
|
||||
}
|
||||
if ((b-a) * m != n) return -1;
|
||||
if (inv) {
|
||||
return ((w - xb) << 6) | (s+w+xa-xb);
|
||||
} else {
|
||||
return ((w - xa) << 6) | (s+xb-xa);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: if ((ins & 0x8000)) ofs += 8; break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
if ((ins & 0x8000)) ofs += 8;
|
||||
break;
|
||||
case DASM_REL_A:
|
||||
b[pos++] = n;
|
||||
b[pos++] = va_arg(ap, int);
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if ((ins & 0x8000))
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM6:
|
||||
CK((n >> 6) == 0, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM13W:
|
||||
CK(dasm_imm13(n, n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM13X: {
|
||||
int m = va_arg(ap, int);
|
||||
CK(dasm_imm13(n, m) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
b[pos++] = m;
|
||||
break;
|
||||
}
|
||||
case DASM_IMML: {
|
||||
#ifdef DASM_CHECKS
|
||||
int scale = (ins & 3);
|
||||
CK((!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ||
|
||||
(unsigned int)(n+256) < 512, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
case DASM_IMMV:
|
||||
ofs += 4;
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_VREG:
|
||||
CK(n < 32, RANGE_VREG);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMM6: case DASM_IMM12: case DASM_IMM13W:
|
||||
case DASM_IMML: case DASM_IMMV: case DASM_VREG: pos++; break;
|
||||
case DASM_IMM13X: case DASM_REL_A: pos += 2; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(int)(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xd503201f;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
ptrdiff_t na = (ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp + 4;
|
||||
n = (int)na;
|
||||
CK((ptrdiff_t)n == na, RANGE_REL);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) + 4;
|
||||
patchrel:
|
||||
if (!(ins & 0xf800)) { /* B, BL */
|
||||
CK((n & 3) == 0 && ((n+0x08000000) >> 28) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n >> 2) & 0x03ffffff);
|
||||
} else if ((ins & 0x800)) { /* B.cond, CBZ, CBNZ, LDR* literal */
|
||||
CK((n & 3) == 0 && ((n+0x00100000) >> 21) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n << 3) & 0x00ffffe0);
|
||||
} else if ((ins & 0x3000) == 0x2000) { /* ADR */
|
||||
CK(((n+0x00100000) >> 21) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n << 3) & 0x00ffffe0) | ((n & 3) << 29);
|
||||
} else if ((ins & 0x3000) == 0x3000) { /* ADRP */
|
||||
cp[-1] |= ((n >> 9) & 0x00ffffe0) | (((n >> 12) & 3) << 29);
|
||||
} else if ((ins & 0x1000)) { /* TBZ, TBNZ */
|
||||
CK((n & 3) == 0 && ((n+0x00008000) >> 16) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n << 3) & 0x0007ffe0);
|
||||
} else if ((ins & 0x8000)) { /* absolute */
|
||||
cp[0] = (unsigned int)((ptrdiff_t)cp - 4 + n);
|
||||
cp[1] = (unsigned int)(((ptrdiff_t)cp - 4 + n) >> 32);
|
||||
cp += 2;
|
||||
}
|
||||
break;
|
||||
case DASM_REL_A: {
|
||||
ptrdiff_t na = (((ptrdiff_t)(*b++) << 32) | (unsigned int)n);
|
||||
if ((ins & 0x3000) == 0x3000) { /* ADRP */
|
||||
ins &= ~0x1000;
|
||||
na = (na >> 12) - (((ptrdiff_t)cp - 4) >> 12);
|
||||
} else {
|
||||
na = na - (ptrdiff_t)cp + 4;
|
||||
}
|
||||
n = (int)na;
|
||||
CK((ptrdiff_t)n == na, RANGE_REL);
|
||||
goto patchrel;
|
||||
}
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMM6:
|
||||
cp[-1] |= ((n&31) << 19) | ((n&32) << 26);
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
cp[-1] |= (dasm_imm12((unsigned int)n) << 10);
|
||||
break;
|
||||
case DASM_IMM13W:
|
||||
cp[-1] |= (dasm_imm13(n, n) << 10);
|
||||
break;
|
||||
case DASM_IMM13X:
|
||||
cp[-1] |= (dasm_imm13(n, *b++) << 10);
|
||||
break;
|
||||
case DASM_IMML: {
|
||||
int scale = (ins & 3);
|
||||
cp[-1] |= (!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ?
|
||||
((n << (10-scale)) | 0x01000000) : ((n & 511) << 12);
|
||||
break;
|
||||
}
|
||||
case DASM_IMMV:
|
||||
*cp++ = n;
|
||||
break;
|
||||
case DASM_VREG:
|
||||
cp[-1] |= (n & 0x1f) << (ins & 0x1f);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
** DynASM MIPS encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "mips"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMS,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM: case DASM_IMMS:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMMS: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(int)(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1);
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
n = (int)((ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n);
|
||||
if (ins & 2048)
|
||||
n = (n + (int)(size_t)base) & 0x0fffffff;
|
||||
else
|
||||
n = n - (int)((char *)cp - base);
|
||||
patchrel: {
|
||||
unsigned int e = 16 + ((ins >> 12) & 15);
|
||||
CK((n & 3) == 0 &&
|
||||
((n + ((ins & 2048) ? 0 : (1<<(e+1)))) >> (e+2)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n>>2) & ((1<<e)-1));
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMMS:
|
||||
cp[-1] |= ((n>>3) & 4); n &= 0x1f;
|
||||
/* fallthrough */
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,12 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM MIPS64 module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
-- This module just sets 64 bit mode for the combined MIPS/MIPS64 module.
|
||||
-- All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
mips64 = true -- Using a global is an ugly, but effective solution.
|
||||
return require("dasm_mips")
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
** DynASM PPC/PPC64 encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "ppc"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMSH,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMSH:
|
||||
CK((n >> 6) == 0, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMMSH: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1) - 4;
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
if (n < 0) {
|
||||
n = (int)((ptrdiff_t)D->globals[-n-10] - (ptrdiff_t)cp);
|
||||
goto patchrel;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base);
|
||||
patchrel:
|
||||
CK((n & 3) == 0 &&
|
||||
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >>
|
||||
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc));
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-20] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMMSH:
|
||||
cp[-1] |= (ins & 1) ? ((n&31)<<11)|((n&32)>>4) : ((n&31)<<6)|(n&32);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
** DynASM encoding engine prototypes.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#ifndef _DASM_PROTO_H
|
||||
#define _DASM_PROTO_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define DASM_IDENT "DynASM 1.5.0"
|
||||
#define DASM_VERSION 10500 /* 1.5.0 */
|
||||
|
||||
#ifndef Dst_DECL
|
||||
#define Dst_DECL dasm_State **Dst
|
||||
#endif
|
||||
|
||||
#ifndef Dst_REF
|
||||
#define Dst_REF (*Dst)
|
||||
#endif
|
||||
|
||||
#ifndef DASM_FDEF
|
||||
#define DASM_FDEF extern
|
||||
#endif
|
||||
|
||||
#ifndef DASM_M_GROW
|
||||
#define DASM_M_GROW(ctx, t, p, sz, need) \
|
||||
do { \
|
||||
size_t _sz = (sz), _need = (need); \
|
||||
if (_sz < _need) { \
|
||||
if (_sz < 16) _sz = 16; \
|
||||
while (_sz < _need) _sz += _sz; \
|
||||
(p) = (t *)realloc((p), _sz); \
|
||||
if ((p) == NULL) exit(1); \
|
||||
(sz) = _sz; \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef DASM_M_FREE
|
||||
#define DASM_M_FREE(ctx, p, sz) free(p)
|
||||
#endif
|
||||
|
||||
/* Internal DynASM encoder state. */
|
||||
typedef struct dasm_State dasm_State;
|
||||
|
||||
|
||||
/* Initialize and free DynASM state. */
|
||||
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
|
||||
DASM_FDEF void dasm_free(Dst_DECL);
|
||||
|
||||
/* Setup global array. Must be called before dasm_setup(). */
|
||||
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
|
||||
|
||||
/* Setup encoder. */
|
||||
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist);
|
||||
|
||||
/* Feed encoder with actions. Calls are generated by pre-processor. */
|
||||
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
|
||||
|
||||
/* Link sections and return the resulting size. */
|
||||
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
|
||||
|
||||
/* Encode sections into buffer. */
|
||||
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
|
||||
|
||||
/* Get PC label offset. */
|
||||
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
|
||||
#else
|
||||
#define dasm_checkstep(a, b) 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _DASM_PROTO_H */
|
||||
@@ -0,0 +1,12 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM x64 module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
-- This module just sets 64 bit mode for the combined x86/x64 module.
|
||||
-- All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
x64 = true -- Using a global is an ugly, but effective solution.
|
||||
return require("dasm_x86")
|
||||
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
** DynASM x86 encoding engine.
|
||||
** Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "x86"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. DASM_STOP must be 255. */
|
||||
enum {
|
||||
DASM_DISP = 233,
|
||||
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
|
||||
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
|
||||
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
|
||||
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_VREG 0x15000000
|
||||
#define DASM_S_UNDEF_L 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned char *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals. */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
memset((void *)D->sections, 0, maxsection * sizeof(dasm_Section));
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl;
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].rbuf = D->sections[i].buf - D->sections[i].pos;
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs, mrm = -1;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
int action = *p++;
|
||||
if (action < DASM_DISP) {
|
||||
ofs++;
|
||||
} else if (action <= DASM_REL_A) {
|
||||
int n = va_arg(ap, int);
|
||||
b[pos++] = n;
|
||||
switch (action) {
|
||||
case DASM_DISP:
|
||||
if (n == 0) { if (mrm < 0) mrm = p[-2]; if ((mrm&7) != 5) break; }
|
||||
/* fallthrough */
|
||||
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */
|
||||
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */
|
||||
case DASM_IMM_D: ofs += 4; break;
|
||||
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob;
|
||||
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break;
|
||||
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */
|
||||
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break;
|
||||
case DASM_SPACE: p++; ofs += n; break;
|
||||
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */
|
||||
case DASM_VREG: CK((n&-16) == 0 && (n != 4 || (*p>>5) != 2), RANGE_VREG);
|
||||
if (*p < 0x40 && p[1] == DASM_DISP) mrm = n;
|
||||
if (*p < 0x20 && (n&7) == 4) ofs++;
|
||||
switch ((*p++ >> 3) & 3) {
|
||||
case 3: n |= b[pos-3]; /* fallthrough */
|
||||
case 2: n |= b[pos-2]; /* fallthrough */
|
||||
case 1: if (n <= 7) { b[pos-1] |= 0x10; ofs--; }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
mrm = -1;
|
||||
} else {
|
||||
int *pl, n;
|
||||
switch (action) {
|
||||
case DASM_REL_LG:
|
||||
case DASM_IMM_LG:
|
||||
n = *p++; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n <= 246) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl -= 246; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
ofs += 4; /* Maximum offset needed. */
|
||||
if (action == DASM_REL_LG || action == DASM_REL_PC) {
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
} else if (sizeof(ptrdiff_t) == 8) {
|
||||
ofs += 4;
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; }
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_ALIGN:
|
||||
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_EXTERN: p += 2; ofs += 4; break;
|
||||
case DASM_ESC: p++; ofs++; break;
|
||||
case DASM_MARK: mrm = p[-2]; break;
|
||||
case DASM_SECTION:
|
||||
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n];
|
||||
case DASM_STOP: goto stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
int op = 0;
|
||||
while (1) {
|
||||
int action = *p++;
|
||||
switch (action) {
|
||||
case DASM_REL_LG: p++;
|
||||
/* fallthrough */
|
||||
case DASM_REL_PC: {
|
||||
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0);
|
||||
if (shrink) { /* Shrinkable branch opcode? */
|
||||
int lofs, lpos = b[pos];
|
||||
if (lpos < 0) goto noshrink; /* Ext global? */
|
||||
lofs = *DASM_POS2PTR(D, lpos);
|
||||
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */
|
||||
int i;
|
||||
for (i = secnum; i < DASM_POS2SEC(lpos); i++)
|
||||
lofs += D->sections[i].ofs;
|
||||
} else {
|
||||
lofs -= ofs; /* Bkwd label: unfix offset. */
|
||||
}
|
||||
lofs -= b[pos+1]; /* Short branch ok? */
|
||||
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */
|
||||
else { noshrink: shrink = 0; } /* No, cannot shrink op. */
|
||||
}
|
||||
b[pos+1] = shrink;
|
||||
pos += 2;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++;
|
||||
/* fallthrough */
|
||||
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W:
|
||||
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB:
|
||||
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break;
|
||||
case DASM_LABEL_LG: p++;
|
||||
/* fallthrough */
|
||||
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */
|
||||
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */
|
||||
case DASM_EXTERN: p += 2; break;
|
||||
case DASM_ESC: op = *p++; break;
|
||||
case DASM_MARK: break;
|
||||
case DASM_SECTION: case DASM_STOP: goto stop;
|
||||
default: op = action; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#define dasmb(x) *cp++ = (unsigned char)(x)
|
||||
#ifndef DASM_ALIGNED_WRITES
|
||||
#define dasmw(x) \
|
||||
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
|
||||
#define dasmd(x) \
|
||||
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
|
||||
#define dasmq(x) \
|
||||
do { *((unsigned long long *)cp) = (unsigned long long)(x); cp+=8; } while (0)
|
||||
#else
|
||||
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
|
||||
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
|
||||
#define dasmq(x) do { dasmd(x); dasmd((x)>>32); } while (0)
|
||||
#endif
|
||||
static unsigned char *dasma_(unsigned char *cp, ptrdiff_t x)
|
||||
{
|
||||
if (sizeof(ptrdiff_t) == 8)
|
||||
dasmq((unsigned long long)x);
|
||||
else
|
||||
dasmd((unsigned int)x);
|
||||
return cp;
|
||||
}
|
||||
#define dasma(x) (cp = dasma_(cp, (x)))
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
unsigned char *base = (unsigned char *)buffer;
|
||||
unsigned char *cp = base;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
unsigned char *mark = NULL;
|
||||
while (1) {
|
||||
int action = *p++;
|
||||
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_DISP: if (!mark) mark = cp; {
|
||||
unsigned char *mm = mark;
|
||||
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL;
|
||||
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7;
|
||||
if (mrm != 5) { mm[-1] -= 0x80; break; } }
|
||||
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40;
|
||||
}
|
||||
/* fallthrough */
|
||||
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break;
|
||||
case DASM_IMM_DB: if (((n+128)&-256) == 0) {
|
||||
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb;
|
||||
} else mark = NULL;
|
||||
/* fallthrough */
|
||||
case DASM_IMM_D: wd: dasmd(n); break;
|
||||
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL;
|
||||
/* fallthrough */
|
||||
case DASM_IMM_W: dasmw(n); break;
|
||||
case DASM_VREG: {
|
||||
int t = *p++;
|
||||
unsigned char *ex = cp - (t&7);
|
||||
if ((n & 8) && t < 0xa0) {
|
||||
if (*ex & 0x80) ex[1] ^= 0x20 << (t>>6); else *ex ^= 1 << (t>>6);
|
||||
n &= 7;
|
||||
} else if (n & 0x10) {
|
||||
if (*ex & 0x80) {
|
||||
*ex = 0xc5; ex[1] = (ex[1] & 0x80) | ex[2]; ex += 2;
|
||||
}
|
||||
while (++ex < cp) ex[-1] = *ex;
|
||||
if (mark) mark--;
|
||||
cp--;
|
||||
n &= 7;
|
||||
}
|
||||
if (t >= 0xc0) n <<= 4;
|
||||
else if (t >= 0x40) n <<= 3;
|
||||
else if (n == 4 && t < 0x20) { cp[-1] ^= n; *cp++ = 0x20; }
|
||||
cp[-1] ^= n;
|
||||
break;
|
||||
}
|
||||
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc;
|
||||
b++; n = (int)(ptrdiff_t)D->globals[-n-10];
|
||||
/* fallthrough */
|
||||
case DASM_REL_A: rel_a:
|
||||
n -= (unsigned int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */
|
||||
case DASM_REL_PC: rel_pc: {
|
||||
int shrink = *b++;
|
||||
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; }
|
||||
n = *pb - ((int)(cp-base) + 4-shrink);
|
||||
if (shrink == 0) goto wd;
|
||||
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb;
|
||||
goto wb;
|
||||
}
|
||||
case DASM_IMM_LG:
|
||||
p++;
|
||||
if (n < 0) { dasma((ptrdiff_t)D->globals[-n-10]); break; }
|
||||
/* fallthrough */
|
||||
case DASM_IMM_PC: {
|
||||
int *pb = DASM_POS2PTR(D, n);
|
||||
dasma(*pb < 0 ? (ptrdiff_t)pb[1] : (*pb + (ptrdiff_t)base));
|
||||
break;
|
||||
}
|
||||
case DASM_LABEL_LG: {
|
||||
int idx = *p++;
|
||||
if (idx >= 10)
|
||||
D->globals[idx-10] = (void *)(base + (*p == DASM_SETLABEL ? *b : n));
|
||||
break;
|
||||
}
|
||||
case DASM_LABEL_PC: case DASM_SETLABEL: break;
|
||||
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; }
|
||||
case DASM_ALIGN:
|
||||
n = *p++;
|
||||
while (((cp-base) & n)) *cp++ = 0x90; /* nop */
|
||||
break;
|
||||
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd;
|
||||
case DASM_MARK: mark = cp; break;
|
||||
case DASM_ESC: action = *p++;
|
||||
/* fallthrough */
|
||||
default: *cp++ = action; break;
|
||||
case DASM_SECTION: case DASM_STOP: goto stop;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,3 @@
|
||||
minilua
|
||||
buildvm
|
||||
buildvm_arch.h
|
||||
@@ -0,0 +1,4 @@
|
||||
The files in this directory are only used during the build process of LuaJIT.
|
||||
For cross-compilation, they must be executed on the host, not on the target.
|
||||
|
||||
These files should NOT be installed!
|
||||
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
** LuaJIT VM builder.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** This is a tool to build the hand-tuned assembler code required for
|
||||
** LuaJIT's bytecode interpreter. It supports a variety of output formats
|
||||
** to feed different toolchains (see usage() below).
|
||||
**
|
||||
** This tool is not particularly optimized because it's only used while
|
||||
** _building_ LuaJIT. There's no point in distributing or installing it.
|
||||
** Only the object code generated by this tool is linked into LuaJIT.
|
||||
**
|
||||
** Caveat: some memory is not free'd, error handling is lazy.
|
||||
** It's a one-shot tool -- any effort fixing this would be wasted.
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_ir.h"
|
||||
#include "lj_ircall.h"
|
||||
#endif
|
||||
#include "lj_frame.h"
|
||||
#include "lj_dispatch.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_ccall.h"
|
||||
#endif
|
||||
#include "luajit.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* DynASM glue definitions. */
|
||||
#define Dst ctx
|
||||
#define Dst_DECL BuildCtx *ctx
|
||||
#define Dst_REF (ctx->D)
|
||||
#define DASM_CHECKS 1
|
||||
|
||||
#include "../dynasm/dasm_proto.h"
|
||||
|
||||
/* Glue macros for DynASM. */
|
||||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type);
|
||||
|
||||
#define DASM_EXTERN(ctx, addr, idx, type) \
|
||||
collect_reloc(ctx, addr, idx, type)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */
|
||||
#define DASM_ALIGNED_WRITES 1
|
||||
|
||||
/* Embed architecture-specific DynASM encoder. */
|
||||
#if LJ_TARGET_X86ORX64
|
||||
#include "../dynasm/dasm_x86.h"
|
||||
#elif LJ_TARGET_ARM
|
||||
#include "../dynasm/dasm_arm.h"
|
||||
#elif LJ_TARGET_ARM64
|
||||
#include "../dynasm/dasm_arm64.h"
|
||||
#elif LJ_TARGET_PPC
|
||||
#include "../dynasm/dasm_ppc.h"
|
||||
#elif LJ_TARGET_MIPS
|
||||
#include "../dynasm/dasm_mips.h"
|
||||
#else
|
||||
#error "No support for this architecture (yet)"
|
||||
#endif
|
||||
|
||||
/* Embed generated architecture-specific backend. */
|
||||
#include "buildvm_arch.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
void owrite(BuildCtx *ctx, const void *ptr, size_t sz)
|
||||
{
|
||||
if (fwrite(ptr, 1, sz, ctx->fp) != sz) {
|
||||
fprintf(stderr, "Error: cannot write to output file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Emit code as raw bytes. Only used for DynASM debugging. */
|
||||
static void emit_raw(BuildCtx *ctx)
|
||||
{
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
}
|
||||
|
||||
/* -- Build machine code -------------------------------------------------- */
|
||||
|
||||
static const char *sym_decorate(BuildCtx *ctx,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
char name[256];
|
||||
char *p;
|
||||
#if LJ_64
|
||||
const char *symprefix = ctx->mode == BUILD_machasm ? "_" : "";
|
||||
#elif LJ_TARGET_XBOX360
|
||||
const char *symprefix = "";
|
||||
#else
|
||||
const char *symprefix = ctx->mode != BUILD_elfasm ? "_" : "";
|
||||
#endif
|
||||
sprintf(name, "%s%s%s", symprefix, prefix, suffix);
|
||||
p = strchr(name, '@');
|
||||
if (p) {
|
||||
#if LJ_TARGET_X86ORX64
|
||||
if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj))
|
||||
name[0] = name[1] == 'R' ? '_' : '@'; /* Just for _RtlUnwind@16. */
|
||||
else
|
||||
*p = '\0';
|
||||
#elif LJ_TARGET_PPC && !LJ_TARGET_CONSOLE
|
||||
/* Keep @plt etc. */
|
||||
#else
|
||||
*p = '\0';
|
||||
#endif
|
||||
}
|
||||
p = (char *)malloc(strlen(name)+1); /* MSVC doesn't like strdup. */
|
||||
strcpy(p, name);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1)
|
||||
|
||||
static int relocmap[NRELOCSYM];
|
||||
|
||||
/* Collect external relocations. */
|
||||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type)
|
||||
{
|
||||
if (ctx->nreloc >= BUILD_MAX_RELOC) {
|
||||
fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (relocmap[idx] < 0) {
|
||||
relocmap[idx] = ctx->nrelocsym;
|
||||
ctx->relocsym[ctx->nrelocsym] = sym_decorate(ctx, "", extnames[idx]);
|
||||
ctx->nrelocsym++;
|
||||
}
|
||||
ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code);
|
||||
ctx->reloc[ctx->nreloc].sym = relocmap[idx];
|
||||
ctx->reloc[ctx->nreloc].type = type;
|
||||
ctx->nreloc++;
|
||||
#if LJ_TARGET_XBOX360
|
||||
return (int)(ctx->code - addr) + 4; /* Encode symbol offset of .text. */
|
||||
#else
|
||||
return 0; /* Encode symbol offset of 0. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Naive insertion sort. Performance doesn't matter here. */
|
||||
static void sym_insert(BuildCtx *ctx, int32_t ofs,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
ptrdiff_t i = ctx->nsym++;
|
||||
while (i > 0) {
|
||||
if (ctx->sym[i-1].ofs <= ofs)
|
||||
break;
|
||||
ctx->sym[i] = ctx->sym[i-1];
|
||||
i--;
|
||||
}
|
||||
ctx->sym[i].ofs = ofs;
|
||||
ctx->sym[i].name = sym_decorate(ctx, prefix, suffix);
|
||||
}
|
||||
|
||||
/* Build the machine code. */
|
||||
static int build_code(BuildCtx *ctx)
|
||||
{
|
||||
int status;
|
||||
int i;
|
||||
|
||||
/* Initialize DynASM structures. */
|
||||
ctx->nglob = GLOB__MAX;
|
||||
ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *));
|
||||
memset(ctx->glob, 0, ctx->nglob*sizeof(void *));
|
||||
ctx->nreloc = 0;
|
||||
|
||||
ctx->globnames = globnames;
|
||||
ctx->extnames = extnames;
|
||||
ctx->relocsym = (const char **)malloc(NRELOCSYM*sizeof(const char *));
|
||||
ctx->nrelocsym = 0;
|
||||
for (i = 0; i < (int)NRELOCSYM; i++) relocmap[i] = -1;
|
||||
|
||||
ctx->dasm_ident = DASM_IDENT;
|
||||
ctx->dasm_arch = DASM_ARCH;
|
||||
|
||||
dasm_init(Dst, DASM_MAXSECTION);
|
||||
dasm_setupglobal(Dst, ctx->glob, ctx->nglob);
|
||||
dasm_setup(Dst, build_actionlist);
|
||||
|
||||
/* Call arch-specific backend to emit the code. */
|
||||
ctx->npc = build_backend(ctx);
|
||||
|
||||
/* Finalize the code. */
|
||||
(void)dasm_checkstep(Dst, -1);
|
||||
if ((status = dasm_link(Dst, &ctx->codesz))) return status;
|
||||
ctx->code = (uint8_t *)malloc(ctx->codesz);
|
||||
if ((status = dasm_encode(Dst, (void *)ctx->code))) return status;
|
||||
|
||||
/* Allocate symbol table and bytecode offsets. */
|
||||
ctx->beginsym = sym_decorate(ctx, "", LABEL_PREFIX "vm_asm_begin");
|
||||
ctx->sym = (BuildSym *)malloc((ctx->npc+ctx->nglob+1)*sizeof(BuildSym));
|
||||
ctx->nsym = 0;
|
||||
ctx->bc_ofs = (int32_t *)malloc(ctx->npc*sizeof(int32_t));
|
||||
|
||||
/* Collect the opcodes (PC labels). */
|
||||
for (i = 0; i < ctx->npc; i++) {
|
||||
int32_t ofs = dasm_getpclabel(Dst, i);
|
||||
if (ofs < 0) return 0x22000000|i;
|
||||
ctx->bc_ofs[i] = ofs;
|
||||
if ((LJ_HASJIT ||
|
||||
!(i == BC_JFORI || i == BC_JFORL || i == BC_JITERL || i == BC_JLOOP ||
|
||||
i == BC_IFORL || i == BC_IITERL || i == BC_ILOOP)) &&
|
||||
(LJ_HASFFI || i != BC_KCDATA))
|
||||
sym_insert(ctx, ofs, LABEL_PREFIX_BC, bc_names[i]);
|
||||
}
|
||||
|
||||
/* Collect the globals (named labels). */
|
||||
for (i = 0; i < ctx->nglob; i++) {
|
||||
const char *gl = globnames[i];
|
||||
int len = (int)strlen(gl);
|
||||
if (!ctx->glob[i]) {
|
||||
fprintf(stderr, "Error: undefined global %s\n", gl);
|
||||
exit(2);
|
||||
}
|
||||
/* Skip the _Z symbols. */
|
||||
if (!(len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z'))
|
||||
sym_insert(ctx, (int32_t)((uint8_t *)(ctx->glob[i]) - ctx->code),
|
||||
LABEL_PREFIX, globnames[i]);
|
||||
}
|
||||
|
||||
/* Close the address range. */
|
||||
sym_insert(ctx, (int32_t)ctx->codesz, "", "");
|
||||
ctx->nsym--;
|
||||
|
||||
dasm_free(Dst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- Generate VM enums --------------------------------------------------- */
|
||||
|
||||
const char *const bc_names[] = {
|
||||
#define BCNAME(name, ma, mb, mc, mt) #name,
|
||||
BCDEF(BCNAME)
|
||||
#undef BCNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
#if LJ_HASJIT
|
||||
const char *const ir_names[] = {
|
||||
#define IRNAME(name, m, m1, m2) #name,
|
||||
IRDEF(IRNAME)
|
||||
#undef IRNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irt_names[] = {
|
||||
#define IRTNAME(name, size) #name,
|
||||
IRTDEF(IRTNAME)
|
||||
#undef IRTNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irfpm_names[] = {
|
||||
#define FPMNAME(name) #name,
|
||||
IRFPMDEF(FPMNAME)
|
||||
#undef FPMNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irfield_names[] = {
|
||||
#define FLNAME(name, ofs) #name,
|
||||
IRFLDEF(FLNAME)
|
||||
#undef FLNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const ircall_names[] = {
|
||||
#define IRCALLNAME(cond, name, nargs, kind, type, flags) #name,
|
||||
IRCALLDEF(IRCALLNAME)
|
||||
#undef IRCALLNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *const trace_errors[] = {
|
||||
#define TREDEF(name, msg) msg,
|
||||
#include "lj_traceerr.h"
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
#if LJ_HASJIT
|
||||
static const char *lower(char *buf, const char *s)
|
||||
{
|
||||
char *p = buf;
|
||||
while (*s) {
|
||||
*p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s;
|
||||
s++;
|
||||
}
|
||||
*p = '\0';
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Emit C source code for bytecode-related definitions. */
|
||||
static void emit_bcdef(BuildCtx *ctx)
|
||||
{
|
||||
int i;
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n");
|
||||
for (i = 0; i < ctx->npc; i++) {
|
||||
if (i != 0)
|
||||
fprintf(ctx->fp, ",\n");
|
||||
fprintf(ctx->fp, "%d", ctx->bc_ofs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit VM definitions as Lua code for debug modules. */
|
||||
static void emit_vmdef(BuildCtx *ctx)
|
||||
{
|
||||
#if LJ_HASJIT
|
||||
char buf[80];
|
||||
#endif
|
||||
int i;
|
||||
fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n");
|
||||
fprintf(ctx->fp, "assert(require(\"jit\").version == \"%s\", \"LuaJIT core/library version mismatch\")\n\n", LUAJIT_VERSION);
|
||||
fprintf(ctx->fp, "return {\n\n");
|
||||
|
||||
fprintf(ctx->fp, "bcnames = \"");
|
||||
for (i = 0; bc_names[i]; i++) fprintf(ctx->fp, "%-6s", bc_names[i]);
|
||||
fprintf(ctx->fp, "\",\n\n");
|
||||
|
||||
#if LJ_HASJIT
|
||||
fprintf(ctx->fp, "irnames = \"");
|
||||
for (i = 0; ir_names[i]; i++) fprintf(ctx->fp, "%-6s", ir_names[i]);
|
||||
fprintf(ctx->fp, "\",\n\n");
|
||||
|
||||
fprintf(ctx->fp, "irfpm = { [0]=");
|
||||
for (i = 0; irfpm_names[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\", ", lower(buf, irfpm_names[i]));
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
|
||||
fprintf(ctx->fp, "irfield = { [0]=");
|
||||
for (i = 0; irfield_names[i]; i++) {
|
||||
char *p;
|
||||
lower(buf, irfield_names[i]);
|
||||
p = strchr(buf, '_');
|
||||
if (p) *p = '.';
|
||||
fprintf(ctx->fp, "\"%s\", ", buf);
|
||||
}
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
|
||||
fprintf(ctx->fp, "ircall = {\n[0]=");
|
||||
for (i = 0; ircall_names[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\",\n", ircall_names[i]);
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
|
||||
fprintf(ctx->fp, "traceerr = {\n[0]=");
|
||||
for (i = 0; trace_errors[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]);
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -- Argument parsing ---------------------------------------------------- */
|
||||
|
||||
/* Build mode names. */
|
||||
static const char *const modenames[] = {
|
||||
#define BUILDNAME(name) #name,
|
||||
BUILDDEF(BUILDNAME)
|
||||
#undef BUILDNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Print usage information and exit. */
|
||||
static void usage(void)
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, LUAJIT_VERSION " VM builder.\n");
|
||||
fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n");
|
||||
fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n");
|
||||
fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n");
|
||||
fprintf(stderr, "Available modes:\n");
|
||||
for (i = 0; i < BUILD__MAX; i++)
|
||||
fprintf(stderr, " %s\n", modenames[i]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Parse the output mode name. */
|
||||
static BuildMode parsemode(const char *mode)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; modenames[i]; i++)
|
||||
if (!strcmp(mode, modenames[i]))
|
||||
return (BuildMode)i;
|
||||
usage();
|
||||
return (BuildMode)-1;
|
||||
}
|
||||
|
||||
/* Parse arguments. */
|
||||
static void parseargs(BuildCtx *ctx, char **argv)
|
||||
{
|
||||
const char *a;
|
||||
int i;
|
||||
ctx->mode = (BuildMode)-1;
|
||||
ctx->outname = "-";
|
||||
for (i = 1; (a = argv[i]) != NULL; i++) {
|
||||
if (a[0] != '-')
|
||||
break;
|
||||
switch (a[1]) {
|
||||
case '-':
|
||||
if (a[2]) goto err;
|
||||
i++;
|
||||
goto ok;
|
||||
case '\0':
|
||||
goto ok;
|
||||
case 'm':
|
||||
i++;
|
||||
if (a[2] || argv[i] == NULL) goto err;
|
||||
ctx->mode = parsemode(argv[i]);
|
||||
break;
|
||||
case 'o':
|
||||
i++;
|
||||
if (a[2] || argv[i] == NULL) goto err;
|
||||
ctx->outname = argv[i];
|
||||
break;
|
||||
default: err:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok:
|
||||
ctx->args = argv+i;
|
||||
if (ctx->mode == (BuildMode)-1) goto err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
BuildCtx ctx_;
|
||||
BuildCtx *ctx = &ctx_;
|
||||
int status, binmode;
|
||||
|
||||
if (sizeof(void *) != 4*LJ_32+8*LJ_64) {
|
||||
fprintf(stderr,"Error: pointer size mismatch in cross-build.\n");
|
||||
fprintf(stderr,"Try: make HOST_CC=\"gcc -m32\" CROSS=...\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
UNUSED(argc);
|
||||
parseargs(ctx, argv);
|
||||
|
||||
if ((status = build_code(ctx))) {
|
||||
fprintf(stderr,"Error: DASM error %08x\n", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (ctx->mode) {
|
||||
case BUILD_peobj:
|
||||
case BUILD_raw:
|
||||
binmode = 1;
|
||||
break;
|
||||
default:
|
||||
binmode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') {
|
||||
ctx->fp = stdout;
|
||||
#if defined(_WIN32)
|
||||
if (binmode)
|
||||
_setmode(_fileno(stdout), _O_BINARY); /* Yuck. */
|
||||
#endif
|
||||
} else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) {
|
||||
fprintf(stderr, "Error: cannot open output file '%s': %s\n",
|
||||
ctx->outname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
case BUILD_coffasm:
|
||||
case BUILD_machasm:
|
||||
emit_asm(ctx);
|
||||
emit_asm_debug(ctx);
|
||||
break;
|
||||
case BUILD_peobj:
|
||||
emit_peobj(ctx);
|
||||
break;
|
||||
case BUILD_raw:
|
||||
emit_raw(ctx);
|
||||
break;
|
||||
case BUILD_bcdef:
|
||||
emit_bcdef(ctx);
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_vmdef:
|
||||
emit_vmdef(ctx);
|
||||
emit_lib(ctx);
|
||||
fprintf(ctx->fp, "}\n\n");
|
||||
break;
|
||||
case BUILD_ffdef:
|
||||
case BUILD_libdef:
|
||||
case BUILD_recdef:
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_folddef:
|
||||
emit_fold(ctx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fflush(ctx->fp);
|
||||
if (ferror(ctx->fp)) {
|
||||
fprintf(stderr, "Error: cannot write to output file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
fclose(ctx->fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
** LuaJIT VM builder.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _BUILDVM_H
|
||||
#define _BUILDVM_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lj_def.h"
|
||||
#include "lj_arch.h"
|
||||
|
||||
/* Hardcoded limits. Increase as needed. */
|
||||
#define BUILD_MAX_RELOC 200 /* Max. number of relocations. */
|
||||
#define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */
|
||||
|
||||
/* Prefix for scanned library definitions. */
|
||||
#define LIBDEF_PREFIX "LJLIB_"
|
||||
|
||||
/* Prefix for scanned fold definitions. */
|
||||
#define FOLDDEF_PREFIX "LJFOLD"
|
||||
|
||||
/* Prefixes for generated labels. */
|
||||
#define LABEL_PREFIX "lj_"
|
||||
#define LABEL_PREFIX_BC LABEL_PREFIX "BC_"
|
||||
#define LABEL_PREFIX_FF LABEL_PREFIX "ff_"
|
||||
#define LABEL_PREFIX_CF LABEL_PREFIX "cf_"
|
||||
#define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_"
|
||||
#define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_"
|
||||
#define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_"
|
||||
|
||||
/* Forward declaration. */
|
||||
struct dasm_State;
|
||||
|
||||
/* Build modes. */
|
||||
#define BUILDDEF(_) \
|
||||
_(elfasm) _(coffasm) _(machasm) _(peobj) _(raw) \
|
||||
_(bcdef) _(ffdef) _(libdef) _(recdef) _(vmdef) \
|
||||
_(folddef)
|
||||
|
||||
typedef enum {
|
||||
#define BUILDENUM(name) BUILD_##name,
|
||||
BUILDDEF(BUILDENUM)
|
||||
#undef BUILDENUM
|
||||
BUILD__MAX
|
||||
} BuildMode;
|
||||
|
||||
/* Code relocation. */
|
||||
typedef struct BuildReloc {
|
||||
int32_t ofs;
|
||||
int sym;
|
||||
int type;
|
||||
} BuildReloc;
|
||||
|
||||
typedef struct BuildSym {
|
||||
const char *name;
|
||||
int32_t ofs;
|
||||
} BuildSym;
|
||||
|
||||
/* Build context structure. */
|
||||
typedef struct BuildCtx {
|
||||
/* DynASM state pointer. Should be first member. */
|
||||
struct dasm_State *D;
|
||||
/* Parsed command line. */
|
||||
BuildMode mode;
|
||||
FILE *fp;
|
||||
const char *outname;
|
||||
char **args;
|
||||
/* Code and symbols generated by DynASM. */
|
||||
uint8_t *code;
|
||||
size_t codesz;
|
||||
int npc, nglob, nsym, nreloc, nrelocsym;
|
||||
void **glob;
|
||||
BuildSym *sym;
|
||||
const char **relocsym;
|
||||
int32_t *bc_ofs;
|
||||
const char *beginsym;
|
||||
/* Strings generated by DynASM. */
|
||||
const char *const *globnames;
|
||||
const char *const *extnames;
|
||||
const char *dasm_ident;
|
||||
const char *dasm_arch;
|
||||
/* Relocations. */
|
||||
BuildReloc reloc[BUILD_MAX_RELOC];
|
||||
} BuildCtx;
|
||||
|
||||
extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz);
|
||||
extern void emit_asm(BuildCtx *ctx);
|
||||
extern void emit_peobj(BuildCtx *ctx);
|
||||
extern void emit_lib(BuildCtx *ctx);
|
||||
extern void emit_fold(BuildCtx *ctx);
|
||||
|
||||
extern const char *const bc_names[];
|
||||
extern const char *const ir_names[];
|
||||
extern const char *const irt_names[];
|
||||
extern const char *const irfpm_names[];
|
||||
extern const char *const irfield_names[];
|
||||
extern const char *const ircall_names[];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
** LuaJIT VM builder: Assembler source code emitter.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if LJ_TARGET_X86ORX64
|
||||
/* Emit bytes piecewise as assembler text. */
|
||||
static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
if ((i & 15) == 0)
|
||||
fprintf(ctx->fp, "\t.byte %d", p[i]);
|
||||
else
|
||||
fprintf(ctx->fp, ",%d", p[i]);
|
||||
if ((i & 15) == 15) putc('\n', ctx->fp);
|
||||
}
|
||||
if ((n & 15) != 0) putc('\n', ctx->fp);
|
||||
}
|
||||
|
||||
/* Emit relocation */
|
||||
static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
if (type)
|
||||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
|
||||
else
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym);
|
||||
if (type)
|
||||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
|
||||
else
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
default: /* BUILD_machasm for relative relocations handled below. */
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const jccnames[] = {
|
||||
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja",
|
||||
"js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg"
|
||||
};
|
||||
|
||||
/* Emit x86/x64 text relocations. */
|
||||
static void emit_asm_reloc_text(BuildCtx *ctx, uint8_t *cp, int n,
|
||||
const char *sym)
|
||||
{
|
||||
const char *opname = NULL;
|
||||
if (--n < 0) goto err;
|
||||
if (cp[n] == 0xe8) {
|
||||
opname = "call";
|
||||
} else if (cp[n] == 0xe9) {
|
||||
opname = "jmp";
|
||||
} else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) {
|
||||
opname = jccnames[cp[n]-0x80];
|
||||
n--;
|
||||
} else {
|
||||
err:
|
||||
fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n",
|
||||
sym);
|
||||
exit(1);
|
||||
}
|
||||
emit_asm_bytes(ctx, cp, n);
|
||||
if (strncmp(sym+(*sym == '_'), LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) {
|
||||
/* Various fixups for external symbols outside of our binary. */
|
||||
if (ctx->mode == BUILD_elfasm) {
|
||||
if (LJ_32)
|
||||
fprintf(ctx->fp, "#if __PIC__\n\t%s lj_wrap_%s\n#else\n", opname, sym);
|
||||
fprintf(ctx->fp, "\t%s %s@PLT\n", opname, sym);
|
||||
if (LJ_32)
|
||||
fprintf(ctx->fp, "#endif\n");
|
||||
return;
|
||||
} else if (LJ_32 && ctx->mode == BUILD_machasm) {
|
||||
fprintf(ctx->fp, "\t%s L%s$stub\n", opname, sym);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(ctx->fp, "\t%s %s\n", opname, sym);
|
||||
}
|
||||
#else
|
||||
/* Emit words piecewise as assembler text. */
|
||||
static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i += 4) {
|
||||
uint32_t ins = *(uint32_t *)(p+i);
|
||||
#if LJ_TARGET_ARM64 && LJ_BE
|
||||
ins = lj_bswap(ins); /* ARM64 instructions are always little-endian. */
|
||||
#endif
|
||||
if ((i & 15) == 0)
|
||||
fprintf(ctx->fp, "\t.long 0x%08x", ins);
|
||||
else
|
||||
fprintf(ctx->fp, ",0x%08x", ins);
|
||||
if ((i & 15) == 12) putc('\n', ctx->fp);
|
||||
}
|
||||
if ((n & 15) != 0) putc('\n', ctx->fp);
|
||||
}
|
||||
|
||||
/* Emit relocation as part of an instruction. */
|
||||
static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n,
|
||||
const char *sym)
|
||||
{
|
||||
uint32_t ins;
|
||||
emit_asm_words(ctx, p, n-4);
|
||||
ins = *(uint32_t *)(p+n-4);
|
||||
#if LJ_TARGET_ARM
|
||||
if ((ins & 0xff000000u) == 0xfa000000u) {
|
||||
fprintf(ctx->fp, "\tblx %s\n", sym);
|
||||
} else if ((ins & 0x0e000000u) == 0x0a000000u) {
|
||||
fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b",
|
||||
&"eqnecsccmiplvsvchilsgeltgtle"[2*(ins >> 28)], sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_ARM64
|
||||
if ((ins >> 26) == 0x25u) {
|
||||
fprintf(ctx->fp, "\tbl %s\n", sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_PPC
|
||||
#if LJ_TARGET_PS3
|
||||
#define TOCPREFIX "."
|
||||
#else
|
||||
#define TOCPREFIX ""
|
||||
#endif
|
||||
if ((ins >> 26) == 16) {
|
||||
fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n",
|
||||
(ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym);
|
||||
} else if ((ins >> 26) == 18) {
|
||||
fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_MIPS
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
#else
|
||||
#error "missing relocation support for this architecture"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_ARM
|
||||
#define ELFASM_PX "%%"
|
||||
#else
|
||||
#define ELFASM_PX "@"
|
||||
#endif
|
||||
|
||||
/* Emit an assembler label. */
|
||||
static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
#if LJ_TARGET_PS3
|
||||
if (!strncmp(name, "lj_vm_", 6) &&
|
||||
strcmp(name, ctx->beginsym) &&
|
||||
!strstr(name, "hook")) {
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.globl %s\n"
|
||||
"\t.section \".opd\",\"aw\"\n"
|
||||
"%s:\n"
|
||||
"\t.long .%s,.TOC.@tocbase32\n"
|
||||
"\t.size %s,8\n"
|
||||
"\t.previous\n"
|
||||
"\t.globl .%s\n"
|
||||
"\t.hidden .%s\n"
|
||||
"\t.type .%s, " ELFASM_PX "function\n"
|
||||
"\t.size .%s, %d\n"
|
||||
".%s:\n",
|
||||
name, name, name, name, name, name, name, name, size, name);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.globl %s\n"
|
||||
"\t.hidden %s\n"
|
||||
"\t.type %s, " ELFASM_PX "%s\n"
|
||||
"\t.size %s, %d\n"
|
||||
"%s:\n",
|
||||
name, name, name, isfunc ? "function" : "object", name, size, name);
|
||||
break;
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\n\t.globl %s\n", name);
|
||||
if (isfunc)
|
||||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name);
|
||||
fprintf(ctx->fp, "%s:\n", name);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.private_extern %s\n"
|
||||
"\t.no_dead_strip %s\n"
|
||||
"%s:\n", name, name, name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit alignment. */
|
||||
static void emit_asm_align(BuildCtx *ctx, int bits)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.p2align %d\n", bits);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
fprintf(ctx->fp, "\t.align %d\n", bits);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Emit assembler source code. */
|
||||
void emit_asm(BuildCtx *ctx)
|
||||
{
|
||||
int i, rel;
|
||||
|
||||
fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch);
|
||||
fprintf(ctx->fp, "\t.text\n");
|
||||
#if LJ_TARGET_MIPS32 && !LJ_ABI_SOFTFP
|
||||
fprintf(ctx->fp, "\t.module fp=32\n");
|
||||
#endif
|
||||
#if LJ_TARGET_MIPS
|
||||
fprintf(ctx->fp, "\t.set nomips16\n\t.abicalls\n\t.set noreorder\n\t.set nomacro\n");
|
||||
#endif
|
||||
emit_asm_align(ctx, 4);
|
||||
|
||||
#if LJ_TARGET_PS3
|
||||
emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0);
|
||||
#else
|
||||
emit_asm_label(ctx, ctx->beginsym, 0, 0);
|
||||
#endif
|
||||
if (ctx->mode != BUILD_machasm)
|
||||
fprintf(ctx->fp, ".Lbegin:\n");
|
||||
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
|
||||
/* This should really be moved into buildvm_arm.dasc. */
|
||||
#if LJ_ARCH_HASFPU
|
||||
fprintf(ctx->fp,
|
||||
".fnstart\n"
|
||||
".save {r5, r6, r7, r8, r9, r10, r11, lr}\n"
|
||||
".vsave {d8-d15}\n"
|
||||
".save {r4}\n"
|
||||
".pad #28\n");
|
||||
#else
|
||||
fprintf(ctx->fp,
|
||||
".fnstart\n"
|
||||
".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
|
||||
".pad #28\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (i = rel = 0; i < ctx->nsym; i++) {
|
||||
int32_t ofs = ctx->sym[i].ofs;
|
||||
int32_t next = ctx->sym[i+1].ofs;
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI
|
||||
if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call"))
|
||||
fprintf(ctx->fp,
|
||||
".globl lj_err_unwind_arm\n"
|
||||
".personality lj_err_unwind_arm\n"
|
||||
".fnend\n"
|
||||
".fnstart\n"
|
||||
".save {r4, r5, r11, lr}\n"
|
||||
".setfp r11, sp\n");
|
||||
#endif
|
||||
emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1);
|
||||
while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) {
|
||||
BuildReloc *r = &ctx->reloc[rel];
|
||||
int n = r->ofs - ofs;
|
||||
#if LJ_TARGET_X86ORX64
|
||||
if (r->type != 0 &&
|
||||
(ctx->mode == BUILD_elfasm || ctx->mode == BUILD_machasm)) {
|
||||
emit_asm_reloc_text(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
|
||||
} else {
|
||||
emit_asm_bytes(ctx, ctx->code+ofs, n);
|
||||
emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]);
|
||||
}
|
||||
ofs += n+4;
|
||||
#else
|
||||
emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
|
||||
ofs += n;
|
||||
#endif
|
||||
rel++;
|
||||
}
|
||||
#if LJ_TARGET_X86ORX64
|
||||
emit_asm_bytes(ctx, ctx->code+ofs, next-ofs);
|
||||
#else
|
||||
emit_asm_words(ctx, ctx->code+ofs, next-ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
|
||||
fprintf(ctx->fp,
|
||||
#if !LJ_HASFFI
|
||||
".globl lj_err_unwind_arm\n"
|
||||
".personality lj_err_unwind_arm\n"
|
||||
#endif
|
||||
".fnend\n");
|
||||
#endif
|
||||
|
||||
fprintf(ctx->fp, "\n");
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
#if !(LJ_TARGET_PS3 || LJ_TARGET_PSVITA)
|
||||
fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n");
|
||||
#endif
|
||||
#if LJ_TARGET_PPC && !LJ_TARGET_PS3 && !LJ_ABI_SOFTFP
|
||||
/* Hard-float ABI. */
|
||||
fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n");
|
||||
#endif
|
||||
/* fallthrough */
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
#if defined(__apple_build_version__) && __apple_build_version__ >= 15000000 && __apple_build_version__ < 15000300
|
||||
/* Workaround for XCode 15.0 - 15.2. */
|
||||
fprintf(ctx->fp, "\t.subsections_via_symbols\n");
|
||||
#endif
|
||||
fprintf(ctx->fp,
|
||||
"\t.cstring\n"
|
||||
"\t.ascii \"%s\\0\"\n", ctx->dasm_ident);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(ctx->fp, "\n");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
** LuaJIT VM builder: IR folding hash table generator.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_ir.h"
|
||||
|
||||
/* Context for the folding hash table generator. */
|
||||
static int lineno;
|
||||
static uint32_t funcidx;
|
||||
static uint32_t foldkeys[BUILD_MAX_FOLD];
|
||||
static uint32_t nkeys;
|
||||
|
||||
/* Try to fill the hash table with keys using the hash parameters. */
|
||||
static int tryhash(uint32_t *htab, uint32_t sz, uint32_t r, int dorol)
|
||||
{
|
||||
uint32_t i;
|
||||
if (dorol && ((r & 31) == 0 || (r>>5) == 0))
|
||||
return 0; /* Avoid zero rotates. */
|
||||
memset(htab, 0xff, (sz+1)*sizeof(uint32_t));
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
uint32_t key = foldkeys[i];
|
||||
uint32_t k = key & 0xffffff;
|
||||
uint32_t h = (dorol ? lj_rol(lj_rol(k, r>>5) - k, r&31) :
|
||||
(((k << (r>>5)) - k) << (r&31))) % sz;
|
||||
if (htab[h] != 0xffffffff) { /* Collision on primary slot. */
|
||||
if (htab[h+1] != 0xffffffff) { /* Collision on secondary slot. */
|
||||
/* Try to move the colliding key, if possible. */
|
||||
if (h < sz-1 && htab[h+2] == 0xffffffff) {
|
||||
uint32_t k2 = htab[h+1] & 0xffffff;
|
||||
uint32_t h2 = (dorol ? lj_rol(lj_rol(k2, r>>5) - k2, r&31) :
|
||||
(((k2 << (r>>5)) - k2) << (r&31))) % sz;
|
||||
if (h2 != h+1) return 0; /* Cannot resolve collision. */
|
||||
htab[h+2] = htab[h+1]; /* Move colliding key to secondary slot. */
|
||||
} else {
|
||||
return 0; /* Collision. */
|
||||
}
|
||||
}
|
||||
htab[h+1] = key;
|
||||
} else {
|
||||
htab[h] = key;
|
||||
}
|
||||
}
|
||||
return 1; /* Success, all keys could be stored. */
|
||||
}
|
||||
|
||||
/* Print the generated hash table. */
|
||||
static void printhash(BuildCtx *ctx, uint32_t *htab, uint32_t sz)
|
||||
{
|
||||
uint32_t i;
|
||||
fprintf(ctx->fp, "static const uint32_t fold_hash[%d] = {\n0x%08x",
|
||||
sz+1, htab[0]);
|
||||
for (i = 1; i < sz+1; i++)
|
||||
fprintf(ctx->fp, ",\n0x%08x", htab[i]);
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
}
|
||||
|
||||
/* Exhaustive search for the shortest semi-perfect hash table. */
|
||||
static void makehash(BuildCtx *ctx)
|
||||
{
|
||||
uint32_t htab[BUILD_MAX_FOLD*2+1];
|
||||
uint32_t sz, r;
|
||||
/* Search for the smallest hash table with an odd size. */
|
||||
for (sz = (nkeys|1); sz < BUILD_MAX_FOLD*2; sz += 2) {
|
||||
/* First try all shift hash combinations. */
|
||||
for (r = 0; r < 32*32; r++) {
|
||||
if (tryhash(htab, sz, r, 0)) {
|
||||
printhash(ctx, htab, sz);
|
||||
fprintf(ctx->fp,
|
||||
"#define fold_hashkey(k)\t(((((k)<<%u)-(k))<<%u)%%%u)\n\n",
|
||||
r>>5, r&31, sz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Then try all rotate hash combinations. */
|
||||
for (r = 0; r < 32*32; r++) {
|
||||
if (tryhash(htab, sz, r, 1)) {
|
||||
printhash(ctx, htab, sz);
|
||||
fprintf(ctx->fp,
|
||||
"#define fold_hashkey(k)\t(lj_rol(lj_rol((k),%u)-(k),%u)%%%u)\n\n",
|
||||
r>>5, r&31, sz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: search for perfect hash failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Parse one token of a fold rule. */
|
||||
static uint32_t nexttoken(char **pp, int allowlit, int allowany)
|
||||
{
|
||||
char *p = *pp;
|
||||
if (p) {
|
||||
uint32_t i;
|
||||
char *q = strchr(p, ' ');
|
||||
if (q) *q++ = '\0';
|
||||
*pp = q;
|
||||
if (allowlit && !strncmp(p, "IRFPM_", 6)) {
|
||||
for (i = 0; irfpm_names[i]; i++)
|
||||
if (!strcmp(irfpm_names[i], p+6))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRFL_", 5)) {
|
||||
for (i = 0; irfield_names[i]; i++)
|
||||
if (!strcmp(irfield_names[i], p+5))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRCALL_", 7)) {
|
||||
for (i = 0; ircall_names[i]; i++)
|
||||
if (!strcmp(ircall_names[i], p+7))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRCONV_", 7)) {
|
||||
for (i = 0; irt_names[i]; i++) {
|
||||
const char *r = strchr(p+7, '_');
|
||||
if (r && !strncmp(irt_names[i], p+7, r-(p+7))) {
|
||||
uint32_t j;
|
||||
for (j = 0; irt_names[j]; j++)
|
||||
if (!strcmp(irt_names[j], r+1))
|
||||
return (i << 5) + j;
|
||||
}
|
||||
}
|
||||
} else if (allowlit && *p >= '0' && *p <= '9') {
|
||||
for (i = 0; *p >= '0' && *p <= '9'; p++)
|
||||
i = i*10 + (*p - '0');
|
||||
if (*p == '\0')
|
||||
return i;
|
||||
} else if (allowany && !strcmp("any", p)) {
|
||||
return allowany;
|
||||
} else {
|
||||
for (i = 0; ir_names[i]; i++)
|
||||
if (!strcmp(ir_names[i], p))
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a fold rule. */
|
||||
static void foldrule(char *p)
|
||||
{
|
||||
uint32_t op = nexttoken(&p, 0, 0);
|
||||
uint32_t left = nexttoken(&p, 0, 0x7f);
|
||||
uint32_t right = nexttoken(&p, 1, 0x3ff);
|
||||
uint32_t key = (funcidx << 24) | (op << 17) | (left << 10) | right;
|
||||
uint32_t i;
|
||||
if (nkeys >= BUILD_MAX_FOLD) {
|
||||
fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n");
|
||||
exit(1);
|
||||
}
|
||||
/* Simple insertion sort to detect duplicates. */
|
||||
for (i = nkeys; i > 0; i--) {
|
||||
if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff))
|
||||
break;
|
||||
if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) {
|
||||
fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
foldkeys[i] = foldkeys[i-1];
|
||||
}
|
||||
foldkeys[i] = key;
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
/* Emit C source code for IR folding hash table. */
|
||||
void emit_fold(BuildCtx *ctx)
|
||||
{
|
||||
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
||||
const char *fname = ctx->args[0];
|
||||
FILE *fp;
|
||||
|
||||
if (fname == NULL) {
|
||||
fprintf(stderr, "Error: missing input filename\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fname[0] == '-' && fname[1] == '\0') {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
||||
fname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n");
|
||||
|
||||
lineno = 0;
|
||||
funcidx = 0;
|
||||
nkeys = 0;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
lineno++;
|
||||
/* The prefix must be at the start of a line, otherwise it's ignored. */
|
||||
if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) {
|
||||
char *p = buf+sizeof(FOLDDEF_PREFIX)-1;
|
||||
char *q = strchr(p, ')');
|
||||
if (p[0] == '(' && q) {
|
||||
p++;
|
||||
*q = '\0';
|
||||
foldrule(p);
|
||||
} else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) {
|
||||
p += 2;
|
||||
*q = '\0';
|
||||
if (funcidx)
|
||||
fprintf(ctx->fp, ",\n");
|
||||
if (p[-2] == 'X')
|
||||
fprintf(ctx->fp, " %s", p);
|
||||
else
|
||||
fprintf(ctx->fp, " fold_%s", p);
|
||||
funcidx++;
|
||||
} else {
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n",
|
||||
FOLDDEF_PREFIX, p, lineno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
|
||||
makehash(ctx);
|
||||
}
|
||||
#else
|
||||
void emit_fold(BuildCtx *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
** LuaJIT VM builder: library definition compiler.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_bc.h"
|
||||
#include "lj_lib.h"
|
||||
#include "buildvm_libbc.h"
|
||||
|
||||
/* Context for library definitions. */
|
||||
static uint8_t obuf[8192];
|
||||
static uint8_t *optr;
|
||||
static char modname[80];
|
||||
static size_t modnamelen;
|
||||
static char funcname[80];
|
||||
static int modstate, regfunc;
|
||||
static int ffid, recffid, ffasmfunc;
|
||||
|
||||
enum {
|
||||
REGFUNC_OK,
|
||||
REGFUNC_NOREG,
|
||||
REGFUNC_NOREGUV
|
||||
};
|
||||
|
||||
static void libdef_name(const char *p, int kind)
|
||||
{
|
||||
size_t n = strlen(p);
|
||||
if (kind != LIBINIT_STRING) {
|
||||
if (n > modnamelen && p[modnamelen] == '_' &&
|
||||
!strncmp(p, modname, modnamelen)) {
|
||||
p += modnamelen+1;
|
||||
n -= modnamelen+1;
|
||||
}
|
||||
}
|
||||
if (n > LIBINIT_MAXSTR) {
|
||||
fprintf(stderr, "Error: string too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = (uint8_t)(n | kind);
|
||||
memcpy(optr, p, n);
|
||||
optr += n;
|
||||
}
|
||||
|
||||
static void libdef_endmodule(BuildCtx *ctx)
|
||||
{
|
||||
if (modstate != 0) {
|
||||
char line[80];
|
||||
const uint8_t *p;
|
||||
int n;
|
||||
if (modstate == 1)
|
||||
fprintf(ctx->fp, " (lua_CFunction)0");
|
||||
fprintf(ctx->fp, "\n};\n");
|
||||
fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n",
|
||||
LABEL_PREFIX_LIBINIT, modname);
|
||||
line[0] = '\0';
|
||||
for (n = 0, p = obuf; p < optr; p++) {
|
||||
n += sprintf(line+n, "%d,", *p);
|
||||
if (n >= 75) {
|
||||
fprintf(ctx->fp, "%s\n", line);
|
||||
n = 0;
|
||||
line[0] = '\0';
|
||||
}
|
||||
}
|
||||
fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END);
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_module(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
libdef_endmodule(ctx);
|
||||
optr = obuf;
|
||||
*optr++ = (uint8_t)ffid;
|
||||
*optr++ = (uint8_t)ffasmfunc;
|
||||
*optr++ = 0; /* Hash table size. */
|
||||
modstate = 1;
|
||||
fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
||||
fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
||||
fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n",
|
||||
LABEL_PREFIX_LIBCF, p);
|
||||
}
|
||||
modnamelen = strlen(p);
|
||||
if (modnamelen > sizeof(modname)-1) {
|
||||
fprintf(stderr, "Error: module name too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(modname, p);
|
||||
}
|
||||
|
||||
static int find_ffofs(BuildCtx *ctx, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ctx->nglob; i++) {
|
||||
const char *gl = ctx->globnames[i];
|
||||
if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) {
|
||||
return (int)((uint8_t *)ctx->glob[i] - ctx->code);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: undefined fast function %s%s\n",
|
||||
LABEL_PREFIX_FF, name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void libdef_func(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
if (arg != LIBINIT_CF)
|
||||
ffasmfunc++;
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
if (modstate == 0) {
|
||||
fprintf(stderr, "Error: no module for function definition %s\n", p);
|
||||
exit(1);
|
||||
}
|
||||
if (regfunc == REGFUNC_NOREG) {
|
||||
if (optr+1 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_FFID;
|
||||
} else {
|
||||
if (arg != LIBINIT_ASM_) {
|
||||
if (modstate != 1) fprintf(ctx->fp, ",\n");
|
||||
modstate = 2;
|
||||
fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p);
|
||||
}
|
||||
if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */
|
||||
libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg);
|
||||
}
|
||||
} else if (ctx->mode == BUILD_ffdef) {
|
||||
fprintf(ctx->fp, "FFDEF(%s)\n", p);
|
||||
} else if (ctx->mode == BUILD_recdef) {
|
||||
if (strlen(p) > sizeof(funcname)-1) {
|
||||
fprintf(stderr, "Error: function name too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(funcname, p);
|
||||
} else if (ctx->mode == BUILD_vmdef) {
|
||||
int i;
|
||||
for (i = 1; p[i] && modname[i-1]; i++)
|
||||
if (p[i] == '_') p[i] = '.';
|
||||
fprintf(ctx->fp, "\"%s\",\n", p);
|
||||
} else if (ctx->mode == BUILD_bcdef) {
|
||||
if (arg != LIBINIT_CF)
|
||||
fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p));
|
||||
}
|
||||
ffid++;
|
||||
regfunc = REGFUNC_OK;
|
||||
}
|
||||
|
||||
static uint8_t *libdef_uleb128(uint8_t *p, uint32_t *vv)
|
||||
{
|
||||
uint32_t v = *p++;
|
||||
if (v >= 0x80) {
|
||||
int sh = 0; v &= 0x7f;
|
||||
do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80);
|
||||
}
|
||||
*vv = v;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void libdef_fixupbc(uint8_t *p)
|
||||
{
|
||||
uint32_t i, sizebc;
|
||||
p += 4;
|
||||
p = libdef_uleb128(p, &sizebc);
|
||||
p = libdef_uleb128(p, &sizebc);
|
||||
p = libdef_uleb128(p, &sizebc);
|
||||
for (i = 0; i < sizebc; i++, p += 4) {
|
||||
uint8_t op = p[libbc_endian ? 3 : 0];
|
||||
uint8_t ra = p[libbc_endian ? 2 : 1];
|
||||
uint8_t rc = p[libbc_endian ? 1 : 2];
|
||||
uint8_t rb = p[libbc_endian ? 0 : 3];
|
||||
if (!LJ_DUALNUM && op == BC_ISTYPE && rc == ~LJ_TNUMX+1) {
|
||||
op = BC_ISNUM; rc++;
|
||||
}
|
||||
p[LJ_ENDIAN_SELECT(0, 3)] = op;
|
||||
p[LJ_ENDIAN_SELECT(1, 2)] = ra;
|
||||
p[LJ_ENDIAN_SELECT(2, 1)] = rc;
|
||||
p[LJ_ENDIAN_SELECT(3, 0)] = rb;
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_lua(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
int i;
|
||||
for (i = 0; libbc_map[i].name != NULL; i++) {
|
||||
if (!strcmp(libbc_map[i].name, p)) {
|
||||
int ofs = libbc_map[i].ofs;
|
||||
int len = libbc_map[i+1].ofs - ofs;
|
||||
obuf[2]++; /* Bump hash table size. */
|
||||
*optr++ = LIBINIT_LUA;
|
||||
libdef_name(p, 0);
|
||||
memcpy(optr, libbc_code + ofs, len);
|
||||
libdef_fixupbc(optr);
|
||||
optr += len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: missing libbc definition for %s\n", p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t find_rec(char *name)
|
||||
{
|
||||
char *p = (char *)obuf;
|
||||
uint32_t n;
|
||||
for (n = 2; *p; n++) {
|
||||
if (strcmp(p, name) == 0)
|
||||
return n;
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
strcpy(p, name);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void libdef_rec(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_recdef) {
|
||||
char *q;
|
||||
uint32_t n;
|
||||
for (; recffid+1 < ffid; recffid++)
|
||||
fprintf(ctx->fp, ",\n0");
|
||||
recffid = ffid;
|
||||
if (*p == '.') p = funcname;
|
||||
q = strchr(p, ' ');
|
||||
if (q) *q++ = '\0';
|
||||
n = find_rec(p);
|
||||
if (q)
|
||||
fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q);
|
||||
else
|
||||
fprintf(ctx->fp, ",\n0x%02x00", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void memcpy_endian(void *dst, void *src, size_t n)
|
||||
{
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) {
|
||||
memcpy(dst, src, n);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_push(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
int len = (int)strlen(p);
|
||||
if (*p == '"') {
|
||||
if (len > 1 && p[len-1] == '"') {
|
||||
p[len-1] = '\0';
|
||||
libdef_name(p+1, LIBINIT_STRING);
|
||||
return;
|
||||
}
|
||||
} else if (*p >= '0' && *p <= '9') {
|
||||
char *ep;
|
||||
double d = strtod(p, &ep);
|
||||
if (*ep == '\0') {
|
||||
if (optr+1+sizeof(double) > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_NUMBER;
|
||||
memcpy_endian(optr, &d, sizeof(double));
|
||||
optr += sizeof(double);
|
||||
return;
|
||||
}
|
||||
} else if (!strcmp(p, "lastcl")) {
|
||||
if (optr+1 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_LASTCL;
|
||||
return;
|
||||
} else if (len > 4 && !strncmp(p, "top-", 4)) {
|
||||
if (optr+2 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_COPY;
|
||||
*optr++ = (uint8_t)atoi(p+4);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_set(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */
|
||||
libdef_name(p, LIBINIT_STRING);
|
||||
*optr++ = LIBINIT_SET;
|
||||
obuf[2]++; /* Bump hash table size. */
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_regfunc(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(ctx); UNUSED(p);
|
||||
regfunc = arg;
|
||||
}
|
||||
|
||||
typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg);
|
||||
|
||||
typedef struct LibDefHandler {
|
||||
const char *suffix;
|
||||
const char *stop;
|
||||
const LibDefFunc func;
|
||||
const int arg;
|
||||
} LibDefHandler;
|
||||
|
||||
static const LibDefHandler libdef_handlers[] = {
|
||||
{ "MODULE_", " \t\r\n", libdef_module, 0 },
|
||||
{ "CF(", ")", libdef_func, LIBINIT_CF },
|
||||
{ "ASM(", ")", libdef_func, LIBINIT_ASM },
|
||||
{ "ASM_(", ")", libdef_func, LIBINIT_ASM_ },
|
||||
{ "LUA(", ")", libdef_lua, 0 },
|
||||
{ "REC(", ")", libdef_rec, 0 },
|
||||
{ "PUSH(", ")", libdef_push, 0 },
|
||||
{ "SET(", ")", libdef_set, 0 },
|
||||
{ "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV },
|
||||
{ "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG },
|
||||
{ NULL, NULL, (LibDefFunc)0, 0 }
|
||||
};
|
||||
|
||||
/* Emit C source code for library function definitions. */
|
||||
void emit_lib(BuildCtx *ctx)
|
||||
{
|
||||
const char *fname;
|
||||
|
||||
if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef ||
|
||||
ctx->mode == BUILD_recdef)
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
else if (ctx->mode == BUILD_vmdef)
|
||||
fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n");
|
||||
if (ctx->mode == BUILD_recdef)
|
||||
fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100");
|
||||
recffid = ffid = FF_C+1;
|
||||
ffasmfunc = 0;
|
||||
|
||||
while ((fname = *ctx->args++)) {
|
||||
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
||||
FILE *fp;
|
||||
if (fname[0] == '-' && fname[1] == '\0') {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
||||
fname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
modstate = 0;
|
||||
regfunc = REGFUNC_OK;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
char *p;
|
||||
/* Simplistic pre-processor. Only handles top-level #if/#endif. */
|
||||
if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
||||
int ok = 1;
|
||||
size_t len = strlen(buf);
|
||||
if (buf[len-1] == '\n') {
|
||||
buf[len-1] = 0;
|
||||
if (buf[len-2] == '\r') {
|
||||
buf[len-2] = 0;
|
||||
}
|
||||
}
|
||||
if (!strcmp(buf, "#if LJ_52"))
|
||||
ok = LJ_52;
|
||||
else if (!strcmp(buf, "#if LJ_HASJIT"))
|
||||
ok = LJ_HASJIT;
|
||||
else if (!strcmp(buf, "#if LJ_HASFFI"))
|
||||
ok = LJ_HASFFI;
|
||||
else if (!strcmp(buf, "#if LJ_HASBUFFER"))
|
||||
ok = LJ_HASBUFFER;
|
||||
else if (!strcmp(buf, "#if !LJ_SRB2LIB"))
|
||||
ok = !LJ_SRB2LIB;
|
||||
else if (!strcmp(buf, "#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO)"))
|
||||
ok = !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO); /* whacky! */
|
||||
if (!ok) {
|
||||
int lvl = 1;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') {
|
||||
if (--lvl == 0) break;
|
||||
} else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
||||
lvl++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) {
|
||||
const LibDefHandler *ldh;
|
||||
p += sizeof(LIBDEF_PREFIX)-1;
|
||||
for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
|
||||
size_t n, len = strlen(ldh->suffix);
|
||||
if (!strncmp(p, ldh->suffix, len)) {
|
||||
p += len;
|
||||
n = ldh->stop ? strcspn(p, ldh->stop) : 0;
|
||||
if (!p[n]) break;
|
||||
p[n] = '\0';
|
||||
ldh->func(ctx, p, ldh->arg);
|
||||
p += n+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ldh->suffix == NULL) {
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
fprintf(stderr, "Error: unknown library definition tag %s%s\n",
|
||||
LIBDEF_PREFIX, p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
libdef_endmodule(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->mode == BUILD_ffdef) {
|
||||
fprintf(ctx->fp, "\n#undef FFDEF\n\n");
|
||||
fprintf(ctx->fp,
|
||||
"#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n",
|
||||
ffasmfunc);
|
||||
} else if (ctx->mode == BUILD_vmdef) {
|
||||
fprintf(ctx->fp, "},\n\n");
|
||||
} else if (ctx->mode == BUILD_bcdef) {
|
||||
int i;
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n");
|
||||
fprintf(ctx->fp, "BCDEF(BCMODE)\n");
|
||||
for (i = ffasmfunc-1; i > 0; i--)
|
||||
fprintf(ctx->fp, "BCMODE_FF,\n");
|
||||
fprintf(ctx->fp, "BCMODE_FF\n};\n\n");
|
||||
} else if (ctx->mode == BUILD_recdef) {
|
||||
char *p = (char *)obuf;
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n"
|
||||
"recff_nyi,\n"
|
||||
"recff_c");
|
||||
while (*p) {
|
||||
fprintf(ctx->fp, ",\nrecff_%s", p);
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/* This is a generated file. DO NOT EDIT! */
|
||||
|
||||
static const int libbc_endian = 0;
|
||||
|
||||
static const uint8_t libbc_code[] = {
|
||||
#if LJ_FR2
|
||||
/* math.deg */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,241,135,158,166,3,
|
||||
220,203,178,130,4,
|
||||
/* math.rad */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,243,244,148,165,20,
|
||||
198,190,199,252,3,
|
||||
/* string.len */ 0,1,2,0,0,0,3,BC_ISTYPE,0,5,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.foreachi */ 0,2,10,0,0,0,15,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,
|
||||
BC_KSHORT,2,1,0,BC_LEN,3,0,0,BC_KSHORT,4,1,0,BC_FORI,2,8,128,BC_MOV,6,1,0,
|
||||
BC_MOV,8,5,0,BC_TGETR,9,5,0,BC_CALL,6,3,2,BC_ISEQP,6,0,0,BC_JMP,7,1,128,
|
||||
BC_RET1,6,2,0,BC_FORL,2,248,127,BC_RET0,0,1,0,
|
||||
/* table.foreach */ 0,2,11,0,0,1,16,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,BC_KPRI,
|
||||
2,0,0,BC_MOV,3,0,0,BC_KNUM,4,0,0,BC_JMP,5,7,128,BC_MOV,7,1,0,BC_MOV,9,5,0,
|
||||
BC_MOV,10,6,0,BC_CALL,7,3,2,BC_ISEQP,7,0,0,BC_JMP,8,1,128,BC_RET1,7,2,0,
|
||||
BC_ITERN,5,3,3,BC_ITERL,5,247,127,BC_RET0,0,1,0,1,255,255,249,255,15,
|
||||
/* table.getn */ 0,1,2,0,0,0,3,BC_ISTYPE,0,12,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.remove */ 0,2,10,0,0,2,30,BC_ISTYPE,0,12,0,BC_LEN,2,0,0,BC_ISNEP,1,0,
|
||||
0,BC_JMP,3,7,128,BC_ISEQN,2,0,0,BC_JMP,3,23,128,BC_TGETR,3,2,0,BC_KPRI,4,0,0,
|
||||
BC_TSETR,4,2,0,BC_RET1,3,2,0,BC_JMP,3,18,128,BC_ISTYPE,1,14,0,BC_KSHORT,3,1,0,
|
||||
BC_ISGT,3,1,0,BC_JMP,3,14,128,BC_ISGT,1,2,0,BC_JMP,3,12,128,BC_TGETR,3,1,0,
|
||||
BC_ADDVN,4,1,1,BC_MOV,5,2,0,BC_KSHORT,6,1,0,BC_FORI,4,4,128,BC_SUBVN,8,1,7,
|
||||
BC_TGETR,9,7,0,BC_TSETR,9,8,0,BC_FORL,4,252,127,BC_KPRI,4,0,0,BC_TSETR,4,2,0,
|
||||
BC_RET1,3,2,0,BC_RET0,0,1,0,0,2,
|
||||
/* table.move */ 0,5,12,0,0,0,35,BC_ISTYPE,0,12,0,BC_ISTYPE,1,14,0,BC_ISTYPE,
|
||||
2,14,0,BC_ISTYPE,3,14,0,BC_ISNEP,4,0,0,BC_JMP,5,1,128,BC_MOV,4,0,0,BC_ISTYPE,
|
||||
4,12,0,BC_ISGT,1,2,0,BC_JMP,5,24,128,BC_SUBVV,5,1,3,BC_ISLT,2,3,0,BC_JMP,6,4,
|
||||
128,BC_ISLE,3,1,0,BC_JMP,6,2,128,BC_ISEQV,4,0,0,BC_JMP,6,9,128,BC_MOV,6,1,0,
|
||||
BC_MOV,7,2,0,BC_KSHORT,8,1,0,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,
|
||||
BC_TSETR,11,10,4,BC_FORL,6,252,127,BC_JMP,6,8,128,BC_MOV,6,2,0,BC_MOV,7,1,0,
|
||||
BC_KSHORT,8,255,255,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,BC_TSETR,
|
||||
11,10,4,BC_FORL,6,252,127,BC_RET1,4,2,0,
|
||||
#else
|
||||
/* math.deg */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,241,135,158,166,3,
|
||||
220,203,178,130,4,
|
||||
/* math.rad */ 0,1,2,0,0,1,2,BC_MULVN,1,0,0,BC_RET1,1,2,0,243,244,148,165,20,
|
||||
198,190,199,252,3,
|
||||
/* string.len */ 0,1,2,0,0,0,3,BC_ISTYPE,0,5,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.foreachi */ 0,2,9,0,0,0,15,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,
|
||||
BC_KSHORT,2,1,0,BC_LEN,3,0,0,BC_KSHORT,4,1,0,BC_FORI,2,8,128,BC_MOV,6,1,0,
|
||||
BC_MOV,7,5,0,BC_TGETR,8,5,0,BC_CALL,6,3,2,BC_ISEQP,6,0,0,BC_JMP,7,1,128,
|
||||
BC_RET1,6,2,0,BC_FORL,2,248,127,BC_RET0,0,1,0,
|
||||
/* table.foreach */ 0,2,10,0,0,1,16,BC_ISTYPE,0,12,0,BC_ISTYPE,1,9,0,BC_KPRI,
|
||||
2,0,0,BC_MOV,3,0,0,BC_KNUM,4,0,0,BC_JMP,5,7,128,BC_MOV,7,1,0,BC_MOV,8,5,0,
|
||||
BC_MOV,9,6,0,BC_CALL,7,3,2,BC_ISEQP,7,0,0,BC_JMP,8,1,128,BC_RET1,7,2,0,
|
||||
BC_ITERN,5,3,3,BC_ITERL,5,247,127,BC_RET0,0,1,0,1,255,255,249,255,15,
|
||||
/* table.getn */ 0,1,2,0,0,0,3,BC_ISTYPE,0,12,0,BC_LEN,1,0,0,BC_RET1,1,2,0,
|
||||
/* table.remove */ 0,2,10,0,0,2,30,BC_ISTYPE,0,12,0,BC_LEN,2,0,0,BC_ISNEP,1,0,
|
||||
0,BC_JMP,3,7,128,BC_ISEQN,2,0,0,BC_JMP,3,23,128,BC_TGETR,3,2,0,BC_KPRI,4,0,0,
|
||||
BC_TSETR,4,2,0,BC_RET1,3,2,0,BC_JMP,3,18,128,BC_ISTYPE,1,14,0,BC_KSHORT,3,1,0,
|
||||
BC_ISGT,3,1,0,BC_JMP,3,14,128,BC_ISGT,1,2,0,BC_JMP,3,12,128,BC_TGETR,3,1,0,
|
||||
BC_ADDVN,4,1,1,BC_MOV,5,2,0,BC_KSHORT,6,1,0,BC_FORI,4,4,128,BC_SUBVN,8,1,7,
|
||||
BC_TGETR,9,7,0,BC_TSETR,9,8,0,BC_FORL,4,252,127,BC_KPRI,4,0,0,BC_TSETR,4,2,0,
|
||||
BC_RET1,3,2,0,BC_RET0,0,1,0,0,2,
|
||||
/* table.move */ 0,5,12,0,0,0,35,BC_ISTYPE,0,12,0,BC_ISTYPE,1,14,0,BC_ISTYPE,
|
||||
2,14,0,BC_ISTYPE,3,14,0,BC_ISNEP,4,0,0,BC_JMP,5,1,128,BC_MOV,4,0,0,BC_ISTYPE,
|
||||
4,12,0,BC_ISGT,1,2,0,BC_JMP,5,24,128,BC_SUBVV,5,1,3,BC_ISLT,2,3,0,BC_JMP,6,4,
|
||||
128,BC_ISLE,3,1,0,BC_JMP,6,2,128,BC_ISEQV,4,0,0,BC_JMP,6,9,128,BC_MOV,6,1,0,
|
||||
BC_MOV,7,2,0,BC_KSHORT,8,1,0,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,
|
||||
BC_TSETR,11,10,4,BC_FORL,6,252,127,BC_JMP,6,8,128,BC_MOV,6,2,0,BC_MOV,7,1,0,
|
||||
BC_KSHORT,8,255,255,BC_FORI,6,4,128,BC_ADDVV,10,5,9,BC_TGETR,11,9,0,BC_TSETR,
|
||||
11,10,4,BC_FORL,6,252,127,BC_RET1,4,2,0,
|
||||
#endif
|
||||
0
|
||||
};
|
||||
|
||||
static const struct { const char *name; int ofs; } libbc_map[] = {
|
||||
{"math_deg",0},
|
||||
{"math_rad",25},
|
||||
{"string_len",50},
|
||||
{"table_foreachi",69},
|
||||
{"table_foreach",136},
|
||||
{"table_getn",213},
|
||||
{"table_remove",232},
|
||||
{"table_move",361},
|
||||
{NULL,508}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
** LuaJIT VM builder: PE object emitter.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Only used for building on Windows, since we cannot assume the presence
|
||||
** of a suitable assembler. The host and target byte order must match.
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
#if LJ_TARGET_WINDOWS || LJ_TARGET_CYGWIN
|
||||
|
||||
/* Context for PE object emitter. */
|
||||
static char *strtab;
|
||||
static size_t strtabofs;
|
||||
|
||||
/* -- PE object definitions ----------------------------------------------- */
|
||||
|
||||
/* PE header. */
|
||||
typedef struct PEheader {
|
||||
uint16_t arch;
|
||||
uint16_t nsects;
|
||||
uint32_t time;
|
||||
uint32_t symtabofs;
|
||||
uint32_t nsyms;
|
||||
uint16_t opthdrsz;
|
||||
uint16_t flags;
|
||||
} PEheader;
|
||||
|
||||
/* PE section. */
|
||||
typedef struct PEsection {
|
||||
char name[8];
|
||||
uint32_t vsize;
|
||||
uint32_t vaddr;
|
||||
uint32_t size;
|
||||
uint32_t ofs;
|
||||
uint32_t relocofs;
|
||||
uint32_t lineofs;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
|
||||
/* PE relocation. */
|
||||
typedef struct PEreloc {
|
||||
uint32_t vaddr;
|
||||
uint32_t symidx;
|
||||
uint16_t type;
|
||||
} PEreloc;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_RELOC_SIZE (4+4+2)
|
||||
|
||||
/* PE symbol table entry. */
|
||||
typedef struct PEsym {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
} n;
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl;
|
||||
uint8_t naux;
|
||||
} PEsym;
|
||||
|
||||
/* PE symbol table auxiliary entry for a section. */
|
||||
typedef struct PEsymaux {
|
||||
uint32_t size;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel;
|
||||
uint8_t unused[3];
|
||||
} PEsymaux;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_SYM_SIZE (8+4+2+2+1+1)
|
||||
|
||||
/* PE object CPU specific defines. */
|
||||
#if LJ_TARGET_X86
|
||||
#define PEOBJ_ARCH_TARGET 0x014c
|
||||
#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x06
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#elif LJ_TARGET_X64
|
||||
#define PEOBJ_ARCH_TARGET 0x8664
|
||||
#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x02
|
||||
#define PEOBJ_RELOC_ADDR32NB 0x03
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#define PEOBJ_PDATA_NRELOC 6
|
||||
#define PEOBJ_XDATA_SIZE (8*2+4+6*2)
|
||||
#elif LJ_TARGET_ARM64
|
||||
#define PEOBJ_ARCH_TARGET 0xaa64
|
||||
#define PEOBJ_RELOC_REL32 0x03 /* MS: BRANCH26. */
|
||||
#define PEOBJ_RELOC_DIR32 0x01
|
||||
#define PEOBJ_RELOC_ADDR32NB 0x02
|
||||
#define PEOBJ_RELOC_OFS (-4)
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#define PEOBJ_PDATA_NRELOC 4
|
||||
#define PEOBJ_XDATA_SIZE (4+24+4 +4+8)
|
||||
#endif
|
||||
|
||||
/* Section numbers (0-based). */
|
||||
enum {
|
||||
PEOBJ_SECT_ABS = -2,
|
||||
PEOBJ_SECT_UNDEF = -1,
|
||||
PEOBJ_SECT_TEXT,
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
PEOBJ_SECT_PDATA,
|
||||
PEOBJ_SECT_XDATA,
|
||||
#elif LJ_TARGET_X86
|
||||
PEOBJ_SECT_SXDATA,
|
||||
#endif
|
||||
PEOBJ_SECT_RDATA_Z,
|
||||
PEOBJ_NSECTIONS
|
||||
};
|
||||
|
||||
/* Symbol types. */
|
||||
#define PEOBJ_TYPE_NULL 0
|
||||
#define PEOBJ_TYPE_FUNC 0x20
|
||||
|
||||
/* Symbol storage class. */
|
||||
#define PEOBJ_SCL_EXTERN 2
|
||||
#define PEOBJ_SCL_STATIC 3
|
||||
|
||||
/* -- PE object emitter --------------------------------------------------- */
|
||||
|
||||
/* Emit PE object symbol. */
|
||||
static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
|
||||
int sect, int type, int scl)
|
||||
{
|
||||
PEsym sym;
|
||||
size_t len = strlen(name);
|
||||
if (!strtab) { /* Pass 1: only calculate string table length. */
|
||||
if (len > 8) strtabofs += len+1;
|
||||
return;
|
||||
}
|
||||
if (len <= 8) {
|
||||
memcpy(sym.n.name, name, len);
|
||||
memset(sym.n.name+len, 0, 8-len);
|
||||
} else {
|
||||
sym.n.nameref[0] = 0;
|
||||
sym.n.nameref[1] = (uint32_t)strtabofs;
|
||||
memcpy(strtab + strtabofs, name, len);
|
||||
strtab[strtabofs+len] = 0;
|
||||
strtabofs += len+1;
|
||||
}
|
||||
sym.value = value;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = (uint16_t)type;
|
||||
sym.scl = (uint8_t)scl;
|
||||
sym.naux = 0;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit PE object section symbol. */
|
||||
static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
|
||||
{
|
||||
PEsym sym;
|
||||
PEsymaux aux;
|
||||
if (!strtab) return; /* Pass 1: no output. */
|
||||
memcpy(sym.n.name, pesect[sect].name, 8);
|
||||
sym.value = 0;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = PEOBJ_TYPE_NULL;
|
||||
sym.scl = PEOBJ_SCL_STATIC;
|
||||
sym.naux = 1;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
memset(&aux, 0, sizeof(PEsymaux));
|
||||
aux.size = pesect[sect].size;
|
||||
aux.nreloc = pesect[sect].nreloc;
|
||||
owrite(ctx, &aux, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit Windows PE object file. */
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
PEheader pehdr;
|
||||
PEsection pesect[PEOBJ_NSECTIONS];
|
||||
uint32_t sofs;
|
||||
int i, nrsym;
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs;
|
||||
#endif
|
||||
|
||||
sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
|
||||
|
||||
/* Fill in PE sections. */
|
||||
memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
|
||||
memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
|
||||
pesect[PEOBJ_SECT_TEXT].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
|
||||
pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 60 = read+execute, 50 = align16, 20 = code. */
|
||||
pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS;
|
||||
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1);
|
||||
pesect[PEOBJ_SECT_PDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].size = PEOBJ_PDATA_NRELOC*4);
|
||||
pesect[PEOBJ_SECT_PDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = PEOBJ_PDATA_NRELOC) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_PDATA].flags = 0x40300040;
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1);
|
||||
pesect[PEOBJ_SECT_XDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].size = PEOBJ_XDATA_SIZE); /* See below. */
|
||||
pesect[PEOBJ_SECT_XDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_XDATA].flags = 0x40300040;
|
||||
#elif LJ_TARGET_X86
|
||||
memcpy(pesect[PEOBJ_SECT_SXDATA].name, ".sxdata", sizeof(".sxdata")-1);
|
||||
pesect[PEOBJ_SECT_SXDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_SXDATA].size = 4);
|
||||
pesect[PEOBJ_SECT_SXDATA].relocofs = sofs;
|
||||
/* Flags: 40 = read, 30 = align4, 02 = lnk_info, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_SXDATA].flags = 0x40300240;
|
||||
#endif
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
|
||||
pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
|
||||
|
||||
/* Fill in PE header. */
|
||||
pehdr.arch = PEOBJ_ARCH_TARGET;
|
||||
pehdr.nsects = PEOBJ_NSECTIONS;
|
||||
pehdr.time = 0; /* Timestamp is optional. */
|
||||
pehdr.symtabofs = sofs;
|
||||
pehdr.opthdrsz = 0;
|
||||
pehdr.flags = 0;
|
||||
|
||||
/* Compute the size of the symbol table:
|
||||
** @feat.00 + nsections*2
|
||||
** + asm_start + nsym
|
||||
** + nrsym
|
||||
*/
|
||||
nrsym = ctx->nrelocsym;
|
||||
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win. */
|
||||
#endif
|
||||
|
||||
/* Write PE object header and all sections. */
|
||||
owrite(ctx, &pehdr, sizeof(PEheader));
|
||||
owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
|
||||
|
||||
/* Write .text section. */
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
|
||||
fprintf(stderr, "Error: different byte order for host and target\n");
|
||||
exit(1);
|
||||
}
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
for (i = 0; i < ctx->nreloc; i++) {
|
||||
PEreloc reloc;
|
||||
reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS;
|
||||
reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
|
||||
reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
{ /* Write .pdata section. */
|
||||
uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */
|
||||
PEreloc reloc;
|
||||
pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
{ /* Write .xdata section. */
|
||||
uint16_t xdata[8+2+6];
|
||||
PEreloc reloc;
|
||||
xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */
|
||||
xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */
|
||||
xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */
|
||||
xdata[3] = 0x3000; /* Push rbx. */
|
||||
xdata[4] = 0x6000; /* Push rsi. */
|
||||
xdata[5] = 0x7000; /* Push rdi. */
|
||||
xdata[6] = 0x5000; /* Push rbp. */
|
||||
xdata[7] = 0; /* Alignment. */
|
||||
xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */
|
||||
xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */
|
||||
xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */
|
||||
xdata[12] = 0x0300; /* set_fpreg. */
|
||||
xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */
|
||||
xdata[14] = 0x3000; /* Push rbx. */
|
||||
xdata[15] = 0x5000; /* Push rbp. */
|
||||
owrite(ctx, &xdata, sizeof(xdata));
|
||||
reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_ARM64
|
||||
/* https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling */
|
||||
{ /* Write .pdata section. */
|
||||
uint32_t pdata[4];
|
||||
PEreloc reloc;
|
||||
pdata[0] = 0;
|
||||
pdata[1] = 0;
|
||||
pdata[2] = fcofs;
|
||||
pdata[3] = 4+24+4;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
/* Start of .text and start of .xdata. */
|
||||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
/* Start of vm_ffi_call and start of second part of .xdata. */
|
||||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
{ /* Write .xdata section. */
|
||||
uint32_t u32;
|
||||
uint8_t *p, uwc[24];
|
||||
PEreloc reloc;
|
||||
|
||||
#define CBE16(x) (*p = ((x) >> 8) & 0xff, p[1] = (x) & 0xff, p += 2)
|
||||
#define CALLOC_S(s) (*p++ = ((s) >> 4)) /* s < 512 */
|
||||
#define CSAVE_FPLR(o) (*p++ = 0x40 | ((o) >> 3)) /* o <= 504 */
|
||||
#define CSAVE_REGP(r,o) CBE16(0xc800 | (((r) - 19) << 6) | ((o) >> 3))
|
||||
#define CSAVE_REGS(r1,r2,o1) do { \
|
||||
int r, o; for (r = r1, o = o1; r <= r2; r += 2, o -= 16) CSAVE_REGP(r, o); \
|
||||
} while (0)
|
||||
#define CSAVE_REGPX(r,o) CBE16(0xcc00 | (((r) - 19) << 6) | (~(o) >> 3))
|
||||
#define CSAVE_FREGP(r,o) CBE16(0xd800 | (((r) - 8) << 6) | ((o) >> 3))
|
||||
#define CSAVE_FREGS(r1,r2,o1) do { \
|
||||
int r, o; for (r = r1, o = o1; r <= r2; r += 2, o -= 16) CSAVE_FREGP(r, o); \
|
||||
} while (0)
|
||||
#define CADD_FP(s) CBE16(0xe200 | ((s) >> 3)) /* s < 8*256 */
|
||||
#define CODE_NOP 0xe3
|
||||
#define CODE_END 0xe4
|
||||
#define CEND_ALIGN do { \
|
||||
*p++ = CODE_END; \
|
||||
while ((p - uwc) & 3) *p++ = CODE_NOP; \
|
||||
} while (0)
|
||||
|
||||
/* Unwind codes for .text section with handler. */
|
||||
p = uwc;
|
||||
CADD_FP(192); /* +2 */
|
||||
CSAVE_REGS(19, 28, 176); /* +5*2 */
|
||||
CSAVE_FREGS(8, 15, 96); /* +4*2 */
|
||||
CSAVE_FPLR(192); /* +1 */
|
||||
CALLOC_S(208); /* +1 */
|
||||
CEND_ALIGN; /* +1 +1 -> 24 */
|
||||
|
||||
u32 = ((24u >> 2) << 27) | (1u << 20) | (fcofs >> 2);
|
||||
owrite(ctx, &u32, 4);
|
||||
owrite(ctx, &uwc, 24);
|
||||
|
||||
u32 = 0; /* Handler RVA to be relocated at 4 + 24. */
|
||||
owrite(ctx, &u32, 4);
|
||||
|
||||
/* Unwind codes for vm_ffi_call without handler. */
|
||||
p = uwc;
|
||||
CADD_FP(16); /* +2 */
|
||||
CSAVE_FPLR(16); /* +1 */
|
||||
CSAVE_REGPX(19, -32); /* +2 */
|
||||
CEND_ALIGN; /* +1 +2 -> 8 */
|
||||
|
||||
u32 = ((8u >> 2) << 27) | (((uint32_t)ctx->codesz - fcofs) >> 2);
|
||||
owrite(ctx, &u32, 4);
|
||||
owrite(ctx, &uwc, 8);
|
||||
|
||||
reloc.vaddr = 4 + 24; reloc.symidx = 1+2+nrsym+2+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_X86
|
||||
/* Write .sxdata section. */
|
||||
for (i = 0; i < nrsym; i++) {
|
||||
if (!strcmp(ctx->relocsym[i], "_lj_err_unwind_win")) {
|
||||
uint32_t symidx = 1+2+i;
|
||||
owrite(ctx, &symidx, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == nrsym) {
|
||||
fprintf(stderr, "Error: extern lj_err_unwind_win not used\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write .rdata$Z section. */
|
||||
owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
|
||||
|
||||
/* Write symbol table. */
|
||||
strtab = NULL; /* 1st pass: collect string sizes. */
|
||||
for (;;) {
|
||||
strtabofs = 4;
|
||||
/* Mark as SafeSEH compliant. */
|
||||
emit_peobj_sym(ctx, "@feat.00", 1,
|
||||
PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
|
||||
for (i = 0; i < nrsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->relocsym[i], 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
#ifdef PEOBJ_PDATA_NRELOC
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
|
||||
emit_peobj_sym(ctx, "lj_err_unwind_win", 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
#elif LJ_TARGET_X86
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_SXDATA);
|
||||
#endif
|
||||
|
||||
emit_peobj_sym(ctx, ctx->beginsym, 0,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN);
|
||||
for (i = 0; i < ctx->nsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
|
||||
|
||||
if (strtab)
|
||||
break;
|
||||
/* 2nd pass: alloc strtab, write syms and copy strings. */
|
||||
strtab = (char *)malloc(strtabofs);
|
||||
*(uint32_t *)strtab = (uint32_t)strtabofs;
|
||||
}
|
||||
|
||||
/* Write string table. */
|
||||
owrite(ctx, strtab, strtabofs);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
fprintf(stderr, "Error: no PE object support for this target\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,234 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to dump the bytecode of the library functions written in Lua.
|
||||
-- The resulting 'buildvm_libbc.h' is used for the build process of LuaJIT.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local ffi = require("ffi")
|
||||
local bit = require("bit")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local bcnames = vmdef.bcnames
|
||||
|
||||
local format = string.format
|
||||
|
||||
local isbe = (string.byte(string.dump(function() end), 5) % 2 == 1)
|
||||
|
||||
local function usage(arg)
|
||||
io.stderr:write("Usage: ", arg and arg[0] or "genlibbc",
|
||||
" [-o buildvm_libbc.h] lib_*.c\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function parse_arg(arg)
|
||||
local outfile = "-"
|
||||
if not (arg and arg[1]) then
|
||||
usage(arg)
|
||||
end
|
||||
if arg[1] == "-o" then
|
||||
outfile = arg[2]
|
||||
if not outfile then usage(arg) end
|
||||
table.remove(arg, 1)
|
||||
table.remove(arg, 1)
|
||||
end
|
||||
return outfile
|
||||
end
|
||||
|
||||
local function read_files(names)
|
||||
local src = ""
|
||||
for _,name in ipairs(names) do
|
||||
local fp = assert(io.open(name))
|
||||
src = src .. fp:read("*a")
|
||||
fp:close()
|
||||
end
|
||||
return src
|
||||
end
|
||||
|
||||
local function transform_lua(code)
|
||||
local fixup = {}
|
||||
local n = -30000
|
||||
code = string.gsub(code, "CHECK_(%w*)%((.-)%)", function(tp, var)
|
||||
n = n + 1
|
||||
fixup[n] = { "CHECK", tp }
|
||||
return format("%s=%d", var, n)
|
||||
end)
|
||||
code = string.gsub(code, "PAIRS%((.-)%)", function(var)
|
||||
fixup.PAIRS = true
|
||||
return format("nil, %s, 0x4dp80", var)
|
||||
end)
|
||||
return "return "..code, fixup
|
||||
end
|
||||
|
||||
local function read_uleb128(p)
|
||||
local v = p[0]; p = p + 1
|
||||
if v >= 128 then
|
||||
local sh = 7; v = v - 128
|
||||
repeat
|
||||
local r = p[0]
|
||||
v = v + bit.lshift(bit.band(r, 127), sh)
|
||||
sh = sh + 7
|
||||
p = p + 1
|
||||
until r < 128
|
||||
end
|
||||
return p, v
|
||||
end
|
||||
|
||||
-- ORDER LJ_T
|
||||
local name2itype = {
|
||||
str = 5, func = 9, tab = 12, int = 14, num = 15
|
||||
}
|
||||
|
||||
local BC, BCN = {}, {}
|
||||
for i=0,#bcnames/6-1 do
|
||||
local name = bcnames:sub(i*6+1, i*6+6):gsub(" ", "")
|
||||
BC[name] = i
|
||||
BCN[i] = name
|
||||
end
|
||||
local xop, xra = isbe and 3 or 0, isbe and 2 or 1
|
||||
local xrc, xrb = isbe and 1 or 2, isbe and 0 or 3
|
||||
|
||||
local function fixup_dump(dump, fixup)
|
||||
local buf = ffi.new("uint8_t[?]", #dump+1, dump)
|
||||
local p = buf+5
|
||||
local n, sizebc
|
||||
p, n = read_uleb128(p)
|
||||
local start = p
|
||||
p = p + 4
|
||||
p = read_uleb128(p)
|
||||
p = read_uleb128(p)
|
||||
p, sizebc = read_uleb128(p)
|
||||
local startbc = tonumber(p - start)
|
||||
local rawtab = {}
|
||||
for i=0,sizebc-1 do
|
||||
local op = p[xop]
|
||||
if op == BC.KSHORT then
|
||||
local rd = p[xrc] + 256*p[xrb]
|
||||
rd = bit.arshift(bit.lshift(rd, 16), 16)
|
||||
local f = fixup[rd]
|
||||
if f then
|
||||
if f[1] == "CHECK" then
|
||||
local tp = f[2]
|
||||
if tp == "tab" then rawtab[p[xra]] = true end
|
||||
p[xop] = tp == "num" and BC.ISNUM or BC.ISTYPE
|
||||
p[xrb] = 0
|
||||
p[xrc] = name2itype[tp]
|
||||
else
|
||||
error("unhandled fixup type: "..f[1])
|
||||
end
|
||||
end
|
||||
elseif op == BC.TGETV then
|
||||
if rawtab[p[xrb]] then
|
||||
p[xop] = BC.TGETR
|
||||
end
|
||||
elseif op == BC.TSETV then
|
||||
if rawtab[p[xrb]] then
|
||||
p[xop] = BC.TSETR
|
||||
end
|
||||
elseif op == BC.ITERC then
|
||||
if fixup.PAIRS then
|
||||
p[xop] = BC.ITERN
|
||||
end
|
||||
end
|
||||
p = p + 4
|
||||
end
|
||||
local ndump = ffi.string(start, n)
|
||||
-- Fixup hi-part of 0x4dp80 to LJ_KEYINDEX.
|
||||
ndump = ndump:gsub("\x80\x80\xcd\xaa\x04", "\xff\xff\xf9\xff\x0f")
|
||||
return { dump = ndump, startbc = startbc, sizebc = sizebc }
|
||||
end
|
||||
|
||||
local function find_defs(src, mode)
|
||||
local defs = {}
|
||||
for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do
|
||||
local tcode, fixup = transform_lua(code)
|
||||
local func = assert(load(tcode, "", mode))
|
||||
defs[name] = fixup_dump(string.dump(func, mode), fixup)
|
||||
defs[#defs+1] = name
|
||||
end
|
||||
return defs
|
||||
end
|
||||
|
||||
local function gen_header(defs32, defs64)
|
||||
local t = {}
|
||||
local function w(x) t[#t+1] = x end
|
||||
w("/* This is a generated file. DO NOT EDIT! */\n\n")
|
||||
w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n")
|
||||
for j,defs in ipairs{defs64, defs32} do
|
||||
local s, sb = "", ""
|
||||
for i,name in ipairs(defs) do
|
||||
local d = defs[name]
|
||||
s = s .. d.dump
|
||||
sb = sb .. string.char(i) .. ("\0"):rep(d.startbc - 1)
|
||||
.. (isbe and "\0\0\0\255" or "\255\0\0\0"):rep(d.sizebc)
|
||||
.. ("\0"):rep(#d.dump - d.startbc - d.sizebc*4)
|
||||
end
|
||||
if j == 1 then
|
||||
w("static const uint8_t libbc_code[] = {\n#if LJ_FR2\n")
|
||||
else
|
||||
w("\n#else\n")
|
||||
end
|
||||
local n = 0
|
||||
for i=1,#s do
|
||||
local x = string.byte(s, i)
|
||||
local xb = string.byte(sb, i)
|
||||
if xb == 255 then
|
||||
local name = BCN[x]
|
||||
local m = #name + 4
|
||||
if n + m > 78 then n = 0; w("\n") end
|
||||
n = n + m
|
||||
w("BC_"); w(name)
|
||||
else
|
||||
local m = x < 10 and 2 or (x < 100 and 3 or 4)
|
||||
if xb == 0 then
|
||||
if n + m > 78 then n = 0; w("\n") end
|
||||
else
|
||||
local name = defs[xb]:gsub("_", ".")
|
||||
if n ~= 0 then w("\n") end
|
||||
w("/* "); w(name); w(" */ ")
|
||||
n = #name + 7
|
||||
end
|
||||
n = n + m
|
||||
w(x)
|
||||
end
|
||||
w(",")
|
||||
end
|
||||
end
|
||||
w("\n#endif\n0\n};\n\n")
|
||||
w("static const struct { const char *name; int ofs; } libbc_map[] = {\n")
|
||||
local m32, m64 = 0, 0
|
||||
for i,name in ipairs(defs32) do
|
||||
assert(name == defs64[i])
|
||||
w('{"'); w(name); w('",'); w(m32) w('},\n')
|
||||
m32 = m32 + #defs32[name].dump
|
||||
m64 = m64 + #defs64[name].dump
|
||||
assert(m32 == m64)
|
||||
end
|
||||
w("{NULL,"); w(m32); w("}\n};\n\n")
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local function write_file(name, data)
|
||||
if name == "-" then
|
||||
assert(io.write(data))
|
||||
assert(io.flush())
|
||||
else
|
||||
local fp = io.open(name)
|
||||
if fp then
|
||||
local old = fp:read("*a")
|
||||
fp:close()
|
||||
if data == old then return end
|
||||
end
|
||||
fp = assert(io.open(name, "w"))
|
||||
assert(fp:write(data))
|
||||
assert(fp:close())
|
||||
end
|
||||
end
|
||||
|
||||
local outfile = parse_arg(arg)
|
||||
local src = read_files(arg)
|
||||
local defs32 = find_defs(src, "Wdts")
|
||||
local defs64 = find_defs(src, "Xdts")
|
||||
local hdr = gen_header(defs32, defs64)
|
||||
write_file(outfile, hdr)
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to generate a customized, minified version of Lua.
|
||||
-- The resulting 'minilua' is used for the build process of LuaJIT.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local sub, match, gsub = string.sub, string.match, string.gsub
|
||||
|
||||
local LUA_VERSION = "5.1.5"
|
||||
local LUA_SOURCE
|
||||
|
||||
local function usage()
|
||||
io.stderr:write("Usage: ", arg and arg[0] or "genminilua",
|
||||
" lua-", LUA_VERSION, "-source-dir\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function find_sources()
|
||||
LUA_SOURCE = arg and arg[1]
|
||||
if not LUA_SOURCE then usage() end
|
||||
if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end
|
||||
local fp = io.open(LUA_SOURCE .. "lua.h")
|
||||
if not fp then
|
||||
LUA_SOURCE = LUA_SOURCE.."src/"
|
||||
fp = io.open(LUA_SOURCE .. "lua.h")
|
||||
if not fp then usage() end
|
||||
end
|
||||
local all = fp:read("*a")
|
||||
fp:close()
|
||||
if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then
|
||||
io.stderr:write("Error: version mismatch\n")
|
||||
usage()
|
||||
end
|
||||
end
|
||||
|
||||
local LUA_FILES = {
|
||||
"lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c",
|
||||
"lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c",
|
||||
"llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c",
|
||||
"lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c",
|
||||
}
|
||||
|
||||
local REMOVE_LIB = {}
|
||||
gsub([[
|
||||
collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset
|
||||
select tostring xpcall
|
||||
foreach foreachi getn maxn setn
|
||||
popen tmpfile seek setvbuf __tostring
|
||||
clock date difftime execute getenv rename setlocale time tmpname
|
||||
dump gfind len reverse
|
||||
LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME
|
||||
]], "%S+", function(name)
|
||||
REMOVE_LIB[name] = true
|
||||
end)
|
||||
|
||||
local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, }
|
||||
|
||||
local CUSTOM_MAIN = [[
|
||||
typedef unsigned int UB;
|
||||
static UB barg(lua_State *L,int idx){
|
||||
union{lua_Number n;U64 b;}bn;
|
||||
bn.n=lua_tonumber(L,idx)+6755399441055744.0;
|
||||
if (bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number");
|
||||
return(UB)bn.b;
|
||||
}
|
||||
#define BRET(b) lua_pushnumber(L,(lua_Number)(int)(b));return 1;
|
||||
static int tobit(lua_State *L){
|
||||
BRET(barg(L,1))}
|
||||
static int bnot(lua_State *L){
|
||||
BRET(~barg(L,1))}
|
||||
static int band(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)}
|
||||
static int bor(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)}
|
||||
static int bxor(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)}
|
||||
static int lshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)}
|
||||
static int rshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)}
|
||||
static int arshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)}
|
||||
static int rol(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))}
|
||||
static int ror(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))}
|
||||
static int bswap(lua_State *L){
|
||||
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)}
|
||||
static int tohex(lua_State *L){
|
||||
UB b=barg(L,1);
|
||||
int n=lua_isnone(L,2)?8:(int)barg(L,2);
|
||||
const char *hexdigits="0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if(n<0){n=-n;hexdigits="0123456789ABCDEF";}
|
||||
if(n>8)n=8;
|
||||
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;}
|
||||
lua_pushlstring(L,buf,(size_t)n);
|
||||
return 1;
|
||||
}
|
||||
static const struct luaL_Reg bitlib[] = {
|
||||
{"tobit",tobit},
|
||||
{"bnot",bnot},
|
||||
{"band",band},
|
||||
{"bor",bor},
|
||||
{"bxor",bxor},
|
||||
{"lshift",lshift},
|
||||
{"rshift",rshift},
|
||||
{"arshift",arshift},
|
||||
{"rol",rol},
|
||||
{"ror",ror},
|
||||
{"bswap",bswap},
|
||||
{"tohex",tohex},
|
||||
{NULL,NULL}
|
||||
};
|
||||
int main(int argc, char **argv){
|
||||
lua_State *L = luaL_newstate();
|
||||
int i;
|
||||
luaL_openlibs(L);
|
||||
luaL_register(L, "bit", bitlib);
|
||||
if (argc < 2) return sizeof(void *);
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_pushstring(L, argv[1]);
|
||||
lua_rawseti(L, -2, 0);
|
||||
lua_setglobal(L, "arg");
|
||||
if (luaL_loadfile(L, argv[1]))
|
||||
goto err;
|
||||
for (i = 2; i < argc; i++)
|
||||
lua_pushstring(L, argv[i]);
|
||||
if (lua_pcall(L, argc - 2, 0, 0)) {
|
||||
err:
|
||||
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
|
||||
return 1;
|
||||
}
|
||||
lua_close(L);
|
||||
return 0;
|
||||
}
|
||||
]]
|
||||
|
||||
local function read_sources()
|
||||
local t = {}
|
||||
for i, name in ipairs(LUA_FILES) do
|
||||
local fp = assert(io.open(LUA_SOURCE..name, "r"))
|
||||
t[i] = fp:read("*a")
|
||||
assert(fp:close())
|
||||
end
|
||||
t[#t+1] = CUSTOM_MAIN
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local includes = {}
|
||||
|
||||
local function merge_includes(src)
|
||||
return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name)
|
||||
if includes[name] then return "" end
|
||||
includes[name] = true
|
||||
local fp = assert(io.open(LUA_SOURCE..name, "r"))
|
||||
local inc = fp:read("*a")
|
||||
assert(fp:close())
|
||||
inc = gsub(inc, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "")
|
||||
inc = gsub(inc, "#endif%s*$", "")
|
||||
return merge_includes(inc)
|
||||
end)
|
||||
end
|
||||
|
||||
local function get_license(src)
|
||||
return match(src, "/%*+\n%* Copyright %(.-%*/\n")
|
||||
end
|
||||
|
||||
local function fold_lines(src)
|
||||
return gsub(src, "\\\n", " ")
|
||||
end
|
||||
|
||||
local strings = {}
|
||||
|
||||
local function save_str(str)
|
||||
local n = #strings+1
|
||||
strings[n] = str
|
||||
return "\1"..n.."\2"
|
||||
end
|
||||
|
||||
local function save_strings(src)
|
||||
src = gsub(src, '"[^"\n]*"', save_str)
|
||||
return gsub(src, "'[^'\n]*'", save_str)
|
||||
end
|
||||
|
||||
local function restore_strings(src)
|
||||
return gsub(src, "\1(%d+)\2", function(numstr)
|
||||
return strings[tonumber(numstr)]
|
||||
end)
|
||||
end
|
||||
|
||||
local function def_istrue(def)
|
||||
return def == "INT_MAX > 2147483640L" or
|
||||
def == "LUAI_BITSINT >= 32" or
|
||||
def == "SIZE_Bx < LUAI_BITSINT-1" or
|
||||
def == "cast" or
|
||||
def == "defined(LUA_CORE)" or
|
||||
def == "MINSTRTABSIZE" or
|
||||
def == "LUA_MINBUFFER" or
|
||||
def == "HARDSTACKTESTS" or
|
||||
def == "UNUSED"
|
||||
end
|
||||
|
||||
local head, defs = {[[
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int64 U64;
|
||||
#else
|
||||
typedef unsigned long long U64;
|
||||
#endif
|
||||
int _CRT_glob = 0;
|
||||
]]}, {}
|
||||
|
||||
local function preprocess(src)
|
||||
local t = { match(src, "^(.-)#") }
|
||||
local lvl, on, oldon = 0, true, {}
|
||||
for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do
|
||||
if pp == "if" or pp == "ifdef" or pp == "ifndef" then
|
||||
lvl = lvl + 1
|
||||
oldon[lvl] = on
|
||||
on = def_istrue(def)
|
||||
elseif pp == "else" then
|
||||
if oldon[lvl] then
|
||||
if on == false then on = true else on = false end
|
||||
end
|
||||
elseif pp == "elif" then
|
||||
if oldon[lvl] then
|
||||
on = def_istrue(def)
|
||||
end
|
||||
elseif pp == "endif" then
|
||||
on = oldon[lvl]
|
||||
lvl = lvl - 1
|
||||
elseif on then
|
||||
if pp == "include" then
|
||||
if not head[def] and not REMOVE_EXTINC[def] then
|
||||
head[def] = true
|
||||
head[#head+1] = "#include "..def.."\n"
|
||||
end
|
||||
elseif pp == "define" then
|
||||
local k, sp, v = match(def, "([%w_]+)(%s*)(.*)")
|
||||
if k and not (sp == "" and sub(v, 1, 1) == "(") then
|
||||
defs[k] = gsub(v, "%a[%w_]*", function(tok)
|
||||
return defs[tok] or tok
|
||||
end)
|
||||
else
|
||||
t[#t+1] = "#define "..def.."\n"
|
||||
end
|
||||
elseif pp ~= "undef" then
|
||||
error("unexpected directive: "..pp.." "..def)
|
||||
end
|
||||
end
|
||||
if on then t[#t+1] = txt end
|
||||
end
|
||||
return gsub(table.concat(t), "%a[%w_]*", function(tok)
|
||||
return defs[tok] or tok
|
||||
end)
|
||||
end
|
||||
|
||||
local function merge_header(src, license)
|
||||
local hdr = string.format([[
|
||||
/* This is a heavily customized and minimized copy of Lua %s. */
|
||||
/* It's only used to build LuaJIT. It does NOT have all standard functions! */
|
||||
]], LUA_VERSION)
|
||||
return hdr..license..table.concat(head)..src
|
||||
end
|
||||
|
||||
local function strip_unused1(src)
|
||||
return gsub(src, '( {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func)
|
||||
return REMOVE_LIB[func] and "" or line
|
||||
end)
|
||||
end
|
||||
|
||||
local function strip_unused2(src)
|
||||
return gsub(src, "Symbolic Execution.-}=", "")
|
||||
end
|
||||
|
||||
local function strip_unused3(src)
|
||||
src = gsub(src, "extern", "static")
|
||||
src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(")
|
||||
src = gsub(src, "#define lua_assert[^\n]*\n", "")
|
||||
src = gsub(src, "lua_assert%b();?", "")
|
||||
src = gsub(src, "default:\n}", "default:;\n}")
|
||||
src = gsub(src, "lua_lock%b();", "")
|
||||
src = gsub(src, "lua_unlock%b();", "")
|
||||
src = gsub(src, "luai_threadyield%b();", "")
|
||||
src = gsub(src, "luai_userstateopen%b();", "{}")
|
||||
src = gsub(src, "luai_userstate%w+%b();", "")
|
||||
src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser")
|
||||
src = gsub(src, "trydecpoint%(ls,seminfo%)",
|
||||
"luaX_lexerror(ls,\"malformed number\",TK_NUMBER)")
|
||||
src = gsub(src, "int c=luaZ_lookahead%b();", "")
|
||||
src = gsub(src, "luaL_register%(L,[^,]*,co_funcs%);\nreturn 2;",
|
||||
"return 1;")
|
||||
src = gsub(src, "getfuncname%b():", "NULL:")
|
||||
src = gsub(src, "getobjname%b():", "NULL:")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "")
|
||||
src = gsub(src, "(twoto%b()%()", "%1(size_t)")
|
||||
src = gsub(src, "i<sizenode", "i<(int)sizenode")
|
||||
src = gsub(src, "cast%(unsigned int,key%-1%)", "cast(unsigned int,key)-1")
|
||||
return gsub(src, "\n\n+", "\n")
|
||||
end
|
||||
|
||||
local function strip_comments(src)
|
||||
return gsub(src, "/%*.-%*/", " ")
|
||||
end
|
||||
|
||||
local function strip_whitespace(src)
|
||||
src = gsub(src, "^%s+", "")
|
||||
src = gsub(src, "%s*\n%s*", "\n")
|
||||
src = gsub(src, "[ \t]+", " ")
|
||||
src = gsub(src, "(%W) ", "%1")
|
||||
return gsub(src, " (%W)", "%1")
|
||||
end
|
||||
|
||||
local function rename_tokens1(src)
|
||||
src = gsub(src, "getline", "getline_")
|
||||
src = gsub(src, "struct ([%w_]+)", "ZX%1")
|
||||
return gsub(src, "union ([%w_]+)", "ZY%1")
|
||||
end
|
||||
|
||||
local function rename_tokens2(src)
|
||||
src = gsub(src, "ZX([%w_]+)", "struct %1")
|
||||
return gsub(src, "ZY([%w_]+)", "union %1")
|
||||
end
|
||||
|
||||
local function fix_bugs_and_warnings(src)
|
||||
src = gsub(src, "(luaD_checkstack%(L,p%->maxstacksize)%)", "%1+p->numparams)")
|
||||
src = gsub(src, "if%(sep==%-1%)(return'%[';)\nelse (luaX_lexerror%b();)", "if (sep!=-1)%2\n%1")
|
||||
return gsub(src, "(default:{\nNode%*n=mainposition)", "/*fallthrough*/\n%1")
|
||||
end
|
||||
|
||||
local function func_gather(src)
|
||||
local nodes, list = {}, {}
|
||||
local pos, len = 1, #src
|
||||
while pos < len do
|
||||
local d, w = match(src, "^(#define ([%w_]+)[^\n]*\n)", pos)
|
||||
if d then
|
||||
local n = #list+1
|
||||
list[n] = d
|
||||
nodes[w] = n
|
||||
else
|
||||
local s
|
||||
d, w, s = match(src, "^(([%w_]+)[^\n]*([{;])\n)", pos)
|
||||
if not d then
|
||||
d, w, s = match(src, "^(([%w_]+)[^(]*%b()([{;])\n)", pos)
|
||||
if not d then d = match(src, "^[^\n]*\n", pos) end
|
||||
end
|
||||
if s == "{" then
|
||||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
|
||||
if sub(d, -2) == "{\n" then
|
||||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
|
||||
end
|
||||
end
|
||||
local k, v = nil, d
|
||||
if w == "typedef" then
|
||||
if match(d, "^typedef enum") then
|
||||
head[#head+1] = d
|
||||
else
|
||||
k = match(d, "([%w_]+);\n$")
|
||||
if not k then k = match(d, "^.-%(.-([%w_]+)%)%(") end
|
||||
end
|
||||
elseif w == "enum" then
|
||||
head[#head+1] = v
|
||||
elseif w ~= nil then
|
||||
k = match(d, "^[^\n]-([%w_]+)[(%[=]")
|
||||
if k then
|
||||
if w ~= "static" and k ~= "main" then v = "static "..d end
|
||||
else
|
||||
k = w
|
||||
end
|
||||
end
|
||||
if w and k then
|
||||
local o = nodes[k]
|
||||
if o then nodes["*"..k] = o end
|
||||
local n = #list+1
|
||||
list[n] = v
|
||||
nodes[k] = n
|
||||
end
|
||||
end
|
||||
pos = pos + #d
|
||||
end
|
||||
return nodes, list
|
||||
end
|
||||
|
||||
local function func_visit(nodes, list, used, n)
|
||||
local i = nodes[n]
|
||||
for m in string.gmatch(list[i], "[%w_]+") do
|
||||
if nodes[m] then
|
||||
local j = used[m]
|
||||
if not j then
|
||||
used[m] = i
|
||||
func_visit(nodes, list, used, m)
|
||||
elseif i < j then
|
||||
used[m] = i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function func_collect(src)
|
||||
local nodes, list = func_gather(src)
|
||||
local used = {}
|
||||
func_visit(nodes, list, used, "main")
|
||||
for n,i in pairs(nodes) do
|
||||
local j = used[n]
|
||||
if j and j < i then used["*"..n] = j end
|
||||
end
|
||||
for n,i in pairs(nodes) do
|
||||
if not used[n] then list[i] = "" end
|
||||
end
|
||||
return table.concat(list)
|
||||
end
|
||||
|
||||
find_sources()
|
||||
local src = read_sources()
|
||||
src = merge_includes(src)
|
||||
local license = get_license(src)
|
||||
src = fold_lines(src)
|
||||
src = strip_unused1(src)
|
||||
src = save_strings(src)
|
||||
src = strip_unused2(src)
|
||||
src = strip_comments(src)
|
||||
src = preprocess(src)
|
||||
src = strip_whitespace(src)
|
||||
src = strip_unused3(src)
|
||||
src = rename_tokens1(src)
|
||||
src = func_collect(src)
|
||||
src = rename_tokens2(src)
|
||||
src = restore_strings(src)
|
||||
src = fix_bugs_and_warnings(src)
|
||||
src = merge_header(src, license)
|
||||
io.write(src)
|
||||
@@ -0,0 +1,45 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to embed the rolling release version in luajit.h.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local arg = {...}
|
||||
local FILE_ROLLING_H = arg[1] or "luajit_rolling.h"
|
||||
local FILE_RELVER_TXT = arg[2] or "luajit_relver.txt"
|
||||
local FILE_LUAJIT_H = arg[3] or "luajit.h"
|
||||
|
||||
local function file_read(file)
|
||||
local fp = assert(io.open(file, "rb"), "run from the wrong directory")
|
||||
local data = assert(fp:read("*a"))
|
||||
fp:close()
|
||||
return data
|
||||
end
|
||||
|
||||
local function file_write_mod(file, data)
|
||||
local fp = io.open(file, "rb")
|
||||
if fp then
|
||||
local odata = assert(fp:read("*a"))
|
||||
fp:close()
|
||||
if odata == data then return end
|
||||
end
|
||||
fp = assert(io.open(file, "wb"))
|
||||
assert(fp:write(data))
|
||||
assert(fp:close())
|
||||
end
|
||||
|
||||
local text = file_read(FILE_ROLLING_H):gsub("#error.-\n", "")
|
||||
local relver = file_read(FILE_RELVER_TXT):match("(%d+)")
|
||||
|
||||
if relver then
|
||||
text = text:gsub("ROLLING", relver)
|
||||
else
|
||||
io.stderr:write([[
|
||||
**** WARNING Cannot determine rolling release version from git log.
|
||||
**** WARNING The 'git' command must be available during the build.
|
||||
]])
|
||||
file_write_mod(FILE_RELVER_TXT, "ROLLING\n") -- Fallback for install target.
|
||||
end
|
||||
|
||||
file_write_mod(FILE_LUAJIT_H, text)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
vmdef.lua
|
||||
@@ -0,0 +1,189 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT bytecode listing module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
|
||||
-- it hooks into the parser and lists all functions of a chunk as they
|
||||
-- are parsed.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
|
||||
-- luajit -jbc=- foo.lua
|
||||
-- luajit -jbc=foo.list foo.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
|
||||
-- is started.
|
||||
--
|
||||
-- This module can also be used programmatically:
|
||||
--
|
||||
-- local bc = require("jit.bc")
|
||||
--
|
||||
-- local function foo() print("hello") end
|
||||
--
|
||||
-- bc.dump(foo) --> -- BYTECODE -- [...]
|
||||
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
|
||||
--
|
||||
-- local out = {
|
||||
-- -- Do something with each line:
|
||||
-- write = function(t, ...) io.write(...) end,
|
||||
-- close = function(t) end,
|
||||
-- flush = function(t) end,
|
||||
-- }
|
||||
-- bc.dump(foo, out)
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local bit = require("bit")
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, band, shr = string.byte, bit.band, bit.rshift
|
||||
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
|
||||
local funcuvname = jutil.funcuvname
|
||||
local bcnames = vmdef.bcnames
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
-- Return one bytecode line.
|
||||
local function bcline(func, pc, prefix)
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then return end
|
||||
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
|
||||
local a = band(shr(ins, 8), 0xff)
|
||||
local oidx = 6*band(ins, 0xff)
|
||||
local op = sub(bcnames, oidx+1, oidx+6)
|
||||
local s = format("%04d %s %-6s %3s ",
|
||||
pc, prefix or " ", op, ma == 0 and "" or a)
|
||||
local d = shr(ins, 16)
|
||||
if mc == 13*128 then -- BCMjump
|
||||
return format("%s=> %04d\n", s, pc+d-0x7fff)
|
||||
end
|
||||
if mb ~= 0 then
|
||||
d = band(d, 0xff)
|
||||
elseif mc == 0 then
|
||||
return s.."\n"
|
||||
end
|
||||
local kc
|
||||
if mc == 10*128 then -- BCMstr
|
||||
kc = funck(func, -d-1)
|
||||
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
|
||||
elseif mc == 9*128 then -- BCMnum
|
||||
kc = funck(func, d)
|
||||
if op == "TSETM " then kc = kc - 2^52 end
|
||||
elseif mc == 12*128 then -- BCMfunc
|
||||
local fi = funcinfo(funck(func, -d-1))
|
||||
if fi.ffid then
|
||||
kc = vmdef.ffnames[fi.ffid]
|
||||
else
|
||||
kc = fi.loc
|
||||
end
|
||||
elseif mc == 5*128 then -- BCMuv
|
||||
kc = funcuvname(func, d)
|
||||
end
|
||||
if ma == 5 then -- BCMuv
|
||||
local ka = funcuvname(func, a)
|
||||
if kc then kc = ka.." ; "..kc else kc = ka end
|
||||
end
|
||||
if mb ~= 0 then
|
||||
local b = shr(ins, 24)
|
||||
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
|
||||
return format("%s%3d %3d\n", s, b, d)
|
||||
end
|
||||
if kc then return format("%s%3d ; %s\n", s, d, kc) end
|
||||
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
|
||||
return format("%s%3d\n", s, d)
|
||||
end
|
||||
|
||||
-- Collect branch targets of a function.
|
||||
local function bctargets(func)
|
||||
local target = {}
|
||||
for pc=1,1000000000 do
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then break end
|
||||
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
-- Dump bytecode instructions of a function.
|
||||
local function bcdump(func, out, all)
|
||||
if not out then out = stdout end
|
||||
local fi = funcinfo(func)
|
||||
if all and fi.children then
|
||||
for n=-1,-1000000000,-1 do
|
||||
local k = funck(func, n)
|
||||
if not k then break end
|
||||
if type(k) == "proto" then bcdump(k, out, true) end
|
||||
end
|
||||
end
|
||||
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
|
||||
local target = bctargets(func)
|
||||
for pc=1,1000000000 do
|
||||
local s = bcline(func, pc, target[pc] and "=>")
|
||||
if not s then break end
|
||||
out:write(s)
|
||||
end
|
||||
out:write("\n")
|
||||
out:flush()
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
-- List handler.
|
||||
local function h_list(func)
|
||||
return bcdump(func, out)
|
||||
end
|
||||
|
||||
-- Detach list handler.
|
||||
local function bclistoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(h_list)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach list handler.
|
||||
local function bcliston(outfile)
|
||||
if active then bclistoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(h_list, "bc")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
line = bcline,
|
||||
dump = bcdump,
|
||||
targets = bctargets,
|
||||
on = bcliston,
|
||||
off = bclistoff,
|
||||
start = bcliston -- For -j command line option.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,629 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT module to save/list bytecode.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module saves or lists the bytecode for an input file.
|
||||
-- It's run by the -b command line option.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20199, "LuaJIT core/library version mismatch")
|
||||
local bit = require("bit")
|
||||
|
||||
-- Symbol name prefix for LuaJIT bytecode.
|
||||
local LJBC_PREFIX = "luaJIT_BC_"
|
||||
|
||||
local type, assert = type, assert
|
||||
local format = string.format
|
||||
local tremove, tconcat = table.remove, table.concat
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function usage()
|
||||
io.stderr:write[[
|
||||
Save LuaJIT bytecode: luajit -b[options] input output
|
||||
-l Only list bytecode.
|
||||
-s Strip debug info (default).
|
||||
-g Keep debug info.
|
||||
-W Generate 32 bit (non-GC64) bytecode.
|
||||
-X Generate 64 bit (GC64) bytecode.
|
||||
-d Generate bytecode in deterministic manner.
|
||||
-n name Set module name (default: auto-detect from input name).
|
||||
-t type Set output file type (default: auto-detect from output name).
|
||||
-a arch Override architecture for object files (default: native).
|
||||
-o os Override OS for object files (default: native).
|
||||
-F name Override filename (default: input filename).
|
||||
-e chunk Use chunk string as input.
|
||||
-- Stop handling options.
|
||||
- Use stdin as input and/or stdout as output.
|
||||
|
||||
File types: c cc h obj o raw (default)
|
||||
]]
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function check(ok, ...)
|
||||
if ok then return ok, ... end
|
||||
io.stderr:write("luajit: ", ...)
|
||||
io.stderr:write("\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function readfile(ctx, input)
|
||||
if ctx.string then
|
||||
return check(loadstring(input, nil, ctx.mode))
|
||||
elseif ctx.filename then
|
||||
local data
|
||||
if input == "-" then
|
||||
data = io.stdin:read("*a")
|
||||
else
|
||||
local fp = assert(io.open(input, "rb"))
|
||||
data = assert(fp:read("*a"))
|
||||
assert(fp:close())
|
||||
end
|
||||
return check(load(data, ctx.filename, ctx.mode))
|
||||
else
|
||||
if input == "-" then input = nil end
|
||||
return check(loadfile(input, ctx.mode))
|
||||
end
|
||||
end
|
||||
|
||||
local function savefile(name, mode)
|
||||
if name == "-" then return io.stdout end
|
||||
return check(io.open(name, mode))
|
||||
end
|
||||
|
||||
local function set_stdout_binary(ffi)
|
||||
ffi.cdef[[int _setmode(int fd, int mode);]]
|
||||
ffi.C._setmode(1, 0x8000)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_type = {
|
||||
raw = "raw", c = "c", cc = "c", h = "h", o = "obj", obj = "obj",
|
||||
}
|
||||
|
||||
local map_arch = {
|
||||
x86 = { e = "le", b = 32, m = 3, p = 0x14c, },
|
||||
x64 = { e = "le", b = 64, m = 62, p = 0x8664, },
|
||||
arm = { e = "le", b = 32, m = 40, p = 0x1c0, },
|
||||
arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, },
|
||||
arm64be = { e = "be", b = 64, m = 183, },
|
||||
ppc = { e = "be", b = 32, m = 20, },
|
||||
mips = { e = "be", b = 32, m = 8, f = 0x50001006, },
|
||||
mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, },
|
||||
mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, },
|
||||
mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, },
|
||||
mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, },
|
||||
mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, },
|
||||
}
|
||||
|
||||
local map_os = {
|
||||
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
|
||||
openbsd = true, dragonfly = true, solaris = true,
|
||||
}
|
||||
|
||||
local function checkarg(str, map, err)
|
||||
str = str:lower()
|
||||
local s = check(map[str], "unknown ", err)
|
||||
return type(s) == "string" and s or str
|
||||
end
|
||||
|
||||
local function detecttype(str)
|
||||
local ext = str:lower():match("%.(%a+)$")
|
||||
return map_type[ext] or "raw"
|
||||
end
|
||||
|
||||
local function checkmodname(str)
|
||||
check(str:match("^[%w_.%-]+$"), "bad module name")
|
||||
return str:gsub("[%.%-]", "_")
|
||||
end
|
||||
|
||||
local function detectmodname(str)
|
||||
if type(str) == "string" then
|
||||
local tail = str:match("[^/\\]+$")
|
||||
if tail then str = tail end
|
||||
local head = str:match("^(.*)%.[^.]*$")
|
||||
if head then str = head end
|
||||
str = str:match("^[%w_.%-]+")
|
||||
else
|
||||
str = nil
|
||||
end
|
||||
check(str, "cannot derive module name, use -n name")
|
||||
return str:gsub("[%.%-]", "_")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bcsave_tail(fp, output, s)
|
||||
local ok, err = fp:write(s)
|
||||
if ok and output ~= "-" then ok, err = fp:close() end
|
||||
check(ok, "cannot write ", output, ": ", err)
|
||||
end
|
||||
|
||||
local function bcsave_raw(output, s)
|
||||
if output == "-" and jit.os == "Windows" then
|
||||
local ok, ffi = pcall(require, "ffi")
|
||||
check(ok, "FFI library required to write binary file to stdout")
|
||||
set_stdout_binary(ffi)
|
||||
end
|
||||
local fp = savefile(output, "wb")
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_c(ctx, output, s)
|
||||
local fp = savefile(output, "w")
|
||||
if ctx.type == "c" then
|
||||
fp:write(format([[
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
const unsigned char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname))
|
||||
else
|
||||
fp:write(format([[
|
||||
#define %s%s_SIZE %d
|
||||
static const unsigned char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
|
||||
end
|
||||
local t, n, m = {}, 0, 0
|
||||
for i=1,#s do
|
||||
local b = tostring(string.byte(s, i))
|
||||
m = m + #b + 1
|
||||
if m > 78 then
|
||||
fp:write(tconcat(t, ",", 1, n), ",\n")
|
||||
n, m = 0, #b + 1
|
||||
end
|
||||
n = n + 1
|
||||
t[n] = b
|
||||
end
|
||||
bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n")
|
||||
end
|
||||
|
||||
local function bcsave_elfobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint32_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF32header;
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint64_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF64header;
|
||||
typedef struct {
|
||||
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
|
||||
} ELF32sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, type;
|
||||
uint64_t flags, addr, ofs, size;
|
||||
uint32_t link, info;
|
||||
uint64_t align, entsize;
|
||||
} ELF64sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, value, size;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
} ELF32symbol;
|
||||
typedef struct {
|
||||
uint32_t name;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
uint64_t value, size;
|
||||
} ELF64symbol;
|
||||
typedef struct {
|
||||
ELF32header hdr;
|
||||
ELF32sectheader sect[6];
|
||||
ELF32symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF32obj;
|
||||
typedef struct {
|
||||
ELF64header hdr;
|
||||
ELF64sectheader sect[6];
|
||||
ELF64symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF64obj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local ai = assert(map_arch[ctx.arch])
|
||||
local is64, isbe = ai.b == 64, ai.e == "be"
|
||||
|
||||
-- Handle different host/target endianess.
|
||||
local function f32(x) return x end
|
||||
local f16, fofs = f32, f32
|
||||
if ffi.abi("be") ~= isbe then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
if is64 then
|
||||
local two32 = ffi.cast("int64_t", 2^32)
|
||||
function fofs(x) return bit.bswap(x)*two32 end
|
||||
else
|
||||
fofs = f32
|
||||
end
|
||||
end
|
||||
|
||||
-- Create ELF object and fill in header.
|
||||
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
|
||||
local hdr = o.hdr
|
||||
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
|
||||
local bf = assert(io.open("/bin/ls", "rb"))
|
||||
local bs = bf:read(9)
|
||||
bf:close()
|
||||
ffi.copy(o, bs, 9)
|
||||
check(hdr.emagic[0] == 127, "no support for writing native object files")
|
||||
else
|
||||
hdr.emagic = "\127ELF"
|
||||
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
|
||||
end
|
||||
hdr.eclass = is64 and 2 or 1
|
||||
hdr.eendian = isbe and 2 or 1
|
||||
hdr.eversion = 1
|
||||
hdr.type = f16(1)
|
||||
hdr.machine = f16(ai.m)
|
||||
hdr.flags = f32(ai.f or 0)
|
||||
hdr.version = f32(1)
|
||||
hdr.shofs = fofs(ffi.offsetof(o, "sect"))
|
||||
hdr.ehsize = f16(ffi.sizeof(hdr))
|
||||
hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
|
||||
hdr.shnum = f16(6)
|
||||
hdr.shstridx = f16(2)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
local sofs, ofs = ffi.offsetof(o, "space"), 1
|
||||
for i,name in ipairs{
|
||||
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
|
||||
} do
|
||||
local sect = o.sect[i]
|
||||
sect.align = fofs(1)
|
||||
sect.name = f32(ofs)
|
||||
ffi.copy(o.space+ofs, name)
|
||||
ofs = ofs + #name+1
|
||||
end
|
||||
o.sect[1].type = f32(2) -- .symtab
|
||||
o.sect[1].link = f32(3)
|
||||
o.sect[1].info = f32(1)
|
||||
o.sect[1].align = fofs(8)
|
||||
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
|
||||
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
|
||||
o.sect[1].size = fofs(ffi.sizeof(o.sym))
|
||||
o.sym[1].name = f32(1)
|
||||
o.sym[1].sectidx = f16(4)
|
||||
o.sym[1].size = fofs(#s)
|
||||
o.sym[1].info = 17
|
||||
o.sect[2].type = f32(3) -- .shstrtab
|
||||
o.sect[2].ofs = fofs(sofs)
|
||||
o.sect[2].size = fofs(ofs)
|
||||
o.sect[3].type = f32(3) -- .strtab
|
||||
o.sect[3].ofs = fofs(sofs + ofs)
|
||||
o.sect[3].size = fofs(#symname+2)
|
||||
ffi.copy(o.space+ofs+1, symname)
|
||||
ofs = ofs + #symname + 2
|
||||
o.sect[4].type = f32(1) -- .rodata
|
||||
o.sect[4].flags = fofs(2)
|
||||
o.sect[4].ofs = fofs(sofs + ofs)
|
||||
o.sect[4].size = fofs(#s)
|
||||
o.sect[5].type = f32(1) -- .note.GNU-stack
|
||||
o.sect[5].ofs = fofs(sofs + ofs + #s)
|
||||
|
||||
-- Write ELF object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_peobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint16_t arch, nsects;
|
||||
uint32_t time, symtabofs, nsyms;
|
||||
uint16_t opthdrsz, flags;
|
||||
} PEheader;
|
||||
typedef struct {
|
||||
char name[8];
|
||||
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
typedef struct __attribute((packed)) {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
};
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl, naux;
|
||||
} PEsym;
|
||||
typedef struct __attribute((packed)) {
|
||||
uint32_t size;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel, unused[3];
|
||||
} PEsymaux;
|
||||
typedef struct {
|
||||
PEheader hdr;
|
||||
PEsection sect[2];
|
||||
// Must be an even number of symbol structs.
|
||||
PEsym sym0;
|
||||
PEsymaux sym0aux;
|
||||
PEsym sym1;
|
||||
PEsymaux sym1aux;
|
||||
PEsym sym2;
|
||||
PEsym sym3;
|
||||
uint32_t strtabsize;
|
||||
uint8_t space[4096];
|
||||
} PEobj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local ai = assert(map_arch[ctx.arch])
|
||||
local is64 = ai.b == 64
|
||||
local symexport = " /EXPORT:"..symname..",DATA "
|
||||
|
||||
-- The file format is always little-endian. Swap if the host is big-endian.
|
||||
local function f32(x) return x end
|
||||
local f16 = f32
|
||||
if ffi.abi("be") then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
end
|
||||
|
||||
-- Create PE object and fill in header.
|
||||
local o = ffi.new("PEobj")
|
||||
local hdr = o.hdr
|
||||
hdr.arch = f16(assert(ai.p))
|
||||
hdr.nsects = f16(2)
|
||||
hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
|
||||
hdr.nsyms = f32(6)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
o.sect[0].name = ".drectve"
|
||||
o.sect[0].size = f32(#symexport)
|
||||
o.sect[0].flags = f32(0x00100a00)
|
||||
o.sym0.sect = f16(1)
|
||||
o.sym0.scl = 3
|
||||
o.sym0.name = ".drectve"
|
||||
o.sym0.naux = 1
|
||||
o.sym0aux.size = f32(#symexport)
|
||||
o.sect[1].name = ".rdata"
|
||||
o.sect[1].size = f32(#s)
|
||||
o.sect[1].flags = f32(0x40300040)
|
||||
o.sym1.sect = f16(2)
|
||||
o.sym1.scl = 3
|
||||
o.sym1.name = ".rdata"
|
||||
o.sym1.naux = 1
|
||||
o.sym1aux.size = f32(#s)
|
||||
o.sym2.sect = f16(2)
|
||||
o.sym2.scl = 2
|
||||
o.sym2.nameref[1] = f32(4)
|
||||
o.sym3.sect = f16(-1)
|
||||
o.sym3.scl = 2
|
||||
o.sym3.value = f32(1)
|
||||
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
|
||||
ffi.copy(o.space, symname)
|
||||
local ofs = #symname + 1
|
||||
o.strtabsize = f32(ofs + 4)
|
||||
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
ffi.copy(o.space + ofs, symexport)
|
||||
ofs = ofs + #symexport
|
||||
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
|
||||
-- Write PE object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_machobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
|
||||
} mach_header;
|
||||
typedef struct
|
||||
{
|
||||
mach_header; uint32_t reserved;
|
||||
} mach_header_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize;
|
||||
char segname[16];
|
||||
uint64_t vmaddr, vmsize, fileoff, filesize;
|
||||
uint32_t maxprot, initprot, nsects, flags;
|
||||
} mach_segment_command_64;
|
||||
typedef struct {
|
||||
char sectname[16], segname[16];
|
||||
uint64_t addr, size;
|
||||
uint32_t offset, align, reloff, nreloc, flags;
|
||||
uint32_t reserved1, reserved2, reserved3;
|
||||
} mach_section_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
|
||||
} mach_symtab_command;
|
||||
typedef struct {
|
||||
int32_t strx;
|
||||
uint8_t type, sect;
|
||||
uint16_t desc;
|
||||
uint64_t value;
|
||||
} mach_nlist_64;
|
||||
typedef struct {
|
||||
mach_header_64 hdr;
|
||||
mach_segment_command_64 seg;
|
||||
mach_section_64 sec;
|
||||
mach_symtab_command sym;
|
||||
mach_nlist_64 sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_obj_64;
|
||||
]]
|
||||
local symname = '_'..LJBC_PREFIX..ctx.modname
|
||||
local cputype, cpusubtype = 0x01000007, 3
|
||||
if ctx.arch ~= "x64" then
|
||||
check(ctx.arch == "arm64", "unsupported architecture for OSX")
|
||||
cputype, cpusubtype = 0x0100000c, 0
|
||||
end
|
||||
local function aligned(v, a) return bit.band(v+a-1, -a) end
|
||||
|
||||
-- Create Mach-O object and fill in header.
|
||||
local o = ffi.new("mach_obj_64")
|
||||
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, 8)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
o.hdr.magic = 0xfeedfacf
|
||||
o.hdr.cputype = cputype
|
||||
o.hdr.cpusubtype = cpusubtype
|
||||
o.hdr.filetype = 1
|
||||
o.hdr.ncmds = 2
|
||||
o.hdr.sizeofcmds = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)+ffi.sizeof(o.sym)
|
||||
o.seg.cmd = 0x19
|
||||
o.seg.cmdsize = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)
|
||||
o.seg.vmsize = #s
|
||||
o.seg.fileoff = mach_size
|
||||
o.seg.filesize = #s
|
||||
o.seg.maxprot = 1
|
||||
o.seg.initprot = 1
|
||||
o.seg.nsects = 1
|
||||
ffi.copy(o.sec.sectname, "__data")
|
||||
ffi.copy(o.sec.segname, "__DATA")
|
||||
o.sec.size = #s
|
||||
o.sec.offset = mach_size
|
||||
o.sym.cmd = 2
|
||||
o.sym.cmdsize = ffi.sizeof(o.sym)
|
||||
o.sym.symoff = ffi.offsetof(o, "sym_entry")
|
||||
o.sym.nsyms = 1
|
||||
o.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)
|
||||
o.sym.strsize = aligned(#symname+2, 8)
|
||||
o.sym_entry.type = 0xf
|
||||
o.sym_entry.sect = 1
|
||||
o.sym_entry.strx = 1
|
||||
ffi.copy(o.space+1, symname)
|
||||
|
||||
-- Write Mach-O object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, mach_size))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_obj(ctx, output, s)
|
||||
local ok, ffi = pcall(require, "ffi")
|
||||
check(ok, "FFI library required to write this file type")
|
||||
if output == "-" and jit.os == "Windows" then
|
||||
set_stdout_binary(ffi)
|
||||
end
|
||||
if ctx.os == "windows" then
|
||||
return bcsave_peobj(ctx, output, s, ffi)
|
||||
elseif ctx.os == "osx" then
|
||||
return bcsave_machobj(ctx, output, s, ffi)
|
||||
else
|
||||
return bcsave_elfobj(ctx, output, s, ffi)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bclist(ctx, input, output)
|
||||
local f = readfile(ctx, input)
|
||||
require("jit.bc").dump(f, savefile(output, "w"), true)
|
||||
end
|
||||
|
||||
local function bcsave(ctx, input, output)
|
||||
local f = readfile(ctx, input)
|
||||
local s = string.dump(f, ctx.mode)
|
||||
local t = ctx.type
|
||||
if not t then
|
||||
t = detecttype(output)
|
||||
ctx.type = t
|
||||
end
|
||||
if t == "raw" then
|
||||
bcsave_raw(output, s)
|
||||
else
|
||||
if not ctx.modname then ctx.modname = detectmodname(input) end
|
||||
if t == "obj" then
|
||||
bcsave_obj(ctx, output, s)
|
||||
else
|
||||
bcsave_c(ctx, output, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function docmd(...)
|
||||
local arg = {...}
|
||||
local n = 1
|
||||
local list = false
|
||||
local ctx = {
|
||||
mode = "bt", arch = jit.arch, os = jit.os:lower(),
|
||||
type = false, modname = false, string = false,
|
||||
}
|
||||
local strip = "s"
|
||||
local gc64 = ""
|
||||
while n <= #arg do
|
||||
local a = arg[n]
|
||||
if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then
|
||||
tremove(arg, n)
|
||||
if a == "--" then break end
|
||||
for m=2,#a do
|
||||
local opt = a:sub(m, m)
|
||||
if opt == "l" then
|
||||
list = true
|
||||
elseif opt == "s" then
|
||||
strip = "s"
|
||||
elseif opt == "g" then
|
||||
strip = ""
|
||||
elseif opt == "W" or opt == "X" then
|
||||
gc64 = opt
|
||||
elseif opt == "d" then
|
||||
ctx.mode = ctx.mode .. opt
|
||||
else
|
||||
if arg[n] == nil or m ~= #a then usage() end
|
||||
if opt == "e" then
|
||||
if n ~= 1 then usage() end
|
||||
ctx.string = true
|
||||
elseif opt == "n" then
|
||||
ctx.modname = checkmodname(tremove(arg, n))
|
||||
elseif opt == "t" then
|
||||
ctx.type = checkarg(tremove(arg, n), map_type, "file type")
|
||||
elseif opt == "a" then
|
||||
ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture")
|
||||
elseif opt == "o" then
|
||||
ctx.os = checkarg(tremove(arg, n), map_os, "OS name")
|
||||
elseif opt == "F" then
|
||||
ctx.filename = "@"..tremove(arg, n)
|
||||
else
|
||||
usage()
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
ctx.mode = ctx.mode .. strip .. gc64
|
||||
if list then
|
||||
if #arg == 0 or #arg > 2 then usage() end
|
||||
bclist(ctx, arg[1], arg[2] or "-")
|
||||
else
|
||||
if #arg ~= 2 then usage() end
|
||||
bcsave(ctx, arg[1], arg[2])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
start = docmd -- Process -b command line option.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,689 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT ARM disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles most user-mode ARMv7 instructions
|
||||
-- NYI: Advanced SIMD and VFP instructions.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch = string.match, string.gmatch
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_loadc = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFmDN", "vstmFNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrFdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrFdl", "vldmdbFNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
[11] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGmDN", "vstmGNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrGdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrGdl", "vldmdbGNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
_ = {
|
||||
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfps = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaF.dnm", "vmlsF.dnm",
|
||||
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
|
||||
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
|
||||
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
|
||||
[0x20000] = "vdivF.dnm",
|
||||
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
|
||||
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
|
||||
[0x2c000] = "vmovF.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovF.dm", "vabsF.dm",
|
||||
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
|
||||
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
|
||||
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
|
||||
[0x0e01] = "vcvtG.dF.m",
|
||||
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
|
||||
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
|
||||
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfpd = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaG.dnm", "vmlsG.dnm",
|
||||
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
|
||||
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
|
||||
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
|
||||
[0x20000] = "vdivG.dnm",
|
||||
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
|
||||
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
|
||||
[0x2c000] = "vmovG.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovG.dm", "vabsG.dm",
|
||||
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
|
||||
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
|
||||
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
|
||||
[0x0e01] = "vcvtF.dG.m",
|
||||
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
|
||||
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
|
||||
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
|
||||
},
|
||||
}
|
||||
|
||||
local map_datac = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = map_vfps,
|
||||
[11] = map_vfpd,
|
||||
-- NYI cdp, mcr, mrc.
|
||||
},
|
||||
{
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 15,
|
||||
[0] = "vmovFnD", "vmovFDn",
|
||||
[14] = "vmsrD",
|
||||
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
|
||||
},
|
||||
},
|
||||
},
|
||||
"svcT",
|
||||
}
|
||||
|
||||
local map_loadcu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP load/store.
|
||||
}
|
||||
|
||||
local map_datacu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP data.
|
||||
}
|
||||
|
||||
local map_simddata = {
|
||||
shift = 0, mask = 0, -- NYI SIMD data.
|
||||
}
|
||||
|
||||
local map_simdload = {
|
||||
shift = 0, mask = 0, -- NYI SIMD load/store, preload.
|
||||
}
|
||||
|
||||
local map_preload = {
|
||||
shift = 0, mask = 0, -- NYI preload.
|
||||
}
|
||||
|
||||
local map_media = {
|
||||
shift = 20, mask = 31,
|
||||
[0] = false,
|
||||
{ --01
|
||||
shift = 5, mask = 7,
|
||||
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
|
||||
"sadd8DNM", false, false, "ssub8DNM",
|
||||
},
|
||||
{ --02
|
||||
shift = 5, mask = 7,
|
||||
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
|
||||
"qadd8DNM", false, false, "qsub8DNM",
|
||||
},
|
||||
{ --03
|
||||
shift = 5, mask = 7,
|
||||
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
|
||||
"shadd8DNM", false, false, "shsub8DNM",
|
||||
},
|
||||
false,
|
||||
{ --05
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
|
||||
"uadd8DNM", false, false, "usub8DNM",
|
||||
},
|
||||
{ --06
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
|
||||
"uqadd8DNM", false, false, "uqsub8DNM",
|
||||
},
|
||||
{ --07
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
|
||||
"uhadd8DNM", false, false, "uhsub8DNM",
|
||||
},
|
||||
{ --08
|
||||
shift = 5, mask = 7,
|
||||
[0] = "pkhbtDNMU", false, "pkhtbDNMU",
|
||||
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
|
||||
"pkhbtDNMU", "selDNM", "pkhtbDNMU",
|
||||
},
|
||||
false,
|
||||
{ --0a
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
|
||||
"ssatDxMu", false, "ssatDxMu",
|
||||
},
|
||||
{ --0b
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "revDM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
|
||||
"ssatDxMu", "rev16DM", "ssatDxMu",
|
||||
},
|
||||
{ --0c
|
||||
shift = 5, mask = 7,
|
||||
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
|
||||
},
|
||||
false,
|
||||
{ --0e
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "usat16DwM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
|
||||
"usatDwMu", false, "usatDwMu",
|
||||
},
|
||||
{ --0f
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "rbitDM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
|
||||
"usatDwMu", "revshDM", "usatDwMu",
|
||||
},
|
||||
{ --10
|
||||
shift = 12, mask = 15,
|
||||
[15] = {
|
||||
shift = 5, mask = 7,
|
||||
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
|
||||
},
|
||||
_ = {
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
|
||||
},
|
||||
},
|
||||
false, false, false,
|
||||
{ --14
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
|
||||
},
|
||||
{ --15
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
|
||||
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
|
||||
false, false, false, false,
|
||||
"smmlsNMSD", "smmlsrNMSD",
|
||||
},
|
||||
false, false,
|
||||
{ --18
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
|
||||
},
|
||||
false,
|
||||
{ --1a
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1b
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1c
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1d
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1e
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
{ --1f
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
}
|
||||
|
||||
local map_load = {
|
||||
shift = 21, mask = 9,
|
||||
{
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
|
||||
}
|
||||
}
|
||||
|
||||
local map_load1 = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = map_load, map_media,
|
||||
}
|
||||
|
||||
local map_loadm = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "stmdaNR", "stmNR",
|
||||
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
|
||||
"ldmdbNR", "ldmibNR",
|
||||
},
|
||||
}
|
||||
|
||||
local map_data = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
|
||||
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
|
||||
"tstNP", "teqNP", "cmpNP", "cmnNP",
|
||||
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
|
||||
}
|
||||
|
||||
local map_mul = {
|
||||
shift = 21, mask = 7,
|
||||
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
|
||||
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
|
||||
}
|
||||
|
||||
local map_sync = {
|
||||
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
|
||||
[0] = "swpDMN", false, false, false,
|
||||
"swpbDMN", false, false, false,
|
||||
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
|
||||
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
|
||||
}
|
||||
|
||||
local map_mulh = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = { shift = 5, mask = 3,
|
||||
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
|
||||
}
|
||||
|
||||
local map_misc = {
|
||||
shift = 4, mask = 7,
|
||||
-- NYI: decode PSR bits of msr.
|
||||
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
|
||||
{ shift = 21, mask = 3, "bxM", false, "clzDM", },
|
||||
{ shift = 21, mask = 3, "bxjM", },
|
||||
{ shift = 21, mask = 3, "blxM", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, "bkptK", },
|
||||
}
|
||||
|
||||
local map_datar = {
|
||||
shift = 4, mask = 9,
|
||||
[9] = {
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
|
||||
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
|
||||
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
|
||||
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 25,
|
||||
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
|
||||
_ = {
|
||||
shift = 0, mask = 0xffffffff,
|
||||
[bor(0xe1a00000)] = "nop",
|
||||
_ = map_data,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
local map_datai = {
|
||||
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
|
||||
[16] = "movwDW", [20] = "movtDW",
|
||||
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
|
||||
[22] = "msrNW",
|
||||
_ = map_data,
|
||||
}
|
||||
|
||||
local map_branch = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = "bB", "blB"
|
||||
}
|
||||
|
||||
local map_condins = {
|
||||
[0] = map_datar, map_datai, map_load, map_load1,
|
||||
map_loadm, map_branch, map_loadc, map_datac
|
||||
}
|
||||
|
||||
-- NYI: setend.
|
||||
local map_uncondins = {
|
||||
[0] = false, map_simddata, map_simdload, map_preload,
|
||||
false, "blxB", map_loadcu, map_datacu,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
}
|
||||
|
||||
local map_cond = {
|
||||
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
|
||||
"hi", "ls", "ge", "lt", "gt", "le", "al",
|
||||
}
|
||||
|
||||
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then
|
||||
extra = "\t->"..sym
|
||||
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
|
||||
extra = "\t; 0x"..tohex(ctx.rel)
|
||||
end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-5s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-5s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Format operand 2 of load/store opcodes.
|
||||
local function fmtload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local x, ofs
|
||||
local ext = (band(op, 0x04000000) == 0)
|
||||
if not ext and band(op, 0x02000000) == 0 then
|
||||
ofs = band(op, 4095)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
elseif ext and band(op, 0x00400000) ~= 0 then
|
||||
ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
else
|
||||
ofs = map_gpr[band(op, 15)]
|
||||
if ext or band(op, 0xfe0) == 0 then
|
||||
elseif band(op, 0xfe0) == 0x60 then
|
||||
ofs = format("%s, rrx", ofs)
|
||||
else
|
||||
local sh = band(rshift(op, 7), 31)
|
||||
if sh == 0 then sh = 32 end
|
||||
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
|
||||
end
|
||||
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
|
||||
end
|
||||
if ofs == "#0" then
|
||||
x = format("[%s]", base)
|
||||
elseif band(op, 0x01000000) == 0 then
|
||||
x = format("[%s], %s", base, ofs)
|
||||
else
|
||||
x = format("[%s, %s]", base, ofs)
|
||||
end
|
||||
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
|
||||
return x
|
||||
end
|
||||
|
||||
-- Format operand 2 of vector load/store opcodes.
|
||||
local function fmtvload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local ofs = band(op, 255)*4
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
if ofs == 0 then
|
||||
return format("[%s]", base)
|
||||
else
|
||||
return format("[%s, #%d]", base, ofs)
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtvr(op, vr, sh0, sh1)
|
||||
if vr == "s" then
|
||||
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
|
||||
else
|
||||
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
|
||||
end
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
local operands = {}
|
||||
local suffix = ""
|
||||
local last, name, pat
|
||||
local vr
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local cond = rshift(op, 28)
|
||||
local opat
|
||||
if cond == 15 then
|
||||
opat = map_uncondins[band(rshift(op, 25), 7)]
|
||||
else
|
||||
if cond ~= 14 then suffix = map_cond[cond] end
|
||||
opat = map_condins[band(rshift(op, 25), 7)]
|
||||
end
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
name, pat = match(opat, "^([a-z0-9]*)(.*)")
|
||||
if sub(pat, 1, 1) == "." then
|
||||
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
|
||||
suffix = suffix..s2
|
||||
pat = p2
|
||||
end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "D" then
|
||||
x = map_gpr[band(rshift(op, 12), 15)]
|
||||
elseif p == "N" then
|
||||
x = map_gpr[band(rshift(op, 16), 15)]
|
||||
elseif p == "S" then
|
||||
x = map_gpr[band(rshift(op, 8), 15)]
|
||||
elseif p == "M" then
|
||||
x = map_gpr[band(op, 15)]
|
||||
elseif p == "d" then
|
||||
x = fmtvr(op, vr, 12, 22)
|
||||
elseif p == "n" then
|
||||
x = fmtvr(op, vr, 16, 7)
|
||||
elseif p == "m" then
|
||||
x = fmtvr(op, vr, 0, 5)
|
||||
elseif p == "P" then
|
||||
if band(op, 0x02000000) ~= 0 then
|
||||
x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
|
||||
else
|
||||
x = map_gpr[band(op, 15)]
|
||||
if band(op, 0xff0) ~= 0 then
|
||||
operands[#operands+1] = x
|
||||
local s = map_shift[band(rshift(op, 5), 3)]
|
||||
local r = nil
|
||||
if band(op, 0xf90) == 0 then
|
||||
if s == "ror" then s = "rrx" else r = "#32" end
|
||||
elseif band(op, 0x10) == 0 then
|
||||
r = "#"..band(rshift(op, 7), 31)
|
||||
else
|
||||
r = map_gpr[band(rshift(op, 8), 15)]
|
||||
end
|
||||
if name == "mov" then name = s; x = r
|
||||
elseif r then x = format("%s %s", s, r)
|
||||
else x = s end
|
||||
end
|
||||
end
|
||||
elseif p == "L" then
|
||||
x = fmtload(ctx, op, pos)
|
||||
elseif p == "l" then
|
||||
x = fmtvload(ctx, op, pos)
|
||||
elseif p == "B" then
|
||||
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
|
||||
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
|
||||
ctx.rel = addr
|
||||
x = "0x"..tohex(addr)
|
||||
elseif p == "F" then
|
||||
vr = "s"
|
||||
elseif p == "G" then
|
||||
vr = "d"
|
||||
elseif p == "." then
|
||||
suffix = suffix..(vr == "s" and ".f32" or ".f64")
|
||||
elseif p == "R" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 1 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local t = {}
|
||||
for i=0,15 do
|
||||
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
|
||||
end
|
||||
x = "{"..concat(t, ", ").."}"
|
||||
elseif p == "r" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 2 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local s = tonumber(sub(last, 2))
|
||||
local n = band(op, 255)
|
||||
if vr == "d" then n = rshift(n, 1) end
|
||||
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
|
||||
elseif p == "W" then
|
||||
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
|
||||
elseif p == "T" then
|
||||
x = "#0x"..tohex(band(op, 0x00ffffff), 6)
|
||||
elseif p == "U" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "u" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if band(op, 0x40) == 0 then
|
||||
if x == 0 then x = nil else x = "lsl #"..x end
|
||||
else
|
||||
if x == 0 then x = "asr #32" else x = "asr #"..x end
|
||||
end
|
||||
elseif p == "v" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
elseif p == "w" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "x" then
|
||||
x = band(rshift(op, 16), 31) + 1
|
||||
elseif p == "X" then
|
||||
x = band(rshift(op, 16), 31) - last + 1
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
|
||||
elseif p == "K" then
|
||||
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
|
||||
elseif p == "s" then
|
||||
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then
|
||||
last = x
|
||||
if type(x) == "number" then x = "#"..x end
|
||||
operands[#operands+1] = x
|
||||
end
|
||||
end
|
||||
|
||||
return putop(ctx, name..suffix, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ctx.pos = ofs
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 16 then return map_gpr[r] end
|
||||
return "d"..(r-16)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
disass = disass,
|
||||
regname = regname
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,12 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT ARM64BE disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- ARM64 instructions are always little-endian. So just forward to the
|
||||
-- common ARM64 disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
return require((string.match(..., ".*%.") or "").."dis_arm64")
|
||||
|
||||
@@ -0,0 +1,694 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all standard MIPS32R1/R2 instructions.
|
||||
-- Default mode is big-endian, but see: dis_mipsel.lua
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local byte, format = string.byte, string.format
|
||||
local match, gmatch = string.match, string.gmatch
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Extended opcode maps common to all MIPS releases
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
|
||||
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
|
||||
|
||||
local map_cop0 = {
|
||||
shift = 25, mask = 1,
|
||||
[0] = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "mfc0TDW", [4] = "mtc0TDW",
|
||||
[10] = "rdpgprDT",
|
||||
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
|
||||
[14] = "wrpgprDT",
|
||||
}, {
|
||||
shift = 0, mask = 63,
|
||||
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
|
||||
[24] = "eret", [31] = "deret",
|
||||
[32] = "wait",
|
||||
},
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps for MIPS R1-R5
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
|
||||
|
||||
local map_special = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
|
||||
map_movci, map_srl, "sraDTA",
|
||||
"sllvDTS", false, map_srlv, "sravDTS",
|
||||
"jrS", "jalrD1S", "movzDST", "movnDST",
|
||||
"syscallY", "breakY", false, "sync",
|
||||
"mfhiD", "mthiS", "mfloD", "mtloS",
|
||||
"dsllvDST", false, "dsrlvDST", "dsravDST",
|
||||
"multST", "multuST", "divST", "divuST",
|
||||
"dmultST", "dmultuST", "ddivST", "ddivuST",
|
||||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
|
||||
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
|
||||
false, false, "sltDST", "sltuDST",
|
||||
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
|
||||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
|
||||
"teqSTZ", false, "tneSTZ", false,
|
||||
"dsllDTA", false, "dsrlDTA", "dsraDTA",
|
||||
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
|
||||
}
|
||||
|
||||
local map_special2 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "maddST", "madduST", "mulDST", false,
|
||||
"msubST", "msubuST",
|
||||
[32] = "clzDS", [33] = "cloDS",
|
||||
[63] = "sdbbpY",
|
||||
}
|
||||
|
||||
local map_bshfl = {
|
||||
shift = 6, mask = 31,
|
||||
[2] = "wsbhDT",
|
||||
[16] = "sebDT",
|
||||
[24] = "sehDT",
|
||||
}
|
||||
|
||||
local map_dbshfl = {
|
||||
shift = 6, mask = 31,
|
||||
[2] = "dsbhDT",
|
||||
[5] = "dshdDT",
|
||||
}
|
||||
|
||||
local map_special3 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
|
||||
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
|
||||
[32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD",
|
||||
}
|
||||
|
||||
local map_regimm = {
|
||||
shift = 16, mask = 31,
|
||||
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
|
||||
false, false, false, false,
|
||||
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
|
||||
"teqiSI", false, "tneiSI", false,
|
||||
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, "synciSO",
|
||||
}
|
||||
|
||||
local map_cop1s = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
|
||||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
|
||||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
|
||||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
|
||||
"movz.sFGT", "movn.sFGT",
|
||||
false, "recip.sFG", "rsqrt.sFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, "cvt.d.sFG", false, false,
|
||||
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
|
||||
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
|
||||
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
|
||||
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
|
||||
}
|
||||
|
||||
local map_cop1d = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
|
||||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
|
||||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
|
||||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
|
||||
"movz.dFGT", "movn.dFGT",
|
||||
false, "recip.dFG", "rsqrt.dFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.dFG", false, false, false,
|
||||
"cvt.w.dFG", "cvt.l.dFG", false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
|
||||
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
|
||||
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
|
||||
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
|
||||
}
|
||||
|
||||
local map_cop1ps = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
|
||||
false, "abs.psFG", "mov.psFG", "neg.psFG",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
|
||||
"movz.psFGT", "movn.psFGT",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.puFG", false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.plFG", false, false, false,
|
||||
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
|
||||
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
|
||||
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
|
||||
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
|
||||
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
|
||||
}
|
||||
|
||||
local map_cop1w = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
|
||||
}
|
||||
|
||||
local map_cop1l = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
|
||||
}
|
||||
|
||||
local map_cop1bc = {
|
||||
shift = 16, mask = 3,
|
||||
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
|
||||
}
|
||||
|
||||
local map_cop1 = {
|
||||
shift = 21, mask = 31,
|
||||
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
|
||||
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
|
||||
map_cop1bc, false, false, false,
|
||||
false, false, false, false,
|
||||
map_cop1s, map_cop1d, false, false,
|
||||
map_cop1w, map_cop1l, map_cop1ps,
|
||||
}
|
||||
|
||||
local map_cop1x = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "lwxc1FSX", "ldxc1FSX", false, false,
|
||||
false, "luxc1FSX", false, false,
|
||||
"swxc1FSX", "sdxc1FSX", false, false,
|
||||
false, "suxc1FSX", false, "prefxMSX",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, "alnv.psFGHS", false,
|
||||
"madd.sFRGH", "madd.dFRGH", false, false,
|
||||
false, false, "madd.psFRGH", false,
|
||||
"msub.sFRGH", "msub.dFRGH", false, false,
|
||||
false, false, "msub.psFRGH", false,
|
||||
"nmadd.sFRGH", "nmadd.dFRGH", false, false,
|
||||
false, false, "nmadd.psFRGH", false,
|
||||
"nmsub.sFRGH", "nmsub.dFRGH", false, false,
|
||||
false, false, "nmsub.psFRGH", false,
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = map_special, map_regimm, "jJ", "jalJ",
|
||||
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
|
||||
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
|
||||
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
|
||||
map_cop0, map_cop1, false, map_cop1x,
|
||||
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
|
||||
"daddiTSI", "daddiuTSI", false, false,
|
||||
map_special2, "jalxJ", false, map_special3,
|
||||
"lbTSO", "lhTSO", "lwlTSO", "lwTSO",
|
||||
"lbuTSO", "lhuTSO", "lwrTSO", false,
|
||||
"sbTSO", "shTSO", "swlTSO", "swTSO",
|
||||
false, false, "swrTSO", "cacheNSO",
|
||||
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
|
||||
false, "ldc1HSO", "ldc2TSO", "ldTSO",
|
||||
"scTSO", "swc1HSO", "swc2TSO", false,
|
||||
false, "sdc1HSO", "sdc2TSO", "sdTSO",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps for MIPS R6
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" }
|
||||
local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" }
|
||||
local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" }
|
||||
local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" }
|
||||
local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" }
|
||||
local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" }
|
||||
local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" }
|
||||
local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" }
|
||||
|
||||
local map_special_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
|
||||
false, map_srl, "sraDTA",
|
||||
"sllvDTS", false, map_srlv, "sravDTS",
|
||||
"jrS", "jalrD1S", false, false,
|
||||
"syscallY", "breakY", false, "sync",
|
||||
"clzDS", "cloDS", "dclzDS", "dcloDS",
|
||||
"dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST",
|
||||
map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6,
|
||||
map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6,
|
||||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
|
||||
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
|
||||
false, false, "sltDST", "sltuDST",
|
||||
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
|
||||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
|
||||
"teqSTZ", "seleqzDST", "tneSTZ", "selnezDST",
|
||||
"dsllDTA", false, "dsrlDTA", "dsraDTA",
|
||||
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
|
||||
}
|
||||
|
||||
local map_bshfl_r6 = {
|
||||
shift = 9, mask = 3,
|
||||
[1] = "alignDSTa",
|
||||
_ = {
|
||||
shift = 6, mask = 31,
|
||||
[0] = "bitswapDT",
|
||||
[2] = "wsbhDT",
|
||||
[16] = "sebDT",
|
||||
[24] = "sehDT",
|
||||
}
|
||||
}
|
||||
|
||||
local map_dbshfl_r6 = {
|
||||
shift = 9, mask = 3,
|
||||
[1] = "dalignDSTa",
|
||||
_ = {
|
||||
shift = 6, mask = 31,
|
||||
[0] = "dbitswapDT",
|
||||
[2] = "dsbhDT",
|
||||
[5] = "dshdDT",
|
||||
}
|
||||
}
|
||||
|
||||
local map_special3_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
|
||||
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
|
||||
[32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD",
|
||||
}
|
||||
|
||||
local map_regimm_r6 = {
|
||||
shift = 16, mask = 31,
|
||||
[0] = "bltzSB", [1] = "bgezSB",
|
||||
[6] = "dahiSI", [30] = "datiSI",
|
||||
[23] = "sigrieI", [31] = "synciSO",
|
||||
}
|
||||
|
||||
local map_pcrel_r6 = {
|
||||
shift = 19, mask = 3,
|
||||
[0] = "addiupcS2", "lwpcS2", "lwupcS2", {
|
||||
shift = 18, mask = 1,
|
||||
[0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" }
|
||||
}
|
||||
}
|
||||
|
||||
local map_cop1s_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
|
||||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
|
||||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
|
||||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
|
||||
"sel.sFGH", false, false, false,
|
||||
"seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH",
|
||||
"maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG",
|
||||
"min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH",
|
||||
false, "cvt.d.sFG", false, false,
|
||||
"cvt.w.sFG", "cvt.l.sFG",
|
||||
}
|
||||
|
||||
local map_cop1d_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
|
||||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
|
||||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
|
||||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
|
||||
"sel.dFGH", false, false, false,
|
||||
"seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH",
|
||||
"maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG",
|
||||
"min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH",
|
||||
"cvt.s.dFG", false, false, false,
|
||||
"cvt.w.dFG", "cvt.l.dFG",
|
||||
}
|
||||
|
||||
local map_cop1w_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH",
|
||||
"cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH",
|
||||
"cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH",
|
||||
"cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH",
|
||||
false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH",
|
||||
false, false, false, false,
|
||||
false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH",
|
||||
false, false, false, false,
|
||||
"cvt.s.wFG", "cvt.d.wFG",
|
||||
}
|
||||
|
||||
local map_cop1l_r6 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH",
|
||||
"cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH",
|
||||
"cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH",
|
||||
"cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH",
|
||||
false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH",
|
||||
false, false, false, false,
|
||||
false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH",
|
||||
false, false, false, false,
|
||||
"cvt.s.lFG", "cvt.d.lFG",
|
||||
}
|
||||
|
||||
local map_cop1_r6 = {
|
||||
shift = 21, mask = 31,
|
||||
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
|
||||
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
|
||||
false, "bc1eqzHB", false, false,
|
||||
false, "bc1nezHB", false, false,
|
||||
map_cop1s_r6, map_cop1d_r6, false, false,
|
||||
map_cop1w_r6, map_cop1l_r6,
|
||||
}
|
||||
|
||||
local function maprs_popTS(rs, rt)
|
||||
if rt == 0 then return 0 elseif rs == 0 then return 1
|
||||
elseif rs == rt then return 2 else return 3 end
|
||||
end
|
||||
|
||||
local map_pop06_r6 = {
|
||||
maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB"
|
||||
}
|
||||
local map_pop07_r6 = {
|
||||
maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB"
|
||||
}
|
||||
local map_pop26_r6 = {
|
||||
maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB"
|
||||
}
|
||||
local map_pop27_r6 = {
|
||||
maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB"
|
||||
}
|
||||
|
||||
local function maprs_popS(rs, rt)
|
||||
if rs == 0 then return 0 else return 1 end
|
||||
end
|
||||
|
||||
local map_pop66_r6 = {
|
||||
maprs = maprs_popS, [0] = "jicTI", "beqzcSb"
|
||||
}
|
||||
local map_pop76_r6 = {
|
||||
maprs = maprs_popS, [0] = "jialcTI", "bnezcSb"
|
||||
}
|
||||
|
||||
local function maprs_popST(rs, rt)
|
||||
if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end
|
||||
end
|
||||
|
||||
local map_pop10_r6 = {
|
||||
maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB"
|
||||
}
|
||||
local map_pop30_r6 = {
|
||||
maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB"
|
||||
}
|
||||
|
||||
local map_pri_r6 = {
|
||||
[0] = map_special_r6, map_regimm_r6, "jJ", "jalJ",
|
||||
"beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6,
|
||||
map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI",
|
||||
"andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U",
|
||||
map_cop0, map_cop1_r6, false, false,
|
||||
false, false, map_pop26_r6, map_pop27_r6,
|
||||
map_pop30_r6, "daddiuTSI", false, false,
|
||||
false, "dauiTSI", false, map_special3_r6,
|
||||
"lbTSO", "lhTSO", false, "lwTSO",
|
||||
"lbuTSO", "lhuTSO", false, false,
|
||||
"sbTSO", "shTSO", false, "swTSO",
|
||||
false, false, false, false,
|
||||
false, "lwc1HSO", "bc#", false,
|
||||
false, "ldc1HSO", map_pop66_r6, "ldTSO",
|
||||
false, "swc1HSO", "balc#", map_pcrel_r6,
|
||||
false, "sdc1HSO", map_pop76_r6, "sdTSO",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
local function get_be(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
end
|
||||
|
||||
local function get_le(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local op = ctx:get()
|
||||
local operands = {}
|
||||
local last = nil
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = ctx.map_pri[rshift(op, 26)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
if opat.maprs then
|
||||
opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))]
|
||||
else
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "S" then
|
||||
x = map_gpr[band(rshift(op, 21), 31)]
|
||||
elseif p == "T" then
|
||||
x = map_gpr[band(rshift(op, 16), 31)]
|
||||
elseif p == "D" then
|
||||
x = map_gpr[band(rshift(op, 11), 31)]
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, 6), 31)
|
||||
elseif p == "G" then
|
||||
x = "f"..band(rshift(op, 11), 31)
|
||||
elseif p == "H" then
|
||||
x = "f"..band(rshift(op, 16), 31)
|
||||
elseif p == "R" then
|
||||
x = "f"..band(rshift(op, 21), 31)
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, 6), 31)
|
||||
elseif p == "a" then
|
||||
x = band(rshift(op, 6), 7)
|
||||
elseif p == "E" then
|
||||
x = band(rshift(op, 6), 31) + 32
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, 11), 31)
|
||||
elseif p == "N" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "C" then
|
||||
x = band(rshift(op, 18), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "K" then
|
||||
x = band(rshift(op, 11), 31) + 1
|
||||
elseif p == "P" then
|
||||
x = band(rshift(op, 11), 31) + 33
|
||||
elseif p == "L" then
|
||||
x = band(rshift(op, 11), 31) - last + 1
|
||||
elseif p == "Q" then
|
||||
x = band(rshift(op, 11), 31) - last + 33
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "2" then
|
||||
x = arshift(lshift(op, 13), 11)
|
||||
elseif p == "3" then
|
||||
x = arshift(lshift(op, 14), 11)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "O" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "X" then
|
||||
local index = map_gpr[band(rshift(op, 16), 31)]
|
||||
operands[#operands] = format("%s(%s)", index, last)
|
||||
elseif p == "B" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "b" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "#" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "J" then
|
||||
local a = ctx.addr + ctx.pos
|
||||
x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4
|
||||
ctx.rel = x
|
||||
x = format("0x%08x", x)
|
||||
elseif p == "V" then
|
||||
x = band(rshift(op, 8), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "W" then
|
||||
x = band(op, 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 6), 0x000fffff)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, 6), 1023)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "0" then
|
||||
if last == "r0" or last == 0 then
|
||||
local n = #operands
|
||||
operands[n] = nil
|
||||
last = operands[n-1]
|
||||
if altname then
|
||||
local a1, a2 = match(altname, "([^|]*)|(.*)")
|
||||
if a1 then name, altname = a1, a2
|
||||
else name = altname end
|
||||
end
|
||||
end
|
||||
elseif p == "1" then
|
||||
if last == "ra" then
|
||||
operands[#operands] = nil
|
||||
end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
ctx.get = get_be
|
||||
ctx.map_pri = map_pri
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_el(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.get = get_le
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_r6(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.map_pri = map_pri_r6
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_r6_el(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.get = get_le
|
||||
ctx.map_pri = map_pri_r6
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_el(code, addr, out)
|
||||
create_el(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_r6(code, addr, out)
|
||||
create_r6(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_r6_el(code, addr, out)
|
||||
create_r6_el(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
create_el = create_el,
|
||||
create_r6 = create_r6,
|
||||
create_r6_el = create_r6_el,
|
||||
disass = disass,
|
||||
disass_el = disass_el,
|
||||
disass_r6 = disass_r6,
|
||||
disass_r6_el = disass_r6_el,
|
||||
regname = regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the big-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create,
|
||||
disass = dis_mips.disass,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64EL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_el,
|
||||
disass = dis_mips.disass_el,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64R6 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the r6 big-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_r6,
|
||||
disass = dis_mips.disass_r6,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS64R6EL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the r6 little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_r6_el,
|
||||
disass = dis_mips.disass_r6_el,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPSEL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
|
||||
return {
|
||||
create = dis_mips.create_el,
|
||||
disass = dis_mips.disass_el,
|
||||
regname = dis_mips.regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,591 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT PPC disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
|
||||
-- plus the e500 SPE instructions and some Cell/Xenon extensions.
|
||||
--
|
||||
-- NYI: VMX, VMX128
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local byte, format = string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_crops = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "mcrfXX",
|
||||
[33] = "crnor|crnotCCC=", [129] = "crandcCCC",
|
||||
[193] = "crxor|crclrCCC%", [225] = "crnandCCC",
|
||||
[257] = "crandCCC", [289] = "creqv|crsetCCC%",
|
||||
[417] = "crorcCCC", [449] = "cror|crmoveCCC=",
|
||||
[16] = "b_lrKB", [528] = "b_ctrKB",
|
||||
[150] = "isync",
|
||||
}
|
||||
|
||||
local map_rlwinm = setmetatable({
|
||||
shift = 0, mask = -1,
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
local rot = band(rshift(x, 11), 31)
|
||||
local mb = band(rshift(x, 6), 31)
|
||||
local me = band(rshift(x, 1), 31)
|
||||
if mb == 0 and me == 31-rot then
|
||||
return "slwiRR~A."
|
||||
elseif me == 31 and mb == 32-rot then
|
||||
return "srwiRR~-A."
|
||||
else
|
||||
return "rlwinmRR~AAA."
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local map_rld = {
|
||||
shift = 2, mask = 7,
|
||||
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
|
||||
{
|
||||
shift = 1, mask = 1,
|
||||
[0] = "rldclRR~RM.", "rldcrRR~RM.",
|
||||
},
|
||||
}
|
||||
|
||||
local map_ext = setmetatable({
|
||||
shift = 1, mask = 1023,
|
||||
|
||||
[0] = "cmp_YLRR", [32] = "cmpl_YLRR",
|
||||
[4] = "twARR", [68] = "tdARR",
|
||||
|
||||
[8] = "subfcRRR.", [40] = "subfRRR.",
|
||||
[104] = "negRR.", [136] = "subfeRRR.",
|
||||
[200] = "subfzeRR.", [232] = "subfmeRR.",
|
||||
[520] = "subfcoRRR.", [552] = "subfoRRR.",
|
||||
[616] = "negoRR.", [648] = "subfeoRRR.",
|
||||
[712] = "subfzeoRR.", [744] = "subfmeoRR.",
|
||||
|
||||
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
|
||||
[457] = "divduRRR.", [489] = "divdRRR.",
|
||||
[745] = "mulldoRRR.",
|
||||
[969] = "divduoRRR.", [1001] = "divdoRRR.",
|
||||
|
||||
[10] = "addcRRR.", [138] = "addeRRR.",
|
||||
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
|
||||
[522] = "addcoRRR.", [650] = "addeoRRR.",
|
||||
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
|
||||
|
||||
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
|
||||
[459] = "divwuRRR.", [491] = "divwRRR.",
|
||||
[747] = "mullwoRRR.",
|
||||
[971] = "divwouRRR.", [1003] = "divwoRRR.",
|
||||
|
||||
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
|
||||
|
||||
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
|
||||
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
|
||||
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
|
||||
[339] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
|
||||
},
|
||||
[467] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
|
||||
},
|
||||
|
||||
[20] = "lwarxRR0R", [84] = "ldarxRR0R",
|
||||
|
||||
[21] = "ldxRR0R", [53] = "lduxRRR",
|
||||
[149] = "stdxRR0R", [181] = "stduxRRR",
|
||||
[341] = "lwaxRR0R", [373] = "lwauxRRR",
|
||||
|
||||
[23] = "lwzxRR0R", [55] = "lwzuxRRR",
|
||||
[87] = "lbzxRR0R", [119] = "lbzuxRRR",
|
||||
[151] = "stwxRR0R", [183] = "stwuxRRR",
|
||||
[215] = "stbxRR0R", [247] = "stbuxRRR",
|
||||
[279] = "lhzxRR0R", [311] = "lhzuxRRR",
|
||||
[343] = "lhaxRR0R", [375] = "lhauxRRR",
|
||||
[407] = "sthxRR0R", [439] = "sthuxRRR",
|
||||
|
||||
[54] = "dcbst-R0R", [86] = "dcbf-R0R",
|
||||
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
|
||||
[246] = "dcbtst-R0R", [278] = "dcbt-R0R",
|
||||
[310] = "eciwxRR0R", [438] = "ecowxRR0R",
|
||||
[470] = "dcbi-RR",
|
||||
|
||||
[598] = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = "sync", "lwsync", "ptesync",
|
||||
},
|
||||
[758] = "dcba-RR",
|
||||
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
|
||||
|
||||
[26] = "cntlzwRR~", [58] = "cntlzdRR~",
|
||||
[122] = "popcntbRR~",
|
||||
[154] = "prtywRR~", [186] = "prtydRR~",
|
||||
|
||||
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
|
||||
[284] = "eqvRR~R.", [316] = "xorRR~R.",
|
||||
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
|
||||
[508] = "cmpbRR~R",
|
||||
|
||||
[512] = "mcrxrX",
|
||||
|
||||
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
|
||||
|
||||
[533] = "lswxRR0R", [597] = "lswiRR0A",
|
||||
[661] = "stswxRR0R", [725] = "stswiRR0A",
|
||||
|
||||
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
|
||||
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
|
||||
|
||||
[535] = "lfsxFR0R", [567] = "lfsuxFRR",
|
||||
[599] = "lfdxFR0R", [631] = "lfduxFRR",
|
||||
[663] = "stfsxFR0R", [695] = "stfsuxFRR",
|
||||
[727] = "stfdxFR0R", [759] = "stfduxFR0R",
|
||||
[855] = "lfiwaxFR0R",
|
||||
[983] = "stfiwxFR0R",
|
||||
|
||||
[24] = "slwRR~R.",
|
||||
|
||||
[27] = "sldRR~R.", [536] = "srwRR~R.",
|
||||
[792] = "srawRR~R.", [824] = "srawiRR~A.",
|
||||
|
||||
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
|
||||
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
|
||||
|
||||
[539] = "srdRR~R.",
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
if band(x, 31) == 15 then return "iselRRRC" end
|
||||
end
|
||||
})
|
||||
|
||||
local map_ld = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "ldRRE", "lduRRE", "lwaRRE",
|
||||
}
|
||||
|
||||
local map_std = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "stdRRE", "stduRRE",
|
||||
}
|
||||
|
||||
local map_fps = {
|
||||
shift = 5, mask = 1,
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivsFFF.", false,
|
||||
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
|
||||
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
|
||||
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_fpd = {
|
||||
shift = 5, mask = 1,
|
||||
[0] = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
|
||||
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
|
||||
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
|
||||
[136] = "fnabsF-F.", [264] = "fabsF-F.",
|
||||
[12] = "frspF-F.",
|
||||
[14] = "fctiwF-F.", [15] = "fctiwzF-F.",
|
||||
[583] = "mffsF.", [711] = "mtfsfZF.",
|
||||
[392] = "frinF-F.", [424] = "frizF-F.",
|
||||
[456] = "fripF-F.", [488] = "frimF-F.",
|
||||
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
|
||||
},
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivFFF.", false,
|
||||
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
|
||||
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
|
||||
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_spe = {
|
||||
shift = 0, mask = 2047,
|
||||
|
||||
[512] = "evaddwRRR", [514] = "evaddiwRAR~",
|
||||
[516] = "evsubwRRR~", [518] = "evsubiwRAR~",
|
||||
[520] = "evabsRR", [521] = "evnegRR",
|
||||
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
|
||||
[525] = "evcntlzwRR", [526] = "evcntlswRR",
|
||||
|
||||
[527] = "brincRRR",
|
||||
|
||||
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
|
||||
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
|
||||
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
|
||||
|
||||
[544] = "evsrwuRRR", [545] = "evsrwsRRR",
|
||||
[546] = "evsrwiuRRA", [547] = "evsrwisRRA",
|
||||
[548] = "evslwRRR", [550] = "evslwiRRA",
|
||||
[552] = "evrlwRRR", [553] = "evsplatiRS",
|
||||
[554] = "evrlwiRRA", [555] = "evsplatfiRS",
|
||||
[556] = "evmergehiRRR", [557] = "evmergeloRRR",
|
||||
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
|
||||
|
||||
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
|
||||
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
|
||||
[564] = "evcmpeqYRR",
|
||||
|
||||
[632] = "evselRRR", [633] = "evselRRRW",
|
||||
[634] = "evselRRRW", [635] = "evselRRRW",
|
||||
[636] = "evselRRRW", [637] = "evselRRRW",
|
||||
[638] = "evselRRRW", [639] = "evselRRRW",
|
||||
|
||||
[640] = "evfsaddRRR", [641] = "evfssubRRR",
|
||||
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
|
||||
[648] = "evfsmulRRR", [649] = "evfsdivRRR",
|
||||
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
|
||||
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
|
||||
[658] = "evfscfufR-R", [659] = "evfscfsfR-R",
|
||||
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
|
||||
[662] = "evfsctufR-R", [663] = "evfsctsfR-R",
|
||||
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
|
||||
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
|
||||
|
||||
[704] = "efsaddRRR", [705] = "efssubRRR",
|
||||
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
|
||||
[712] = "efsmulRRR", [713] = "efsdivRRR",
|
||||
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
|
||||
[719] = "efscfdR-R",
|
||||
[720] = "efscfuiR-R", [721] = "efscfsiR-R",
|
||||
[722] = "efscfufR-R", [723] = "efscfsfR-R",
|
||||
[724] = "efsctuiR-R", [725] = "efsctsiR-R",
|
||||
[726] = "efsctufR-R", [727] = "efsctsfR-R",
|
||||
[728] = "efsctuizR-R", [730] = "efsctsizR-R",
|
||||
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
|
||||
|
||||
[736] = "efdaddRRR", [737] = "efdsubRRR",
|
||||
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
|
||||
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
|
||||
[744] = "efdmulRRR", [745] = "efddivRRR",
|
||||
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
|
||||
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
|
||||
[751] = "efdcfsR-R",
|
||||
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
|
||||
[754] = "efdcfufR-R", [755] = "efdcfsfR-R",
|
||||
[756] = "efdctuiR-R", [757] = "efdctsiR-R",
|
||||
[758] = "efdctufR-R", [759] = "efdctsfR-R",
|
||||
[760] = "efdctuizR-R", [762] = "efdctsizR-R",
|
||||
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
|
||||
|
||||
[768] = "evlddxRR0R", [769] = "evlddRR8",
|
||||
[770] = "evldwxRR0R", [771] = "evldwRR8",
|
||||
[772] = "evldhxRR0R", [773] = "evldhRR8",
|
||||
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
|
||||
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
|
||||
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
|
||||
[784] = "evlwhexRR0R", [785] = "evlwheRR4",
|
||||
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
|
||||
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
|
||||
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
|
||||
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
|
||||
|
||||
[800] = "evstddxRR0R", [801] = "evstddRR8",
|
||||
[802] = "evstdwxRR0R", [803] = "evstdwRR8",
|
||||
[804] = "evstdhxRR0R", [805] = "evstdhRR8",
|
||||
[816] = "evstwhexRR0R", [817] = "evstwheRR4",
|
||||
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
|
||||
[824] = "evstwwexRR0R", [825] = "evstwweRR4",
|
||||
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
|
||||
|
||||
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
|
||||
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
|
||||
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
|
||||
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
|
||||
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
|
||||
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
|
||||
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
|
||||
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
|
||||
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
|
||||
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
|
||||
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
|
||||
[1147] = "evmwsmfaRRR",
|
||||
|
||||
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
|
||||
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
|
||||
[1220] = "evmraRR",
|
||||
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
|
||||
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
|
||||
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
|
||||
|
||||
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
|
||||
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
|
||||
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
|
||||
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
|
||||
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
|
||||
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
|
||||
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
|
||||
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
|
||||
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
|
||||
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
|
||||
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
|
||||
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
|
||||
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
|
||||
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
|
||||
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
|
||||
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
|
||||
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
|
||||
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
|
||||
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
|
||||
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
|
||||
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
|
||||
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
|
||||
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
|
||||
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
|
||||
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
|
||||
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = false, false, "tdiARI", "twiARI",
|
||||
map_spe, false, false, "mulliRRI",
|
||||
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
|
||||
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
|
||||
"b_KBJ", "sc", "bKJ", map_crops,
|
||||
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
|
||||
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
|
||||
"andi.RR~U", "andis.RR~U", map_rld, map_ext,
|
||||
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
|
||||
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
|
||||
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
|
||||
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
|
||||
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
|
||||
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
|
||||
false, false, map_ld, map_fps,
|
||||
false, false, map_std, map_fpd,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
||||
}
|
||||
|
||||
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
|
||||
|
||||
-- Format a condition bit.
|
||||
local function condfmt(cond)
|
||||
if cond <= 3 then
|
||||
return map_cond[band(cond, 3)]
|
||||
else
|
||||
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
local operands = {}
|
||||
local last = nil
|
||||
local rs = 21
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = map_pri[rshift(b0, 2)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)]
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "R" then
|
||||
x = map_gpr[band(rshift(op, rs), 31)]
|
||||
rs = rs - 5
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "S" then
|
||||
x = arshift(lshift(op, 27-rs), 27)
|
||||
rs = rs - 5
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "D" or p == "E" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
if p == "E" then disp = band(disp, -4) end
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p >= "2" and p <= "8" then
|
||||
local disp = band(rshift(op, rs), 31) * p
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "H" then
|
||||
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
|
||||
rs = rs - 5
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, rs), 31) + band(op, 0x20)
|
||||
elseif p == "C" then
|
||||
x = condfmt(band(rshift(op, rs), 31))
|
||||
rs = rs - 5
|
||||
elseif p == "B" then
|
||||
local bo = rshift(op, 21)
|
||||
local cond = band(rshift(op, 16), 31)
|
||||
local cn = ""
|
||||
rs = rs - 10
|
||||
if band(bo, 4) == 0 then
|
||||
cn = band(bo, 2) == 0 and "dnz" or "dz"
|
||||
if band(bo, 0x10) == 0 then
|
||||
cn = cn..(band(bo, 8) == 0 and "f" or "t")
|
||||
end
|
||||
if band(bo, 0x10) == 0 then x = condfmt(cond) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
elseif band(bo, 0x10) == 0 then
|
||||
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
|
||||
if cond > 3 then x = "cr"..rshift(cond, 2) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
end
|
||||
name = gsub(name, "_", cn)
|
||||
elseif p == "J" then
|
||||
x = arshift(lshift(op, 27-rs), 29-rs)*4
|
||||
if band(op, 2) == 0 then x = ctx.addr + pos + x end
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "K" then
|
||||
if band(op, 1) ~= 0 then name = name.."l" end
|
||||
if band(op, 2) ~= 0 then name = name.."a" end
|
||||
elseif p == "X" or p == "Y" then
|
||||
x = band(rshift(op, rs+2), 7)
|
||||
if x == 0 and p == "Y" then x = nil else x = "cr"..x end
|
||||
rs = rs - 5
|
||||
elseif p == "W" then
|
||||
x = "cr"..band(op, 7)
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, rs-4), 255)
|
||||
rs = rs - 10
|
||||
elseif p == ">" then
|
||||
operands[#operands] = rshift(operands[#operands], 1)
|
||||
elseif p == "0" then
|
||||
if last == "r0" then
|
||||
operands[#operands] = nil
|
||||
if altname then name = altname end
|
||||
end
|
||||
elseif p == "L" then
|
||||
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
|
||||
elseif p == "." then
|
||||
if band(op, 1) == 1 then name = name.."." end
|
||||
elseif p == "N" then
|
||||
if op == 0x60000000 then name = "nop"; break end
|
||||
elseif p == "~" then
|
||||
local n = #operands
|
||||
operands[n-1], operands[n] = operands[n], operands[n-1]
|
||||
elseif p == "=" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] then
|
||||
operands[n] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "%" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] and last == operands[n-2] then
|
||||
operands[n] = nil
|
||||
operands[n-1] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "-" then
|
||||
rs = rs - 5
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
disass = disass,
|
||||
regname = regname
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x64 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the 64 bit functions from the combined
|
||||
-- x86/x64 disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86")
|
||||
return {
|
||||
create = dis_x86.create64,
|
||||
disass = dis_x86.disass64,
|
||||
regname = dis_x86.regname64
|
||||
}
|
||||
|
||||
@@ -0,0 +1,953 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x86/x64 disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- Sending small code snippets to an external disassembler and mixing the
|
||||
-- output with our own stuff was too fragile. So I had to bite the bullet
|
||||
-- and write yet another x86 disassembler. Oh well ...
|
||||
--
|
||||
-- The output format is very similar to what ndisasm generates. But it has
|
||||
-- been developed independently by looking at the opcode tables from the
|
||||
-- Intel and AMD manuals. The supported instruction set is quite extensive
|
||||
-- and reflects what a current generation Intel or AMD CPU implements in
|
||||
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
|
||||
-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
|
||||
-- (VMX/SVM) instructions.
|
||||
--
|
||||
-- Notes:
|
||||
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
|
||||
-- * No attempt at optimization has been made -- it's fast enough for my needs.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local lower, rep = string.lower, string.rep
|
||||
local bit = require("bit")
|
||||
local tohex = bit.tohex
|
||||
|
||||
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
|
||||
local map_opc1_32 = {
|
||||
--0x
|
||||
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
|
||||
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
|
||||
--1x
|
||||
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
|
||||
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
|
||||
--2x
|
||||
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
|
||||
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
|
||||
--3x
|
||||
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
|
||||
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
|
||||
--4x
|
||||
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
|
||||
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
|
||||
--5x
|
||||
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
|
||||
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
|
||||
--6x
|
||||
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
|
||||
"fs:seg","gs:seg","o16:","a16",
|
||||
"pushUi","imulVrmi","pushBs","imulVrms",
|
||||
"insb","insVS","outsb","outsVS",
|
||||
--7x
|
||||
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
|
||||
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
|
||||
--8x
|
||||
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
|
||||
"testBmr","testVmr","xchgBrm","xchgVrm",
|
||||
"movBmr","movVmr","movBrm","movVrm",
|
||||
"movVmg","leaVrm","movWgm","popUm",
|
||||
--9x
|
||||
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
|
||||
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
|
||||
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
|
||||
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
|
||||
--Ax
|
||||
"movBao","movVao","movBoa","movVoa",
|
||||
"movsb","movsVS","cmpsb","cmpsVS",
|
||||
"testBai","testVai","stosb","stosVS",
|
||||
"lodsb","lodsVS","scasb","scasVS",
|
||||
--Bx
|
||||
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
|
||||
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
|
||||
--Cx
|
||||
"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
|
||||
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
|
||||
--Dx
|
||||
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
|
||||
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
|
||||
--Ex
|
||||
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
|
||||
"inBau","inVau","outBua","outVua",
|
||||
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
|
||||
--Fx
|
||||
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
|
||||
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
|
||||
}
|
||||
assert(#map_opc1_32 == 255)
|
||||
|
||||
-- Map for 1st opcode byte in 64 bit mode (overrides only).
|
||||
local map_opc1_64 = setmetatable({
|
||||
[0x06]=false, [0x07]=false, [0x0e]=false,
|
||||
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
|
||||
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
|
||||
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
|
||||
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
|
||||
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
|
||||
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
|
||||
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
|
||||
[0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
|
||||
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
|
||||
}, { __index = map_opc1_32 })
|
||||
|
||||
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
|
||||
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
|
||||
local map_opc2 = {
|
||||
--0x
|
||||
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
|
||||
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
|
||||
--1x
|
||||
"movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
|
||||
"movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
|
||||
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
|
||||
"movlpsXmr||movlpdXmr",
|
||||
"unpcklpsXrvm||unpcklpdXrvm",
|
||||
"unpckhpsXrvm||unpckhpdXrvm",
|
||||
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
|
||||
"movhpsXmr||movhpdXmr",
|
||||
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
|
||||
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
|
||||
--2x
|
||||
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
|
||||
"movapsXrm||movapdXrm",
|
||||
"movapsXmr||movapdXmr",
|
||||
"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
|
||||
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
|
||||
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
|
||||
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
|
||||
"ucomissXrm||ucomisdXrm",
|
||||
"comissXrm||comisdXrm",
|
||||
--3x
|
||||
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
|
||||
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
|
||||
--4x
|
||||
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
|
||||
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
|
||||
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
|
||||
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
|
||||
--5x
|
||||
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
|
||||
"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
|
||||
"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
|
||||
"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
|
||||
"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
|
||||
"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
|
||||
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
|
||||
"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
|
||||
"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
|
||||
--6x
|
||||
"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
|
||||
"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
|
||||
"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
|
||||
"||punpcklqdqXrvm","||punpckhqdqXrvm",
|
||||
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
|
||||
--7x
|
||||
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
|
||||
"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
|
||||
"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
|
||||
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
|
||||
nil,nil,
|
||||
"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
|
||||
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
|
||||
--8x
|
||||
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
|
||||
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
|
||||
--9x
|
||||
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
|
||||
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
|
||||
--Ax
|
||||
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
|
||||
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
|
||||
--Bx
|
||||
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
|
||||
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
|
||||
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
|
||||
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
|
||||
--Cx
|
||||
"xaddBmr","xaddVmr",
|
||||
"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
|
||||
"pinsrwPrvWmu","pextrwDrPmu",
|
||||
"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
|
||||
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
|
||||
--Dx
|
||||
"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
|
||||
"paddqPrvm","pmullwPrvm",
|
||||
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
|
||||
"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
|
||||
"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
|
||||
--Ex
|
||||
"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
|
||||
"pmulhuwPrvm","pmulhwPrvm",
|
||||
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
|
||||
"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
|
||||
"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
|
||||
--Fx
|
||||
"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
|
||||
"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
|
||||
"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
|
||||
"paddbPrvm","paddwPrvm","padddPrvm","ud",
|
||||
}
|
||||
assert(map_opc2[255] == "ud")
|
||||
|
||||
-- Map for three-byte opcodes. Can't wait for their next invention.
|
||||
local map_opc3 = {
|
||||
["38"] = { -- [66] 0f 38 xx
|
||||
--0x
|
||||
[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
|
||||
"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
|
||||
"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
|
||||
"||permilpsXrvm","||permilpdXrvm",nil,nil,
|
||||
--1x
|
||||
"||pblendvbXrma",nil,nil,nil,
|
||||
"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
|
||||
"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
|
||||
"pabsbPrm","pabswPrm","pabsdPrm",nil,
|
||||
--2x
|
||||
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
|
||||
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
|
||||
"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
|
||||
"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
|
||||
--3x
|
||||
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
|
||||
"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
|
||||
"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
|
||||
"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
|
||||
--4x
|
||||
"||pmulddXrvm","||phminposuwXrm",nil,nil,
|
||||
nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
|
||||
--5x
|
||||
[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
|
||||
[0x5a] = "||broadcasti128XrlXm",
|
||||
--7x
|
||||
[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
|
||||
--8x
|
||||
[0x8c] = "||pmaskmovXrvVSm",
|
||||
[0x8e] = "||pmaskmovVSmXvr",
|
||||
--9x
|
||||
[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
|
||||
[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
|
||||
[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
|
||||
[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
|
||||
[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
|
||||
--Ax
|
||||
[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
|
||||
[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
|
||||
[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
|
||||
[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
|
||||
[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
|
||||
--Bx
|
||||
[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
|
||||
[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
|
||||
[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
|
||||
[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
|
||||
[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
|
||||
--Dx
|
||||
[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
|
||||
[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
|
||||
--Fx
|
||||
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
|
||||
[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
|
||||
},
|
||||
|
||||
["3a"] = { -- [66] 0f 3a xx
|
||||
--0x
|
||||
[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
|
||||
"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
|
||||
"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
|
||||
"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
|
||||
--1x
|
||||
nil,nil,nil,nil,
|
||||
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
|
||||
"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
|
||||
nil,nil,nil,nil,
|
||||
--2x
|
||||
"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
|
||||
--3x
|
||||
[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
|
||||
--4x
|
||||
[0x40] = "||dppsXrvmu",
|
||||
[0x41] = "||dppdXrvmu",
|
||||
[0x42] = "||mpsadbwXrvmu",
|
||||
[0x44] = "||pclmulqdqXrvmu",
|
||||
[0x46] = "||perm2i128Xrvmu",
|
||||
[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
|
||||
[0x4c] = "||pblendvbXrvmb",
|
||||
--6x
|
||||
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
|
||||
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
|
||||
[0xdf] = "||aeskeygenassistXrmu",
|
||||
--Fx
|
||||
[0xf0] = "||| rorxVrmu",
|
||||
},
|
||||
}
|
||||
|
||||
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
|
||||
local map_opcvm = {
|
||||
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
|
||||
[0xc8]="monitor",[0xc9]="mwait",
|
||||
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
|
||||
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
|
||||
[0xf8]="swapgs",[0xf9]="rdtscp",
|
||||
}
|
||||
|
||||
-- Map for FP opcodes. And you thought stack machines are simple?
|
||||
local map_opcfp = {
|
||||
-- D8-DF 00-BF: opcodes with a memory operand.
|
||||
-- D8
|
||||
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
|
||||
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
|
||||
-- DA
|
||||
"fiaddDm","fimulDm","ficomDm","ficompDm",
|
||||
"fisubDm","fisubrDm","fidivDm","fidivrDm",
|
||||
-- DB
|
||||
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
|
||||
-- DC
|
||||
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
|
||||
-- DD
|
||||
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
|
||||
-- DE
|
||||
"fiaddWm","fimulWm","ficomWm","ficompWm",
|
||||
"fisubWm","fisubrWm","fidivWm","fidivrWm",
|
||||
-- DF
|
||||
"fildWm","fisttpWm","fistWm","fistpWm",
|
||||
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
|
||||
-- xx C0-FF: opcodes with a pseudo-register operand.
|
||||
-- D8
|
||||
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
|
||||
-- D9
|
||||
"fldFf","fxchFf",{"fnop"},nil,
|
||||
{"fchs","fabs",nil,nil,"ftst","fxam"},
|
||||
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
|
||||
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
|
||||
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
|
||||
-- DA
|
||||
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
|
||||
-- DB
|
||||
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
|
||||
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
|
||||
-- DC
|
||||
"fadd toFf","fmul toFf",nil,nil,
|
||||
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
|
||||
-- DD
|
||||
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
|
||||
-- DE
|
||||
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
|
||||
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
|
||||
-- DF
|
||||
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
|
||||
}
|
||||
assert(map_opcfp[126] == "fcomipFf")
|
||||
|
||||
-- Map for opcode groups. The subkey is sp from the ModRM byte.
|
||||
local map_opcgroup = {
|
||||
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
|
||||
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
|
||||
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
||||
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
||||
incb = { "inc", "dec" },
|
||||
incd = { "inc", "dec", "callUmp", "$call farDmp",
|
||||
"jmpUmp", "$jmp farDmp", "pushUm" },
|
||||
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
|
||||
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
|
||||
"smsw", nil, "lmsw", "vm*$invlpg" },
|
||||
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
|
||||
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
|
||||
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
|
||||
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
|
||||
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
|
||||
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
|
||||
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
|
||||
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
|
||||
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
|
||||
prefetch = { "prefetch", "prefetchw" },
|
||||
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Maps for register names.
|
||||
local map_regs = {
|
||||
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
|
||||
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
|
||||
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
||||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
|
||||
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
|
||||
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
|
||||
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
||||
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
|
||||
Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
|
||||
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
|
||||
}
|
||||
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
|
||||
|
||||
-- Maps for size names.
|
||||
local map_sz2n = {
|
||||
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32,
|
||||
}
|
||||
local map_sz2prefix = {
|
||||
B = "byte", W = "word", D = "dword",
|
||||
Q = "qword",
|
||||
M = "qword", X = "xword", Y = "yword",
|
||||
F = "dword", G = "qword", -- No need for sizes/register names for these two.
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local code, pos, hex = ctx.code, ctx.pos, ""
|
||||
local hmax = ctx.hexdump
|
||||
if hmax > 0 then
|
||||
for i=ctx.start,pos-1 do
|
||||
hex = hex..format("%02X", byte(code, i, i))
|
||||
end
|
||||
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
|
||||
else hex = hex..rep(" ", hmax-#hex+2) end
|
||||
end
|
||||
if operands then text = text.." "..operands end
|
||||
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
|
||||
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
|
||||
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
|
||||
if ctx.rex then
|
||||
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
|
||||
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")..
|
||||
(ctx.vexl and "l" or "")
|
||||
if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end
|
||||
if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "")
|
||||
elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end
|
||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
||||
ctx.rex = false; ctx.vexl = false; ctx.vexv = false
|
||||
end
|
||||
if ctx.seg then
|
||||
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
|
||||
if n == 0 then text = ctx.seg.." "..text else text = text2 end
|
||||
ctx.seg = false
|
||||
end
|
||||
if ctx.lock then text = "lock "..text; ctx.lock = false end
|
||||
local imm = ctx.imm
|
||||
if imm then
|
||||
local sym = ctx.symtab[imm]
|
||||
if sym then text = text.."\t->"..sym end
|
||||
end
|
||||
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
|
||||
ctx.mrm = false
|
||||
ctx.vexv = false
|
||||
ctx.start = pos
|
||||
ctx.imm = nil
|
||||
end
|
||||
|
||||
-- Clear all prefix flags.
|
||||
local function clearprefixes(ctx)
|
||||
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
|
||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
||||
ctx.rex = false; ctx.a32 = false; ctx.vexl = false
|
||||
end
|
||||
|
||||
-- Fallback for incomplete opcodes at the end.
|
||||
local function incomplete(ctx)
|
||||
ctx.pos = ctx.stop+1
|
||||
clearprefixes(ctx)
|
||||
return putop(ctx, "(incomplete)")
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
clearprefixes(ctx)
|
||||
return putop(ctx, "(unknown)")
|
||||
end
|
||||
|
||||
-- Return an immediate of the specified size.
|
||||
local function getimm(ctx, pos, n)
|
||||
if pos+n-1 > ctx.stop then return incomplete(ctx) end
|
||||
local code = ctx.code
|
||||
if n == 1 then
|
||||
local b1 = byte(code, pos, pos)
|
||||
return b1
|
||||
elseif n == 2 then
|
||||
local b1, b2 = byte(code, pos, pos+1)
|
||||
return b1+b2*256
|
||||
else
|
||||
local b1, b2, b3, b4 = byte(code, pos, pos+3)
|
||||
local imm = b1+b2*256+b3*65536+b4*16777216
|
||||
ctx.imm = imm
|
||||
return imm
|
||||
end
|
||||
end
|
||||
|
||||
-- Process pattern string and generate the operands.
|
||||
local function putpat(ctx, name, pat)
|
||||
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
|
||||
local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl
|
||||
|
||||
-- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "V" or p == "U" then
|
||||
if ctx.rexw then sz = "Q"; ctx.rexw = false
|
||||
elseif ctx.o16 then sz = "W"; ctx.o16 = false
|
||||
elseif p == "U" and ctx.x64 then sz = "Q"
|
||||
else sz = "D" end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "T" then
|
||||
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "B" then
|
||||
sz = "B"
|
||||
regs = ctx.rex and map_regs.B64 or map_regs.B
|
||||
elseif match(p, "[WDQMXYFG]") then
|
||||
sz = p
|
||||
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "P" then
|
||||
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
|
||||
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "H" then
|
||||
name = name..(ctx.rexw and "d" or "s")
|
||||
ctx.rexw = false
|
||||
elseif p == "S" then
|
||||
name = name..lower(sz)
|
||||
elseif p == "s" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = imm <= 127 and format("+0x%02x", imm)
|
||||
or format("-0x%02x", 256-imm)
|
||||
pos = pos+1
|
||||
elseif p == "u" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = format("0x%02x", imm)
|
||||
pos = pos+1
|
||||
elseif p == "b" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = regs[imm/16+1]
|
||||
pos = pos+1
|
||||
elseif p == "w" then
|
||||
local imm = getimm(ctx, pos, 2); if not imm then return end
|
||||
x = format("0x%x", imm)
|
||||
pos = pos+2
|
||||
elseif p == "o" then -- [offset]
|
||||
if ctx.x64 then
|
||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
||||
x = format("[0x%08x%08x]", imm2, imm1)
|
||||
pos = pos+8
|
||||
else
|
||||
local imm = getimm(ctx, pos, 4); if not imm then return end
|
||||
x = format("[0x%08x]", imm)
|
||||
pos = pos+4
|
||||
end
|
||||
elseif p == "i" or p == "I" then
|
||||
local n = map_sz2n[sz]
|
||||
if n == 8 and ctx.x64 and p == "I" then
|
||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
||||
x = format("0x%08x%08x", imm2, imm1)
|
||||
else
|
||||
if n == 8 then n = 4 end
|
||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
||||
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
|
||||
imm = (0xffffffff+1)-imm
|
||||
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
|
||||
else
|
||||
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
|
||||
end
|
||||
end
|
||||
pos = pos+n
|
||||
elseif p == "j" then
|
||||
local n = map_sz2n[sz]
|
||||
if n == 8 then n = 4 end
|
||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
||||
if sz == "B" and imm > 127 then imm = imm-256
|
||||
elseif imm > 2147483647 then imm = imm-4294967296 end
|
||||
pos = pos+n
|
||||
imm = imm + pos + ctx.addr
|
||||
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
|
||||
ctx.imm = imm
|
||||
if sz == "W" then
|
||||
x = format("word 0x%04x", imm%65536)
|
||||
elseif ctx.x64 then
|
||||
local lo = imm % 0x1000000
|
||||
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
|
||||
else
|
||||
x = "0x"..tohex(imm)
|
||||
end
|
||||
elseif p == "R" then
|
||||
local r = byte(code, pos-1, pos-1)%8
|
||||
if ctx.rexb then r = r + 8; ctx.rexb = false end
|
||||
x = regs[r+1]
|
||||
elseif p == "a" then x = regs[1]
|
||||
elseif p == "c" then x = "cl"
|
||||
elseif p == "d" then x = "dx"
|
||||
elseif p == "1" then x = "1"
|
||||
else
|
||||
if not mode then
|
||||
mode = ctx.mrm
|
||||
if not mode then
|
||||
if pos > stop then return incomplete(ctx) end
|
||||
mode = byte(code, pos, pos)
|
||||
pos = pos+1
|
||||
end
|
||||
rm = mode%8; mode = (mode-rm)/8
|
||||
sp = mode%8; mode = (mode-sp)/8
|
||||
sdisp = ""
|
||||
if mode < 3 then
|
||||
if rm == 4 then
|
||||
if pos > stop then return incomplete(ctx) end
|
||||
sc = byte(code, pos, pos)
|
||||
pos = pos+1
|
||||
rm = sc%8; sc = (sc-rm)/8
|
||||
rx = sc%8; sc = (sc-rx)/8
|
||||
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
|
||||
if rx == 4 then rx = nil end
|
||||
end
|
||||
if mode > 0 or rm == 5 then
|
||||
local dsz = mode
|
||||
if dsz ~= 1 then dsz = 4 end
|
||||
local disp = getimm(ctx, pos, dsz); if not disp then return end
|
||||
if mode == 0 then rm = nil end
|
||||
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
|
||||
if dsz == 1 and disp > 127 then
|
||||
sdisp = format("-0x%x", 256-disp)
|
||||
elseif disp >= 0 and disp <= 0x7fffffff then
|
||||
sdisp = format("+0x%x", disp)
|
||||
else
|
||||
sdisp = format("-0x%x", (0xffffffff+1)-disp)
|
||||
end
|
||||
else
|
||||
sdisp = format(ctx.x64 and not ctx.a32 and
|
||||
not (disp >= 0 and disp <= 0x7fffffff)
|
||||
and "0xffffffff%08x" or "0x%08x", disp)
|
||||
end
|
||||
pos = pos+dsz
|
||||
end
|
||||
end
|
||||
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
|
||||
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
|
||||
end
|
||||
if p == "m" then
|
||||
if mode == 3 then x = regs[rm+1]
|
||||
else
|
||||
local aregs = ctx.a32 and map_regs.D or ctx.aregs
|
||||
local srm, srx = "", ""
|
||||
if rm then srm = aregs[rm+1]
|
||||
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
|
||||
ctx.a32 = false
|
||||
if rx then
|
||||
if rm then srm = srm.."+" end
|
||||
srx = aregs[rx+1]
|
||||
if sc > 0 then srx = srx.."*"..(2^sc) end
|
||||
end
|
||||
x = format("[%s%s%s]", srm, srx, sdisp)
|
||||
end
|
||||
if mode < 3 and
|
||||
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
|
||||
x = map_sz2prefix[sz].." "..x
|
||||
end
|
||||
elseif p == "r" then x = regs[sp+1]
|
||||
elseif p == "g" then x = map_segregs[sp+1]
|
||||
elseif p == "p" then -- Suppress prefix.
|
||||
elseif p == "f" then x = "st"..rm
|
||||
elseif p == "x" then
|
||||
if sp == 0 and ctx.lock and not ctx.x64 then
|
||||
x = "CR8"; ctx.lock = false
|
||||
else
|
||||
x = "CR"..sp
|
||||
end
|
||||
elseif p == "v" then
|
||||
if ctx.vexv then
|
||||
x = regs[ctx.vexv+1]; ctx.vexv = false
|
||||
end
|
||||
elseif p == "y" then x = "DR"..sp
|
||||
elseif p == "z" then x = "TR"..sp
|
||||
elseif p == "l" then vexl = false
|
||||
elseif p == "t" then
|
||||
else
|
||||
error("bad pattern `"..pat.."'")
|
||||
end
|
||||
end
|
||||
if x then operands = operands and operands..", "..x or x end
|
||||
end
|
||||
ctx.pos = pos
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
-- Forward declaration.
|
||||
local map_act
|
||||
|
||||
-- Fetch and cache MRM byte.
|
||||
local function getmrm(ctx)
|
||||
local mrm = ctx.mrm
|
||||
if not mrm then
|
||||
local pos = ctx.pos
|
||||
if pos > ctx.stop then return nil end
|
||||
mrm = byte(ctx.code, pos, pos)
|
||||
ctx.pos = pos+1
|
||||
ctx.mrm = mrm
|
||||
end
|
||||
return mrm
|
||||
end
|
||||
|
||||
-- Dispatch to handler depending on pattern.
|
||||
local function dispatch(ctx, opat, patgrp)
|
||||
if not opat then return unknown(ctx) end
|
||||
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
|
||||
local p
|
||||
if ctx.rep then
|
||||
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
|
||||
ctx.rep = false
|
||||
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
|
||||
else p = "^[^%|]*" end
|
||||
opat = match(opat, p)
|
||||
if not opat then return unknown(ctx) end
|
||||
-- ctx.rep = false; ctx.o16 = false
|
||||
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
|
||||
--XXX remove in branches?
|
||||
end
|
||||
if match(opat, "%$") then -- reg$mem variants.
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
|
||||
if opat == "" then return unknown(ctx) end
|
||||
end
|
||||
if opat == "" then return unknown(ctx) end
|
||||
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
|
||||
if pat == "" and patgrp then pat = patgrp end
|
||||
return map_act[sub(pat, 1, 1)](ctx, name, pat)
|
||||
end
|
||||
|
||||
-- Get a pattern from an opcode map and dispatch to handler.
|
||||
local function dispatchmap(ctx, opcmap)
|
||||
local pos = ctx.pos
|
||||
local opat = opcmap[byte(ctx.code, pos, pos)]
|
||||
pos = pos + 1
|
||||
ctx.pos = pos
|
||||
return dispatch(ctx, opat)
|
||||
end
|
||||
|
||||
-- Map for action codes. The key is the first char after the name.
|
||||
map_act = {
|
||||
-- Simple opcodes without operands.
|
||||
[""] = function(ctx, name, pat)
|
||||
return putop(ctx, name)
|
||||
end,
|
||||
|
||||
-- Operand size chars fall right through.
|
||||
B = putpat, W = putpat, D = putpat, Q = putpat,
|
||||
V = putpat, U = putpat, T = putpat,
|
||||
M = putpat, X = putpat, P = putpat,
|
||||
F = putpat, G = putpat, Y = putpat,
|
||||
H = putpat,
|
||||
|
||||
-- Collect prefixes.
|
||||
[":"] = function(ctx, name, pat)
|
||||
ctx[pat == ":" and name or sub(pat, 2)] = name
|
||||
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
|
||||
end,
|
||||
|
||||
-- Chain to special handler specified by name.
|
||||
["*"] = function(ctx, name, pat)
|
||||
return map_act[name](ctx, name, sub(pat, 2))
|
||||
end,
|
||||
|
||||
-- Use named subtable for opcode group.
|
||||
["!"] = function(ctx, name, pat)
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
|
||||
end,
|
||||
|
||||
-- o16,o32[,o64] variants.
|
||||
sz = function(ctx, name, pat)
|
||||
if ctx.o16 then ctx.o16 = false
|
||||
else
|
||||
pat = match(pat, ",(.*)")
|
||||
if ctx.rexw then
|
||||
local p = match(pat, ",(.*)")
|
||||
if p then pat = p; ctx.rexw = false end
|
||||
end
|
||||
end
|
||||
pat = match(pat, "^[^,]*")
|
||||
return dispatch(ctx, pat)
|
||||
end,
|
||||
|
||||
-- Two-byte opcode dispatch.
|
||||
opc2 = function(ctx, name, pat)
|
||||
return dispatchmap(ctx, map_opc2)
|
||||
end,
|
||||
|
||||
-- Three-byte opcode dispatch.
|
||||
opc3 = function(ctx, name, pat)
|
||||
return dispatchmap(ctx, map_opc3[pat])
|
||||
end,
|
||||
|
||||
-- VMX/SVM dispatch.
|
||||
vm = function(ctx, name, pat)
|
||||
return dispatch(ctx, map_opcvm[ctx.mrm])
|
||||
end,
|
||||
|
||||
-- Floating point opcode dispatch.
|
||||
fp = function(ctx, name, pat)
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
local rm = mrm%8
|
||||
local idx = pat*8 + ((mrm-rm)/8)%8
|
||||
if mrm >= 192 then idx = idx + 64 end
|
||||
local opat = map_opcfp[idx]
|
||||
if type(opat) == "table" then opat = opat[rm+1] end
|
||||
return dispatch(ctx, opat)
|
||||
end,
|
||||
|
||||
-- REX prefix.
|
||||
rex = function(ctx, name, pat)
|
||||
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
|
||||
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
|
||||
ctx.rex = "rex"
|
||||
end,
|
||||
|
||||
-- VEX prefix.
|
||||
vex = function(ctx, name, pat)
|
||||
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
|
||||
ctx.rex = "vex"
|
||||
local pos = ctx.pos
|
||||
if ctx.mrm then
|
||||
ctx.mrm = nil
|
||||
pos = pos-1
|
||||
end
|
||||
local b = byte(ctx.code, pos, pos)
|
||||
if not b then return incomplete(ctx) end
|
||||
pos = pos+1
|
||||
if b < 128 then ctx.rexr = true end
|
||||
local m = 1
|
||||
if pat == "3" then
|
||||
m = b%32; b = (b-m)/32
|
||||
local nb = b%2; b = (b-nb)/2
|
||||
if nb == 0 then ctx.rexb = true end
|
||||
local nx = b%2
|
||||
if nx == 0 then ctx.rexx = true end
|
||||
b = byte(ctx.code, pos, pos)
|
||||
if not b then return incomplete(ctx) end
|
||||
pos = pos+1
|
||||
if b >= 128 then ctx.rexw = true end
|
||||
end
|
||||
ctx.pos = pos
|
||||
local map
|
||||
if m == 1 then map = map_opc2
|
||||
elseif m == 2 then map = map_opc3["38"]
|
||||
elseif m == 3 then map = map_opc3["3a"]
|
||||
else return unknown(ctx) end
|
||||
local p = b%4; b = (b-p)/4
|
||||
if p == 1 then ctx.o16 = "o16"
|
||||
elseif p == 2 then ctx.rep = "rep"
|
||||
elseif p == 3 then ctx.rep = "repne" end
|
||||
local l = b%2; b = (b-l)/2
|
||||
if l ~= 0 then ctx.vexl = true end
|
||||
ctx.vexv = (-1-b)%16
|
||||
return dispatchmap(ctx, map)
|
||||
end,
|
||||
|
||||
-- Special case for nop with REX prefix.
|
||||
nop = function(ctx, name, pat)
|
||||
return dispatch(ctx, ctx.rex and pat or "nop")
|
||||
end,
|
||||
|
||||
-- Special case for 0F 77.
|
||||
emms = function(ctx, name, pat)
|
||||
if ctx.rex ~= "vex" then
|
||||
return putop(ctx, "emms")
|
||||
elseif ctx.vexl then
|
||||
ctx.vexl = false
|
||||
return putop(ctx, "zeroall")
|
||||
else
|
||||
return putop(ctx, "zeroupper")
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ofs = ofs + 1
|
||||
ctx.start = ofs
|
||||
ctx.pos = ofs
|
||||
ctx.stop = stop
|
||||
ctx.imm = nil
|
||||
ctx.mrm = false
|
||||
clearprefixes(ctx)
|
||||
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
|
||||
if ctx.pos ~= ctx.start then incomplete(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = (addr or 0) - 1
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 16
|
||||
ctx.x64 = false
|
||||
ctx.map1 = map_opc1_32
|
||||
ctx.aregs = map_regs.D
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create64(code, addr, out)
|
||||
local ctx = create(code, addr, out)
|
||||
ctx.x64 = true
|
||||
ctx.map1 = map_opc1_64
|
||||
ctx.aregs = map_regs.Q
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass(code, addr, out)
|
||||
create(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass64(code, addr, out)
|
||||
create64(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname(r)
|
||||
if r < 8 then return map_regs.D[r+1] end
|
||||
return map_regs.X[r-7]
|
||||
end
|
||||
|
||||
local function regname64(r)
|
||||
if r < 16 then return map_regs.Q[r+1] end
|
||||
return map_regs.X[r-15]
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
create = create,
|
||||
create64 = create64,
|
||||
disass = disass,
|
||||
disass64 = disass64,
|
||||
regname = regname,
|
||||
regname64 = regname64
|
||||
}
|
||||
|
||||
@@ -0,0 +1,731 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT compiler dump module.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module can be used to debug the JIT compiler itself. It dumps the
|
||||
-- code representations and structures used in various compiler stages.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
|
||||
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
|
||||
-- luajit -jdump=is myapp.lua | less -R
|
||||
-- luajit -jdump=-b myapp.lua
|
||||
-- luajit -jdump=+aH,myapp.html myapp.lua
|
||||
-- luajit -jdump=ixT,myapp.dump myapp.lua
|
||||
--
|
||||
-- The first argument specifies the dump mode. The second argument gives
|
||||
-- the output file name. Default output is to stdout, unless the environment
|
||||
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- Different features can be turned on or off with the dump mode. If the
|
||||
-- mode starts with a '+', the following features are added to the default
|
||||
-- set of features; a '-' removes them. Otherwise the features are replaced.
|
||||
--
|
||||
-- The following dump features are available (* marks the default):
|
||||
--
|
||||
-- * t Print a line for each started, ended or aborted trace (see also -jv).
|
||||
-- * b Dump the traced bytecode.
|
||||
-- * i Dump the IR (intermediate representation).
|
||||
-- r Augment the IR with register/stack slots.
|
||||
-- s Dump the snapshot map.
|
||||
-- * m Dump the generated machine code.
|
||||
-- x Print each taken trace exit.
|
||||
-- X Print each taken trace exit and the contents of all registers.
|
||||
-- a Print the IR of aborted traces, too.
|
||||
--
|
||||
-- The output format can be set with the following characters:
|
||||
--
|
||||
-- T Plain text output.
|
||||
-- A ANSI-colored text output
|
||||
-- H Colorized HTML + CSS output.
|
||||
--
|
||||
-- The default output format is plain text. It's set to ANSI-colored text
|
||||
-- if the COLORTERM variable is set. Note: this is independent of any output
|
||||
-- redirection, which is actually considered a feature.
|
||||
--
|
||||
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
|
||||
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
|
||||
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
|
||||
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
|
||||
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
|
||||
local bit = require("bit")
|
||||
local band, shr, tohex = bit.band, bit.rshift, bit.tohex
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, rep = string.byte, string.rep
|
||||
local type, tostring = type, tostring
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Load other modules on-demand.
|
||||
local bcline, disass
|
||||
|
||||
-- Active flag, output file handle and dump mode.
|
||||
local active, out, dumpmode
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local symtabmt = { __index = false }
|
||||
local symtab = {}
|
||||
local nexitsym = 0
|
||||
|
||||
-- Fill nested symbol table with per-trace exit stub addresses.
|
||||
local function fillsymtab_tr(tr, nexit)
|
||||
local t = {}
|
||||
symtabmt.__index = t
|
||||
if jit.arch:sub(1, 4) == "mips" then
|
||||
t[traceexitstub(tr, 0)] = "exit"
|
||||
return
|
||||
end
|
||||
for i=0,nexit-1 do
|
||||
local addr = traceexitstub(tr, i)
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
local addr = traceexitstub(tr, nexit)
|
||||
if addr then t[addr] = "stack_check" end
|
||||
end
|
||||
|
||||
-- Fill symbol table with trace exit stub addresses.
|
||||
local function fillsymtab(tr, nexit)
|
||||
local t = symtab
|
||||
if nexitsym == 0 then
|
||||
local maskaddr = jit.arch == "arm" and -2
|
||||
local ircall = vmdef.ircall
|
||||
for i=0,#ircall do
|
||||
local addr = ircalladdr(i)
|
||||
if addr ~= 0 then
|
||||
if maskaddr then addr = band(addr, maskaddr) end
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
t[addr] = ircall[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
if nexitsym == 1000000 then -- Per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
elseif nexit > nexitsym then -- Shared exit stubs.
|
||||
for i=nexitsym,nexit-1 do
|
||||
local addr = traceexitstub(i)
|
||||
if addr == nil then -- Fall back to per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
setmetatable(symtab, symtabmt)
|
||||
nexit = 1000000
|
||||
break
|
||||
end
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
nexitsym = nexit
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function dumpwrite(s)
|
||||
out:write(s)
|
||||
end
|
||||
|
||||
-- Disassemble machine code.
|
||||
local function dump_mcode(tr)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local mcode, addr, loop = tracemc(tr)
|
||||
if not mcode then return end
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
if addr < 0 then addr = addr + 2^32 end
|
||||
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
|
||||
local ctx = disass.create(mcode, addr, dumpwrite)
|
||||
ctx.hexdump = 0
|
||||
ctx.symtab = fillsymtab(tr, info.nexit)
|
||||
if loop ~= 0 then
|
||||
symtab[addr+loop] = "LOOP"
|
||||
ctx:disass(0, loop)
|
||||
out:write("->LOOP:\n")
|
||||
ctx:disass(loop, #mcode-loop)
|
||||
symtab[addr+loop] = nil
|
||||
else
|
||||
ctx:disass(0, #mcode)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local irtype_text = {
|
||||
[0] = "nil",
|
||||
"fal",
|
||||
"tru",
|
||||
"lud",
|
||||
"str",
|
||||
"p32",
|
||||
"thr",
|
||||
"pro",
|
||||
"fun",
|
||||
"p64",
|
||||
"cdt",
|
||||
"tab",
|
||||
"udt",
|
||||
"flt",
|
||||
"num",
|
||||
"i8 ",
|
||||
"u8 ",
|
||||
"i16",
|
||||
"u16",
|
||||
"int",
|
||||
"u32",
|
||||
"i64",
|
||||
"u64",
|
||||
"sfp",
|
||||
}
|
||||
|
||||
local colortype_ansi = {
|
||||
[0] = "%s",
|
||||
"%s",
|
||||
"%s",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[32m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[33m%s\027[m",
|
||||
"\027[31m%s\027[m",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
}
|
||||
|
||||
local function colorize_text(s)
|
||||
return s
|
||||
end
|
||||
|
||||
local function colorize_ansi(s, t, extra)
|
||||
local out = format(colortype_ansi[t], s)
|
||||
if extra then out = "\027[3m"..out end
|
||||
return out
|
||||
end
|
||||
|
||||
local irtype_ansi = setmetatable({},
|
||||
{ __index = function(tab, t)
|
||||
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
|
||||
|
||||
local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
|
||||
|
||||
local function colorize_html(s, t, extra)
|
||||
s = gsub(s, "[<>&]", html_escape)
|
||||
return format('<span class="irt_%s%s">%s</span>',
|
||||
irtype_text[t], extra and " irt_extra" or "", s)
|
||||
end
|
||||
|
||||
local irtype_html = setmetatable({},
|
||||
{ __index = function(tab, t)
|
||||
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
|
||||
|
||||
local header_html = [[
|
||||
<style type="text/css">
|
||||
background { background: #ffffff; color: #000000; }
|
||||
pre.ljdump {
|
||||
font-size: 10pt;
|
||||
background: #f0f4ff;
|
||||
color: #000000;
|
||||
border: 1px solid #bfcfff;
|
||||
padding: 0.5em;
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
span.irt_str { color: #00a000; }
|
||||
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
|
||||
span.irt_tab { color: #c00000; }
|
||||
span.irt_udt, span.irt_lud { color: #00c0c0; }
|
||||
span.irt_num { color: #4040c0; }
|
||||
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
|
||||
span.irt_extra { font-style: italic; }
|
||||
</style>
|
||||
]]
|
||||
|
||||
local colorize, irtype
|
||||
|
||||
-- Lookup tables to convert some literals into names.
|
||||
local litname = {
|
||||
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = ""
|
||||
if band(mode, 1) ~= 0 then s = s.."P" end
|
||||
if band(mode, 2) ~= 0 then s = s.."F" end
|
||||
if band(mode, 4) ~= 0 then s = s.."T" end
|
||||
if band(mode, 8) ~= 0 then s = s.."C" end
|
||||
if band(mode, 16) ~= 0 then s = s.."R" end
|
||||
if band(mode, 32) ~= 0 then s = s.."I" end
|
||||
if band(mode, 64) ~= 0 then s = s.."K" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
|
||||
["CONV "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = irtype[band(mode, 31)]
|
||||
s = irtype[band(shr(mode, 5), 31)].."."..s
|
||||
if band(mode, 0x800) ~= 0 then s = s.." sext" end
|
||||
local c = shr(mode, 12)
|
||||
if c == 1 then s = s.." none"
|
||||
elseif c == 2 then s = s.." index"
|
||||
elseif c == 3 then s = s.." check" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["FLOAD "] = vmdef.irfield,
|
||||
["FREF "] = vmdef.irfield,
|
||||
["FPMATH"] = vmdef.irfpm,
|
||||
["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" },
|
||||
["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" },
|
||||
["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
|
||||
}
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
local function formatk(tr, idx, sn)
|
||||
local k, t, slot = tracek(tr, idx)
|
||||
local tn = type(k)
|
||||
local s
|
||||
if tn == "number" then
|
||||
if t < 12 then
|
||||
s = k == 0 and "NULL" or format("[0x%08x]", k)
|
||||
elseif band(sn or 0, 0x30000) ~= 0 then
|
||||
s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
|
||||
elseif k == 2^52+2^51 then
|
||||
s = "bias"
|
||||
else
|
||||
local m = tonumber("0x1p-1026")
|
||||
s = format(m ~= nil and 0 < k and k < m and "%+a" or "%+.14g", k)
|
||||
end
|
||||
elseif tn == "string" then
|
||||
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
|
||||
elseif tn == "function" then
|
||||
s = fmtfunc(k)
|
||||
elseif tn == "table" then
|
||||
s = format("{%p}", k)
|
||||
elseif tn == "userdata" then
|
||||
if t == 12 then
|
||||
s = format("userdata:%p", k)
|
||||
else
|
||||
s = format("[%p]", k)
|
||||
if s == "[NULL]" then s = "NULL" end
|
||||
end
|
||||
elseif t == 21 then -- int64_t
|
||||
s = sub(tostring(k), 1, -3)
|
||||
if sub(s, 1, 1) ~= "-" then s = "+"..s end
|
||||
elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL)
|
||||
return "----" -- Special case for LJ_FR2 slot 1.
|
||||
else
|
||||
s = tostring(k) -- For primitives.
|
||||
end
|
||||
s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0)
|
||||
if slot then
|
||||
s = format("%s @%d", s, slot)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function printsnap(tr, snap)
|
||||
local n = 2
|
||||
for s=0,snap[1]-1 do
|
||||
local sn = snap[n]
|
||||
if shr(sn, 24) == s then
|
||||
n = n + 1
|
||||
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
|
||||
if ref < 0 then
|
||||
out:write(formatk(tr, ref, sn))
|
||||
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
|
||||
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ref)
|
||||
out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0))
|
||||
end
|
||||
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
|
||||
else
|
||||
out:write("---- ")
|
||||
end
|
||||
end
|
||||
out:write("]\n")
|
||||
end
|
||||
|
||||
-- Dump snapshots (not interleaved with IR).
|
||||
local function dump_snap(tr)
|
||||
out:write("---- TRACE ", tr, " snapshots\n")
|
||||
for i=0,1000000000 do
|
||||
local snap = tracesnap(tr, i)
|
||||
if not snap then break end
|
||||
out:write(format("#%-3d %04d [ ", i, snap[0]))
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return a register name or stack slot for a rid/sp location.
|
||||
local function ridsp_name(ridsp, ins)
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
|
||||
if rid == 253 or rid == 254 then
|
||||
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
|
||||
end
|
||||
if ridsp > 255 then return format("[%x]", slot*4) end
|
||||
if rid < 128 then return disass.regname(rid) end
|
||||
return ""
|
||||
end
|
||||
|
||||
-- Dump CALL* function ref and return optional ctype.
|
||||
local function dumpcallfunc(tr, ins)
|
||||
local ctype
|
||||
if ins > 0 then
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
|
||||
ins = op1
|
||||
ctype = formatk(tr, op2)
|
||||
end
|
||||
end
|
||||
if ins < 0 then
|
||||
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
|
||||
else
|
||||
out:write(format("%04d (", ins))
|
||||
end
|
||||
return ctype
|
||||
end
|
||||
|
||||
-- Recursively gather CALL* args and dump them.
|
||||
local function dumpcallargs(tr, ins)
|
||||
if ins < 0 then
|
||||
out:write(formatk(tr, ins))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
local oidx = 6*shr(ot, 8)
|
||||
local op = sub(vmdef.irnames, oidx+1, oidx+6)
|
||||
if op == "CARG " then
|
||||
dumpcallargs(tr, op1)
|
||||
if op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(" ", format("%04d", op2))
|
||||
end
|
||||
else
|
||||
out:write(format("%04d", ins))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Dump IR and interleaved snapshots.
|
||||
local function dump_ir(tr, dumpsnap, dumpreg)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local nins = info.nins
|
||||
out:write("---- TRACE ", tr, " IR\n")
|
||||
local irnames = vmdef.irnames
|
||||
local snapref = 65536
|
||||
local snap, snapno
|
||||
if dumpsnap then
|
||||
snap = tracesnap(tr, 0)
|
||||
snapref = snap[0]
|
||||
snapno = 0
|
||||
end
|
||||
for ins=1,nins do
|
||||
if ins >= snapref then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
snapno = snapno + 1
|
||||
snap = tracesnap(tr, snapno)
|
||||
snapref = snap and snap[0] or 65536
|
||||
end
|
||||
local m, ot, op1, op2, ridsp = traceir(tr, ins)
|
||||
local oidx, t = 6*shr(ot, 8), band(ot, 31)
|
||||
local op = sub(irnames, oidx+1, oidx+6)
|
||||
if op == "LOOP " then
|
||||
if dumpreg then
|
||||
out:write(format("%04d ------------ LOOP ------------\n", ins))
|
||||
else
|
||||
out:write(format("%04d ------ LOOP ------------\n", ins))
|
||||
end
|
||||
elseif op ~= "NOP " and op ~= "CARG " and
|
||||
(dumpreg or op ~= "RENAME") then
|
||||
local rid = band(ridsp, 255)
|
||||
if dumpreg then
|
||||
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
|
||||
else
|
||||
out:write(format("%04d ", ins))
|
||||
end
|
||||
out:write(format("%s%s %s %s ",
|
||||
(rid == 254 or rid == 253) and "}" or
|
||||
(band(ot, 128) == 0 and " " or ">"),
|
||||
band(ot, 64) == 0 and " " or "+",
|
||||
irtype[t], op))
|
||||
local m1, m2 = band(m, 3), band(m, 3*4)
|
||||
if sub(op, 1, 4) == "CALL" then
|
||||
local ctype
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
out:write(format("%-10s (", vmdef.ircall[op2]))
|
||||
else
|
||||
ctype = dumpcallfunc(tr, op2)
|
||||
end
|
||||
if op1 ~= -1 then dumpcallargs(tr, op1) end
|
||||
out:write(")")
|
||||
if ctype then out:write(" ctype ", ctype) end
|
||||
elseif op == "CNEW " and op2 == -1 then
|
||||
out:write(formatk(tr, op1))
|
||||
elseif m1 ~= 3 then -- op1 != IRMnone
|
||||
if op1 < 0 then
|
||||
out:write(formatk(tr, op1))
|
||||
else
|
||||
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
|
||||
end
|
||||
if m2 ~= 3*4 then -- op2 != IRMnone
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
local litn = litname[op]
|
||||
if litn and litn[op2] then
|
||||
out:write(" ", litn[op2])
|
||||
elseif op == "UREFO " or op == "UREFC " then
|
||||
out:write(format(" #%-3d", shr(op2, 8)))
|
||||
else
|
||||
out:write(format(" #%-3d", op2))
|
||||
end
|
||||
elseif op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(format(" %04d", op2))
|
||||
end
|
||||
end
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
end
|
||||
if snap then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local recprefix = ""
|
||||
local recdepth = 0
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
local fmt = vmdef.traceerr[err]
|
||||
if fmt == "NYI: bytecode %s" then
|
||||
local oidx = 6 * info
|
||||
info = sub(vmdef.bcnames, oidx+1, oidx+6)
|
||||
end
|
||||
err = format(fmt, info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "stop" or (what == "abort" and dumpmode.a) then
|
||||
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
|
||||
elseif dumpmode.s then dump_snap(tr) end
|
||||
if dumpmode.m then dump_mcode(tr) end
|
||||
end
|
||||
if what == "start" then
|
||||
if dumpmode.H then out:write('<pre class="ljdump">\n') end
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
|
||||
out:write(" ", fmtfunc(func, pc), "\n")
|
||||
elseif what == "stop" or what == "abort" then
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
if what == "abort" then
|
||||
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
|
||||
else
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if link == tr or link == 0 then
|
||||
out:write(" -> ", ltype, "\n")
|
||||
elseif ltype == "root" then
|
||||
out:write(" -> ", link, "\n")
|
||||
else
|
||||
out:write(" -> ", link, " ", ltype, "\n")
|
||||
end
|
||||
end
|
||||
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
|
||||
else
|
||||
if what == "flush" then symtab, nexitsym = {}, 0 end
|
||||
out:write("---- TRACE ", what, "\n\n")
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
|
||||
-- Dump recorded bytecode.
|
||||
local function dump_record(tr, func, pc, depth)
|
||||
if depth ~= recdepth then
|
||||
recdepth = depth
|
||||
recprefix = rep(" .", depth)
|
||||
end
|
||||
local line
|
||||
if pc >= 0 then
|
||||
line = bcline(func, pc, recprefix)
|
||||
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
|
||||
else
|
||||
line = "0000 "..recprefix.." FUNCC \n"
|
||||
end
|
||||
if pc <= 0 then
|
||||
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
|
||||
else
|
||||
out:write(line)
|
||||
end
|
||||
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
|
||||
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local gpr64 = jit.arch:match("64")
|
||||
local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel"
|
||||
|
||||
-- Dump taken trace exits.
|
||||
local function dump_texit(tr, ex, ngpr, nfpr, ...)
|
||||
out:write("---- TRACE ", tr, " exit ", ex, "\n")
|
||||
if dumpmode.X then
|
||||
local regs = {...}
|
||||
if gpr64 then
|
||||
for i=1,ngpr do
|
||||
out:write(format(" %016x", regs[i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,ngpr do
|
||||
out:write(" ", tohex(regs[i]))
|
||||
if i % 8 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
if fprmips32 then
|
||||
for i=1,nfpr,2 do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 8 == 7 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,nfpr do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_texit)
|
||||
jit.attach(dump_record)
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(opt, outfile)
|
||||
if active then dumpoff() end
|
||||
|
||||
local term = os.getenv("TERM")
|
||||
local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
|
||||
if opt then
|
||||
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
|
||||
end
|
||||
|
||||
local m = { t=true, b=true, i=true, m=true, }
|
||||
if opt and opt ~= "" then
|
||||
local o = sub(opt, 1, 1)
|
||||
if o ~= "+" and o ~= "-" then m = {} end
|
||||
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
|
||||
end
|
||||
dumpmode = m
|
||||
|
||||
if m.t or m.b or m.i or m.s or m.m then
|
||||
jit.attach(dump_trace, "trace")
|
||||
end
|
||||
if m.b then
|
||||
jit.attach(dump_record, "record")
|
||||
if not bcline then bcline = require("jit.bc").line end
|
||||
end
|
||||
if m.x or m.X then
|
||||
jit.attach(dump_texit, "texit")
|
||||
end
|
||||
|
||||
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stdout
|
||||
end
|
||||
|
||||
m[colormode] = true
|
||||
if colormode == "A" then
|
||||
colorize = colorize_ansi
|
||||
irtype = irtype_ansi
|
||||
elseif colormode == "H" then
|
||||
colorize = colorize_html
|
||||
irtype = irtype_html
|
||||
out:write(header_html)
|
||||
else
|
||||
colorize = colorize_text
|
||||
irtype = irtype_text
|
||||
end
|
||||
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
on = dumpon,
|
||||
off = dumpoff,
|
||||
start = dumpon -- For -j command line option.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT profiler.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module is a simple command line interface to the built-in
|
||||
-- low-overhead profiler of LuaJIT.
|
||||
--
|
||||
-- The lower-level API of the profiler is accessible via the "jit.profile"
|
||||
-- module or the luaJIT_profile_* C API.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jp myapp.lua
|
||||
-- luajit -jp=s myapp.lua
|
||||
-- luajit -jp=-s myapp.lua
|
||||
-- luajit -jp=vl myapp.lua
|
||||
-- luajit -jp=G,profile.txt myapp.lua
|
||||
--
|
||||
-- The following dump features are available:
|
||||
--
|
||||
-- f Stack dump: function name, otherwise module:line. Default mode.
|
||||
-- F Stack dump: ditto, but always prepend module.
|
||||
-- l Stack dump: module:line.
|
||||
-- <number> stack dump depth (callee < caller). Default: 1.
|
||||
-- -<number> Inverse stack dump depth (caller > callee).
|
||||
-- s Split stack dump after first stack level. Implies abs(depth) >= 2.
|
||||
-- p Show full path for module names.
|
||||
-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv.
|
||||
-- z Show zones. Can be combined with stack dumps, e.g. zf or fz.
|
||||
-- r Show raw sample counts. Default: show percentages.
|
||||
-- a Annotate excerpts from source code files.
|
||||
-- A Annotate complete source code files.
|
||||
-- G Produce raw output suitable for graphical tools (e.g. flame graphs).
|
||||
-- m<number> Minimum sample percentage to be shown. Default: 3.
|
||||
-- i<number> Sampling interval in milliseconds. Default: 10.
|
||||
--
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local profile = require("jit.profile")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local math = math
|
||||
local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor
|
||||
local sort, format = table.sort, string.format
|
||||
local stdout = io.stdout
|
||||
local zone -- Load jit.zone module on demand.
|
||||
|
||||
-- Output file handle.
|
||||
local out
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local prof_ud
|
||||
local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth
|
||||
local prof_ann, prof_count1, prof_count2, prof_samples
|
||||
|
||||
local map_vmmode = {
|
||||
N = "Compiled",
|
||||
I = "Interpreted",
|
||||
C = "C code",
|
||||
G = "Garbage Collector",
|
||||
J = "JIT Compiler",
|
||||
}
|
||||
|
||||
-- Profiler callback.
|
||||
local function prof_cb(th, samples, vmmode)
|
||||
prof_samples = prof_samples + samples
|
||||
local key_stack, key_stack2, key_state
|
||||
-- Collect keys for sample.
|
||||
if prof_states then
|
||||
if prof_states == "v" then
|
||||
key_state = map_vmmode[vmmode] or vmmode
|
||||
else
|
||||
key_state = zone:get() or "(none)"
|
||||
end
|
||||
end
|
||||
if prof_fmt then
|
||||
key_stack = profile.dumpstack(th, prof_fmt, prof_depth)
|
||||
key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x)
|
||||
return vmdef.ffnames[tonumber(x)]
|
||||
end)
|
||||
if prof_split == 2 then
|
||||
local k1, k2 = key_stack:match("(.-) [<>] (.*)")
|
||||
if k2 then key_stack, key_stack2 = k1, k2 end
|
||||
elseif prof_split == 3 then
|
||||
key_stack2 = profile.dumpstack(th, "l", 1)
|
||||
end
|
||||
end
|
||||
-- Order keys.
|
||||
local k1, k2
|
||||
if prof_split == 1 then
|
||||
if key_state then
|
||||
k1 = key_state
|
||||
if key_stack then k2 = key_stack end
|
||||
end
|
||||
elseif key_stack then
|
||||
k1 = key_stack
|
||||
if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end
|
||||
end
|
||||
-- Coalesce samples in one or two levels.
|
||||
if k1 then
|
||||
local t1 = prof_count1
|
||||
t1[k1] = (t1[k1] or 0) + samples
|
||||
if k2 then
|
||||
local t2 = prof_count2
|
||||
local t3 = t2[k1]
|
||||
if not t3 then t3 = {}; t2[k1] = t3 end
|
||||
t3[k2] = (t3[k2] or 0) + samples
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Show top N list.
|
||||
local function prof_top(count1, count2, samples, indent)
|
||||
local t, n = {}, 0
|
||||
for k in pairs(count1) do
|
||||
n = n + 1
|
||||
t[n] = k
|
||||
end
|
||||
sort(t, function(a, b) return count1[a] > count1[b] end)
|
||||
for i=1,n do
|
||||
local k = t[i]
|
||||
local v = count1[k]
|
||||
local pct = floor(v*100/samples + 0.5)
|
||||
if pct < prof_min then break end
|
||||
if not prof_raw then
|
||||
out:write(format("%s%2d%% %s\n", indent, pct, k))
|
||||
elseif prof_raw == "r" then
|
||||
out:write(format("%s%5d %s\n", indent, v, k))
|
||||
else
|
||||
out:write(format("%s %d\n", k, v))
|
||||
end
|
||||
if count2 then
|
||||
local r = count2[k]
|
||||
if r then
|
||||
prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or
|
||||
(prof_depth < 0 and " -> " or " <- "))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Annotate source code
|
||||
local function prof_annotate(count1, samples)
|
||||
local files = {}
|
||||
local ms = 0
|
||||
for k, v in pairs(count1) do
|
||||
local pct = floor(v*100/samples + 0.5)
|
||||
ms = math.max(ms, v)
|
||||
if pct >= prof_min then
|
||||
local file, line = k:match("^(.*):(%d+)$")
|
||||
if not file then file = k; line = 0 end
|
||||
local fl = files[file]
|
||||
if not fl then fl = {}; files[file] = fl; files[#files+1] = file end
|
||||
line = tonumber(line)
|
||||
fl[line] = prof_raw and v or pct
|
||||
end
|
||||
end
|
||||
sort(files)
|
||||
local fmtv, fmtn = " %3d%% | %s\n", " | %s\n"
|
||||
if prof_raw then
|
||||
local n = math.max(5, math.ceil(math.log10(ms)))
|
||||
fmtv = "%"..n.."d | %s\n"
|
||||
fmtn = (" "):rep(n).." | %s\n"
|
||||
end
|
||||
local ann = prof_ann
|
||||
for _, file in ipairs(files) do
|
||||
local f0 = file:byte()
|
||||
if f0 == 40 or f0 == 91 then
|
||||
out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file))
|
||||
break
|
||||
end
|
||||
local fp, err = io.open(file)
|
||||
if not fp then
|
||||
out:write(format("====== ERROR: %s: %s\n", file, err))
|
||||
break
|
||||
end
|
||||
out:write(format("\n====== %s ======\n", file))
|
||||
local fl = files[file]
|
||||
local n, show = 1, false
|
||||
if ann ~= 0 then
|
||||
for i=1,ann do
|
||||
if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
|
||||
end
|
||||
end
|
||||
for line in fp:lines() do
|
||||
if line:byte() == 27 then
|
||||
out:write("[Cannot annotate bytecode file]\n")
|
||||
break
|
||||
end
|
||||
local v = fl[n]
|
||||
if ann ~= 0 then
|
||||
local v2 = fl[n+ann]
|
||||
if show then
|
||||
if v2 then show = n+ann elseif v then show = n
|
||||
elseif show+ann < n then show = false end
|
||||
elseif v2 then
|
||||
show = n+ann
|
||||
out:write(format("@@ %d @@\n", n))
|
||||
end
|
||||
if not show then goto next end
|
||||
end
|
||||
if v then
|
||||
out:write(format(fmtv, v, line))
|
||||
else
|
||||
out:write(format(fmtn, line))
|
||||
end
|
||||
::next::
|
||||
n = n + 1
|
||||
end
|
||||
fp:close()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Finish profiling and dump result.
|
||||
local function prof_finish()
|
||||
if prof_ud then
|
||||
profile.stop()
|
||||
local samples = prof_samples
|
||||
if samples == 0 then
|
||||
if prof_raw ~= true then out:write("[No samples collected]\n") end
|
||||
elseif prof_ann then
|
||||
prof_annotate(prof_count1, samples)
|
||||
else
|
||||
prof_top(prof_count1, prof_count2, samples, "")
|
||||
end
|
||||
prof_count1 = nil
|
||||
prof_count2 = nil
|
||||
prof_ud = nil
|
||||
if out ~= stdout then out:close() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Start profiling.
|
||||
local function prof_start(mode)
|
||||
local interval = ""
|
||||
mode = mode:gsub("i%d*", function(s) interval = s; return "" end)
|
||||
prof_min = 3
|
||||
mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end)
|
||||
prof_depth = 1
|
||||
mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end)
|
||||
local m = {}
|
||||
for c in mode:gmatch(".") do m[c] = c end
|
||||
prof_states = m.z or m.v
|
||||
if prof_states == "z" then zone = require("jit.zone") end
|
||||
local scope = m.l or m.f or m.F or (prof_states and "" or "f")
|
||||
local flags = (m.p or "")
|
||||
prof_raw = m.r
|
||||
if m.s then
|
||||
prof_split = 2
|
||||
if prof_depth == -1 or m["-"] then prof_depth = -2
|
||||
elseif prof_depth == 1 then prof_depth = 2 end
|
||||
elseif mode:find("[fF].*l") then
|
||||
scope = "l"
|
||||
prof_split = 3
|
||||
else
|
||||
prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0
|
||||
end
|
||||
prof_ann = m.A and 0 or (m.a and 3)
|
||||
if prof_ann then
|
||||
scope = "l"
|
||||
prof_fmt = "pl"
|
||||
prof_split = 0
|
||||
prof_depth = 1
|
||||
elseif m.G and scope ~= "" then
|
||||
prof_fmt = flags..scope.."Z;"
|
||||
prof_depth = -100
|
||||
prof_raw = true
|
||||
prof_min = 0
|
||||
elseif scope == "" then
|
||||
prof_fmt = false
|
||||
else
|
||||
local sc = prof_split == 3 and m.f or m.F or scope
|
||||
prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ")
|
||||
end
|
||||
prof_count1 = {}
|
||||
prof_count2 = {}
|
||||
prof_samples = 0
|
||||
profile.start(scope:lower()..interval, prof_cb)
|
||||
prof_ud = newproxy(true)
|
||||
getmetatable(prof_ud).__gc = prof_finish
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function start(mode, outfile)
|
||||
if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stdout
|
||||
end
|
||||
prof_start(mode or "f")
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
start = start, -- For -j command line option.
|
||||
stop = prof_finish
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Verbose mode of the LuaJIT compiler.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module shows verbose information about the progress of the
|
||||
-- JIT compiler. It prints one line for each generated trace. This module
|
||||
-- is useful to see which code has been compiled or where the compiler
|
||||
-- punts and falls back to the interpreter.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
|
||||
-- luajit -jv=myapp.out myapp.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- The output from the first example should look like this:
|
||||
--
|
||||
-- [TRACE 1 (command line):1 loop]
|
||||
-- [TRACE 2 (1/3) (command line):1 -> 1]
|
||||
--
|
||||
-- The first number in each line is the internal trace number. Next are
|
||||
-- the file name ('(command line)') and the line number (':1') where the
|
||||
-- trace has started. Side traces also show the parent trace number and
|
||||
-- the exit number where they are attached to in parentheses ('(1/3)').
|
||||
-- An arrow at the end shows where the trace links to ('-> 1'), unless
|
||||
-- it loops to itself.
|
||||
--
|
||||
-- In this case the inner loop gets hot and is traced first, generating
|
||||
-- a root trace. Then the last exit from the 1st trace gets hot, too,
|
||||
-- and triggers generation of the 2nd trace. The side trace follows the
|
||||
-- path along the outer loop and *around* the inner loop, back to its
|
||||
-- start, and then links to the 1st trace. Yes, this may seem unusual,
|
||||
-- if you know how traditional compilers work. Trace compilers are full
|
||||
-- of surprises like this -- have fun! :-)
|
||||
--
|
||||
-- Aborted traces are shown like this:
|
||||
--
|
||||
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
|
||||
--
|
||||
-- Don't worry -- trace aborts are quite common, even in programs which
|
||||
-- can be fully compiled. The compiler may retry several times until it
|
||||
-- finds a suitable trace.
|
||||
--
|
||||
-- Of course this doesn't work with features that are not-yet-implemented
|
||||
-- (NYI error messages). The VM simply falls back to the interpreter. This
|
||||
-- may not matter at all if the particular trace is not very high up in
|
||||
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
|
||||
--
|
||||
-- Also check out the -jdump module, which prints all the gory details.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
|
||||
local type, sub, format = type, string.sub, string.format
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local startloc, startex
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
local fmt = vmdef.traceerr[err]
|
||||
if fmt == "NYI: bytecode %s" then
|
||||
local oidx = 6 * info
|
||||
info = sub(vmdef.bcnames, oidx+1, oidx+6)
|
||||
end
|
||||
err = format(fmt, info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "start" then
|
||||
startloc = fmtfunc(func, pc)
|
||||
startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or ""
|
||||
else
|
||||
if what == "abort" then
|
||||
local loc = fmtfunc(func, pc)
|
||||
if loc ~= startloc then
|
||||
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
|
||||
startex, startloc, fmterr(otr, oex), loc))
|
||||
else
|
||||
out:write(format("[TRACE --- %s%s -- %s]\n",
|
||||
startex, startloc, fmterr(otr, oex)))
|
||||
end
|
||||
elseif what == "stop" then
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if ltype == "interpreter" then
|
||||
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
|
||||
tr, startex, startloc))
|
||||
elseif ltype == "stitch" then
|
||||
out:write(format("[TRACE %3s %s%s %s %s]\n",
|
||||
tr, startex, startloc, ltype, fmtfunc(func, pc)))
|
||||
elseif link == tr or link == 0 then
|
||||
out:write(format("[TRACE %3s %s%s %s]\n",
|
||||
tr, startex, startloc, ltype))
|
||||
elseif ltype == "root" then
|
||||
out:write(format("[TRACE %3s %s%s -> %d]\n",
|
||||
tr, startex, startloc, link))
|
||||
else
|
||||
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
|
||||
tr, startex, startloc, link, ltype))
|
||||
end
|
||||
else
|
||||
out:write(format("[TRACE %s]\n", what))
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(outfile)
|
||||
if active then dumpoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(dump_trace, "trace")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
return {
|
||||
on = dumpon,
|
||||
off = dumpoff,
|
||||
start = dumpon -- For -j command line option.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT profiler zones.
|
||||
--
|
||||
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module implements a simple hierarchical zone model.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- local zone = require("jit.zone")
|
||||
-- zone("AI")
|
||||
-- ...
|
||||
-- zone("A*")
|
||||
-- ...
|
||||
-- print(zone:get()) --> "A*"
|
||||
-- ...
|
||||
-- zone()
|
||||
-- ...
|
||||
-- print(zone:get()) --> "AI"
|
||||
-- ...
|
||||
-- zone()
|
||||
--
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local remove = table.remove
|
||||
|
||||
return setmetatable({
|
||||
flush = function(t)
|
||||
for i=#t,1,-1 do t[i] = nil end
|
||||
end,
|
||||
get = function(t)
|
||||
return t[#t]
|
||||
end
|
||||
}, {
|
||||
__call = function(t, zone)
|
||||
if zone then
|
||||
t[#t+1] = zone
|
||||
else
|
||||
return (assert(remove(t), "empty zone stack"))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
|
||||
** Auxiliary functions for building Lua libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lauxlib_h
|
||||
#define lauxlib_h
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* extra error code for `luaL_load' */
|
||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||
|
||||
typedef struct luaL_Reg {
|
||||
const char *name;
|
||||
lua_CFunction func;
|
||||
} luaL_Reg;
|
||||
|
||||
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup);
|
||||
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l);
|
||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
|
||||
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
|
||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
|
||||
size_t *l);
|
||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
|
||||
const char *def, size_t *l);
|
||||
#ifdef LUAJIT_INTONLY
|
||||
#define luaL_checknumber luaL_checkinteger
|
||||
#define luaL_optnumber luaL_optinteger
|
||||
#else
|
||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
|
||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
|
||||
#endif
|
||||
|
||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
|
||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
|
||||
lua_Integer def);
|
||||
|
||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
|
||||
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
|
||||
|
||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||
|
||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
||||
|
||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
|
||||
const char *const lst[]);
|
||||
|
||||
/* pre-defined references */
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
|
||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
||||
|
||||
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
|
||||
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name);
|
||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||
|
||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
|
||||
const char *r);
|
||||
|
||||
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
|
||||
const char *fname, int szhint);
|
||||
|
||||
/* From Lua 5.2. */
|
||||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname);
|
||||
LUALIB_API int luaL_execresult(lua_State *L, int stat);
|
||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||
const char *mode);
|
||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name, const char *mode);
|
||||
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
|
||||
int level);
|
||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||
LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname,
|
||||
int sizehint);
|
||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
||||
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** some useful macros
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
#define luaL_argcheck(L, cond,numarg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||
|
||||
#define luaL_dofile(L, fn) \
|
||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_dostring(L, s) \
|
||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
|
||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||
|
||||
/* From Lua 5.2. */
|
||||
#define luaL_newlibtable(L, l) \
|
||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0))
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
typedef struct luaL_Buffer {
|
||||
char *p; /* current position in buffer */
|
||||
int lvl; /* number of strings in the stack (level) */
|
||||
lua_State *L;
|
||||
char buffer[LUAL_BUFFERSIZE];
|
||||
} luaL_Buffer;
|
||||
|
||||
#define luaL_addchar(B,c) \
|
||||
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
|
||||
(*(B)->p++ = (char)(c)))
|
||||
|
||||
/* compatibility only */
|
||||
#define luaL_putchar(B,c) luaL_addchar(B,c)
|
||||
|
||||
#define luaL_addsize(B,n) ((B)->p += (n))
|
||||
|
||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/* compatibility with ref system */
|
||||
|
||||
/* pre-defined references */
|
||||
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
|
||||
(lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
|
||||
|
||||
#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
|
||||
|
||||
#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
|
||||
|
||||
#define luaL_reg luaL_Reg
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
** Auxiliary library for the Lua/C API.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major parts taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_aux_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_lib.h"
|
||||
#include "lj_vmevent.h"
|
||||
|
||||
#if LJ_TARGET_POSIX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
/* -- I/O error handling -------------------------------------------------- */
|
||||
|
||||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname)
|
||||
{
|
||||
if (stat) {
|
||||
setboolV(L->top++, 1);
|
||||
return 1;
|
||||
} else {
|
||||
int en = errno; /* Lua API calls may change this value. */
|
||||
setnilV(L->top++);
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, strerror(en));
|
||||
else
|
||||
lua_pushfstring(L, "%s", strerror(en));
|
||||
setintV(L->top++, en);
|
||||
lj_trace_abort(G(L));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_execresult(lua_State *L, int stat)
|
||||
{
|
||||
if (stat != -1) {
|
||||
#if LJ_TARGET_POSIX
|
||||
if (WIFSIGNALED(stat)) {
|
||||
stat = WTERMSIG(stat);
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "signal");
|
||||
} else {
|
||||
if (WIFEXITED(stat))
|
||||
stat = WEXITSTATUS(stat);
|
||||
if (stat == 0)
|
||||
setboolV(L->top++, 1);
|
||||
else
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "exit");
|
||||
}
|
||||
#else
|
||||
if (stat == 0)
|
||||
setboolV(L->top++, 1);
|
||||
else
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "exit");
|
||||
#endif
|
||||
setintV(L->top++, stat);
|
||||
return 3;
|
||||
}
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
}
|
||||
|
||||
/* -- Module registration ------------------------------------------------- */
|
||||
|
||||
LUALIB_API const char *luaL_findtable(lua_State *L, int idx,
|
||||
const char *fname, int szhint)
|
||||
{
|
||||
const char *e;
|
||||
lua_pushvalue(L, idx);
|
||||
do {
|
||||
e = strchr(fname, '.');
|
||||
if (e == NULL) e = fname + strlen(fname);
|
||||
lua_pushlstring(L, fname, (size_t)(e - fname));
|
||||
lua_rawget(L, -2);
|
||||
if (lua_isnil(L, -1)) { /* no such field? */
|
||||
lua_pop(L, 1); /* remove this nil */
|
||||
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
|
||||
lua_pushlstring(L, fname, (size_t)(e - fname));
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -4); /* set new table into field */
|
||||
} else if (!lua_istable(L, -1)) { /* field has a non-table value? */
|
||||
lua_pop(L, 2); /* remove table and value */
|
||||
return fname; /* return problematic part of the name */
|
||||
}
|
||||
lua_remove(L, -2); /* remove previous table */
|
||||
fname = e + 1;
|
||||
} while (*e == '.');
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int libsize(const luaL_Reg *l)
|
||||
{
|
||||
int size = 0;
|
||||
for (; l && l->name; l++) size++;
|
||||
return size;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_pushmodule(lua_State *L, const char *modname, int sizehint)
|
||||
{
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
|
||||
lua_getfield(L, -1, modname);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, sizehint) != NULL)
|
||||
lj_err_callerv(L, LJ_ERR_BADMODN, modname);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, modname); /* _LOADED[modname] = new table. */
|
||||
}
|
||||
lua_remove(L, -2); /* Remove _LOADED table. */
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_openlib(lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup)
|
||||
{
|
||||
lj_lib_checkfpu(L);
|
||||
if (libname) {
|
||||
luaL_pushmodule(L, libname, libsize(l));
|
||||
lua_insert(L, -(nup + 1)); /* Move module table below upvalues. */
|
||||
}
|
||||
if (l)
|
||||
luaL_setfuncs(L, l, nup);
|
||||
else
|
||||
lua_pop(L, nup); /* Remove upvalues. */
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_register(lua_State *L, const char *libname,
|
||||
const luaL_Reg *l)
|
||||
{
|
||||
luaL_openlib(L, libname, l, 0);
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
|
||||
{
|
||||
luaL_checkstack(L, nup, "too many upvalues");
|
||||
for (; l->name; l++) {
|
||||
int i;
|
||||
for (i = 0; i < nup; i++) /* Copy upvalues to the top. */
|
||||
lua_pushvalue(L, -nup);
|
||||
lua_pushcclosure(L, l->func, nup);
|
||||
lua_setfield(L, -(nup + 2), l->name);
|
||||
}
|
||||
lua_pop(L, nup); /* Remove upvalues. */
|
||||
}
|
||||
|
||||
LUALIB_API const char *luaL_gsub(lua_State *L, const char *s,
|
||||
const char *p, const char *r)
|
||||
{
|
||||
const char *wild;
|
||||
size_t l = strlen(p);
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
while ((wild = strstr(s, p)) != NULL) {
|
||||
luaL_addlstring(&b, s, (size_t)(wild - s)); /* push prefix */
|
||||
luaL_addstring(&b, r); /* push replacement in place of pattern */
|
||||
s = wild + l; /* continue after `p' */
|
||||
}
|
||||
luaL_addstring(&b, s); /* push last suffix */
|
||||
luaL_pushresult(&b);
|
||||
return lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
/* -- Buffer handling ----------------------------------------------------- */
|
||||
|
||||
#define bufflen(B) ((size_t)((B)->p - (B)->buffer))
|
||||
#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
|
||||
|
||||
static int emptybuffer(luaL_Buffer *B)
|
||||
{
|
||||
size_t l = bufflen(B);
|
||||
if (l == 0)
|
||||
return 0; /* put nothing on stack */
|
||||
lua_pushlstring(B->L, B->buffer, l);
|
||||
B->p = B->buffer;
|
||||
B->lvl++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void adjuststack(luaL_Buffer *B)
|
||||
{
|
||||
if (B->lvl > 1) {
|
||||
lua_State *L = B->L;
|
||||
int toget = 1; /* number of levels to concat */
|
||||
size_t toplen = lua_strlen(L, -1);
|
||||
do {
|
||||
size_t l = lua_strlen(L, -(toget+1));
|
||||
if (!(B->lvl - toget + 1 >= LUA_MINSTACK/2 || toplen > l))
|
||||
break;
|
||||
toplen += l;
|
||||
toget++;
|
||||
} while (toget < B->lvl);
|
||||
lua_concat(L, toget);
|
||||
B->lvl = B->lvl - toget + 1;
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API char *luaL_prepbuffer(luaL_Buffer *B)
|
||||
{
|
||||
if (emptybuffer(B))
|
||||
adjuststack(B);
|
||||
return B->buffer;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l)
|
||||
{
|
||||
if (l <= bufffree(B)) {
|
||||
memcpy(B->p, s, l);
|
||||
B->p += l;
|
||||
} else {
|
||||
emptybuffer(B);
|
||||
lua_pushlstring(B->L, s, l);
|
||||
B->lvl++;
|
||||
adjuststack(B);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addstring(luaL_Buffer *B, const char *s)
|
||||
{
|
||||
luaL_addlstring(B, s, strlen(s));
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_pushresult(luaL_Buffer *B)
|
||||
{
|
||||
emptybuffer(B);
|
||||
lua_concat(B->L, B->lvl);
|
||||
B->lvl = 1;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_addvalue(luaL_Buffer *B)
|
||||
{
|
||||
lua_State *L = B->L;
|
||||
size_t vl;
|
||||
const char *s = lua_tolstring(L, -1, &vl);
|
||||
if (vl <= bufffree(B)) { /* fit into buffer? */
|
||||
memcpy(B->p, s, vl); /* put it there */
|
||||
B->p += vl;
|
||||
lua_pop(L, 1); /* remove from stack */
|
||||
} else {
|
||||
if (emptybuffer(B))
|
||||
lua_insert(L, -2); /* put buffer before new value */
|
||||
B->lvl++; /* add new value into B stack */
|
||||
adjuststack(B);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B)
|
||||
{
|
||||
B->L = L;
|
||||
B->p = B->buffer;
|
||||
B->lvl = 0;
|
||||
}
|
||||
|
||||
/* -- Reference management ------------------------------------------------ */
|
||||
|
||||
#define FREELIST_REF 0
|
||||
|
||||
/* Convert a stack index to an absolute index. */
|
||||
#define abs_index(L, i) \
|
||||
((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||
|
||||
LUALIB_API int luaL_ref(lua_State *L, int t)
|
||||
{
|
||||
int ref;
|
||||
t = abs_index(L, t);
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1); /* remove from stack */
|
||||
return LUA_REFNIL; /* `nil' has a unique fixed reference */
|
||||
}
|
||||
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
|
||||
lua_pop(L, 1); /* remove it from stack */
|
||||
if (ref != 0) { /* any free element? */
|
||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
|
||||
} else { /* no free elements */
|
||||
ref = (int)lua_objlen(L, t);
|
||||
ref++; /* create new reference */
|
||||
}
|
||||
lua_rawseti(L, t, ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_unref(lua_State *L, int t, int ref)
|
||||
{
|
||||
if (ref >= 0) {
|
||||
t = abs_index(L, t);
|
||||
lua_rawgeti(L, t, FREELIST_REF);
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
|
||||
lua_pushinteger(L, ref);
|
||||
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Default allocator and panic function -------------------------------- */
|
||||
|
||||
static int panic(lua_State *L)
|
||||
{
|
||||
const char *s = lua_tostring(L, -1);
|
||||
fputs("PANIC: unprotected error in call to Lua API (", stderr);
|
||||
fputs(s ? s : "?", stderr);
|
||||
fputc(')', stderr); fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef LUAJIT_DISABLE_VMEVENT
|
||||
static int error_finalizer(lua_State *L)
|
||||
{
|
||||
const char *s = lua_tostring(L, -1);
|
||||
fputs("ERROR in finalizer: ", stderr);
|
||||
fputs(s ? s : "?", stderr);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LUAJIT_USE_SYSMALLOC
|
||||
|
||||
#if LJ_64 && !LJ_GC64 && !defined(LUAJIT_USE_VALGRIND)
|
||||
#error "Must use builtin allocator for 64 bit target"
|
||||
#endif
|
||||
|
||||
static void *mem_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
|
||||
{
|
||||
(void)ud;
|
||||
(void)osize;
|
||||
if (nsize == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
return realloc(ptr, nsize);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate(void)
|
||||
{
|
||||
lua_State *L = lua_newstate(mem_alloc, NULL);
|
||||
if (L) {
|
||||
G(L)->panic = panic;
|
||||
#ifndef LUAJIT_DISABLE_VMEVENT
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
|
||||
lua_pushcfunction(L, error_finalizer);
|
||||
lua_rawseti(L, -2, VMEVENT_HASH(LJ_VMEVENT_ERRFIN));
|
||||
G(L)->vmevmask = VMEVENT_MASK(LJ_VMEVENT_ERRFIN);
|
||||
L->top--;
|
||||
#endif
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate(void)
|
||||
{
|
||||
lua_State *L;
|
||||
#if LJ_64 && !LJ_GC64
|
||||
L = lj_state_newstate(LJ_ALLOCF_INTERNAL, NULL);
|
||||
#else
|
||||
L = lua_newstate(LJ_ALLOCF_INTERNAL, NULL);
|
||||
#endif
|
||||
if (L) {
|
||||
G(L)->panic = panic;
|
||||
#ifndef LUAJIT_DISABLE_VMEVENT
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
|
||||
lua_pushcfunction(L, error_finalizer);
|
||||
lua_rawseti(L, -2, VMEVENT_HASH(LJ_VMEVENT_ERRFIN));
|
||||
G(L)->vmevmask = VMEVENT_MASK(LJ_VMEVENT_ERRFIN);
|
||||
L->top--;
|
||||
#endif
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
#if LJ_64 && !LJ_GC64
|
||||
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
|
||||
{
|
||||
UNUSED(f); UNUSED(ud);
|
||||
fputs("Must use luaL_newstate() for 64 bit target\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
** Base and coroutine library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_base_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#endif
|
||||
#include "lj_bc.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_char.h"
|
||||
#include "lj_strscan.h"
|
||||
#include "lj_strfmt.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* -- Base library: checks ------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_base
|
||||
|
||||
LJLIB_ASM(assert) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
if (L->top == L->base+1)
|
||||
lj_err_caller(L, LJ_ERR_ASSERT);
|
||||
else if (tvisstr(L->base+1) || tvisnumber(L->base+1))
|
||||
lj_err_callermsg(L, strdata(lj_lib_checkstr(L, 2)));
|
||||
else
|
||||
lj_err_run(L);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
/* ORDER LJ_T */
|
||||
LJLIB_PUSH("nil")
|
||||
LJLIB_PUSH("boolean")
|
||||
LJLIB_PUSH(top-1) /* boolean */
|
||||
LJLIB_PUSH("userdata")
|
||||
LJLIB_PUSH("string")
|
||||
LJLIB_PUSH("upval")
|
||||
LJLIB_PUSH("thread")
|
||||
LJLIB_PUSH("proto")
|
||||
LJLIB_PUSH("function")
|
||||
LJLIB_PUSH("trace")
|
||||
LJLIB_PUSH("cdata")
|
||||
LJLIB_PUSH("table")
|
||||
LJLIB_PUSH(top-9) /* userdata */
|
||||
LJLIB_PUSH("number")
|
||||
LJLIB_ASM_(type) LJLIB_REC(.)
|
||||
/* Recycle the lj_lib_checkany(L, 1) from assert. */
|
||||
|
||||
/* -- Base library: iterators --------------------------------------------- */
|
||||
|
||||
/* This solves a circular dependency problem -- change FF_next_N as needed. */
|
||||
LJ_STATIC_ASSERT((int)FF_next == FF_next_N);
|
||||
|
||||
LJLIB_ASM(next) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_err_msg(L, LJ_ERR_NEXTIDX);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
#if LJ_52 || LJ_HASFFI
|
||||
static int ffh_pairs(lua_State *L, MMS mm)
|
||||
{
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
cTValue *mo = lj_meta_lookup(L, o, mm);
|
||||
if ((LJ_52 || tviscdata(o)) && !tvisnil(mo)) {
|
||||
L->top = o+1; /* Only keep one argument. */
|
||||
copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */
|
||||
return FFH_TAILCALL;
|
||||
} else {
|
||||
if (!tvistab(o)) lj_err_argt(L, 1, LUA_TTABLE);
|
||||
if (LJ_FR2) { copyTV(L, o-1, o); o--; }
|
||||
setfuncV(L, o-1, funcV(lj_lib_upvalue(L, 1)));
|
||||
if (mm == MM_pairs) setnilV(o+1); else setintV(o+1, 0);
|
||||
return FFH_RES(3);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define ffh_pairs(L, mm) (lj_lib_checktab(L, 1), FFH_UNREACHABLE)
|
||||
#endif
|
||||
|
||||
LJLIB_PUSH(lastcl)
|
||||
LJLIB_ASM(pairs) LJLIB_REC(xpairs 0)
|
||||
{
|
||||
return ffh_pairs(L, MM_pairs);
|
||||
}
|
||||
|
||||
LJLIB_NOREGUV LJLIB_ASM(ipairs_aux) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkint(L, 2);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(lastcl)
|
||||
LJLIB_ASM(ipairs) LJLIB_REC(xpairs 1)
|
||||
{
|
||||
return ffh_pairs(L, MM_ipairs);
|
||||
}
|
||||
|
||||
/* -- Base library: getters and setters ----------------------------------- */
|
||||
|
||||
LJLIB_ASM_(getmetatable) LJLIB_REC(.)
|
||||
/* Recycle the lj_lib_checkany(L, 1) from assert. */
|
||||
|
||||
LJLIB_ASM(setmetatable) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCtab *mt = lj_lib_checktabornil(L, 2);
|
||||
if (!tvisnil(lj_meta_lookup(L, L->base, MM_metatable)))
|
||||
lj_err_caller(L, LJ_ERR_PROTMT);
|
||||
setgcref(t->metatable, obj2gco(mt));
|
||||
if (mt) { lj_gc_objbarriert(L, t, mt); }
|
||||
settabV(L, L->base-1-LJ_FR2, t);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_CF(getfenv) LJLIB_REC(.)
|
||||
{
|
||||
GCfunc *fn;
|
||||
cTValue *o = L->base;
|
||||
if (!(o < L->top && tvisfunc(o))) {
|
||||
int level = lj_lib_optint(L, 1, 1);
|
||||
o = lj_debug_frame(L, level, &level);
|
||||
if (o == NULL)
|
||||
lj_err_arg(L, 1, LJ_ERR_INVLVL);
|
||||
if (LJ_FR2) o--;
|
||||
}
|
||||
fn = &gcval(o)->fn;
|
||||
settabV(L, L->top++, isluafunc(fn) ? tabref(fn->l.env) : tabref(L->env));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(setfenv)
|
||||
{
|
||||
GCfunc *fn;
|
||||
GCtab *t = lj_lib_checktab(L, 2);
|
||||
cTValue *o = L->base;
|
||||
if (!(o < L->top && tvisfunc(o))) {
|
||||
int level = lj_lib_checkint(L, 1);
|
||||
if (level == 0) {
|
||||
/* NOBARRIER: A thread (i.e. L) is never black. */
|
||||
setgcref(L->env, obj2gco(t));
|
||||
return 0;
|
||||
}
|
||||
o = lj_debug_frame(L, level, &level);
|
||||
if (o == NULL)
|
||||
lj_err_arg(L, 1, LJ_ERR_INVLVL);
|
||||
if (LJ_FR2) o--;
|
||||
}
|
||||
fn = &gcval(o)->fn;
|
||||
if (!isluafunc(fn))
|
||||
lj_err_caller(L, LJ_ERR_SETFENV);
|
||||
setgcref(fn->l.env, obj2gco(t));
|
||||
lj_gc_objbarrier(L, obj2gco(fn), t);
|
||||
setfuncV(L, L->top++, fn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_ASM(rawget) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkany(L, 2);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
LJLIB_CF(rawset) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkany(L, 2);
|
||||
L->top = 1+lj_lib_checkany(L, 3);
|
||||
lua_rawset(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(rawequal) LJLIB_REC(.)
|
||||
{
|
||||
cTValue *o1 = lj_lib_checkany(L, 1);
|
||||
cTValue *o2 = lj_lib_checkany(L, 2);
|
||||
setboolV(L->top-1, lj_obj_equal(o1, o2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_CF(rawlen) LJLIB_REC(.)
|
||||
{
|
||||
cTValue *o = L->base;
|
||||
int32_t len;
|
||||
if (L->top > o && tvisstr(o))
|
||||
len = (int32_t)strV(o)->len;
|
||||
else
|
||||
len = (int32_t)lj_tab_len(lj_lib_checktab(L, 1));
|
||||
setintV(L->top-1, len);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(unpack)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n, i = lj_lib_optint(L, 2, 1);
|
||||
int32_t e = (L->base+3-1 < L->top && !tvisnil(L->base+3-1)) ?
|
||||
lj_lib_checkint(L, 3) : (int32_t)lj_tab_len(t);
|
||||
uint32_t nu;
|
||||
if (i > e) return 0;
|
||||
nu = (uint32_t)e - (uint32_t)i;
|
||||
n = (int32_t)(nu+1);
|
||||
if (nu >= LUAI_MAXCSTACK || !lua_checkstack(L, n))
|
||||
lj_err_caller(L, LJ_ERR_UNPACK);
|
||||
do {
|
||||
cTValue *tv = lj_tab_getint(t, i);
|
||||
if (tv) {
|
||||
copyTV(L, L->top++, tv);
|
||||
} else {
|
||||
setnilV(L->top++);
|
||||
}
|
||||
} while (i++ < e);
|
||||
return n;
|
||||
}
|
||||
|
||||
LJLIB_CF(select) LJLIB_REC(.)
|
||||
{
|
||||
int32_t n = (int32_t)(L->top - L->base);
|
||||
if (n >= 1 && tvisstr(L->base) && *strVdata(L->base) == '#') {
|
||||
setintV(L->top-1, n-1);
|
||||
return 1;
|
||||
} else {
|
||||
int32_t i = lj_lib_checkint(L, 1);
|
||||
if (i < 0) i = n + i; else if (i > n) i = n;
|
||||
if (i < 1)
|
||||
lj_err_arg(L, 1, LJ_ERR_IDXRNG);
|
||||
return n - i;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Base library: conversions ------------------------------------------- */
|
||||
|
||||
LJLIB_ASM(tonumber) LJLIB_REC(.)
|
||||
{
|
||||
int32_t base = lj_lib_optint(L, 2, 10);
|
||||
if (base == 10) {
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
if (lj_strscan_numberobj(o)) {
|
||||
copyTV(L, L->base-1-LJ_FR2, o);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
#if LJ_HASFFI
|
||||
if (tviscdata(o)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = lj_ctype_rawref(cts, cdataV(o)->ctypeid);
|
||||
if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
|
||||
if (ctype_isnum(ct->info) || ctype_iscomplex(ct->info)) {
|
||||
if (LJ_DUALNUM && ctype_isinteger_or_bool(ct->info) &&
|
||||
ct->size <= 4 && !(ct->size == 4 && (ct->info & CTF_UNSIGNED))) {
|
||||
int32_t i;
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, 0);
|
||||
setintV(L->base-1-LJ_FR2, i);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_DOUBLE),
|
||||
(uint8_t *)&(L->base-1-LJ_FR2)->n, o, 0);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
const char *p = strdata(lj_lib_checkstr(L, 1));
|
||||
char *ep;
|
||||
unsigned int neg = 0;
|
||||
unsigned long ul;
|
||||
if (base < 2 || base > 36)
|
||||
lj_err_arg(L, 2, LJ_ERR_BASERNG);
|
||||
while (lj_char_isspace((unsigned char)(*p))) p++;
|
||||
if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; }
|
||||
if (lj_char_isalnum((unsigned char)(*p))) {
|
||||
ul = strtoul(p, &ep, base);
|
||||
if (p != ep) {
|
||||
while (lj_char_isspace((unsigned char)(*ep))) ep++;
|
||||
if (*ep == '\0') {
|
||||
if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u+neg)) {
|
||||
if (neg) ul = ~ul+1u;
|
||||
setintV(L->base-1-LJ_FR2, (int32_t)ul);
|
||||
} else {
|
||||
lua_Number n = (lua_Number)ul;
|
||||
if (neg) n = -n;
|
||||
setnumV(L->base-1-LJ_FR2, n);
|
||||
}
|
||||
return FFH_RES(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setnilV(L->base-1-LJ_FR2);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_ASM(tostring) LJLIB_REC(.)
|
||||
{
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
cTValue *mo;
|
||||
L->top = o+1; /* Only keep one argument. */
|
||||
if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
|
||||
copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */
|
||||
return FFH_TAILCALL;
|
||||
}
|
||||
lj_gc_check(L);
|
||||
setstrV(L, L->base-1-LJ_FR2, lj_strfmt_obj(L, L->base));
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
/* -- Base library: throw and catch errors -------------------------------- */
|
||||
|
||||
LJLIB_CF(error)
|
||||
{
|
||||
int32_t level = lj_lib_optint(L, 2, 1);
|
||||
lua_settop(L, 1);
|
||||
if (lua_isstring(L, 1) && level > 0) {
|
||||
luaL_where(L, level);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
LJLIB_ASM(pcall) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
lj_lib_checkfunc(L, 2); /* For xpcall only. */
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
LJLIB_ASM_(xpcall) LJLIB_REC(.)
|
||||
|
||||
/* -- Base library: load Lua code ----------------------------------------- */
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
static int load_aux(lua_State *L, int status, int envarg)
|
||||
{
|
||||
if (status == LUA_OK) {
|
||||
/*
|
||||
** Set environment table for top-level function.
|
||||
** Don't do this for non-native bytecode, which returns a prototype.
|
||||
*/
|
||||
if (tvistab(L->base+envarg-1) && tvisfunc(L->top-1)) {
|
||||
GCfunc *fn = funcV(L->top-1);
|
||||
GCtab *t = tabV(L->base+envarg-1);
|
||||
setgcref(fn->c.env, obj2gco(t));
|
||||
lj_gc_objbarrier(L, fn, t);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
setnilV(L->top-2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(loadfile)
|
||||
{
|
||||
GCstr *fname = lj_lib_optstr(L, 1);
|
||||
GCstr *mode = lj_lib_optstr(L, 2);
|
||||
int status;
|
||||
lua_settop(L, 3); /* Ensure env arg exists. */
|
||||
status = luaL_loadfilex(L, fname ? strdata(fname) : NULL,
|
||||
mode ? strdata(mode) : NULL);
|
||||
return load_aux(L, status, 3);
|
||||
}
|
||||
|
||||
static const char *reader_func(lua_State *L, void *ud, size_t *size)
|
||||
{
|
||||
UNUSED(ud);
|
||||
luaL_checkstack(L, 2, "too many nested functions");
|
||||
copyTV(L, L->top++, L->base);
|
||||
lua_call(L, 0, 1); /* Call user-supplied function. */
|
||||
L->top--;
|
||||
if (tvisnil(L->top)) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
} else if (tvisstr(L->top) || tvisnumber(L->top)) {
|
||||
copyTV(L, L->base+4, L->top); /* Anchor string in reserved stack slot. */
|
||||
return lua_tolstring(L, 5, size);
|
||||
} else {
|
||||
lj_err_caller(L, LJ_ERR_RDRSTR);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(load)
|
||||
{
|
||||
GCstr *name = lj_lib_optstr(L, 2);
|
||||
GCstr *mode = lj_lib_optstr(L, 3);
|
||||
int status;
|
||||
if (L->base < L->top &&
|
||||
(tvisstr(L->base) || tvisnumber(L->base) || tvisbuf(L->base))) {
|
||||
const char *s;
|
||||
MSize len;
|
||||
if (tvisbuf(L->base)) {
|
||||
SBufExt *sbx = bufV(L->base);
|
||||
s = sbx->r;
|
||||
len = sbufxlen(sbx);
|
||||
if (!name) name = &G(L)->strempty; /* Buffers are not NUL-terminated. */
|
||||
} else {
|
||||
GCstr *str = lj_lib_checkstr(L, 1);
|
||||
s = strdata(str);
|
||||
len = str->len;
|
||||
}
|
||||
lua_settop(L, 4); /* Ensure env arg exists. */
|
||||
status = luaL_loadbufferx(L, s, len, name ? strdata(name) : s,
|
||||
mode ? strdata(mode) : NULL);
|
||||
} else {
|
||||
lj_lib_checkfunc(L, 1);
|
||||
lua_settop(L, 5); /* Reserve a slot for the string from the reader. */
|
||||
status = lua_loadx(L, reader_func, NULL, name ? strdata(name) : "=(load)",
|
||||
mode ? strdata(mode) : NULL);
|
||||
}
|
||||
return load_aux(L, status, 4);
|
||||
}
|
||||
|
||||
LJLIB_CF(loadstring)
|
||||
{
|
||||
return lj_cf_load(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(dofile)
|
||||
{
|
||||
GCstr *fname = lj_lib_optstr(L, 1);
|
||||
setnilV(L->top);
|
||||
L->top = L->base+1;
|
||||
if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != LUA_OK)
|
||||
lua_error(L);
|
||||
lua_call(L, 0, LUA_MULTRET);
|
||||
return (int)(L->top - L->base) - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -- Base library: GC control -------------------------------------------- */
|
||||
|
||||
LJLIB_CF(gcinfo)
|
||||
{
|
||||
setintV(L->top++, (int32_t)(G(L)->gc.total >> 10));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(collectgarbage)
|
||||
{
|
||||
int opt = lj_lib_checkopt(L, 1, LUA_GCCOLLECT, /* ORDER LUA_GC* */
|
||||
"\4stop\7restart\7collect\5count\1\377\4step\10setpause\12setstepmul\1\377\11isrunning");
|
||||
int32_t data = lj_lib_optint(L, 2, 0);
|
||||
if (opt == LUA_GCCOUNT) {
|
||||
#if LJ_INTONLY
|
||||
setintV(L->top, G(L)->gc.total/1024);
|
||||
#else
|
||||
setnumV(L->top, (lua_Number)G(L)->gc.total/1024.0);
|
||||
#endif
|
||||
} else {
|
||||
int res = lua_gc(L, opt, data);
|
||||
if (opt == LUA_GCSTEP || opt == LUA_GCISRUNNING)
|
||||
setboolV(L->top, res);
|
||||
else
|
||||
setintV(L->top, res);
|
||||
}
|
||||
L->top++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* -- Base library: miscellaneous functions ------------------------------- */
|
||||
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds weak table. */
|
||||
LJLIB_CF(newproxy)
|
||||
{
|
||||
lua_settop(L, 1);
|
||||
lua_newuserdata(L, 0);
|
||||
if (lua_toboolean(L, 1) == 0) { /* newproxy(): without metatable. */
|
||||
return 1;
|
||||
} else if (lua_isboolean(L, 1)) { /* newproxy(true): with metatable. */
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawset(L, lua_upvalueindex(1)); /* Remember mt in weak table. */
|
||||
} else { /* newproxy(proxy): inherit metatable. */
|
||||
int validproxy = 0;
|
||||
if (lua_getmetatable(L, 1)) {
|
||||
lua_rawget(L, lua_upvalueindex(1));
|
||||
validproxy = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
if (!validproxy)
|
||||
lj_err_arg(L, 1, LJ_ERR_NOPROXY);
|
||||
lua_getmetatable(L, 1);
|
||||
}
|
||||
lua_setmetatable(L, 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH("tostring")
|
||||
LJLIB_CF(print)
|
||||
{
|
||||
ptrdiff_t i, nargs = L->top - L->base;
|
||||
cTValue *tv = lj_tab_getstr(tabref(L->env), strV(lj_lib_upvalue(L, 1)));
|
||||
int shortcut;
|
||||
if (tv && !tvisnil(tv)) {
|
||||
copyTV(L, L->top++, tv);
|
||||
} else {
|
||||
setstrV(L, L->top++, strV(lj_lib_upvalue(L, 1)));
|
||||
lua_gettable(L, LUA_GLOBALSINDEX);
|
||||
tv = L->top-1;
|
||||
}
|
||||
shortcut = (tvisfunc(tv) && funcV(tv)->c.ffid == FF_tostring) &&
|
||||
!gcrefu(basemt_it(G(L), LJ_TNUMX));
|
||||
for (i = 0; i < nargs; i++) {
|
||||
cTValue *o = &L->base[i];
|
||||
const char *str;
|
||||
size_t size;
|
||||
MSize len;
|
||||
if (shortcut && (str = lj_strfmt_wstrnum(L, o, &len)) != NULL) {
|
||||
size = len;
|
||||
} else {
|
||||
copyTV(L, L->top+1, o);
|
||||
copyTV(L, L->top, L->top-1);
|
||||
L->top += 2;
|
||||
lua_call(L, 1, 1);
|
||||
str = lua_tolstring(L, -1, &size);
|
||||
if (!str)
|
||||
lj_err_caller(L, LJ_ERR_PRTOSTR);
|
||||
L->top--;
|
||||
}
|
||||
if (i)
|
||||
putchar('\t');
|
||||
fwrite(str, 1, size, stdout);
|
||||
}
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-3)
|
||||
LJLIB_SET(_VERSION)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- Coroutine library --------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_coroutine
|
||||
|
||||
LJLIB_CF(coroutine_status)
|
||||
{
|
||||
const char *s;
|
||||
lua_State *co;
|
||||
if (!(L->top > L->base && tvisthread(L->base)))
|
||||
lj_err_arg(L, 1, LJ_ERR_NOCORO);
|
||||
co = threadV(L->base);
|
||||
if (co == L) s = "running";
|
||||
else if (co->status == LUA_YIELD) s = "suspended";
|
||||
else if (co->status != LUA_OK) s = "dead";
|
||||
else if (co->base > tvref(co->stack)+1+LJ_FR2) s = "normal";
|
||||
else if (co->top == co->base) s = "dead";
|
||||
else s = "suspended";
|
||||
lua_pushstring(L, s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_running)
|
||||
{
|
||||
#if LJ_52
|
||||
int ismain = lua_pushthread(L);
|
||||
setboolV(L->top++, ismain);
|
||||
return 2;
|
||||
#else
|
||||
if (lua_pushthread(L))
|
||||
setnilV(L->top++);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_isyieldable)
|
||||
{
|
||||
setboolV(L->top++, cframe_canyield(L->cframe));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_create)
|
||||
{
|
||||
lua_State *L1;
|
||||
if (!(L->base < L->top && tvisfunc(L->base)))
|
||||
lj_err_argt(L, 1, LUA_TFUNCTION);
|
||||
L1 = lua_newthread(L);
|
||||
setfuncV(L, L1->top++, funcV(L->base));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_ASM(coroutine_yield)
|
||||
{
|
||||
lj_err_caller(L, LJ_ERR_CYIELD);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
static int ffh_resume(lua_State *L, lua_State *co, int wrap)
|
||||
{
|
||||
if (co->cframe != NULL || co->status > LUA_YIELD ||
|
||||
(co->status == LUA_OK && co->top == co->base)) {
|
||||
ErrMsg em = co->cframe ? LJ_ERR_CORUN : LJ_ERR_CODEAD;
|
||||
if (wrap) lj_err_caller(L, em);
|
||||
setboolV(L->base-1-LJ_FR2, 0);
|
||||
setstrV(L, L->base-LJ_FR2, lj_err_str(L, em));
|
||||
return FFH_RES(2);
|
||||
}
|
||||
if (lj_state_cpgrowstack(co, (MSize)(L->top - L->base)) != LUA_OK) {
|
||||
cTValue *msg = --co->top;
|
||||
lj_err_callermsg(L, strVdata(msg));
|
||||
}
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(coroutine_resume)
|
||||
{
|
||||
if (!(L->top > L->base && tvisthread(L->base)))
|
||||
lj_err_arg(L, 1, LJ_ERR_NOCORO);
|
||||
return ffh_resume(L, threadV(L->base), 0);
|
||||
}
|
||||
|
||||
LJLIB_NOREG LJLIB_ASM(coroutine_wrap_aux)
|
||||
{
|
||||
return ffh_resume(L, threadV(lj_lib_upvalue(L, 1)), 1);
|
||||
}
|
||||
|
||||
/* Inline declarations. */
|
||||
LJ_ASMF void lj_ff_coroutine_wrap_aux(void);
|
||||
#if !(LJ_TARGET_MIPS && defined(ljamalg_c))
|
||||
LJ_FUNCA_NORET void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L,
|
||||
lua_State *co);
|
||||
#endif
|
||||
|
||||
/* Error handler, called from assembler VM. */
|
||||
void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co)
|
||||
{
|
||||
co->top--; copyTV(L, L->top, co->top); L->top++;
|
||||
if (tvisstr(L->top-1))
|
||||
lj_err_callermsg(L, strVdata(L->top-1));
|
||||
else
|
||||
lj_err_run(L);
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn);
|
||||
|
||||
LJLIB_CF(coroutine_wrap)
|
||||
{
|
||||
GCfunc *fn;
|
||||
lj_cf_coroutine_create(L);
|
||||
fn = lj_lib_pushcc(L, lj_ffh_coroutine_wrap_aux, FF_coroutine_wrap_aux, 1);
|
||||
setpc_wrap_aux(L, fn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* Fix the PC of wrap_aux. Really ugly workaround. */
|
||||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn)
|
||||
{
|
||||
setmref(fn->c.pc, &L2GG(L)->bcff[lj_lib_init_coroutine[1]+2]);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void newproxy_weaktable(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
GCtab *t = lj_tab_new(L, 0, 1);
|
||||
settabV(L, L->top++, t);
|
||||
setgcref(t->metatable, obj2gco(t));
|
||||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")),
|
||||
lj_str_newlit(L, "kv"));
|
||||
t->nomm = (uint8_t)(~(1u<<MM_mode));
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_base(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: Table and value are the same. */
|
||||
GCtab *env = tabref(L->env);
|
||||
settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env);
|
||||
lua_pushliteral(L, LUA_VERSION); /* top-3. */
|
||||
newproxy_weaktable(L); /* top-2. */
|
||||
LJ_LIB_REG(L, "_G", base);
|
||||
LJ_LIB_REG(L, LUA_COLIBNAME, coroutine);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
** Bit manipulation library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lib_bit_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_strscan.h"
|
||||
#include "lj_strfmt.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_carith.h"
|
||||
#endif
|
||||
#include "lj_ff.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_bit
|
||||
|
||||
#if LJ_HASFFI
|
||||
static int bit_result64(lua_State *L, CTypeID id, uint64_t x)
|
||||
{
|
||||
GCcdata *cd = lj_cdata_new_(L, id, 8);
|
||||
*(uint64_t *)cdataptr(cd) = x;
|
||||
setcdataV(L, L->base-1-LJ_FR2, cd);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
#else
|
||||
static int32_t bit_checkbit(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && lj_strscan_numberobj(o)))
|
||||
lj_err_argt(L, narg, LUA_TNUMBER);
|
||||
if (LJ_LIKELY(tvisint(o))) {
|
||||
return intV(o);
|
||||
} else {
|
||||
int32_t i = lj_num2bit(numV(o));
|
||||
if (LJ_DUALNUM) setintV(o, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_ASM(bit_tobit) LJLIB_REC(bit_tobit)
|
||||
{
|
||||
#if LJ_HASFFI
|
||||
CTypeID id = 0;
|
||||
setintV(L->base-1-LJ_FR2, (int32_t)lj_carith_check64(L, 1, &id));
|
||||
return FFH_RES(1);
|
||||
#else
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_ASM(bit_bnot) LJLIB_REC(bit_unary IR_BNOT)
|
||||
{
|
||||
#if LJ_HASFFI
|
||||
CTypeID id = 0;
|
||||
uint64_t x = lj_carith_check64(L, 1, &id);
|
||||
return id ? bit_result64(L, id, ~x) : FFH_RETRY;
|
||||
#else
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_ASM(bit_bswap) LJLIB_REC(bit_unary IR_BSWAP)
|
||||
{
|
||||
#if LJ_HASFFI
|
||||
CTypeID id = 0;
|
||||
uint64_t x = lj_carith_check64(L, 1, &id);
|
||||
return id ? bit_result64(L, id, lj_bswap64(x)) : FFH_RETRY;
|
||||
#else
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_ASM(bit_lshift) LJLIB_REC(bit_shift IR_BSHL)
|
||||
{
|
||||
#if LJ_HASFFI
|
||||
CTypeID id = 0, id2 = 0;
|
||||
uint64_t x = lj_carith_check64(L, 1, &id);
|
||||
int32_t sh = (int32_t)lj_carith_check64(L, 2, &id2);
|
||||
if (id) {
|
||||
x = lj_carith_shift64(x, sh, curr_func(L)->c.ffid - (int)FF_bit_lshift);
|
||||
return bit_result64(L, id, x);
|
||||
}
|
||||
setintV(L->base+1, sh);
|
||||
return FFH_RETRY;
|
||||
#else
|
||||
lj_lib_checknumber(L, 1);
|
||||
bit_checkbit(L, 2);
|
||||
return FFH_RETRY;
|
||||
#endif
|
||||
}
|
||||
LJLIB_ASM_(bit_rshift) LJLIB_REC(bit_shift IR_BSHR)
|
||||
LJLIB_ASM_(bit_arshift) LJLIB_REC(bit_shift IR_BSAR)
|
||||
LJLIB_ASM_(bit_rol) LJLIB_REC(bit_shift IR_BROL)
|
||||
LJLIB_ASM_(bit_ror) LJLIB_REC(bit_shift IR_BROR)
|
||||
|
||||
LJLIB_ASM(bit_band) LJLIB_REC(bit_nary IR_BAND)
|
||||
{
|
||||
#if LJ_HASFFI
|
||||
CTypeID id = 0;
|
||||
TValue *o = L->base, *top = L->top;
|
||||
int i = 0;
|
||||
do { lj_carith_check64(L, ++i, &id); } while (++o < top);
|
||||
if (id) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_get(cts, id);
|
||||
int op = curr_func(L)->c.ffid - (int)FF_bit_bor;
|
||||
uint64_t x, y = op >= 0 ? 0 : ~(uint64_t)0;
|
||||
o = L->base;
|
||||
do {
|
||||
lj_cconv_ct_tv(cts, ct, (uint8_t *)&x, o, 0);
|
||||
if (op < 0) y &= x; else if (op == 0) y |= x; else y ^= x;
|
||||
} while (++o < top);
|
||||
return bit_result64(L, id, y);
|
||||
}
|
||||
return FFH_RETRY;
|
||||
#else
|
||||
int i = 0;
|
||||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top);
|
||||
return FFH_RETRY;
|
||||
#endif
|
||||
}
|
||||
LJLIB_ASM_(bit_bor) LJLIB_REC(bit_nary IR_BOR)
|
||||
LJLIB_ASM_(bit_bxor) LJLIB_REC(bit_nary IR_BXOR)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
LJLIB_CF(bit_tohex) LJLIB_REC(.)
|
||||
{
|
||||
#if LJ_HASFFI
|
||||
CTypeID id = 0, id2 = 0;
|
||||
uint64_t b = lj_carith_check64(L, 1, &id);
|
||||
int32_t n = L->base+1>=L->top ? (id ? 16 : 8) :
|
||||
(int32_t)lj_carith_check64(L, 2, &id2);
|
||||
#else
|
||||
uint32_t b = (uint32_t)bit_checkbit(L, 1);
|
||||
int32_t n = L->base+1>=L->top ? 8 : bit_checkbit(L, 2);
|
||||
#endif
|
||||
SBuf *sb = lj_buf_tmp_(L);
|
||||
SFormat sf = (STRFMT_UINT|STRFMT_T_HEX);
|
||||
if (n < 0) { n = (int32_t)(~(uint32_t)n+1u); sf |= STRFMT_F_UPPER; }
|
||||
if ((uint32_t)n > 254) n = 254;
|
||||
sf |= ((SFormat)((n+1)&255) << STRFMT_SH_PREC);
|
||||
#if LJ_HASFFI
|
||||
if (n < 16) b &= ((uint64_t)1 << 4*n)-1;
|
||||
#else
|
||||
if (n < 8) b &= (1u << 4*n)-1;
|
||||
#endif
|
||||
sb = lj_strfmt_putfxint(sb, sf, b);
|
||||
setstrV(L, L->top-1, lj_buf_str(L, sb));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_bit(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_BITLIBNAME, bit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
** Buffer library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lib_buffer_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASBUFFER
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_udata.h"
|
||||
#include "lj_meta.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_cconv.h"
|
||||
#endif
|
||||
#include "lj_strfmt.h"
|
||||
#include "lj_serialize.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* -- Helper functions ---------------------------------------------------- */
|
||||
|
||||
/* Check that the first argument is a string buffer. */
|
||||
static SBufExt *buffer_tobuf(lua_State *L)
|
||||
{
|
||||
if (!(L->base < L->top && tvisbuf(L->base)))
|
||||
lj_err_argtype(L, 1, "buffer");
|
||||
return bufV(L->base);
|
||||
}
|
||||
|
||||
/* Ditto, but for writers. */
|
||||
static LJ_AINLINE SBufExt *buffer_tobufw(lua_State *L)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
setsbufXL_(sbx, L);
|
||||
return sbx;
|
||||
}
|
||||
|
||||
#define buffer_toudata(sbx) ((GCudata *)(sbx)-1)
|
||||
|
||||
/* -- Buffer methods ------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_buffer_method
|
||||
|
||||
LJLIB_CF(buffer_method_free)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
lj_bufx_free(L, sbx);
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_reset) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
lj_bufx_reset(sbx);
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_skip) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
MSize n = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF);
|
||||
MSize len = sbufxlen(sbx);
|
||||
if (n < len) {
|
||||
sbx->r += n;
|
||||
} else if (sbufiscow(sbx)) {
|
||||
sbx->r = sbx->w;
|
||||
} else {
|
||||
sbx->r = sbx->w = sbx->b;
|
||||
}
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_set) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
GCobj *ref;
|
||||
const char *p;
|
||||
MSize len;
|
||||
#if LJ_HASFFI
|
||||
if (tviscdata(L->base+1)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p,
|
||||
L->base+1, CCF_ARG(2));
|
||||
len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
GCstr *str = lj_lib_checkstrx(L, 2);
|
||||
p = strdata(str);
|
||||
len = str->len;
|
||||
}
|
||||
lj_bufx_free(L, sbx);
|
||||
lj_bufx_set_cow(L, sbx, p, len);
|
||||
ref = gcV(L->base+1);
|
||||
setgcref(sbx->cowref, ref);
|
||||
lj_gc_objbarrier(L, buffer_toudata(sbx), ref);
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_put) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobufw(L);
|
||||
ptrdiff_t arg, narg = L->top - L->base;
|
||||
for (arg = 1; arg < narg; arg++) {
|
||||
cTValue *o = &L->base[arg], *mo = NULL;
|
||||
retry:
|
||||
if (tvisstr(o)) {
|
||||
lj_buf_putstr((SBuf *)sbx, strV(o));
|
||||
} else if (tvisint(o)) {
|
||||
lj_strfmt_putint((SBuf *)sbx, intV(o));
|
||||
} else if (tvisnum(o)) {
|
||||
lj_strfmt_putfnum((SBuf *)sbx, STRFMT_G14, numV(o));
|
||||
} else if (tvisbuf(o)) {
|
||||
SBufExt *sbx2 = bufV(o);
|
||||
if (sbx2 == sbx) lj_err_arg(L, (int)(arg+1), LJ_ERR_BUFFER_SELF);
|
||||
lj_buf_putmem((SBuf *)sbx, sbx2->r, sbufxlen(sbx2));
|
||||
} else if (!mo && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
|
||||
/* Call __tostring metamethod inline. */
|
||||
copyTV(L, L->top++, mo);
|
||||
copyTV(L, L->top++, o);
|
||||
lua_call(L, 1, 1);
|
||||
o = &L->base[arg]; /* The stack may have been reallocated. */
|
||||
copyTV(L, &L->base[arg], L->top-1);
|
||||
L->top = L->base + narg;
|
||||
goto retry; /* Retry with the result. */
|
||||
} else {
|
||||
lj_err_argtype(L, (int)(arg+1), "string/number/__tostring");
|
||||
}
|
||||
/* Probably not useful to inline other __tostring MMs, e.g. FFI numbers. */
|
||||
}
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_putf) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobufw(L);
|
||||
lj_strfmt_putarg(L, (SBuf *)sbx, 2, 2);
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_get) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
ptrdiff_t arg, narg = L->top - L->base;
|
||||
if (narg == 1) {
|
||||
narg++;
|
||||
setnilV(L->top++); /* get() is the same as get(nil). */
|
||||
}
|
||||
for (arg = 1; arg < narg; arg++) {
|
||||
TValue *o = &L->base[arg];
|
||||
MSize n = tvisnil(o) ? LJ_MAX_BUF :
|
||||
(MSize) lj_lib_checkintrange(L, (int)(arg+1), 0, LJ_MAX_BUF);
|
||||
MSize len = sbufxlen(sbx);
|
||||
if (n > len) n = len;
|
||||
setstrV(L, o, lj_str_new(L, sbx->r, n));
|
||||
sbx->r += n;
|
||||
}
|
||||
if (sbx->r == sbx->w && !sbufiscow(sbx)) sbx->r = sbx->w = sbx->b;
|
||||
lj_gc_check(L);
|
||||
return (int)(narg-1);
|
||||
}
|
||||
|
||||
#if LJ_HASFFI
|
||||
LJLIB_CF(buffer_method_putcdata) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobufw(L);
|
||||
const char *p;
|
||||
MSize len;
|
||||
if (tviscdata(L->base+1)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p,
|
||||
L->base+1, CCF_ARG(2));
|
||||
} else {
|
||||
lj_err_argtype(L, 2, "cdata");
|
||||
}
|
||||
len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF);
|
||||
lj_buf_putmem((SBuf *)sbx, p, len);
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_reserve) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobufw(L);
|
||||
MSize sz = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF);
|
||||
GCcdata *cd;
|
||||
lj_buf_more((SBuf *)sbx, sz);
|
||||
ctype_loadffi(L);
|
||||
cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR);
|
||||
*(void **)cdataptr(cd) = sbx->w;
|
||||
setcdataV(L, L->top++, cd);
|
||||
setintV(L->top++, sbufleft(sbx));
|
||||
return 2;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_commit) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
MSize len = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF);
|
||||
if (len > sbufleft(sbx)) lj_err_arg(L, 2, LJ_ERR_NUMRNG);
|
||||
sbx->w += len;
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_ref) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
GCcdata *cd;
|
||||
ctype_loadffi(L);
|
||||
cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR);
|
||||
*(void **)cdataptr(cd) = sbx->r;
|
||||
setcdataV(L, L->top++, cd);
|
||||
setintV(L->top++, sbufxlen(sbx));
|
||||
return 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(buffer_method_encode) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobufw(L);
|
||||
cTValue *o = lj_lib_checkany(L, 2);
|
||||
lj_serialize_put(sbx, o);
|
||||
lj_gc_check(L);
|
||||
L->top = L->base+1; /* Chain buffer object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method_decode) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobufw(L);
|
||||
setnilV(L->top++);
|
||||
sbx->r = lj_serialize_get(sbx, L->top-1);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method___gc)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
lj_bufx_free(L, sbx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method___tostring) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
setstrV(L, L->top-1, lj_str_new(L, sbx->r, sbufxlen(sbx)));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_method___len) LJLIB_REC(.)
|
||||
{
|
||||
SBufExt *sbx = buffer_tobuf(L);
|
||||
setintV(L->top-1, (int32_t)sbufxlen(sbx));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH("buffer") LJLIB_SET(__metatable)
|
||||
LJLIB_PUSH(top-1) LJLIB_SET(__index)
|
||||
|
||||
/* -- Buffer library functions -------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_buffer
|
||||
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
|
||||
|
||||
LJLIB_CF(buffer_new)
|
||||
{
|
||||
MSize sz = 0;
|
||||
int targ = 1;
|
||||
GCtab *env, *dict_str = NULL, *dict_mt = NULL;
|
||||
GCudata *ud;
|
||||
SBufExt *sbx;
|
||||
if (L->base < L->top && !tvistab(L->base)) {
|
||||
targ = 2;
|
||||
if (!tvisnil(L->base))
|
||||
sz = (MSize)lj_lib_checkintrange(L, 1, 0, LJ_MAX_BUF);
|
||||
}
|
||||
if (L->base+targ-1 < L->top) {
|
||||
GCtab *options = lj_lib_checktab(L, targ);
|
||||
cTValue *opt_dict, *opt_mt;
|
||||
opt_dict = lj_tab_getstr(options, lj_str_newlit(L, "dict"));
|
||||
if (opt_dict && tvistab(opt_dict)) {
|
||||
dict_str = tabV(opt_dict);
|
||||
lj_serialize_dict_prep_str(L, dict_str);
|
||||
}
|
||||
opt_mt = lj_tab_getstr(options, lj_str_newlit(L, "metatable"));
|
||||
if (opt_mt && tvistab(opt_mt)) {
|
||||
dict_mt = tabV(opt_mt);
|
||||
lj_serialize_dict_prep_mt(L, dict_mt);
|
||||
}
|
||||
}
|
||||
env = tabref(curr_func(L)->c.env);
|
||||
ud = lj_udata_new(L, sizeof(SBufExt), env);
|
||||
ud->udtype = UDTYPE_BUFFER;
|
||||
/* NOBARRIER: The GCudata is new (marked white). */
|
||||
setgcref(ud->metatable, obj2gco(env));
|
||||
setudataV(L, L->top++, ud);
|
||||
sbx = (SBufExt *)uddata(ud);
|
||||
lj_bufx_init(L, sbx);
|
||||
setgcref(sbx->dict_str, obj2gco(dict_str));
|
||||
setgcref(sbx->dict_mt, obj2gco(dict_mt));
|
||||
if (sz > 0) lj_buf_need2((SBuf *)sbx, sz);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_encode) LJLIB_REC(.)
|
||||
{
|
||||
cTValue *o = lj_lib_checkany(L, 1);
|
||||
setstrV(L, L->top++, lj_serialize_encode(L, o));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(buffer_decode) LJLIB_REC(.)
|
||||
{
|
||||
GCstr *str = lj_lib_checkstrx(L, 1);
|
||||
setnilV(L->top++);
|
||||
lj_serialize_decode(L, L->top-1, str);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
int luaopen_string_buffer(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, NULL, buffer_method);
|
||||
lua_getfield(L, -1, "__tostring");
|
||||
lua_setfield(L, -2, "tostring");
|
||||
LJ_LIB_REG(L, NULL, buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
** Debug library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_debug_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_debug
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(debug_getregistry)
|
||||
{
|
||||
copyTV(L, L->top++, registry(L));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getmetatable) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
if (!lua_getmetatable(L, 1)) {
|
||||
setnilV(L->top-1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setmetatable)
|
||||
{
|
||||
lj_lib_checktabornil(L, 2);
|
||||
L->top = L->base+2;
|
||||
lua_setmetatable(L, 1);
|
||||
#if !LJ_52
|
||||
setboolV(L->top-1, 1);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getfenv)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
lua_getfenv(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setfenv)
|
||||
{
|
||||
lj_lib_checktab(L, 2);
|
||||
L->top = L->base+2;
|
||||
if (!lua_setfenv(L, 1))
|
||||
lj_err_caller(L, LJ_ERR_SETFENV);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void settabss(lua_State *L, const char *i, const char *v)
|
||||
{
|
||||
lua_pushstring(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
static void settabsi(lua_State *L, const char *i, int v)
|
||||
{
|
||||
lua_pushinteger(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
static void settabsb(lua_State *L, const char *i, int v)
|
||||
{
|
||||
lua_pushboolean(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
static lua_State *getthread(lua_State *L, int *arg)
|
||||
{
|
||||
if (L->base < L->top && tvisthread(L->base)) {
|
||||
*arg = 1;
|
||||
return threadV(L->base);
|
||||
} else {
|
||||
*arg = 0;
|
||||
return L;
|
||||
}
|
||||
}
|
||||
|
||||
static void treatstackoption(lua_State *L, lua_State *L1, const char *fname)
|
||||
{
|
||||
if (L == L1) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_remove(L, -3);
|
||||
}
|
||||
else
|
||||
lua_xmove(L1, L, 1);
|
||||
lua_setfield(L, -2, fname);
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getinfo)
|
||||
{
|
||||
lj_Debug ar;
|
||||
int arg, opt_f = 0, opt_L = 0;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *options = luaL_optstring(L, arg+2, "flnSu");
|
||||
if (lua_isnumber(L, arg+1)) {
|
||||
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), (lua_Debug *)&ar)) {
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
}
|
||||
} else if (L->base+arg < L->top && tvisfunc(L->base+arg)) {
|
||||
options = lua_pushfstring(L, ">%s", options);
|
||||
setfuncV(L1, L1->top++, funcV(L->base+arg));
|
||||
} else {
|
||||
lj_err_arg(L, arg+1, LJ_ERR_NOFUNCL);
|
||||
}
|
||||
if (!lj_debug_getinfo(L1, options, &ar, 1))
|
||||
lj_err_arg(L, arg+2, LJ_ERR_INVOPT);
|
||||
lua_createtable(L, 0, 16); /* Create result table. */
|
||||
for (; *options; options++) {
|
||||
switch (*options) {
|
||||
case 'S':
|
||||
settabss(L, "source", ar.source);
|
||||
settabss(L, "short_src", ar.short_src);
|
||||
settabsi(L, "linedefined", ar.linedefined);
|
||||
settabsi(L, "lastlinedefined", ar.lastlinedefined);
|
||||
settabss(L, "what", ar.what);
|
||||
break;
|
||||
case 'l':
|
||||
settabsi(L, "currentline", ar.currentline);
|
||||
break;
|
||||
case 'u':
|
||||
settabsi(L, "nups", ar.nups);
|
||||
settabsi(L, "nparams", ar.nparams);
|
||||
settabsb(L, "isvararg", ar.isvararg);
|
||||
break;
|
||||
case 'n':
|
||||
settabss(L, "name", ar.name);
|
||||
settabss(L, "namewhat", ar.namewhat);
|
||||
break;
|
||||
case 'f': opt_f = 1; break;
|
||||
case 'L': opt_L = 1; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (opt_L) treatstackoption(L, L1, "activelines");
|
||||
if (opt_f) treatstackoption(L, L1, "func");
|
||||
return 1; /* Return result table. */
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getlocal)
|
||||
{
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int slot = lj_lib_checkint(L, arg+2);
|
||||
if (tvisfunc(L->base+arg)) {
|
||||
L->top = L->base+arg+1;
|
||||
lua_pushstring(L, lua_getlocal(L, NULL, slot));
|
||||
return 1;
|
||||
}
|
||||
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar))
|
||||
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG);
|
||||
name = lua_getlocal(L1, &ar, slot);
|
||||
if (name) {
|
||||
lua_xmove(L1, L, 1);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushvalue(L, -2);
|
||||
return 2;
|
||||
} else {
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(debug_setlocal)
|
||||
{
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
TValue *tv;
|
||||
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar))
|
||||
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG);
|
||||
tv = lj_lib_checkany(L, arg+3);
|
||||
copyTV(L1, L1->top++, tv);
|
||||
lua_pushstring(L, lua_setlocal(L1, &ar, lj_lib_checkint(L, arg+2)));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int debug_getupvalue(lua_State *L, int get)
|
||||
{
|
||||
int32_t n = lj_lib_checkint(L, 2);
|
||||
const char *name;
|
||||
lj_lib_checkfunc(L, 1);
|
||||
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
|
||||
if (name) {
|
||||
lua_pushstring(L, name);
|
||||
if (!get) return 1;
|
||||
copyTV(L, L->top, L->top-2);
|
||||
L->top++;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getupvalue)
|
||||
{
|
||||
return debug_getupvalue(L, 1);
|
||||
}
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(debug_setupvalue)
|
||||
{
|
||||
lj_lib_checkany(L, 3);
|
||||
return debug_getupvalue(L, 0);
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_upvalueid)
|
||||
{
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 1);
|
||||
int32_t n = lj_lib_checkint(L, 2) - 1;
|
||||
if ((uint32_t)n >= fn->l.nupvalues)
|
||||
lj_err_arg(L, 2, LJ_ERR_IDXRNG);
|
||||
lua_pushlightuserdata(L, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) :
|
||||
(void *)&fn->c.upvalue[n]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_upvaluejoin)
|
||||
{
|
||||
GCfunc *fn[2];
|
||||
GCRef *p[2];
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
int32_t n;
|
||||
fn[i] = lj_lib_checkfunc(L, 2*i+1);
|
||||
if (!isluafunc(fn[i]))
|
||||
lj_err_arg(L, 2*i+1, LJ_ERR_NOLFUNC);
|
||||
n = lj_lib_checkint(L, 2*i+2) - 1;
|
||||
if ((uint32_t)n >= fn[i]->l.nupvalues)
|
||||
lj_err_arg(L, 2*i+2, LJ_ERR_IDXRNG);
|
||||
p[i] = &fn[i]->l.uvptr[n];
|
||||
}
|
||||
setgcrefr(*p[0], *p[1]);
|
||||
lj_gc_objbarrier(L, fn[0], gcref(*p[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_CF(debug_getuservalue)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (o < L->top && tvisudata(o))
|
||||
settabV(L, o, tabref(udataV(o)->env));
|
||||
else
|
||||
setnilV(o);
|
||||
L->top = o+1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setuservalue)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (!(o < L->top && tvisudata(o)))
|
||||
lj_err_argt(L, 1, LUA_TUSERDATA);
|
||||
if (!(o+1 < L->top && tvistab(o+1)))
|
||||
lj_err_argt(L, 2, LUA_TTABLE);
|
||||
L->top = o+2;
|
||||
lua_setfenv(L, 1);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define KEY_HOOK (U64x(80000000,00000000)|'h')
|
||||
|
||||
static void hookf(lua_State *L, lua_Debug *ar)
|
||||
{
|
||||
static const char *const hooknames[] =
|
||||
{"call", "return", "line", "count", "tail return"};
|
||||
(L->top++)->u64 = KEY_HOOK;
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
if (lua_isfunction(L, -1)) {
|
||||
lua_pushstring(L, hooknames[(int)ar->event]);
|
||||
if (ar->currentline >= 0)
|
||||
lua_pushinteger(L, ar->currentline);
|
||||
else lua_pushnil(L);
|
||||
lua_call(L, 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int makemask(const char *smask, int count)
|
||||
{
|
||||
int mask = 0;
|
||||
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
|
||||
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
|
||||
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
|
||||
if (count > 0) mask |= LUA_MASKCOUNT;
|
||||
return mask;
|
||||
}
|
||||
|
||||
static char *unmakemask(int mask, char *smask)
|
||||
{
|
||||
int i = 0;
|
||||
if (mask & LUA_MASKCALL) smask[i++] = 'c';
|
||||
if (mask & LUA_MASKRET) smask[i++] = 'r';
|
||||
if (mask & LUA_MASKLINE) smask[i++] = 'l';
|
||||
smask[i] = '\0';
|
||||
return smask;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_sethook)
|
||||
{
|
||||
int arg, mask, count;
|
||||
lua_Hook func;
|
||||
(void)getthread(L, &arg);
|
||||
if (lua_isnoneornil(L, arg+1)) {
|
||||
lua_settop(L, arg+1);
|
||||
func = NULL; mask = 0; count = 0; /* turn off hooks */
|
||||
} else {
|
||||
const char *smask = luaL_checkstring(L, arg+2);
|
||||
luaL_checktype(L, arg+1, LUA_TFUNCTION);
|
||||
count = luaL_optint(L, arg+3, 0);
|
||||
func = hookf; mask = makemask(smask, count);
|
||||
}
|
||||
(L->top++)->u64 = KEY_HOOK;
|
||||
lua_pushvalue(L, arg+1);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
lua_sethook(L, func, mask, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_gethook)
|
||||
{
|
||||
char buff[5];
|
||||
int mask = lua_gethookmask(L);
|
||||
lua_Hook hook = lua_gethook(L);
|
||||
if (hook != NULL && hook != hookf) { /* external hook? */
|
||||
lua_pushliteral(L, "external hook");
|
||||
} else {
|
||||
(L->top++)->u64 = KEY_HOOK;
|
||||
lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */
|
||||
}
|
||||
lua_pushstring(L, unmakemask(mask, buff));
|
||||
lua_pushinteger(L, lua_gethookcount(L));
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(debug_debug)
|
||||
{
|
||||
for (;;) {
|
||||
char buffer[250];
|
||||
fputs("lua_debug> ", stderr);
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
|
||||
strcmp(buffer, "cont\n") == 0)
|
||||
return 0;
|
||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||
lua_pcall(L, 0, 0, 0)) {
|
||||
const char *s = lua_tostring(L, -1);
|
||||
fputs(s ? s : "(error object is not a string)", stderr);
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
lua_settop(L, 0); /* remove eventual returns */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LEVELS1 12 /* size of the first part of the stack */
|
||||
#define LEVELS2 10 /* size of the second part of the stack */
|
||||
|
||||
LJLIB_CF(debug_traceback)
|
||||
{
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *msg = lua_tostring(L, arg+1);
|
||||
if (msg == NULL && L->top > L->base+arg)
|
||||
L->top = L->base+arg+1;
|
||||
else
|
||||
luaL_traceback(L, L1, msg, lj_lib_optint(L, arg+2, (L == L1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_debug(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_DBLIBNAME, debug);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,857 @@
|
||||
/*
|
||||
** FFI library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lib_ffi_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cparse.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_carith.h"
|
||||
#include "lj_ccall.h"
|
||||
#include "lj_ccallback.h"
|
||||
#include "lj_clib.h"
|
||||
#include "lj_strfmt.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* -- C type checks ------------------------------------------------------- */
|
||||
|
||||
/* Check first argument for a C type and returns its ID. */
|
||||
static CTypeID ffi_checkctype(lua_State *L, CTState *cts, TValue *param)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (!(o < L->top)) {
|
||||
err_argtype:
|
||||
lj_err_argtype(L, 1, "C type");
|
||||
}
|
||||
if (tvisstr(o)) { /* Parse an abstract C type declaration. */
|
||||
GCstr *s = strV(o);
|
||||
CPState cp;
|
||||
int errcode;
|
||||
cp.L = L;
|
||||
cp.cts = cts;
|
||||
cp.srcname = strdata(s);
|
||||
cp.p = strdata(s);
|
||||
cp.param = param;
|
||||
cp.mode = CPARSE_MODE_ABSTRACT|CPARSE_MODE_NOIMPLICIT;
|
||||
errcode = lj_cparse(&cp);
|
||||
if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */
|
||||
return cp.val.id;
|
||||
} else {
|
||||
GCcdata *cd;
|
||||
if (!tviscdata(o)) goto err_argtype;
|
||||
if (param && param < L->top) lj_err_arg(L, 1, LJ_ERR_FFI_NUMPARAM);
|
||||
cd = cdataV(o);
|
||||
return cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) : cd->ctypeid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check argument for C data and return it. */
|
||||
static GCcdata *ffi_checkcdata(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && tviscdata(o)))
|
||||
lj_err_argt(L, narg, LUA_TCDATA);
|
||||
return cdataV(o);
|
||||
}
|
||||
|
||||
/* Convert argument to C pointer. */
|
||||
static void *ffi_checkptr(lua_State *L, int narg, CTypeID id)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
TValue *o = L->base + narg-1;
|
||||
void *p;
|
||||
if (o >= L->top)
|
||||
lj_err_arg(L, narg, LJ_ERR_NOVAL);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, id), (uint8_t *)&p, o, CCF_ARG(narg));
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Convert argument to int32_t. */
|
||||
static int32_t ffi_checkint(lua_State *L, int narg)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
TValue *o = L->base + narg-1;
|
||||
int32_t i;
|
||||
if (o >= L->top)
|
||||
lj_err_arg(L, narg, LJ_ERR_NOVAL);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o,
|
||||
CCF_ARG(narg));
|
||||
return i;
|
||||
}
|
||||
|
||||
/* -- C type metamethods -------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi_meta
|
||||
|
||||
/* Handle ctype __index/__newindex metamethods. */
|
||||
static int ffi_index_meta(lua_State *L, CTState *cts, CType *ct, MMS mm)
|
||||
{
|
||||
CTypeID id = ctype_typeid(cts, ct);
|
||||
cTValue *tv = lj_ctype_meta(cts, id, mm);
|
||||
TValue *base = L->base;
|
||||
if (!tv) {
|
||||
const char *s;
|
||||
err_index:
|
||||
s = strdata(lj_ctype_repr(L, id, NULL));
|
||||
if (tvisstr(L->base+1)) {
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADMEMBER, s, strVdata(L->base+1));
|
||||
} else {
|
||||
const char *key = tviscdata(L->base+1) ?
|
||||
strdata(lj_ctype_repr(L, cdataV(L->base+1)->ctypeid, NULL)) :
|
||||
lj_typename(L->base+1);
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADIDXW, s, key);
|
||||
}
|
||||
}
|
||||
if (!tvisfunc(tv)) {
|
||||
if (mm == MM_index) {
|
||||
cTValue *o = lj_meta_tget(L, tv, base+1);
|
||||
if (o) {
|
||||
if (tvisnil(o)) goto err_index;
|
||||
copyTV(L, L->top-1, o);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
TValue *o = lj_meta_tset(L, tv, base+1);
|
||||
if (o) {
|
||||
copyTV(L, o, base+2);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
copyTV(L, base, L->top);
|
||||
tv = L->top-1-LJ_FR2;
|
||||
}
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTInfo qual = 0;
|
||||
CType *ct;
|
||||
uint8_t *p;
|
||||
TValue *o = L->base;
|
||||
if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
|
||||
if ((qual & 1))
|
||||
return ffi_index_meta(L, cts, ct, MM_index);
|
||||
if (lj_cdata_get(cts, ct, L->top-1, p))
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___newindex) LJLIB_REC(cdata_index 1)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTInfo qual = 0;
|
||||
CType *ct;
|
||||
uint8_t *p;
|
||||
TValue *o = L->base;
|
||||
if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
|
||||
if ((qual & 1)) {
|
||||
if ((qual & CTF_CONST))
|
||||
lj_err_caller(L, LJ_ERR_FFI_WRCONST);
|
||||
return ffi_index_meta(L, cts, ct, MM_newindex);
|
||||
}
|
||||
lj_cdata_set(cts, ct, p, o+2, qual);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Common handler for cdata arithmetic. */
|
||||
static int ffi_arith(lua_State *L)
|
||||
{
|
||||
MMS mm = (MMS)(curr_func(L)->c.ffid - (int)FF_ffi_meta___eq + (int)MM_eq);
|
||||
return lj_carith_op(L, mm);
|
||||
}
|
||||
|
||||
/* The following functions must be in contiguous ORDER MM. */
|
||||
LJLIB_CF(ffi_meta___eq) LJLIB_REC(cdata_arith MM_eq)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___len) LJLIB_REC(cdata_arith MM_len)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___lt) LJLIB_REC(cdata_arith MM_lt)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___le) LJLIB_REC(cdata_arith MM_le)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___concat) LJLIB_REC(cdata_arith MM_concat)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static int lj_cf_ffi_new(lua_State *L);
|
||||
|
||||
LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
CTypeID id = cd->ctypeid;
|
||||
CType *ct;
|
||||
cTValue *tv;
|
||||
MMS mm = MM_call;
|
||||
if (cd->ctypeid == CTID_CTYPEID) {
|
||||
id = *(CTypeID *)cdataptr(cd);
|
||||
mm = MM_new;
|
||||
} else {
|
||||
int ret = lj_ccall_func(L, cd);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
/* Handle ctype __call/__new metamethod. */
|
||||
ct = ctype_raw(cts, id);
|
||||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
|
||||
tv = lj_ctype_meta(cts, id, mm);
|
||||
if (tv)
|
||||
return lj_meta_tailcall(L, tv);
|
||||
else if (mm == MM_call)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADCALL, strdata(lj_ctype_repr(L, id, NULL)));
|
||||
return lj_cf_ffi_new(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___add) LJLIB_REC(cdata_arith MM_add)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___sub) LJLIB_REC(cdata_arith MM_sub)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___mul) LJLIB_REC(cdata_arith MM_mul)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___div) LJLIB_REC(cdata_arith MM_div)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___mod) LJLIB_REC(cdata_arith MM_mod)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___pow) LJLIB_REC(cdata_arith MM_pow)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___unm) LJLIB_REC(cdata_arith MM_unm)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
/* End of contiguous ORDER MM. */
|
||||
|
||||
LJLIB_CF(ffi_meta___tostring)
|
||||
{
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
const char *msg = "cdata<%s>: %p";
|
||||
CTypeID id = cd->ctypeid;
|
||||
void *p = cdataptr(cd);
|
||||
if (id == CTID_CTYPEID) {
|
||||
msg = "ctype<%s>";
|
||||
id = *(CTypeID *)p;
|
||||
} else {
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
if (ctype_isref(ct->info)) {
|
||||
p = *(void **)p;
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
}
|
||||
if (ctype_iscomplex(ct->info)) {
|
||||
setstrV(L, L->top-1, lj_ctype_repr_complex(L, cdataptr(cd), ct->size));
|
||||
goto checkgc;
|
||||
} else if (ct->size == 8 && ctype_isinteger(ct->info)) {
|
||||
setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd),
|
||||
(ct->info & CTF_UNSIGNED)));
|
||||
goto checkgc;
|
||||
} else if (ctype_isfunc(ct->info)) {
|
||||
p = *(void **)p;
|
||||
} else if (ctype_isenum(ct->info)) {
|
||||
msg = "cdata<%s>: %d";
|
||||
p = (void *)(uintptr_t)*(uint32_t *)p;
|
||||
} else {
|
||||
if (ctype_isptr(ct->info)) {
|
||||
p = cdata_getptr(p, ct->size);
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
}
|
||||
if (ctype_isstruct(ct->info) || ctype_isvector(ct->info)) {
|
||||
/* Handle ctype __tostring metamethod. */
|
||||
cTValue *tv = lj_ctype_meta(cts, ctype_typeid(cts, ct), MM_tostring);
|
||||
if (tv)
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
}
|
||||
}
|
||||
lj_strfmt_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), p);
|
||||
checkgc:
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ffi_pairs(lua_State *L, MMS mm)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkcdata(L, 1)->ctypeid;
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
cTValue *tv;
|
||||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
|
||||
tv = lj_ctype_meta(cts, id, mm);
|
||||
if (!tv)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADMM, strdata(lj_ctype_repr(L, id, NULL)),
|
||||
strdata(mmname_str(G(L), mm)));
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___pairs)
|
||||
{
|
||||
return ffi_pairs(L, MM_pairs);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___ipairs)
|
||||
{
|
||||
return ffi_pairs(L, MM_ipairs);
|
||||
}
|
||||
|
||||
LJLIB_PUSH("ffi") LJLIB_SET(__metatable)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- C library metamethods ----------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi_clib
|
||||
|
||||
/* Index C library by a name. */
|
||||
static TValue *ffi_clib_index(lua_State *L)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
CLibrary *cl;
|
||||
if (!(o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB))
|
||||
lj_err_argt(L, 1, LUA_TUSERDATA);
|
||||
cl = (CLibrary *)uddata(udataV(o));
|
||||
if (!(o+1 < L->top && tvisstr(o+1)))
|
||||
lj_err_argt(L, 2, LUA_TSTRING);
|
||||
return lj_clib_index(L, cl, strV(o+1));
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_clib___index) LJLIB_REC(clib_index 1)
|
||||
{
|
||||
TValue *tv = ffi_clib_index(L);
|
||||
if (tviscdata(tv)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCcdata *cd = cdataV(tv);
|
||||
CType *s = ctype_get(cts, cd->ctypeid);
|
||||
if (ctype_isextern(s->info)) {
|
||||
CTypeID sid = ctype_cid(s->info);
|
||||
void *sp = *(void **)cdataptr(cd);
|
||||
CType *ct = ctype_raw(cts, sid);
|
||||
if (lj_cconv_tv_ct(cts, ct, sid, L->top-1, sp))
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
copyTV(L, L->top-1, tv);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_clib___newindex) LJLIB_REC(clib_index 0)
|
||||
{
|
||||
TValue *tv = ffi_clib_index(L);
|
||||
TValue *o = L->base+2;
|
||||
if (o < L->top && tviscdata(tv)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCcdata *cd = cdataV(tv);
|
||||
CType *d = ctype_get(cts, cd->ctypeid);
|
||||
if (ctype_isextern(d->info)) {
|
||||
CTInfo qual = 0;
|
||||
for (;;) { /* Skip attributes and collect qualifiers. */
|
||||
d = ctype_child(cts, d);
|
||||
if (!ctype_isattrib(d->info)) break;
|
||||
if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size;
|
||||
}
|
||||
if (!((d->info|qual) & CTF_CONST)) {
|
||||
lj_cconv_ct_tv(cts, d, *(void **)cdataptr(cd), o, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
lj_err_caller(L, LJ_ERR_FFI_WRCONST);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_clib___gc)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB)
|
||||
lj_clib_unload((CLibrary *)uddata(udataV(o)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- Callback function metamethods --------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi_callback
|
||||
|
||||
static int ffi_callback_set(lua_State *L, GCfunc *fn)
|
||||
{
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, cd->ctypeid);
|
||||
if (ctype_isptr(ct->info) && (LJ_32 || ct->size == 8)) {
|
||||
MSize slot = lj_ccallback_ptr2slot(cts, *(void **)cdataptr(cd));
|
||||
if (slot < cts->cb.sizeid && cts->cb.cbid[slot] != 0) {
|
||||
GCtab *t = cts->miscmap;
|
||||
TValue *tv = lj_tab_setint(L, t, (int32_t)slot);
|
||||
if (fn) {
|
||||
setfuncV(L, tv, fn);
|
||||
lj_gc_anybarriert(L, t);
|
||||
} else {
|
||||
setnilV(tv);
|
||||
cts->cb.cbid[slot] = 0;
|
||||
cts->cb.topid = slot < cts->cb.topid ? slot : cts->cb.topid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
lj_err_caller(L, LJ_ERR_FFI_BADCBACK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_callback_free)
|
||||
{
|
||||
return ffi_callback_set(L, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_callback_set)
|
||||
{
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 2);
|
||||
return ffi_callback_set(L, fn);
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-1) LJLIB_SET(__index)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- FFI library functions ----------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi
|
||||
|
||||
LJLIB_CF(ffi_cdef)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
CPState cp;
|
||||
int errcode;
|
||||
cp.L = L;
|
||||
cp.cts = ctype_cts(L);
|
||||
cp.srcname = strdata(s);
|
||||
cp.p = strdata(s);
|
||||
cp.param = L->base+1;
|
||||
cp.mode = CPARSE_MODE_MULTI|CPARSE_MODE_DIRECT;
|
||||
errcode = lj_cparse(&cp);
|
||||
if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */
|
||||
lj_gc_check(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_new) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
CTSize sz;
|
||||
CTInfo info = lj_ctype_info(cts, id, &sz);
|
||||
TValue *o = L->base+1;
|
||||
GCcdata *cd;
|
||||
if ((info & CTF_VLA)) {
|
||||
o++;
|
||||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2));
|
||||
}
|
||||
if (sz == CTSIZE_INVALID)
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE);
|
||||
cd = lj_cdata_newx(cts, id, sz, info);
|
||||
setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */
|
||||
lj_cconv_ct_init(cts, ct, sz, cdataptr(cd),
|
||||
o, (MSize)(L->top - o)); /* Initialize cdata. */
|
||||
if (ctype_isstruct(ct->info)) {
|
||||
/* Handle ctype __gc metamethod. Use the fast lookup here. */
|
||||
cTValue *tv = lj_tab_getinth(cts->miscmap, -(int32_t)id);
|
||||
if (tv && tvistab(tv) && (tv = lj_meta_fast(L, tabV(tv), MM_gc))) {
|
||||
GCtab *t = tabref(G(L)->gcroot[GCROOT_FFI_FIN]);
|
||||
if (gcref(t->metatable)) {
|
||||
/* Add to finalizer table, if still enabled. */
|
||||
copyTV(L, lj_tab_set(L, t, o-1), tv);
|
||||
lj_gc_anybarriert(L, t);
|
||||
cd->marked |= LJ_GC_CDATA_FIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
L->top = o; /* Only return the cdata itself. */
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_cast) LJLIB_REC(ffi_new)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CType *d = ctype_raw(cts, id);
|
||||
TValue *o = lj_lib_checkany(L, 2);
|
||||
L->top = o+1; /* Make sure this is the last item on the stack. */
|
||||
if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
if (!(tviscdata(o) && cdataV(o)->ctypeid == id)) {
|
||||
GCcdata *cd = lj_cdata_new(cts, id, d->size);
|
||||
lj_cconv_ct_tv(cts, d, cdataptr(cd), o, CCF_CAST);
|
||||
setcdataV(L, o, cd);
|
||||
lj_gc_check(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_typeof) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, L->base+1);
|
||||
GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
|
||||
*(CTypeID *)cdataptr(cd) = id;
|
||||
setcdataV(L, L->top-1, cd);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Internal and unsupported API. */
|
||||
LJLIB_CF(ffi_typeinfo)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = (CTypeID)ffi_checkint(L, 1);
|
||||
if (id > 0 && id < cts->top) {
|
||||
CType *ct = ctype_get(cts, id);
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "info")), (int32_t)ct->info);
|
||||
if (ct->size != CTSIZE_INVALID)
|
||||
setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "size")), (int32_t)ct->size);
|
||||
if (ct->sib)
|
||||
setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "sib")), (int32_t)ct->sib);
|
||||
if (gcref(ct->name)) {
|
||||
GCstr *s = gco2str(gcref(ct->name));
|
||||
if (isdead(G(L), obj2gco(s))) flipwhite(obj2gco(s));
|
||||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "name")), s);
|
||||
}
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_istype) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id1 = ffi_checkctype(L, cts, NULL);
|
||||
TValue *o = lj_lib_checkany(L, 2);
|
||||
int b = 0;
|
||||
if (tviscdata(o)) {
|
||||
GCcdata *cd = cdataV(o);
|
||||
CTypeID id2 = cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) :
|
||||
cd->ctypeid;
|
||||
CType *ct1 = lj_ctype_rawref(cts, id1);
|
||||
CType *ct2 = lj_ctype_rawref(cts, id2);
|
||||
if (ct1 == ct2) {
|
||||
b = 1;
|
||||
} else if (ctype_type(ct1->info) == ctype_type(ct2->info) &&
|
||||
ct1->size == ct2->size) {
|
||||
if (ctype_ispointer(ct1->info))
|
||||
b = lj_cconv_compatptr(cts, ct1, ct2, CCF_IGNQUAL);
|
||||
else if (ctype_isnum(ct1->info) || ctype_isvoid(ct1->info))
|
||||
b = (((ct1->info ^ ct2->info) & ~(CTF_QUAL|CTF_LONG)) == 0);
|
||||
} else if (ctype_isstruct(ct1->info) && ctype_isptr(ct2->info) &&
|
||||
ct1 == ctype_rawchild(cts, ct2)) {
|
||||
b = 1;
|
||||
}
|
||||
}
|
||||
setboolV(L->top-1, b);
|
||||
setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_sizeof) LJLIB_REC(ffi_xof FF_ffi_sizeof)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CTSize sz;
|
||||
if (LJ_UNLIKELY(tviscdata(L->base) && cdataisv(cdataV(L->base)))) {
|
||||
sz = cdatavlen(cdataV(L->base));
|
||||
} else {
|
||||
CType *ct = lj_ctype_rawref(cts, id);
|
||||
if (ctype_isvltype(ct->info))
|
||||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2));
|
||||
else
|
||||
sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_INVALID;
|
||||
if (LJ_UNLIKELY(sz == CTSIZE_INVALID)) {
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
setintV(L->top-1, (int32_t)sz);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_alignof) LJLIB_REC(ffi_xof FF_ffi_alignof)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CTSize sz = 0;
|
||||
CTInfo info = lj_ctype_info_raw(cts, id, &sz);
|
||||
setintV(L->top-1, 1 << ctype_align(info));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_offsetof) LJLIB_REC(ffi_xof FF_ffi_offsetof)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
GCstr *name = lj_lib_checkstr(L, 2);
|
||||
CType *ct = lj_ctype_rawref(cts, id);
|
||||
CTSize ofs;
|
||||
if (ctype_isstruct(ct->info) && ct->size != CTSIZE_INVALID) {
|
||||
CType *fct = lj_ctype_getfield(cts, ct, name, &ofs);
|
||||
if (fct) {
|
||||
setintV(L->top-1, ofs);
|
||||
if (ctype_isfield(fct->info)) {
|
||||
return 1;
|
||||
} else if (ctype_isbitfield(fct->info)) {
|
||||
setintV(L->top++, ctype_bitpos(fct->info));
|
||||
setintV(L->top++, ctype_bitbsz(fct->info));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_errno) LJLIB_REC(.)
|
||||
{
|
||||
int err = errno;
|
||||
if (L->top > L->base)
|
||||
errno = ffi_checkint(L, 1);
|
||||
setintV(L->top++, err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_string) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
const char *p;
|
||||
size_t len;
|
||||
if (o+1 < L->top && !tvisnil(o+1)) {
|
||||
len = (size_t)ffi_checkint(L, 2);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, o,
|
||||
CCF_ARG(1));
|
||||
} else {
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CCHAR), (uint8_t *)&p, o,
|
||||
CCF_ARG(1));
|
||||
len = strlen(p);
|
||||
}
|
||||
L->top = o+1; /* Make sure this is the last item on the stack. */
|
||||
setstrV(L, o, lj_str_new(L, p, len));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_copy) LJLIB_REC(.)
|
||||
{
|
||||
void *dp = ffi_checkptr(L, 1, CTID_P_VOID);
|
||||
void *sp = ffi_checkptr(L, 2, CTID_P_CVOID);
|
||||
TValue *o = L->base+1;
|
||||
CTSize len;
|
||||
if (tvisstr(o) && o+1 >= L->top)
|
||||
len = strV(o)->len+1; /* Copy Lua string including trailing '\0'. */
|
||||
else
|
||||
len = (CTSize)ffi_checkint(L, 3);
|
||||
memcpy(dp, sp, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_fill) LJLIB_REC(.)
|
||||
{
|
||||
void *dp = ffi_checkptr(L, 1, CTID_P_VOID);
|
||||
CTSize len = (CTSize)ffi_checkint(L, 2);
|
||||
int32_t fill = 0;
|
||||
if (L->base+2 < L->top && !tvisnil(L->base+2)) fill = ffi_checkint(L, 3);
|
||||
memset(dp, fill, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test ABI string. */
|
||||
LJLIB_CF(ffi_abi) LJLIB_REC(.)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
int b = lj_cparse_case(s,
|
||||
#if LJ_64
|
||||
"\00564bit"
|
||||
#else
|
||||
"\00532bit"
|
||||
#endif
|
||||
#if LJ_ARCH_HASFPU
|
||||
"\003fpu"
|
||||
#endif
|
||||
#if LJ_ABI_SOFTFP
|
||||
"\006softfp"
|
||||
#else
|
||||
"\006hardfp"
|
||||
#endif
|
||||
#if LJ_ABI_EABI
|
||||
"\004eabi"
|
||||
#endif
|
||||
#if LJ_ABI_WIN
|
||||
"\003win"
|
||||
#endif
|
||||
#if LJ_ABI_PAUTH
|
||||
"\005pauth"
|
||||
#endif
|
||||
#if LJ_TARGET_UWP
|
||||
"\003uwp"
|
||||
#endif
|
||||
#if LJ_LE
|
||||
"\002le"
|
||||
#else
|
||||
"\002be"
|
||||
#endif
|
||||
#if LJ_GC64
|
||||
"\004gc64"
|
||||
#endif
|
||||
) >= 0;
|
||||
setboolV(L->top-1, b);
|
||||
setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to miscmap table. */
|
||||
|
||||
LJLIB_CF(ffi_metatype)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
GCtab *mt = lj_lib_checktab(L, 2);
|
||||
GCtab *t = cts->miscmap;
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
TValue *tv;
|
||||
GCcdata *cd;
|
||||
if (!(ctype_isstruct(ct->info) || ctype_iscomplex(ct->info) ||
|
||||
ctype_isvector(ct->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
tv = lj_tab_setinth(L, t, -(int32_t)ctype_typeid(cts, ct));
|
||||
if (!tvisnil(tv))
|
||||
lj_err_caller(L, LJ_ERR_PROTMT);
|
||||
settabV(L, tv, mt);
|
||||
lj_gc_anybarriert(L, t);
|
||||
cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
|
||||
*(CTypeID *)cdataptr(cd) = id;
|
||||
setcdataV(L, L->top-1, cd);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_gc) LJLIB_REC(.)
|
||||
{
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
TValue *fin = lj_lib_checkany(L, 2);
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, cd->ctypeid);
|
||||
if (!(ctype_isptr(ct->info) || ctype_isstruct(ct->info) ||
|
||||
ctype_isrefarray(ct->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
lj_cdata_setfin(L, cd, gcval(fin), itype(fin));
|
||||
L->top = L->base+1; /* Pass through the cdata object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */
|
||||
|
||||
LJLIB_CF(ffi_load)
|
||||
{
|
||||
GCstr *name = lj_lib_checkstr(L, 1);
|
||||
int global = (L->base+1 < L->top && tvistruecond(L->base+1));
|
||||
lj_clib_load(L, tabref(curr_func(L)->c.env), name, global);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-4) LJLIB_SET(C)
|
||||
LJLIB_PUSH(top-3) LJLIB_SET(os)
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(arch)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Register FFI module as loaded. */
|
||||
static void ffi_register_module(lua_State *L)
|
||||
{
|
||||
cTValue *tmp = lj_tab_getstr(tabV(registry(L)), lj_str_newlit(L, "_LOADED"));
|
||||
if (tmp && tvistab(tmp)) {
|
||||
GCtab *t = tabV(tmp);
|
||||
copyTV(L, lj_tab_setstr(L, t, lj_str_newlit(L, LUA_FFILIBNAME)), L->top-1);
|
||||
lj_gc_anybarriert(L, t);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_ffi(lua_State *L)
|
||||
{
|
||||
CTState *cts = lj_ctype_init(L);
|
||||
settabV(L, L->top++, (cts->miscmap = lj_tab_new(L, 0, 1)));
|
||||
LJ_LIB_REG(L, NULL, ffi_meta);
|
||||
/* NOBARRIER: basemt is a GC root. */
|
||||
setgcref(basemt_it(G(L), LJ_TCDATA), obj2gco(tabV(L->top-1)));
|
||||
LJ_LIB_REG(L, NULL, ffi_clib);
|
||||
LJ_LIB_REG(L, NULL, ffi_callback);
|
||||
/* NOBARRIER: the key is new and lj_tab_newkey() handles the barrier. */
|
||||
settabV(L, lj_tab_setstr(L, cts->miscmap, &cts->g->strempty), tabV(L->top-1));
|
||||
L->top--;
|
||||
lj_clib_default(L, tabV(L->top-1)); /* Create ffi.C default namespace. */
|
||||
lua_pushliteral(L, LJ_OS_NAME);
|
||||
lua_pushliteral(L, LJ_ARCH_NAME);
|
||||
LJ_LIB_REG(L, NULL, ffi); /* Note: no global "ffi" created! */
|
||||
ffi_register_module(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
** Library initialization.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major parts taken verbatim from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_init_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_arch.h"
|
||||
|
||||
static const luaL_Reg lj_lib_load[] = {
|
||||
{ "", luaopen_base },
|
||||
#if !LJ_SRB2LIB
|
||||
{ LUA_LOADLIBNAME, luaopen_package },
|
||||
#endif
|
||||
{ LUA_TABLIBNAME, luaopen_table },
|
||||
{ LUA_IOLIBNAME, luaopen_io },
|
||||
#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2OS)
|
||||
{ LUA_OSLIBNAME, luaopen_os },
|
||||
#endif
|
||||
{ LUA_STRLIBNAME, luaopen_string },
|
||||
#if !LJ_SRB2LIB
|
||||
{ LUA_MATHLIBNAME, luaopen_math },
|
||||
#endif
|
||||
#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2DEBUG)
|
||||
{ LUA_DBLIBNAME, luaopen_debug },
|
||||
#endif
|
||||
#if !LJ_SRB2LIB
|
||||
{ LUA_BITLIBNAME, luaopen_bit },
|
||||
#endif
|
||||
{ LUA_JITLIBNAME, luaopen_jit },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg lj_lib_preload[] = {
|
||||
#if LJ_HASFFI
|
||||
{ LUA_FFILIBNAME, luaopen_ffi },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
LUALIB_API void luaL_openlibs(lua_State *L)
|
||||
{
|
||||
const luaL_Reg *lib;
|
||||
for (lib = lj_lib_load; lib->func; lib++) {
|
||||
lua_pushcfunction(L, lib->func);
|
||||
lua_pushstring(L, lib->name);
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD",
|
||||
sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1);
|
||||
for (lib = lj_lib_preload; lib->func; lib++) {
|
||||
lua_pushcfunction(L, lib->func);
|
||||
lua_setfield(L, -2, lib->name);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
** I/O library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_io_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_strfmt.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* Userdata payload for I/O file. */
|
||||
typedef struct IOFileUD {
|
||||
FILE *fp; /* File handle. */
|
||||
uint32_t type; /* File type. */
|
||||
} IOFileUD;
|
||||
|
||||
#define IOFILE_TYPE_FILE 0 /* Regular file. */
|
||||
#define IOFILE_TYPE_PIPE 1 /* Pipe. */
|
||||
#define IOFILE_TYPE_STDF 2 /* Standard file handle. */
|
||||
#define IOFILE_TYPE_MASK 3
|
||||
|
||||
#define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */
|
||||
|
||||
#define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud)
|
||||
#define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id))))
|
||||
|
||||
/* -- Open/close helpers -------------------------------------------------- */
|
||||
|
||||
static IOFileUD *io_tofilep(lua_State *L)
|
||||
{
|
||||
if (!(L->base < L->top && tvisudata(L->base) &&
|
||||
udataV(L->base)->udtype == UDTYPE_IO_FILE))
|
||||
lj_err_argtype(L, 1, "FILE*");
|
||||
return (IOFileUD *)uddata(udataV(L->base));
|
||||
}
|
||||
|
||||
static IOFileUD *io_tofile(lua_State *L)
|
||||
{
|
||||
IOFileUD *iof = io_tofilep(L);
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOCLFL);
|
||||
return iof;
|
||||
}
|
||||
|
||||
#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO)
|
||||
static IOFileUD *io_stdfile(lua_State *L, ptrdiff_t id)
|
||||
{
|
||||
IOFileUD *iof = IOSTDF_IOF(L, id);
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOSTDCL);
|
||||
return iof;
|
||||
}
|
||||
#endif
|
||||
|
||||
static IOFileUD *io_file_new(lua_State *L)
|
||||
{
|
||||
IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
|
||||
GCudata *ud = udataV(L->top-1);
|
||||
ud->udtype = UDTYPE_IO_FILE;
|
||||
/* NOBARRIER: The GCudata is new (marked white). */
|
||||
setgcrefr(ud->metatable, curr_func(L)->c.env);
|
||||
iof->fp = NULL;
|
||||
iof->type = IOFILE_TYPE_FILE;
|
||||
return iof;
|
||||
}
|
||||
|
||||
#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO)
|
||||
static IOFileUD *io_file_open(lua_State *L, const char *mode)
|
||||
{
|
||||
const char *fname = strdata(lj_lib_checkstr(L, 1));
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
iof->fp = fopen(fname, mode);
|
||||
if (iof->fp == NULL)
|
||||
luaL_argerror(L, 1, lj_strfmt_pushf(L, "%s: %s", fname, strerror(errno)));
|
||||
return iof;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int io_file_close(lua_State *L, IOFileUD *iof)
|
||||
{
|
||||
int ok;
|
||||
if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) {
|
||||
ok = (fclose(iof->fp) == 0);
|
||||
} else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) {
|
||||
int stat = -1;
|
||||
#if LJ_TARGET_POSIX
|
||||
stat = pclose(iof->fp);
|
||||
#elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP
|
||||
stat = _pclose(iof->fp);
|
||||
#endif
|
||||
#if LJ_52
|
||||
iof->fp = NULL;
|
||||
return luaL_execresult(L, stat);
|
||||
#else
|
||||
ok = (stat != -1);
|
||||
#endif
|
||||
} else {
|
||||
lj_assertL((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF,
|
||||
"close of unknown FILE* type");
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "cannot close standard file");
|
||||
return 2;
|
||||
}
|
||||
iof->fp = NULL;
|
||||
return luaL_fileresult(L, ok, NULL);
|
||||
}
|
||||
|
||||
/* -- Read/write helpers -------------------------------------------------- */
|
||||
|
||||
static int io_file_readnum(lua_State *L, FILE *fp)
|
||||
{
|
||||
lua_Number d;
|
||||
if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) {
|
||||
if (LJ_DUALNUM) {
|
||||
int32_t i = lj_num2int(d);
|
||||
if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) {
|
||||
setintV(L->top++, i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
setnumV(L->top++, d);
|
||||
return 1;
|
||||
} else {
|
||||
setnilV(L->top++);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int io_file_readline(lua_State *L, FILE *fp, MSize chop)
|
||||
{
|
||||
MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0;
|
||||
char *buf;
|
||||
for (;;) {
|
||||
buf = lj_buf_tmp(L, m);
|
||||
if (fgets(buf+n, m-n, fp) == NULL) break;
|
||||
n += (MSize)strlen(buf+n);
|
||||
ok |= n;
|
||||
if (n && buf[n-1] == '\n') { n -= chop; break; }
|
||||
if (n >= m - 64) m += m;
|
||||
}
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
|
||||
lj_gc_check(L);
|
||||
return (int)ok;
|
||||
}
|
||||
|
||||
static void io_file_readall(lua_State *L, FILE *fp)
|
||||
{
|
||||
MSize m, n;
|
||||
for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) {
|
||||
char *buf = lj_buf_tmp(L, m);
|
||||
n += (MSize)fread(buf+n, 1, m-n, fp);
|
||||
if (n != m) {
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
|
||||
lj_gc_check(L);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int io_file_readlen(lua_State *L, FILE *fp, MSize m)
|
||||
{
|
||||
if (m) {
|
||||
char *buf = lj_buf_tmp(L, m);
|
||||
MSize n = (MSize)fread(buf, 1, m, fp);
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
|
||||
lj_gc_check(L);
|
||||
return n > 0;
|
||||
} else {
|
||||
int c = getc(fp);
|
||||
ungetc(c, fp);
|
||||
setstrV(L, L->top++, &G(L)->strempty);
|
||||
return (c != EOF);
|
||||
}
|
||||
}
|
||||
|
||||
static int io_file_read(lua_State *L, IOFileUD *iof, int start)
|
||||
{
|
||||
FILE *fp = iof->fp;
|
||||
int ok, n, nargs = (int)(L->top - L->base) - start;
|
||||
clearerr(fp);
|
||||
if (nargs == 0) {
|
||||
ok = io_file_readline(L, fp, 1);
|
||||
n = start+1; /* Return 1 result. */
|
||||
} else {
|
||||
/* The results plus the buffers go on top of the args. */
|
||||
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
|
||||
ok = 1;
|
||||
for (n = start; nargs-- && ok; n++) {
|
||||
if (tvisstr(L->base+n)) {
|
||||
const char *p = strVdata(L->base+n);
|
||||
if (p[0] == '*') p++;
|
||||
if (p[0] == 'n')
|
||||
ok = io_file_readnum(L, fp);
|
||||
else if ((p[0] & ~0x20) == 'L')
|
||||
ok = io_file_readline(L, fp, (p[0] == 'l'));
|
||||
else if (p[0] == 'a')
|
||||
io_file_readall(L, fp);
|
||||
else
|
||||
lj_err_arg(L, n+1, LJ_ERR_INVFMT);
|
||||
} else if (tvisnumber(L->base+n)) {
|
||||
ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1));
|
||||
} else {
|
||||
lj_err_arg(L, n+1, LJ_ERR_INVOPT);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ferror(fp))
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
if (!ok)
|
||||
setnilV(L->top-1); /* Replace last result with nil. */
|
||||
return n - start;
|
||||
}
|
||||
|
||||
static int io_file_write(lua_State *L, IOFileUD *iof, int start)
|
||||
{
|
||||
FILE *fp = iof->fp;
|
||||
cTValue *tv;
|
||||
int status = 1;
|
||||
for (tv = L->base+start; tv < L->top; tv++) {
|
||||
MSize len;
|
||||
const char *p = lj_strfmt_wstrnum(L, tv, &len);
|
||||
if (!p)
|
||||
lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING);
|
||||
status = status && (fwrite(p, 1, len, fp) == len);
|
||||
}
|
||||
if (LJ_52 && status) {
|
||||
L->top = L->base+1;
|
||||
if (start == 0)
|
||||
setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT));
|
||||
return 1;
|
||||
}
|
||||
return luaL_fileresult(L, status, NULL);
|
||||
}
|
||||
|
||||
static int io_file_iter(lua_State *L)
|
||||
{
|
||||
GCfunc *fn = curr_func(L);
|
||||
IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0]));
|
||||
int n = fn->c.nupvalues - 1;
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOCLFL);
|
||||
L->top = L->base;
|
||||
if (n) { /* Copy upvalues with options to stack. */
|
||||
lj_state_checkstack(L, (MSize)n);
|
||||
memcpy(L->top, &fn->c.upvalue[1], n*sizeof(TValue));
|
||||
L->top += n;
|
||||
}
|
||||
n = io_file_read(L, iof, 0);
|
||||
if (ferror(iof->fp))
|
||||
lj_err_callermsg(L, strVdata(L->top-2));
|
||||
if (tvisnil(L->base) && (iof->type & IOFILE_FLAG_CLOSE)) {
|
||||
io_file_close(L, iof); /* Return values are ignored. */
|
||||
return 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int io_file_lines(lua_State *L)
|
||||
{
|
||||
int n = (int)(L->top - L->base);
|
||||
if (n > LJ_MAX_UPVAL)
|
||||
lj_err_caller(L, LJ_ERR_UNPACK);
|
||||
lua_pushcclosure(L, io_file_iter, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* -- I/O file methods ---------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_io_method
|
||||
|
||||
LJLIB_CF(io_method_close)
|
||||
{
|
||||
IOFileUD *iof;
|
||||
if (L->base < L->top) {
|
||||
iof = io_tofile(L);
|
||||
} else {
|
||||
iof = IOSTDF_IOF(L, GCROOT_IO_OUTPUT);
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOCLFL);
|
||||
}
|
||||
return io_file_close(L, iof);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_read)
|
||||
{
|
||||
return io_file_read(L, io_tofile(L), 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_write) LJLIB_REC(io_write 0)
|
||||
{
|
||||
return io_file_write(L, io_tofile(L), 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_flush) LJLIB_REC(io_flush 0)
|
||||
{
|
||||
return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL);
|
||||
}
|
||||
|
||||
#if LJ_32 && defined(__ANDROID__) && __ANDROID_API__ < 24
|
||||
/* The Android NDK is such an unmatched marvel of engineering. */
|
||||
extern int fseeko32(FILE *, long int, int) __asm__("fseeko");
|
||||
extern long int ftello32(FILE *) __asm__("ftello");
|
||||
#define fseeko(fp, pos, whence) (fseeko32((fp), (pos), (whence)))
|
||||
#define ftello(fp) (ftello32((fp)))
|
||||
#endif
|
||||
|
||||
LJLIB_CF(io_method_seek)
|
||||
{
|
||||
FILE *fp = io_tofile(L)->fp;
|
||||
int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
|
||||
int64_t ofs = 0;
|
||||
cTValue *o;
|
||||
int res;
|
||||
if (opt == 0) opt = SEEK_SET;
|
||||
else if (opt == 1) opt = SEEK_CUR;
|
||||
else if (opt == 2) opt = SEEK_END;
|
||||
o = L->base+2;
|
||||
if (o < L->top) {
|
||||
if (tvisint(o))
|
||||
ofs = (int64_t)intV(o);
|
||||
else if (tvisnum(o))
|
||||
ofs = (int64_t)numV(o);
|
||||
else if (!tvisnil(o))
|
||||
lj_err_argt(L, 3, LUA_TNUMBER);
|
||||
}
|
||||
#if LJ_TARGET_POSIX
|
||||
res = fseeko(fp, ofs, opt);
|
||||
#elif _MSC_VER >= 1400
|
||||
res = _fseeki64(fp, ofs, opt);
|
||||
#elif defined(__MINGW32__)
|
||||
res = fseeko64(fp, ofs, opt);
|
||||
#else
|
||||
res = fseek(fp, (long)ofs, opt);
|
||||
#endif
|
||||
if (res)
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
#if LJ_TARGET_POSIX
|
||||
ofs = ftello(fp);
|
||||
#elif _MSC_VER >= 1400
|
||||
ofs = _ftelli64(fp);
|
||||
#elif defined(__MINGW32__)
|
||||
ofs = ftello64(fp);
|
||||
#else
|
||||
ofs = (int64_t)ftell(fp);
|
||||
#endif
|
||||
setint64V(L->top-1, ofs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_setvbuf)
|
||||
{
|
||||
FILE *fp = io_tofile(L)->fp;
|
||||
int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no");
|
||||
size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE);
|
||||
if (opt == 0) opt = _IOFBF;
|
||||
else if (opt == 1) opt = _IOLBF;
|
||||
else if (opt == 2) opt = _IONBF;
|
||||
return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_lines)
|
||||
{
|
||||
io_tofile(L);
|
||||
return io_file_lines(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method___gc)
|
||||
{
|
||||
IOFileUD *iof = io_tofilep(L);
|
||||
if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF)
|
||||
io_file_close(L, iof);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method___tostring)
|
||||
{
|
||||
IOFileUD *iof = io_tofilep(L);
|
||||
if (iof->fp != NULL)
|
||||
lua_pushfstring(L, "file (%p)", iof->fp);
|
||||
else
|
||||
lua_pushliteral(L, "file (closed)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-1) LJLIB_SET(__index)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- I/O library functions ----------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_io
|
||||
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
|
||||
|
||||
LJLIB_CF(io_open)
|
||||
{
|
||||
const char *fname = strdata(lj_lib_checkstr(L, 1));
|
||||
GCstr *s = lj_lib_optstr(L, 2);
|
||||
const char *mode = s ? strdata(s) : "r";
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
iof->fp = fopen(fname, mode);
|
||||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
|
||||
}
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(io_popen)
|
||||
{
|
||||
#if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP)
|
||||
const char *fname = strdata(lj_lib_checkstr(L, 1));
|
||||
GCstr *s = lj_lib_optstr(L, 2);
|
||||
const char *mode = s ? strdata(s) : "r";
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
iof->type = IOFILE_TYPE_PIPE;
|
||||
#if LJ_TARGET_POSIX
|
||||
fflush(NULL);
|
||||
iof->fp = popen(fname, mode);
|
||||
#else
|
||||
iof->fp = _popen(fname, mode);
|
||||
#endif
|
||||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
|
||||
#else
|
||||
return luaL_error(L, LUA_QL("popen") " not supported");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(io_tmpfile)
|
||||
{
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA || LJ_TARGET_NX
|
||||
iof->fp = NULL; errno = ENOSYS;
|
||||
#else
|
||||
iof->fp = tmpfile();
|
||||
#endif
|
||||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_close)
|
||||
{
|
||||
return lj_cf_io_method_close(L);
|
||||
}
|
||||
|
||||
#if !LJ_SRB2LIB || (LJ_SRB2LIB && LJ_SRB2IO)
|
||||
LJLIB_CF(io_read)
|
||||
{
|
||||
return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_write) LJLIB_REC(io_write GCROOT_IO_OUTPUT)
|
||||
{
|
||||
return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_flush) LJLIB_REC(io_flush GCROOT_IO_OUTPUT)
|
||||
{
|
||||
return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)->fp) == 0, NULL);
|
||||
}
|
||||
|
||||
static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode)
|
||||
{
|
||||
if (L->base < L->top && !tvisnil(L->base)) {
|
||||
if (tvisudata(L->base)) {
|
||||
io_tofile(L);
|
||||
L->top = L->base+1;
|
||||
} else {
|
||||
io_file_open(L, mode);
|
||||
}
|
||||
/* NOBARRIER: The standard I/O handles are GC roots. */
|
||||
setgcref(G(L)->gcroot[id], gcV(L->top-1));
|
||||
} else {
|
||||
setudataV(L, L->top++, IOSTDF_UD(L, id));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_input)
|
||||
{
|
||||
return io_std_getset(L, GCROOT_IO_INPUT, "r");
|
||||
}
|
||||
|
||||
LJLIB_CF(io_output)
|
||||
{
|
||||
return io_std_getset(L, GCROOT_IO_OUTPUT, "w");
|
||||
}
|
||||
|
||||
LJLIB_CF(io_lines)
|
||||
{
|
||||
if (L->base == L->top) setnilV(L->top++);
|
||||
if (!tvisnil(L->base)) { /* io.lines(fname) */
|
||||
IOFileUD *iof = io_file_open(L, "r");
|
||||
iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE;
|
||||
L->top--;
|
||||
setudataV(L, L->base, udataV(L->top));
|
||||
} else { /* io.lines() iterates over stdin. */
|
||||
setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT));
|
||||
}
|
||||
return io_file_lines(L);
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(io_type)
|
||||
{
|
||||
cTValue *o = lj_lib_checkany(L, 1);
|
||||
if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE))
|
||||
setnilV(L->top++);
|
||||
else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL)
|
||||
lua_pushliteral(L, "file");
|
||||
else
|
||||
lua_pushliteral(L, "closed file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name)
|
||||
{
|
||||
IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
|
||||
GCudata *ud = udataV(L->top-1);
|
||||
ud->udtype = UDTYPE_IO_FILE;
|
||||
/* NOBARRIER: The GCudata is new (marked white). */
|
||||
setgcref(ud->metatable, gcV(L->top-3));
|
||||
iof->fp = fp;
|
||||
iof->type = IOFILE_TYPE_STDF;
|
||||
lua_setfield(L, -2, name);
|
||||
return obj2gco(ud);
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_io(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, NULL, io_method);
|
||||
copyTV(L, L->top, L->top-1); L->top++;
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
|
||||
LJ_LIB_REG(L, LUA_IOLIBNAME, io);
|
||||
setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin"));
|
||||
setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout"));
|
||||
io_std_new(L, stderr, "stderr");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
** JIT library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lib_jit_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#endif
|
||||
#if LJ_HASJIT
|
||||
#include "lj_ir.h"
|
||||
#include "lj_jit.h"
|
||||
#include "lj_ircall.h"
|
||||
#include "lj_iropt.h"
|
||||
#include "lj_target.h"
|
||||
#endif
|
||||
#include "lj_trace.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_vm.h"
|
||||
#include "lj_vmevent.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
#include "luajit.h"
|
||||
|
||||
/* -- jit.* functions ----------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_jit
|
||||
|
||||
static int setjitmode(lua_State *L, int mode)
|
||||
{
|
||||
int idx = 0;
|
||||
if (L->base == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */
|
||||
mode |= LUAJIT_MODE_ENGINE;
|
||||
} else {
|
||||
/* jit.on/off/flush(func|proto, nil|true|false) */
|
||||
if (tvisfunc(L->base) || tvisproto(L->base))
|
||||
idx = 1;
|
||||
else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */
|
||||
goto err;
|
||||
if (L->base+1 < L->top && tvisbool(L->base+1))
|
||||
mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC;
|
||||
else
|
||||
mode |= LUAJIT_MODE_FUNC;
|
||||
}
|
||||
if (luaJIT_setmode(L, idx, mode) != 1) {
|
||||
if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE)
|
||||
lj_err_caller(L, LJ_ERR_NOJIT);
|
||||
err:
|
||||
lj_err_argt(L, 1, LUA_TFUNCTION);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_on)
|
||||
{
|
||||
return setjitmode(L, LUAJIT_MODE_ON);
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_off)
|
||||
{
|
||||
return setjitmode(L, LUAJIT_MODE_OFF);
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_flush)
|
||||
{
|
||||
#if LJ_HASJIT
|
||||
if (L->base < L->top && tvisnumber(L->base)) {
|
||||
int traceno = lj_lib_checkint(L, 1);
|
||||
luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return setjitmode(L, LUAJIT_MODE_FLUSH);
|
||||
}
|
||||
|
||||
#if LJ_HASJIT
|
||||
/* Push a string for every flag bit that is set. */
|
||||
static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base,
|
||||
const char *str)
|
||||
{
|
||||
for (; *str; base <<= 1, str += 1+*str)
|
||||
if (flags & base)
|
||||
setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str));
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(jit_status)
|
||||
{
|
||||
#if LJ_HASJIT
|
||||
jit_State *J = L2J(L);
|
||||
L->top = L->base;
|
||||
setboolV(L->top++, (J->flags & JIT_F_ON) ? 1 : 0);
|
||||
flagbits_to_strings(L, J->flags, JIT_F_CPU, JIT_F_CPUSTRING);
|
||||
flagbits_to_strings(L, J->flags, JIT_F_OPT, JIT_F_OPTSTRING);
|
||||
return (int)(L->top - L->base);
|
||||
#else
|
||||
setboolV(L->top++, 0);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_security)
|
||||
{
|
||||
int idx = lj_lib_checkopt(L, 1, -1, LJ_SECURITY_MODESTRING);
|
||||
setintV(L->top++, ((LJ_SECURITY_MODE >> (2*idx)) & 3));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_attach)
|
||||
{
|
||||
#ifdef LUAJIT_DISABLE_VMEVENT
|
||||
luaL_error(L, "vmevent API disabled");
|
||||
#else
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 1);
|
||||
GCstr *s = lj_lib_optstr(L, 2);
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
|
||||
if (s) { /* Attach to given event. */
|
||||
const uint8_t *p = (const uint8_t *)strdata(s);
|
||||
uint32_t h = s->len;
|
||||
while (*p) h = h ^ (lj_rol(h, 6) + *p++);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_rawseti(L, -2, VMEVENT_HASHIDX(h));
|
||||
G(L)->vmevmask = VMEVENT_NOCACHE; /* Invalidate cache. */
|
||||
} else { /* Detach if no event given. */
|
||||
setnilV(L->top++);
|
||||
while (lua_next(L, -2)) {
|
||||
L->top--;
|
||||
if (tvisfunc(L->top) && funcV(L->top) == fn) {
|
||||
setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-5) LJLIB_SET(os)
|
||||
LJLIB_PUSH(top-4) LJLIB_SET(arch)
|
||||
LJLIB_PUSH(top-3) LJLIB_SET(version_num)
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(version)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- jit.util.* functions ------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_jit_util
|
||||
|
||||
/* -- Reflection API for Lua functions ------------------------------------ */
|
||||
|
||||
static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val)
|
||||
{
|
||||
setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val);
|
||||
}
|
||||
|
||||
/* local info = jit.util.funcinfo(func [,pc]) */
|
||||
LJLIB_CF(jit_util_funcinfo)
|
||||
{
|
||||
GCproto *pt = lj_lib_checkLproto(L, 1, 1);
|
||||
if (pt) {
|
||||
BCPos pc = (BCPos)lj_lib_optint(L, 2, 0);
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 16); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
setintfield(L, t, "linedefined", pt->firstline);
|
||||
setintfield(L, t, "lastlinedefined", pt->firstline + pt->numline);
|
||||
setintfield(L, t, "stackslots", pt->framesize);
|
||||
setintfield(L, t, "params", pt->numparams);
|
||||
setintfield(L, t, "bytecodes", (int32_t)pt->sizebc);
|
||||
setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc);
|
||||
setintfield(L, t, "nconsts", (int32_t)pt->sizekn);
|
||||
setintfield(L, t, "upvalues", (int32_t)pt->sizeuv);
|
||||
if (pc < pt->sizebc)
|
||||
setintfield(L, t, "currentline", lj_debug_line(pt, pc));
|
||||
lua_pushboolean(L, (pt->flags & PROTO_VARARG));
|
||||
lua_setfield(L, -2, "isvararg");
|
||||
lua_pushboolean(L, (pt->flags & PROTO_CHILD));
|
||||
lua_setfield(L, -2, "children");
|
||||
setstrV(L, L->top++, proto_chunkname(pt));
|
||||
lua_setfield(L, -2, "source");
|
||||
lj_debug_pushloc(L, pt, pc);
|
||||
lua_setfield(L, -2, "loc");
|
||||
setprotoV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "proto")), pt);
|
||||
} else {
|
||||
GCfunc *fn = funcV(L->base);
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
if (!iscfunc(fn))
|
||||
setintfield(L, t, "ffid", fn->c.ffid);
|
||||
setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")),
|
||||
(intptr_t)(void *)fn->c.f);
|
||||
setintfield(L, t, "upvalues", fn->c.nupvalues);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* local ins, m = jit.util.funcbc(func, pc) */
|
||||
LJLIB_CF(jit_util_funcbc)
|
||||
{
|
||||
GCproto *pt = lj_lib_checkLproto(L, 1, 0);
|
||||
BCPos pc = (BCPos)lj_lib_checkint(L, 2);
|
||||
if (pc < pt->sizebc) {
|
||||
BCIns ins = proto_bc(pt)[pc];
|
||||
BCOp op = bc_op(ins);
|
||||
lj_assertL(op < BC__MAX, "bad bytecode op %d", op);
|
||||
setintV(L->top, ins);
|
||||
setintV(L->top+1, lj_bc_mode[op]);
|
||||
L->top += 2;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local k = jit.util.funck(func, idx) */
|
||||
LJLIB_CF(jit_util_funck)
|
||||
{
|
||||
GCproto *pt = lj_lib_checkLproto(L, 1, 0);
|
||||
ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2);
|
||||
if (idx >= 0) {
|
||||
if (idx < (ptrdiff_t)pt->sizekn) {
|
||||
copyTV(L, L->top-1, proto_knumtv(pt, idx));
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (~idx < (ptrdiff_t)pt->sizekgc) {
|
||||
GCobj *gc = proto_kgc(pt, idx);
|
||||
setgcV(L, L->top-1, gc, ~gc->gch.gct);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local name = jit.util.funcuvname(func, idx) */
|
||||
LJLIB_CF(jit_util_funcuvname)
|
||||
{
|
||||
GCproto *pt = lj_lib_checkLproto(L, 1, 0);
|
||||
uint32_t idx = (uint32_t)lj_lib_checkint(L, 2);
|
||||
if (idx < pt->sizeuv) {
|
||||
setstrV(L, L->top-1, lj_str_newz(L, lj_debug_uvname(pt, idx)));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- Reflection API for traces ------------------------------------------- */
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
/* Check trace argument. Must not throw for non-existent trace numbers. */
|
||||
static GCtrace *jit_checktrace(lua_State *L)
|
||||
{
|
||||
TraceNo tr = (TraceNo)lj_lib_checkint(L, 1);
|
||||
jit_State *J = L2J(L);
|
||||
if (tr > 0 && tr < J->sizetrace)
|
||||
return traceref(J, tr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Names of link types. ORDER LJ_TRLINK */
|
||||
static const char *const jit_trlinkname[] = {
|
||||
"none", "root", "loop", "tail-recursion", "up-recursion", "down-recursion",
|
||||
"interpreter", "return", "stitch"
|
||||
};
|
||||
|
||||
/* local info = jit.util.traceinfo(tr) */
|
||||
LJLIB_CF(jit_util_traceinfo)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
if (T) {
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 8); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1);
|
||||
setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk);
|
||||
setintfield(L, t, "link", T->link);
|
||||
setintfield(L, t, "nexit", T->nsnap);
|
||||
setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype]));
|
||||
lua_setfield(L, -2, "linktype");
|
||||
/* There are many more fields. Add them only when needed. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */
|
||||
LJLIB_CF(jit_util_traceir)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
|
||||
if (T && ref >= REF_BIAS && ref < T->nins) {
|
||||
IRIns *ir = &T->ir[ref];
|
||||
int32_t m = lj_ir_mode[ir->o];
|
||||
setintV(L->top-2, m);
|
||||
setintV(L->top-1, ir->ot);
|
||||
setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0));
|
||||
setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0));
|
||||
setintV(L->top++, ir->prev);
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local k, t [, slot] = jit.util.tracek(tr, idx) */
|
||||
LJLIB_CF(jit_util_tracek)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
|
||||
if (T && ref >= T->nk && ref < REF_BIAS) {
|
||||
IRIns *ir = &T->ir[ref];
|
||||
int32_t slot = -1;
|
||||
if (ir->o == IR_KSLOT) {
|
||||
slot = ir->op2;
|
||||
ir = &T->ir[ir->op1];
|
||||
}
|
||||
#if LJ_HASFFI
|
||||
if (ir->o == IR_KINT64) ctype_loadffi(L);
|
||||
#endif
|
||||
lj_ir_kvalue(L, L->top-2, ir);
|
||||
setintV(L->top-1, (int32_t)irt_type(ir->t));
|
||||
if (slot == -1)
|
||||
return 2;
|
||||
setintV(L->top++, slot);
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local snap = jit.util.tracesnap(tr, sn) */
|
||||
LJLIB_CF(jit_util_tracesnap)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
SnapNo sn = (SnapNo)lj_lib_checkint(L, 2);
|
||||
if (T && sn < T->nsnap) {
|
||||
SnapShot *snap = &T->snap[sn];
|
||||
SnapEntry *map = &T->snapmap[snap->mapofs];
|
||||
MSize n, nent = snap->nent;
|
||||
GCtab *t;
|
||||
lua_createtable(L, nent+2, 0);
|
||||
t = tabV(L->top-1);
|
||||
setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS);
|
||||
setintV(lj_tab_setint(L, t, 1), (int32_t)snap->nslots);
|
||||
for (n = 0; n < nent; n++)
|
||||
setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]);
|
||||
setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local mcode, addr, loop = jit.util.tracemc(tr) */
|
||||
LJLIB_CF(jit_util_tracemc)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
if (T && T->mcode != NULL) {
|
||||
setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode));
|
||||
setintptrV(L->top++, (intptr_t)(void *)T->mcode);
|
||||
setintV(L->top++, T->mcloop);
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local addr = jit.util.traceexitstub([tr,] exitno) */
|
||||
LJLIB_CF(jit_util_traceexitstub)
|
||||
{
|
||||
#ifdef EXITSTUBS_PER_GROUP
|
||||
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1);
|
||||
jit_State *J = L2J(L);
|
||||
if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) {
|
||||
setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno));
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
if (L->top > L->base+1) { /* Don't throw for one-argument variant. */
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 2);
|
||||
ExitNo maxexit = T->root ? T->nsnap+1 : T->nsnap;
|
||||
if (T && T->mcode != NULL && exitno < maxexit) {
|
||||
setintptrV(L->top-1, (intptr_t)(void *)exitstub_trace_addr(T, exitno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local addr = jit.util.ircalladdr(idx) */
|
||||
LJLIB_CF(jit_util_ircalladdr)
|
||||
{
|
||||
uint32_t idx = (uint32_t)lj_lib_checkint(L, 1);
|
||||
if (idx < IRCALL__MAX) {
|
||||
ASMFunction func = lj_ir_callinfo[idx].func;
|
||||
setintptrV(L->top-1, (intptr_t)(void *)lj_ptr_strip(func));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
static int luaopen_jit_util(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, NULL, jit_util);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* -- jit.opt module ------------------------------------------------------ */
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
#define LJLIB_MODULE_jit_opt
|
||||
|
||||
/* Parse optimization level. */
|
||||
static int jitopt_level(jit_State *J, const char *str)
|
||||
{
|
||||
if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') {
|
||||
uint32_t flags;
|
||||
if (str[0] == '0') flags = JIT_F_OPT_0;
|
||||
else if (str[0] == '1') flags = JIT_F_OPT_1;
|
||||
else if (str[0] == '2') flags = JIT_F_OPT_2;
|
||||
else flags = JIT_F_OPT_3;
|
||||
J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags;
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
/* Parse optimization flag. */
|
||||
static int jitopt_flag(jit_State *J, const char *str)
|
||||
{
|
||||
const char *lst = JIT_F_OPTSTRING;
|
||||
uint32_t opt;
|
||||
int set = 1;
|
||||
if (str[0] == '+') {
|
||||
str++;
|
||||
} else if (str[0] == '-') {
|
||||
str++;
|
||||
set = 0;
|
||||
} else if (str[0] == 'n' && str[1] == 'o') {
|
||||
str += str[2] == '-' ? 3 : 2;
|
||||
set = 0;
|
||||
}
|
||||
for (opt = JIT_F_OPT; ; opt <<= 1) {
|
||||
size_t len = *(const uint8_t *)lst;
|
||||
if (len == 0)
|
||||
break;
|
||||
if (strncmp(str, lst+1, len) == 0 && str[len] == '\0') {
|
||||
if (set) J->flags |= opt; else J->flags &= ~opt;
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
lst += 1+len;
|
||||
}
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
/* Parse optimization parameter. */
|
||||
static int jitopt_param(jit_State *J, const char *str)
|
||||
{
|
||||
const char *lst = JIT_P_STRING;
|
||||
int i;
|
||||
for (i = 0; i < JIT_P__MAX; i++) {
|
||||
size_t len = *(const uint8_t *)lst;
|
||||
lj_assertJ(len != 0, "bad JIT_P_STRING");
|
||||
if (strncmp(str, lst+1, len) == 0 && str[len] == '=') {
|
||||
int32_t n = 0;
|
||||
const char *p = &str[len+1];
|
||||
while (*p >= '0' && *p <= '9')
|
||||
n = n*10 + (*p++ - '0');
|
||||
if (*p) return 0; /* Malformed number. */
|
||||
J->param[i] = n;
|
||||
if (i == JIT_P_hotloop)
|
||||
lj_dispatch_init_hotcount(J2G(J));
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
lst += 1+len;
|
||||
}
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
/* jit.opt.start(flags...) */
|
||||
LJLIB_CF(jit_opt_start)
|
||||
{
|
||||
jit_State *J = L2J(L);
|
||||
int nargs = (int)(L->top - L->base);
|
||||
if (nargs == 0) {
|
||||
J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 1; i <= nargs; i++) {
|
||||
const char *str = strdata(lj_lib_checkstr(L, i));
|
||||
if (!jitopt_level(J, str) &&
|
||||
!jitopt_flag(J, str) &&
|
||||
!jitopt_param(J, str))
|
||||
lj_err_callerv(L, LJ_ERR_JITOPT, str);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
#endif
|
||||
|
||||
/* -- jit.profile module -------------------------------------------------- */
|
||||
|
||||
#if LJ_HASPROFILE
|
||||
|
||||
#define LJLIB_MODULE_jit_profile
|
||||
|
||||
/* Not loaded by default, use: local profile = require("jit.profile") */
|
||||
|
||||
#define KEY_PROFILE_THREAD (U64x(80000000,00000000)|'t')
|
||||
#define KEY_PROFILE_FUNC (U64x(80000000,00000000)|'f')
|
||||
|
||||
static void jit_profile_callback(lua_State *L2, lua_State *L, int samples,
|
||||
int vmstate)
|
||||
{
|
||||
TValue key;
|
||||
cTValue *tv;
|
||||
key.u64 = KEY_PROFILE_FUNC;
|
||||
tv = lj_tab_get(L, tabV(registry(L)), &key);
|
||||
if (tvisfunc(tv)) {
|
||||
char vmst = (char)vmstate;
|
||||
int status;
|
||||
setfuncV(L2, L2->top++, funcV(tv));
|
||||
setthreadV(L2, L2->top++, L);
|
||||
setintV(L2->top++, samples);
|
||||
setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1));
|
||||
status = lua_pcall(L2, 3, 0, 0); /* callback(thread, samples, vmstate) */
|
||||
if (status) {
|
||||
if (G(L2)->panic) G(L2)->panic(L2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
lj_trace_abort(G(L2));
|
||||
}
|
||||
}
|
||||
|
||||
/* profile.start(mode, cb) */
|
||||
LJLIB_CF(jit_profile_start)
|
||||
{
|
||||
GCtab *registry = tabV(registry(L));
|
||||
GCstr *mode = lj_lib_optstr(L, 1);
|
||||
GCfunc *func = lj_lib_checkfunc(L, 2);
|
||||
lua_State *L2 = lua_newthread(L); /* Thread that runs profiler callback. */
|
||||
TValue key;
|
||||
/* Anchor thread and function in registry. */
|
||||
key.u64 = KEY_PROFILE_THREAD;
|
||||
setthreadV(L, lj_tab_set(L, registry, &key), L2);
|
||||
key.u64 = KEY_PROFILE_FUNC;
|
||||
setfuncV(L, lj_tab_set(L, registry, &key), func);
|
||||
lj_gc_anybarriert(L, registry);
|
||||
luaJIT_profile_start(L, mode ? strdata(mode) : "",
|
||||
(luaJIT_profile_callback)jit_profile_callback, L2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* profile.stop() */
|
||||
LJLIB_CF(jit_profile_stop)
|
||||
{
|
||||
GCtab *registry;
|
||||
TValue key;
|
||||
luaJIT_profile_stop(L);
|
||||
registry = tabV(registry(L));
|
||||
key.u64 = KEY_PROFILE_THREAD;
|
||||
setnilV(lj_tab_set(L, registry, &key));
|
||||
key.u64 = KEY_PROFILE_FUNC;
|
||||
setnilV(lj_tab_set(L, registry, &key));
|
||||
lj_gc_anybarriert(L, registry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dump = profile.dumpstack([thread,] fmt, depth) */
|
||||
LJLIB_CF(jit_profile_dumpstack)
|
||||
{
|
||||
lua_State *L2 = L;
|
||||
int arg = 0;
|
||||
size_t len;
|
||||
int depth;
|
||||
GCstr *fmt;
|
||||
const char *p;
|
||||
if (L->top > L->base && tvisthread(L->base)) {
|
||||
L2 = threadV(L->base);
|
||||
arg = 1;
|
||||
}
|
||||
fmt = lj_lib_checkstr(L, arg+1);
|
||||
depth = lj_lib_checkint(L, arg+2);
|
||||
p = luaJIT_profile_dumpstack(L2, strdata(fmt), depth, &len);
|
||||
lua_pushlstring(L, p, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
static int luaopen_jit_profile(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, NULL, jit_profile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* -- JIT compiler initialization ----------------------------------------- */
|
||||
|
||||
#if LJ_HASJIT
|
||||
/* Default values for JIT parameters. */
|
||||
static const int32_t jit_param_default[JIT_P__MAX+1] = {
|
||||
#define JIT_PARAMINIT(len, name, value) (value),
|
||||
JIT_PARAMDEF(JIT_PARAMINIT)
|
||||
#undef JIT_PARAMINIT
|
||||
0
|
||||
};
|
||||
|
||||
#if LJ_TARGET_ARM && LJ_TARGET_LINUX
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
/* Arch-dependent CPU feature detection. */
|
||||
static uint32_t jit_cpudetect(void)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
#if LJ_TARGET_X86ORX64
|
||||
|
||||
uint32_t vendor[4];
|
||||
uint32_t features[4];
|
||||
if (lj_vm_cpuid(0, vendor) && lj_vm_cpuid(1, features)) {
|
||||
flags |= ((features[2] >> 0)&1) * JIT_F_SSE3;
|
||||
flags |= ((features[2] >> 19)&1) * JIT_F_SSE4_1;
|
||||
if (vendor[0] >= 7) {
|
||||
uint32_t xfeatures[4];
|
||||
lj_vm_cpuid(7, xfeatures);
|
||||
flags |= ((xfeatures[1] >> 8)&1) * JIT_F_BMI2;
|
||||
}
|
||||
}
|
||||
/* Don't bother checking for SSE2 -- the VM will crash before getting here. */
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
int ver = LJ_ARCH_VERSION; /* Compile-time ARM CPU detection. */
|
||||
#if LJ_TARGET_LINUX
|
||||
if (ver < 70) { /* Runtime ARM CPU detection. */
|
||||
struct utsname ut;
|
||||
uname(&ut);
|
||||
if (strncmp(ut.machine, "armv", 4) == 0) {
|
||||
if (ut.machine[4] >= '8') ver = 80;
|
||||
else if (ut.machine[4] == '7') ver = 70;
|
||||
else if (ut.machine[4] == '6') ver = 60;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
flags |= ver >= 70 ? JIT_F_ARMV7 :
|
||||
ver >= 61 ? JIT_F_ARMV6T2_ :
|
||||
ver >= 60 ? JIT_F_ARMV6_ : 0;
|
||||
flags |= LJ_ARCH_HASFPU == 0 ? 0 : ver >= 70 ? JIT_F_VFPV3 : JIT_F_VFPV2;
|
||||
|
||||
#elif LJ_TARGET_ARM64
|
||||
|
||||
/* No optional CPU features to detect (for now). */
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#if LJ_ARCH_SQRT
|
||||
flags |= JIT_F_SQRT;
|
||||
#endif
|
||||
#if LJ_ARCH_ROUND
|
||||
flags |= JIT_F_ROUND;
|
||||
#endif
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
/* Compile-time MIPS CPU detection. */
|
||||
#if LJ_ARCH_VERSION >= 20
|
||||
flags |= JIT_F_MIPSXXR2;
|
||||
#endif
|
||||
/* Runtime MIPS CPU detection. */
|
||||
#if defined(__GNUC__)
|
||||
if (!(flags & JIT_F_MIPSXXR2)) {
|
||||
int x;
|
||||
#ifdef __mips16
|
||||
x = 0; /* Runtime detection is difficult. Ensure optimal -march flags. */
|
||||
#else
|
||||
/* On MIPS32R1 rotr is treated as srl. rotr r2,r2,1 -> srl r2,r2,1. */
|
||||
__asm__("li $2, 1\n\t.long 0x00221042\n\tmove %0, $2" : "=r"(x) : : "$2");
|
||||
#endif
|
||||
if (x) flags |= JIT_F_MIPSXXR2; /* Either 0x80000000 (R2) or 0 (R1). */
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "Missing CPU detection for this architecture"
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
|
||||
/* Initialize JIT compiler. */
|
||||
static void jit_init(lua_State *L)
|
||||
{
|
||||
jit_State *J = L2J(L);
|
||||
J->flags = jit_cpudetect() | JIT_F_ON | JIT_F_OPT_DEFAULT;
|
||||
memcpy(J->param, jit_param_default, sizeof(J->param));
|
||||
lj_dispatch_update(G(L));
|
||||
}
|
||||
#endif
|
||||
|
||||
LUALIB_API int luaopen_jit(lua_State *L)
|
||||
{
|
||||
#if LJ_HASJIT
|
||||
jit_init(L);
|
||||
#endif
|
||||
lua_pushliteral(L, LJ_OS_NAME);
|
||||
lua_pushliteral(L, LJ_ARCH_NAME);
|
||||
lua_pushinteger(L, LUAJIT_VERSION_NUM); /* Deprecated. */
|
||||
lua_pushliteral(L, LUAJIT_VERSION);
|
||||
LJ_LIB_REG(L, LUA_JITLIBNAME, jit);
|
||||
#if LJ_HASPROFILE
|
||||
lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile,
|
||||
tabref(L->env));
|
||||
#endif
|
||||
#ifndef LUAJIT_DISABLE_JITUTIL
|
||||
lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env));
|
||||
#endif
|
||||
#if LJ_HASJIT
|
||||
LJ_LIB_REG(L, "jit.opt", jit_opt);
|
||||
#endif
|
||||
L->top -= 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
** Math library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define lib_math_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_lib.h"
|
||||
#include "lj_vm.h"
|
||||
#include "lj_prng.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_math
|
||||
|
||||
LJLIB_ASM(math_abs) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_floor) LJLIB_REC(math_round IRFPM_FLOOR)
|
||||
LJLIB_ASM_(math_ceil) LJLIB_REC(math_round IRFPM_CEIL)
|
||||
|
||||
LJLIB_ASM(math_sqrt) LJLIB_REC(math_unary IRFPM_SQRT)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_log10) LJLIB_REC(math_call IRCALL_log10)
|
||||
LJLIB_ASM_(math_exp) LJLIB_REC(math_call IRCALL_exp)
|
||||
LJLIB_ASM_(math_sin) LJLIB_REC(math_call IRCALL_sin)
|
||||
LJLIB_ASM_(math_cos) LJLIB_REC(math_call IRCALL_cos)
|
||||
LJLIB_ASM_(math_tan) LJLIB_REC(math_call IRCALL_tan)
|
||||
LJLIB_ASM_(math_asin) LJLIB_REC(math_call IRCALL_asin)
|
||||
LJLIB_ASM_(math_acos) LJLIB_REC(math_call IRCALL_acos)
|
||||
LJLIB_ASM_(math_atan) LJLIB_REC(math_call IRCALL_atan)
|
||||
LJLIB_ASM_(math_sinh) LJLIB_REC(math_call IRCALL_sinh)
|
||||
LJLIB_ASM_(math_cosh) LJLIB_REC(math_call IRCALL_cosh)
|
||||
LJLIB_ASM_(math_tanh) LJLIB_REC(math_call IRCALL_tanh)
|
||||
LJLIB_ASM_(math_frexp)
|
||||
LJLIB_ASM_(math_modf)
|
||||
|
||||
LJLIB_ASM(math_log) LJLIB_REC(math_log)
|
||||
{
|
||||
double x = lj_lib_checknum(L, 1);
|
||||
if (L->base+1 < L->top) {
|
||||
double y = lj_lib_checknum(L, 2);
|
||||
#ifdef LUAJIT_NO_LOG2
|
||||
x = log(x); y = 1.0 / log(y);
|
||||
#else
|
||||
x = lj_vm_log2(x); y = 1.0 / lj_vm_log2(y);
|
||||
#endif
|
||||
setnumV(L->base-1-LJ_FR2, x*y); /* Do NOT join the expression to x / y. */
|
||||
return FFH_RES(1);
|
||||
}
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_LUA(math_deg) /* function(x) return x * 57.29577951308232 end */
|
||||
LJLIB_LUA(math_rad) /* function(x) return x * 0.017453292519943295 end */
|
||||
|
||||
LJLIB_ASM(math_atan2) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
lj_lib_checknum(L, 2);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_pow) LJLIB_REC(.)
|
||||
LJLIB_ASM_(math_fmod)
|
||||
|
||||
LJLIB_ASM(math_ldexp) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
#if LJ_DUALNUM && !LJ_TARGET_X86ORX64
|
||||
lj_lib_checkint(L, 2);
|
||||
#else
|
||||
lj_lib_checknum(L, 2);
|
||||
#endif
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(math_min) LJLIB_REC(math_minmax IR_MIN)
|
||||
{
|
||||
int i = 0;
|
||||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_max) LJLIB_REC(math_minmax IR_MAX)
|
||||
|
||||
LJLIB_PUSH(3.14159265358979323846) LJLIB_SET(pi)
|
||||
LJLIB_PUSH(1e310) LJLIB_SET(huge)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* This implements a Tausworthe PRNG with period 2^223. Based on:
|
||||
** Tables of maximally-equidistributed combined LFSR generators,
|
||||
** Pierre L'Ecuyer, 1991, table 3, 1st entry.
|
||||
** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
|
||||
*/
|
||||
|
||||
/* Union needed for bit-pattern conversion between uint64_t and double. */
|
||||
typedef union { uint64_t u64; double d; } U64double;
|
||||
|
||||
/* PRNG seeding function. */
|
||||
static void random_seed(PRNGState *rs, double d)
|
||||
{
|
||||
uint32_t r = 0x11090601; /* 64-k[i] as four 8 bit constants. */
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
U64double u;
|
||||
uint32_t m = 1u << (r&255);
|
||||
r >>= 8;
|
||||
u.d = d = d * 3.14159265358979323846 + 2.7182818284590452354;
|
||||
if (u.u64 < m) u.u64 += m; /* Ensure k[i] MSB of u[i] are non-zero. */
|
||||
rs->u[i] = u.u64;
|
||||
}
|
||||
for (i = 0; i < 10; i++)
|
||||
(void)lj_prng_u64(rs);
|
||||
}
|
||||
|
||||
/* PRNG extract function. */
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with PRNGState. */
|
||||
LJLIB_CF(math_random) LJLIB_REC(.)
|
||||
{
|
||||
int n = (int)(L->top - L->base);
|
||||
PRNGState *rs = (PRNGState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
|
||||
U64double u;
|
||||
double d;
|
||||
u.u64 = lj_prng_u64d(rs);
|
||||
d = u.d - 1.0;
|
||||
if (n > 0) {
|
||||
#if LJ_DUALNUM
|
||||
int isint = 1;
|
||||
double r1;
|
||||
lj_lib_checknumber(L, 1);
|
||||
if (tvisint(L->base)) {
|
||||
r1 = (lua_Number)intV(L->base);
|
||||
} else {
|
||||
isint = 0;
|
||||
r1 = numV(L->base);
|
||||
}
|
||||
#else
|
||||
double r1 = lj_lib_checknum(L, 1);
|
||||
#endif
|
||||
if (n == 1) {
|
||||
d = lj_vm_floor(d*r1) + 1.0; /* d is an int in range [1, r1] */
|
||||
} else {
|
||||
#if LJ_DUALNUM
|
||||
double r2;
|
||||
lj_lib_checknumber(L, 2);
|
||||
if (tvisint(L->base+1)) {
|
||||
r2 = (lua_Number)intV(L->base+1);
|
||||
} else {
|
||||
isint = 0;
|
||||
r2 = numV(L->base+1);
|
||||
}
|
||||
#else
|
||||
double r2 = lj_lib_checknum(L, 2);
|
||||
#endif
|
||||
d = lj_vm_floor(d*(r2-r1+1.0)) + r1; /* d is an int in range [r1, r2] */
|
||||
}
|
||||
#if LJ_DUALNUM
|
||||
if (isint) {
|
||||
setintV(L->top-1, lj_num2int(d));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
} /* else: d is a double in range [0, 1] */
|
||||
setnumV(L->top++, d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* PRNG seed function. */
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with PRNGState. */
|
||||
LJLIB_CF(math_randomseed)
|
||||
{
|
||||
PRNGState *rs = (PRNGState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
|
||||
if (L->base != L->top)
|
||||
random_seed(rs, lj_lib_checknum(L, 1));
|
||||
else if (!lj_prng_seed_secure(rs))
|
||||
lj_err_caller(L, LJ_ERR_PRNGSD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_math(lua_State *L)
|
||||
{
|
||||
PRNGState *rs = (PRNGState *)lua_newuserdata(L, sizeof(PRNGState));
|
||||
lj_prng_seed_fixed(rs);
|
||||
LJ_LIB_REG(L, LUA_MATHLIBNAME, math);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
** OS library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#define lib_os_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
#if LJ_TARGET_POSIX
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#if !LJ_TARGET_PSVITA
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_os
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(os_execute)
|
||||
{
|
||||
#if LJ_NO_SYSTEM
|
||||
#if LJ_52
|
||||
errno = ENOSYS;
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
#else
|
||||
lua_pushinteger(L, -1);
|
||||
return 1;
|
||||
#endif
|
||||
#else
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat = system(cmd);
|
||||
#if LJ_52
|
||||
if (cmd)
|
||||
return luaL_execresult(L, stat);
|
||||
setboolV(L->top++, 1);
|
||||
#else
|
||||
setintV(L->top++, stat);
|
||||
#endif
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(os_remove)
|
||||
{
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
return luaL_fileresult(L, remove(filename) == 0, filename);
|
||||
}
|
||||
|
||||
LJLIB_CF(os_rename)
|
||||
{
|
||||
const char *fromname = luaL_checkstring(L, 1);
|
||||
const char *toname = luaL_checkstring(L, 2);
|
||||
return luaL_fileresult(L, rename(fromname, toname) == 0, fromname);
|
||||
}
|
||||
|
||||
LJLIB_CF(os_tmpname)
|
||||
{
|
||||
#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA || LJ_TARGET_NX
|
||||
lj_err_caller(L, LJ_ERR_OSUNIQF);
|
||||
return 0;
|
||||
#else
|
||||
#if LJ_TARGET_POSIX
|
||||
char buf[15+1];
|
||||
int fp;
|
||||
strcpy(buf, "/tmp/lua_XXXXXX");
|
||||
fp = mkstemp(buf);
|
||||
if (fp != -1)
|
||||
close(fp);
|
||||
else
|
||||
lj_err_caller(L, LJ_ERR_OSUNIQF);
|
||||
#else
|
||||
char buf[L_tmpnam];
|
||||
if (tmpnam(buf) == NULL)
|
||||
lj_err_caller(L, LJ_ERR_OSUNIQF);
|
||||
#endif
|
||||
lua_pushstring(L, buf);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(os_getenv)
|
||||
{
|
||||
#if LJ_TARGET_CONSOLE
|
||||
lua_pushnil(L);
|
||||
#else
|
||||
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(os_exit)
|
||||
{
|
||||
int status;
|
||||
if (L->base < L->top && tvisbool(L->base))
|
||||
status = boolV(L->base) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
else
|
||||
status = lj_lib_optint(L, 1, EXIT_SUCCESS);
|
||||
if (L->base+1 < L->top && tvistruecond(L->base+1))
|
||||
lua_close(L);
|
||||
exit(status);
|
||||
return 0; /* Unreachable. */
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(os_clock)
|
||||
{
|
||||
setnumV(L->top++, ((lua_Number)clock())*(1.0/(lua_Number)CLOCKS_PER_SEC));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void setfield(lua_State *L, const char *key, int value)
|
||||
{
|
||||
lua_pushinteger(L, value);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
static void setboolfield(lua_State *L, const char *key, int value)
|
||||
{
|
||||
if (value < 0) /* undefined? */
|
||||
return; /* does not set field */
|
||||
lua_pushboolean(L, value);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
static int getboolfield(lua_State *L, const char *key)
|
||||
{
|
||||
int res;
|
||||
lua_getfield(L, -1, key);
|
||||
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int getfield(lua_State *L, const char *key, int d)
|
||||
{
|
||||
int res;
|
||||
lua_getfield(L, -1, key);
|
||||
if (lua_isnumber(L, -1)) {
|
||||
res = (int)lua_tointeger(L, -1);
|
||||
} else {
|
||||
if (d < 0)
|
||||
lj_err_callerv(L, LJ_ERR_OSDATEF, key);
|
||||
res = d;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
LJLIB_CF(os_date)
|
||||
{
|
||||
const char *s = luaL_optstring(L, 1, "%c");
|
||||
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
|
||||
struct tm *stm;
|
||||
#if LJ_TARGET_POSIX
|
||||
struct tm rtm;
|
||||
#endif
|
||||
if (*s == '!') { /* UTC? */
|
||||
s++; /* Skip '!' */
|
||||
#if LJ_TARGET_POSIX
|
||||
stm = gmtime_r(&t, &rtm);
|
||||
#else
|
||||
stm = gmtime(&t);
|
||||
#endif
|
||||
} else {
|
||||
#if LJ_TARGET_POSIX
|
||||
stm = localtime_r(&t, &rtm);
|
||||
#else
|
||||
stm = localtime(&t);
|
||||
#endif
|
||||
}
|
||||
if (stm == NULL) { /* Invalid date? */
|
||||
setnilV(L->top++);
|
||||
} else if (strcmp(s, "*t") == 0) {
|
||||
lua_createtable(L, 0, 9); /* 9 = number of fields */
|
||||
setfield(L, "sec", stm->tm_sec);
|
||||
setfield(L, "min", stm->tm_min);
|
||||
setfield(L, "hour", stm->tm_hour);
|
||||
setfield(L, "day", stm->tm_mday);
|
||||
setfield(L, "month", stm->tm_mon+1);
|
||||
setfield(L, "year", stm->tm_year+1900);
|
||||
setfield(L, "wday", stm->tm_wday+1);
|
||||
setfield(L, "yday", stm->tm_yday+1);
|
||||
setboolfield(L, "isdst", stm->tm_isdst);
|
||||
} else if (*s) {
|
||||
SBuf *sb = &G(L)->tmpbuf;
|
||||
MSize sz = 0, retry = 4;
|
||||
const char *q;
|
||||
for (q = s; *q; q++)
|
||||
sz += (*q == '%') ? 30 : 1; /* Overflow doesn't matter. */
|
||||
setsbufL(sb, L);
|
||||
while (retry--) { /* Limit growth for invalid format or empty result. */
|
||||
char *buf = lj_buf_need(sb, sz);
|
||||
size_t len = strftime(buf, sbufsz(sb), s, stm);
|
||||
if (len) {
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, len));
|
||||
lj_gc_check(L);
|
||||
break;
|
||||
}
|
||||
sz += (sz|1);
|
||||
}
|
||||
} else {
|
||||
setstrV(L, L->top++, &G(L)->strempty);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(os_time)
|
||||
{
|
||||
time_t t;
|
||||
if (lua_isnoneornil(L, 1)) { /* called without args? */
|
||||
t = time(NULL); /* get current time */
|
||||
} else {
|
||||
struct tm ts;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_settop(L, 1); /* make sure table is at the top */
|
||||
ts.tm_sec = getfield(L, "sec", 0);
|
||||
ts.tm_min = getfield(L, "min", 0);
|
||||
ts.tm_hour = getfield(L, "hour", 12);
|
||||
ts.tm_mday = getfield(L, "day", -1);
|
||||
ts.tm_mon = getfield(L, "month", -1) - 1;
|
||||
ts.tm_year = getfield(L, "year", -1) - 1900;
|
||||
ts.tm_isdst = getboolfield(L, "isdst");
|
||||
t = mktime(&ts);
|
||||
}
|
||||
if (t == (time_t)(-1))
|
||||
lua_pushnil(L);
|
||||
else
|
||||
lua_pushnumber(L, (lua_Number)t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(os_difftime)
|
||||
{
|
||||
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
|
||||
(time_t)(luaL_optnumber(L, 2, (lua_Number)0))));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
LJLIB_CF(os_setlocale)
|
||||
{
|
||||
#if LJ_TARGET_PSVITA
|
||||
lua_pushliteral(L, "C");
|
||||
#else
|
||||
GCstr *s = lj_lib_optstr(L, 1);
|
||||
const char *str = s ? strdata(s) : NULL;
|
||||
int opt = lj_lib_checkopt(L, 2, 6,
|
||||
"\5ctype\7numeric\4time\7collate\10monetary\1\377\3all");
|
||||
if (opt == 0) opt = LC_CTYPE;
|
||||
else if (opt == 1) opt = LC_NUMERIC;
|
||||
else if (opt == 2) opt = LC_TIME;
|
||||
else if (opt == 3) opt = LC_COLLATE;
|
||||
else if (opt == 4) opt = LC_MONETARY;
|
||||
else if (opt == 6) opt = LC_ALL;
|
||||
lua_pushstring(L, setlocale(opt, str));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_os(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_OSLIBNAME, os);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
** Package library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2012 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_package_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Error codes for ll_loadfunc. */
|
||||
#define PACKAGE_ERR_LIB 1
|
||||
#define PACKAGE_ERR_FUNC 2
|
||||
#define PACKAGE_ERR_LOAD 3
|
||||
|
||||
/* Redefined in platform specific part. */
|
||||
#define PACKAGE_LIB_FAIL "open"
|
||||
#define setprogdir(L) ((void)0)
|
||||
|
||||
/* Symbol name prefixes. */
|
||||
#define SYMPREFIX_CF "luaopen_%s"
|
||||
#define SYMPREFIX_BC "luaJIT_BC_%s"
|
||||
|
||||
#if LJ_TARGET_DLOPEN
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void ll_unloadlib(void *lib)
|
||||
{
|
||||
dlclose(lib);
|
||||
}
|
||||
|
||||
static void *ll_load(lua_State *L, const char *path, int gl)
|
||||
{
|
||||
void *lib = dlopen(path, RTLD_NOW | (gl ? RTLD_GLOBAL : RTLD_LOCAL));
|
||||
if (lib == NULL) lua_pushstring(L, dlerror());
|
||||
return lib;
|
||||
}
|
||||
|
||||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
|
||||
{
|
||||
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
|
||||
if (f == NULL) lua_pushstring(L, dlerror());
|
||||
return f;
|
||||
}
|
||||
|
||||
static const char *ll_bcsym(void *lib, const char *sym)
|
||||
{
|
||||
#if defined(RTLD_DEFAULT) && !defined(NO_RTLD_DEFAULT)
|
||||
if (lib == NULL) lib = RTLD_DEFAULT;
|
||||
#elif LJ_TARGET_OSX || LJ_TARGET_BSD
|
||||
if (lib == NULL) lib = (void *)(intptr_t)-2;
|
||||
#endif
|
||||
return (const char *)dlsym(lib, sym);
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
|
||||
BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_UWP
|
||||
void *LJ_WIN_LOADLIBA(const char *path)
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
wchar_t wpath[256];
|
||||
HANDLE lib = NULL;
|
||||
if (MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, 256) > 0) {
|
||||
lib = LoadPackagedLibrary(wpath, 0);
|
||||
}
|
||||
SetLastError(err);
|
||||
return lib;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef setprogdir
|
||||
|
||||
static void setprogdir(lua_State *L)
|
||||
{
|
||||
char buff[MAX_PATH + 1];
|
||||
char *lb;
|
||||
DWORD nsize = sizeof(buff);
|
||||
DWORD n = GetModuleFileNameA(NULL, buff, nsize);
|
||||
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) {
|
||||
luaL_error(L, "unable to get ModuleFileName");
|
||||
} else {
|
||||
*lb = '\0';
|
||||
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
|
||||
lua_remove(L, -2); /* remove original string */
|
||||
}
|
||||
}
|
||||
|
||||
static void pusherror(lua_State *L)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
#if LJ_TARGET_XBOXONE
|
||||
wchar_t wbuffer[128];
|
||||
char buffer[128*2];
|
||||
if (FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL, error, 0, wbuffer, sizeof(wbuffer)/sizeof(wchar_t), NULL) &&
|
||||
WideCharToMultiByte(CP_ACP, 0, wbuffer, 128, buffer, 128*2, NULL, NULL))
|
||||
#else
|
||||
char buffer[128];
|
||||
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL, error, 0, buffer, sizeof(buffer), NULL))
|
||||
#endif
|
||||
lua_pushstring(L, buffer);
|
||||
else
|
||||
lua_pushfstring(L, "system error %d\n", error);
|
||||
}
|
||||
|
||||
static void ll_unloadlib(void *lib)
|
||||
{
|
||||
FreeLibrary((HINSTANCE)lib);
|
||||
}
|
||||
|
||||
static void *ll_load(lua_State *L, const char *path, int gl)
|
||||
{
|
||||
HINSTANCE lib = LJ_WIN_LOADLIBA(path);
|
||||
if (lib == NULL) pusherror(L);
|
||||
UNUSED(gl);
|
||||
return lib;
|
||||
}
|
||||
|
||||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
|
||||
{
|
||||
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
|
||||
if (f == NULL) pusherror(L);
|
||||
return f;
|
||||
}
|
||||
|
||||
#if LJ_TARGET_UWP
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
#endif
|
||||
|
||||
static const char *ll_bcsym(void *lib, const char *sym)
|
||||
{
|
||||
if (lib) {
|
||||
return (const char *)GetProcAddress((HINSTANCE)lib, sym);
|
||||
} else {
|
||||
#if LJ_TARGET_UWP
|
||||
return (const char *)GetProcAddress((HINSTANCE)&__ImageBase, sym);
|
||||
#else
|
||||
HINSTANCE h = GetModuleHandleA(NULL);
|
||||
const char *p = (const char *)GetProcAddress(h, sym);
|
||||
if (p == NULL && GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(const char *)ll_bcsym, &h))
|
||||
p = (const char *)GetProcAddress(h, sym);
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#undef PACKAGE_LIB_FAIL
|
||||
#define PACKAGE_LIB_FAIL "absent"
|
||||
|
||||
#define DLMSG "dynamic libraries not enabled; no support for target OS"
|
||||
|
||||
static void ll_unloadlib(void *lib)
|
||||
{
|
||||
UNUSED(lib);
|
||||
}
|
||||
|
||||
static void *ll_load(lua_State *L, const char *path, int gl)
|
||||
{
|
||||
UNUSED(path); UNUSED(gl);
|
||||
lua_pushliteral(L, DLMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
|
||||
{
|
||||
UNUSED(lib); UNUSED(sym);
|
||||
lua_pushliteral(L, DLMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *ll_bcsym(void *lib, const char *sym)
|
||||
{
|
||||
UNUSED(lib); UNUSED(sym);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void **ll_register(lua_State *L, const char *path)
|
||||
{
|
||||
void **plib;
|
||||
lua_pushfstring(L, "LOADLIB: %s", path);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
|
||||
if (!lua_isnil(L, -1)) { /* is there an entry? */
|
||||
plib = (void **)lua_touserdata(L, -1);
|
||||
} else { /* no entry yet; create one */
|
||||
lua_pop(L, 1);
|
||||
plib = (void **)lua_newuserdata(L, sizeof(void *));
|
||||
*plib = NULL;
|
||||
luaL_setmetatable(L, "_LOADLIB");
|
||||
lua_pushfstring(L, "LOADLIB: %s", path);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
return plib;
|
||||
}
|
||||
|
||||
static const char *mksymname(lua_State *L, const char *modname,
|
||||
const char *prefix)
|
||||
{
|
||||
const char *funcname;
|
||||
const char *mark = strchr(modname, *LUA_IGMARK);
|
||||
if (mark) modname = mark + 1;
|
||||
funcname = luaL_gsub(L, modname, ".", "_");
|
||||
funcname = lua_pushfstring(L, prefix, funcname);
|
||||
lua_remove(L, -2); /* remove 'gsub' result */
|
||||
return funcname;
|
||||
}
|
||||
|
||||
static int ll_loadfunc(lua_State *L, const char *path, const char *name, int r)
|
||||
{
|
||||
void **reg;
|
||||
if (strlen(path) >= 4096) {
|
||||
lua_pushliteral(L, "path too long");
|
||||
return PACKAGE_ERR_LIB;
|
||||
}
|
||||
reg = ll_register(L, path);
|
||||
if (*reg == NULL) *reg = ll_load(L, path, (*name == '*'));
|
||||
if (*reg == NULL) {
|
||||
return PACKAGE_ERR_LIB; /* Unable to load library. */
|
||||
} else if (*name == '*') { /* Only load library into global namespace. */
|
||||
lua_pushboolean(L, 1);
|
||||
return 0;
|
||||
} else {
|
||||
const char *sym = r ? name : mksymname(L, name, SYMPREFIX_CF);
|
||||
lua_CFunction f = ll_sym(L, *reg, sym);
|
||||
if (f) {
|
||||
lua_pushcfunction(L, f);
|
||||
return 0;
|
||||
}
|
||||
if (!r) {
|
||||
const char *bcdata = ll_bcsym(*reg, mksymname(L, name, SYMPREFIX_BC));
|
||||
lua_pop(L, 1);
|
||||
if (bcdata) {
|
||||
if (luaL_loadbuffer(L, bcdata, ~(size_t)0, name) != 0)
|
||||
return PACKAGE_ERR_LOAD;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return PACKAGE_ERR_FUNC; /* Unable to find function. */
|
||||
}
|
||||
}
|
||||
|
||||
static int lj_cf_package_loadlib(lua_State *L)
|
||||
{
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
const char *init = luaL_checkstring(L, 2);
|
||||
int st = ll_loadfunc(L, path, init, 1);
|
||||
if (st == 0) { /* no errors? */
|
||||
return 1; /* return the loaded function */
|
||||
} else { /* error; error message is on stack top */
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2);
|
||||
lua_pushstring(L, (st == PACKAGE_ERR_LIB) ? PACKAGE_LIB_FAIL : "init");
|
||||
return 3; /* return nil, error message, and where */
|
||||
}
|
||||
}
|
||||
|
||||
static int lj_cf_package_unloadlib(lua_State *L)
|
||||
{
|
||||
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
|
||||
if (*lib) ll_unloadlib(*lib);
|
||||
*lib = NULL; /* mark library as closed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static int readable(const char *filename)
|
||||
{
|
||||
FILE *f = fopen(filename, "r"); /* try to open file */
|
||||
if (f == NULL) return 0; /* open failed */
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *pushnexttemplate(lua_State *L, const char *path)
|
||||
{
|
||||
const char *l;
|
||||
while (*path == *LUA_PATHSEP) path++; /* skip separators */
|
||||
if (*path == '\0') return NULL; /* no more templates */
|
||||
l = strchr(path, *LUA_PATHSEP); /* find next separator */
|
||||
if (l == NULL) l = path + strlen(path);
|
||||
lua_pushlstring(L, path, (size_t)(l - path)); /* template */
|
||||
return l;
|
||||
}
|
||||
|
||||
static const char *searchpath (lua_State *L, const char *name,
|
||||
const char *path, const char *sep,
|
||||
const char *dirsep)
|
||||
{
|
||||
luaL_Buffer msg; /* to build error message */
|
||||
luaL_buffinit(L, &msg);
|
||||
if (*sep != '\0') /* non-empty separator? */
|
||||
name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
|
||||
while ((path = pushnexttemplate(L, path)) != NULL) {
|
||||
const char *filename = luaL_gsub(L, lua_tostring(L, -1),
|
||||
LUA_PATH_MARK, name);
|
||||
lua_remove(L, -2); /* remove path template */
|
||||
if (readable(filename)) /* does file exist and is readable? */
|
||||
return filename; /* return that file name */
|
||||
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
|
||||
lua_remove(L, -2); /* remove file name */
|
||||
luaL_addvalue(&msg); /* concatenate error msg. entry */
|
||||
}
|
||||
luaL_pushresult(&msg); /* create error message */
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
static int lj_cf_package_searchpath(lua_State *L)
|
||||
{
|
||||
const char *f = searchpath(L, luaL_checkstring(L, 1),
|
||||
luaL_checkstring(L, 2),
|
||||
luaL_optstring(L, 3, "."),
|
||||
luaL_optstring(L, 4, LUA_DIRSEP));
|
||||
if (f != NULL) {
|
||||
return 1;
|
||||
} else { /* error message is on top of the stack */
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return nil + error message */
|
||||
}
|
||||
}
|
||||
|
||||
static const char *findfile(lua_State *L, const char *name,
|
||||
const char *pname)
|
||||
{
|
||||
const char *path;
|
||||
lua_getfield(L, LUA_ENVIRONINDEX, pname);
|
||||
path = lua_tostring(L, -1);
|
||||
if (path == NULL)
|
||||
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
|
||||
return searchpath(L, name, path, ".", LUA_DIRSEP);
|
||||
}
|
||||
|
||||
static void loaderror(lua_State *L, const char *filename)
|
||||
{
|
||||
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
|
||||
lua_tostring(L, 1), filename, lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_lua(lua_State *L)
|
||||
{
|
||||
const char *filename;
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
filename = findfile(L, name, "path");
|
||||
if (filename == NULL) return 1; /* library not found in this path */
|
||||
if (luaL_loadfile(L, filename) != 0)
|
||||
loaderror(L, filename);
|
||||
return 1; /* library loaded successfully */
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_c(lua_State *L)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
const char *filename = findfile(L, name, "cpath");
|
||||
if (filename == NULL) return 1; /* library not found in this path */
|
||||
if (ll_loadfunc(L, filename, name, 0) != 0)
|
||||
loaderror(L, filename);
|
||||
return 1; /* library loaded successfully */
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_croot(lua_State *L)
|
||||
{
|
||||
const char *filename;
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
const char *p = strchr(name, '.');
|
||||
int st;
|
||||
if (p == NULL) return 0; /* is root */
|
||||
lua_pushlstring(L, name, (size_t)(p - name));
|
||||
filename = findfile(L, lua_tostring(L, -1), "cpath");
|
||||
if (filename == NULL) return 1; /* root not found */
|
||||
if ((st = ll_loadfunc(L, filename, name, 0)) != 0) {
|
||||
if (st != PACKAGE_ERR_FUNC) loaderror(L, filename); /* real error */
|
||||
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
|
||||
name, filename);
|
||||
return 1; /* function not found */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_preload(lua_State *L)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
|
||||
if (!lua_istable(L, -1))
|
||||
luaL_error(L, LUA_QL("package.preload") " must be a table");
|
||||
lua_getfield(L, -1, name);
|
||||
if (lua_isnil(L, -1)) { /* Not found? */
|
||||
const char *bcname = mksymname(L, name, SYMPREFIX_BC);
|
||||
const char *bcdata = ll_bcsym(NULL, bcname);
|
||||
if (bcdata == NULL || luaL_loadbuffer(L, bcdata, ~(size_t)0, name) != 0)
|
||||
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define KEY_SENTINEL (U64x(80000000,00000000)|'s')
|
||||
|
||||
static int lj_cf_package_require(lua_State *L)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
int i;
|
||||
lua_settop(L, 1); /* _LOADED table will be at index 2 */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
|
||||
lua_getfield(L, 2, name);
|
||||
if (lua_toboolean(L, -1)) { /* is it there? */
|
||||
if ((L->top-1)->u64 == KEY_SENTINEL) /* check loops */
|
||||
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
|
||||
return 1; /* package is already loaded */
|
||||
}
|
||||
/* else must load it; iterate over available loaders */
|
||||
lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
|
||||
if (!lua_istable(L, -1))
|
||||
luaL_error(L, LUA_QL("package.loaders") " must be a table");
|
||||
lua_pushliteral(L, ""); /* error message accumulator */
|
||||
for (i = 1; ; i++) {
|
||||
lua_rawgeti(L, -2, i); /* get a loader */
|
||||
if (lua_isnil(L, -1))
|
||||
luaL_error(L, "module " LUA_QS " not found:%s",
|
||||
name, lua_tostring(L, -2));
|
||||
lua_pushstring(L, name);
|
||||
lua_call(L, 1, 1); /* call it */
|
||||
if (lua_isfunction(L, -1)) /* did it find module? */
|
||||
break; /* module loaded successfully */
|
||||
else if (lua_isstring(L, -1)) /* loader returned error message? */
|
||||
lua_concat(L, 2); /* accumulate it */
|
||||
else
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
(L->top++)->u64 = KEY_SENTINEL;
|
||||
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
|
||||
lua_pushstring(L, name); /* pass name as argument to module */
|
||||
lua_call(L, 1, 1); /* run loaded module */
|
||||
if (!lua_isnil(L, -1)) /* non-nil return? */
|
||||
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
|
||||
lua_getfield(L, 2, name);
|
||||
if ((L->top-1)->u64 == KEY_SENTINEL) { /* module did not set a value? */
|
||||
lua_pushboolean(L, 1); /* use true as result */
|
||||
lua_pushvalue(L, -1); /* extra copy to be returned */
|
||||
lua_setfield(L, 2, name); /* _LOADED[name] = true */
|
||||
}
|
||||
lj_lib_checkfpu(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void setfenv(lua_State *L)
|
||||
{
|
||||
lua_Debug ar;
|
||||
if (lua_getstack(L, 1, &ar) == 0 ||
|
||||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
|
||||
lua_iscfunction(L, -1))
|
||||
luaL_error(L, LUA_QL("module") " not called from a Lua function");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_setfenv(L, -2);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static void dooptions(lua_State *L, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 2; i <= n; i++) {
|
||||
lua_pushvalue(L, i); /* get option (a function) */
|
||||
lua_pushvalue(L, -2); /* module */
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void modinit(lua_State *L, const char *modname)
|
||||
{
|
||||
const char *dot;
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "_M"); /* module._M = module */
|
||||
lua_pushstring(L, modname);
|
||||
lua_setfield(L, -2, "_NAME");
|
||||
dot = strrchr(modname, '.'); /* look for last dot in module name */
|
||||
if (dot == NULL) dot = modname; else dot++;
|
||||
/* set _PACKAGE as package name (full module name minus last part) */
|
||||
lua_pushlstring(L, modname, (size_t)(dot - modname));
|
||||
lua_setfield(L, -2, "_PACKAGE");
|
||||
}
|
||||
|
||||
static int lj_cf_package_module(lua_State *L)
|
||||
{
|
||||
const char *modname = luaL_checkstring(L, 1);
|
||||
int lastarg = (int)(L->top - L->base);
|
||||
luaL_pushmodule(L, modname, 1);
|
||||
lua_getfield(L, -1, "_NAME");
|
||||
if (!lua_isnil(L, -1)) { /* Module already initialized? */
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
modinit(L, modname);
|
||||
}
|
||||
lua_pushvalue(L, -1);
|
||||
setfenv(L);
|
||||
dooptions(L, lastarg);
|
||||
return LJ_52;
|
||||
}
|
||||
|
||||
static int lj_cf_package_seeall(lua_State *L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
if (!lua_getmetatable(L, 1)) {
|
||||
lua_createtable(L, 0, 1); /* create new metatable */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setmetatable(L, 1);
|
||||
}
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_setfield(L, -2, "__index"); /* mt.__index = _G */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define AUXMARK "\1"
|
||||
|
||||
static void setpath(lua_State *L, const char *fieldname, const char *envname,
|
||||
const char *def, int noenv)
|
||||
{
|
||||
#if LJ_TARGET_CONSOLE
|
||||
const char *path = NULL;
|
||||
UNUSED(envname);
|
||||
#else
|
||||
const char *path = getenv(envname);
|
||||
#endif
|
||||
if (path == NULL || noenv) {
|
||||
lua_pushstring(L, def);
|
||||
} else {
|
||||
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
|
||||
LUA_PATHSEP AUXMARK LUA_PATHSEP);
|
||||
luaL_gsub(L, path, AUXMARK, def);
|
||||
lua_remove(L, -2);
|
||||
}
|
||||
setprogdir(L);
|
||||
lua_setfield(L, -2, fieldname);
|
||||
}
|
||||
|
||||
static const luaL_Reg package_lib[] = {
|
||||
{ "loadlib", lj_cf_package_loadlib },
|
||||
{ "searchpath", lj_cf_package_searchpath },
|
||||
{ "seeall", lj_cf_package_seeall },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg package_global[] = {
|
||||
{ "module", lj_cf_package_module },
|
||||
{ "require", lj_cf_package_require },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const lua_CFunction package_loaders[] =
|
||||
{
|
||||
lj_cf_package_loader_preload,
|
||||
lj_cf_package_loader_lua,
|
||||
lj_cf_package_loader_c,
|
||||
lj_cf_package_loader_croot,
|
||||
NULL
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_package(lua_State *L)
|
||||
{
|
||||
int i;
|
||||
int noenv;
|
||||
luaL_newmetatable(L, "_LOADLIB");
|
||||
lj_lib_pushcf(L, lj_cf_package_unloadlib, 1);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
luaL_register(L, LUA_LOADLIBNAME, package_lib);
|
||||
lua_copy(L, -1, LUA_ENVIRONINDEX);
|
||||
lua_createtable(L, sizeof(package_loaders)/sizeof(package_loaders[0])-1, 0);
|
||||
for (i = 0; package_loaders[i] != NULL; i++) {
|
||||
lj_lib_pushcf(L, package_loaders[i], 1);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
#if LJ_52
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, "searchers");
|
||||
#endif
|
||||
lua_setfield(L, -2, "loaders");
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
||||
noenv = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT, noenv);
|
||||
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT, noenv);
|
||||
lua_pushliteral(L, LUA_PATH_CONFIG);
|
||||
lua_setfield(L, -2, "config");
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
|
||||
lua_setfield(L, -2, "loaded");
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", 4);
|
||||
lua_setfield(L, -2, "preload");
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
luaL_register(L, NULL, package_global);
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,696 @@
|
||||
/*
|
||||
** String library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_string_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_bcdump.h"
|
||||
#include "lj_char.h"
|
||||
#include "lj_strfmt.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_string
|
||||
|
||||
LJLIB_LUA(string_len) /*
|
||||
function(s)
|
||||
CHECK_str(s)
|
||||
return #s
|
||||
end
|
||||
*/
|
||||
|
||||
LJLIB_ASM(string_byte) LJLIB_REC(string_range 0)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
int32_t len = (int32_t)s->len;
|
||||
int32_t start = lj_lib_optint(L, 2, 1);
|
||||
int32_t stop = lj_lib_optint(L, 3, start);
|
||||
int32_t n, i;
|
||||
const unsigned char *p;
|
||||
if (stop < 0) stop += len+1;
|
||||
if (start < 0) start += len+1;
|
||||
if (start <= 0) start = 1;
|
||||
if (stop > len) stop = len;
|
||||
if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */
|
||||
start--;
|
||||
n = stop - start;
|
||||
if ((uint32_t)n > LUAI_MAXCSTACK)
|
||||
lj_err_caller(L, LJ_ERR_STRSLC);
|
||||
lj_state_checkstack(L, (MSize)n);
|
||||
p = (const unsigned char *)strdata(s) + start;
|
||||
for (i = 0; i < n; i++)
|
||||
setintV(L->base + i-1-LJ_FR2, p[i]);
|
||||
return FFH_RES(n);
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_char) LJLIB_REC(.)
|
||||
{
|
||||
int i, nargs = (int)(L->top - L->base);
|
||||
char *buf = lj_buf_tmp(L, (MSize)nargs);
|
||||
for (i = 1; i <= nargs; i++) {
|
||||
int32_t k = lj_lib_checkint(L, i);
|
||||
if (!checku8(k))
|
||||
lj_err_arg(L, i, LJ_ERR_BADVAL);
|
||||
buf[i-1] = (char)k;
|
||||
}
|
||||
setstrV(L, L->base-1-LJ_FR2, lj_str_new(L, buf, (size_t)nargs));
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
lj_lib_checkint(L, 2);
|
||||
setintV(L->base+2, lj_lib_optint(L, 3, -1));
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_CF(string_rep) LJLIB_REC(.)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
int32_t rep = lj_lib_checkint(L, 2);
|
||||
GCstr *sep = lj_lib_optstr(L, 3);
|
||||
SBuf *sb = lj_buf_tmp_(L);
|
||||
if (sep && rep > 1) {
|
||||
GCstr *s2 = lj_buf_cat2str(L, sep, s);
|
||||
lj_buf_reset(sb);
|
||||
lj_buf_putstr(sb, s);
|
||||
s = s2;
|
||||
rep--;
|
||||
}
|
||||
sb = lj_buf_putstr_rep(sb, s, rep);
|
||||
setstrV(L, L->top-1, lj_buf_str(L, sb));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(string_lower) LJLIB_REC(string_op IRCALL_lj_buf_putstr_lower)
|
||||
LJLIB_ASM_(string_upper) LJLIB_REC(string_op IRCALL_lj_buf_putstr_upper)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if !LJ_SRB2LIB
|
||||
static int writer_buf(lua_State *L, const void *p, size_t size, void *sb)
|
||||
{
|
||||
lj_buf_putmem((SBuf *)sb, p, (MSize)size);
|
||||
UNUSED(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(string_dump)
|
||||
{
|
||||
GCproto *pt = lj_lib_checkLproto(L, 1, 1);
|
||||
uint32_t flags = 0;
|
||||
SBuf *sb;
|
||||
TValue *o = L->base+1;
|
||||
if (o < L->top) {
|
||||
if (tvisstr(o)) {
|
||||
const char *mode = strVdata(o);
|
||||
char c;
|
||||
while ((c = *mode++)) {
|
||||
if (c == 's') flags |= BCDUMP_F_STRIP;
|
||||
if (c == 'd') flags |= BCDUMP_F_DETERMINISTIC;
|
||||
}
|
||||
} else if (tvistruecond(o)) {
|
||||
flags |= BCDUMP_F_STRIP;
|
||||
}
|
||||
}
|
||||
sb = lj_buf_tmp_(L); /* Assumes lj_bcwrite() doesn't use tmpbuf. */
|
||||
L->top = L->base+1;
|
||||
if (!pt || lj_bcwrite(L, pt, writer_buf, sb, flags))
|
||||
lj_err_caller(L, LJ_ERR_STRDUMP);
|
||||
setstrV(L, L->top-1, lj_buf_str(L, sb));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
#define CAP_UNFINISHED (-1)
|
||||
#define CAP_POSITION (-2)
|
||||
|
||||
typedef struct MatchState {
|
||||
const char *src_init; /* init of source string */
|
||||
const char *src_end; /* end (`\0') of source string */
|
||||
lua_State *L;
|
||||
int level; /* total number of captures (finished or unfinished) */
|
||||
int depth;
|
||||
struct {
|
||||
const char *init;
|
||||
ptrdiff_t len;
|
||||
} capture[LUA_MAXCAPTURES];
|
||||
} MatchState;
|
||||
|
||||
#define L_ESC '%'
|
||||
|
||||
static int check_capture(MatchState *ms, int l)
|
||||
{
|
||||
l -= '1';
|
||||
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
|
||||
return l;
|
||||
}
|
||||
|
||||
static int capture_to_close(MatchState *ms)
|
||||
{
|
||||
int level = ms->level;
|
||||
for (level--; level>=0; level--)
|
||||
if (ms->capture[level].len == CAP_UNFINISHED) return level;
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATC);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
static const char *classend(MatchState *ms, const char *p)
|
||||
{
|
||||
switch (*p++) {
|
||||
case L_ESC:
|
||||
if (*p == '\0')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATE);
|
||||
return p+1;
|
||||
case '[':
|
||||
if (*p == '^') p++;
|
||||
do { /* look for a `]' */
|
||||
if (*p == '\0')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATM);
|
||||
if (*(p++) == L_ESC && *p != '\0')
|
||||
p++; /* skip escapes (e.g. `%]') */
|
||||
} while (*p != ']');
|
||||
return p+1;
|
||||
default:
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char match_class_map[32] = {
|
||||
0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0,
|
||||
LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0,
|
||||
LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static int match_class(int c, int cl)
|
||||
{
|
||||
if ((cl & 0xc0) == 0x40) {
|
||||
int t = match_class_map[(cl&0x1f)];
|
||||
if (t) {
|
||||
t = lj_char_isa(c, t);
|
||||
return (cl & 0x20) ? t : !t;
|
||||
}
|
||||
if (cl == 'z') return c == 0;
|
||||
if (cl == 'Z') return c != 0;
|
||||
}
|
||||
return (cl == c);
|
||||
}
|
||||
|
||||
static int matchbracketclass(int c, const char *p, const char *ec)
|
||||
{
|
||||
int sig = 1;
|
||||
if (*(p+1) == '^') {
|
||||
sig = 0;
|
||||
p++; /* skip the `^' */
|
||||
}
|
||||
while (++p < ec) {
|
||||
if (*p == L_ESC) {
|
||||
p++;
|
||||
if (match_class(c, uchar(*p)))
|
||||
return sig;
|
||||
}
|
||||
else if ((*(p+1) == '-') && (p+2 < ec)) {
|
||||
p+=2;
|
||||
if (uchar(*(p-2)) <= c && c <= uchar(*p))
|
||||
return sig;
|
||||
}
|
||||
else if (uchar(*p) == c) return sig;
|
||||
}
|
||||
return !sig;
|
||||
}
|
||||
|
||||
static int singlematch(int c, const char *p, const char *ep)
|
||||
{
|
||||
switch (*p) {
|
||||
case '.': return 1; /* matches any char */
|
||||
case L_ESC: return match_class(c, uchar(*(p+1)));
|
||||
case '[': return matchbracketclass(c, p, ep-1);
|
||||
default: return (uchar(*p) == c);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *match(MatchState *ms, const char *s, const char *p);
|
||||
|
||||
static const char *matchbalance(MatchState *ms, const char *s, const char *p)
|
||||
{
|
||||
if (*p == 0 || *(p+1) == 0)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATU);
|
||||
if (*s != *p) {
|
||||
return NULL;
|
||||
} else {
|
||||
int b = *p;
|
||||
int e = *(p+1);
|
||||
int cont = 1;
|
||||
while (++s < ms->src_end) {
|
||||
if (*s == e) {
|
||||
if (--cont == 0) return s+1;
|
||||
} else if (*s == b) {
|
||||
cont++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; /* string ends out of balance */
|
||||
}
|
||||
|
||||
static const char *max_expand(MatchState *ms, const char *s,
|
||||
const char *p, const char *ep)
|
||||
{
|
||||
ptrdiff_t i = 0; /* counts maximum expand for item */
|
||||
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
|
||||
i++;
|
||||
/* keeps trying to match with the maximum repetitions */
|
||||
while (i>=0) {
|
||||
const char *res = match(ms, (s+i), ep+1);
|
||||
if (res) return res;
|
||||
i--; /* else didn't match; reduce 1 repetition to try again */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *min_expand(MatchState *ms, const char *s,
|
||||
const char *p, const char *ep)
|
||||
{
|
||||
for (;;) {
|
||||
const char *res = match(ms, s, ep+1);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
|
||||
s++; /* try with one more repetition */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *start_capture(MatchState *ms, const char *s,
|
||||
const char *p, int what)
|
||||
{
|
||||
const char *res;
|
||||
int level = ms->level;
|
||||
if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
|
||||
ms->capture[level].init = s;
|
||||
ms->capture[level].len = what;
|
||||
ms->level = level+1;
|
||||
if ((res=match(ms, s, p)) == NULL) /* match failed? */
|
||||
ms->level--; /* undo capture */
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *end_capture(MatchState *ms, const char *s,
|
||||
const char *p)
|
||||
{
|
||||
int l = capture_to_close(ms);
|
||||
const char *res;
|
||||
ms->capture[l].len = s - ms->capture[l].init; /* close capture */
|
||||
if ((res = match(ms, s, p)) == NULL) /* match failed? */
|
||||
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *match_capture(MatchState *ms, const char *s, int l)
|
||||
{
|
||||
size_t len;
|
||||
l = check_capture(ms, l);
|
||||
len = (size_t)ms->capture[l].len;
|
||||
if ((size_t)(ms->src_end-s) >= len &&
|
||||
memcmp(ms->capture[l].init, s, len) == 0)
|
||||
return s+len;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *match(MatchState *ms, const char *s, const char *p)
|
||||
{
|
||||
if (++ms->depth > LJ_MAX_XLEVEL)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATX);
|
||||
init: /* using goto's to optimize tail recursion */
|
||||
switch (*p) {
|
||||
case '(': /* start capture */
|
||||
if (*(p+1) == ')') /* position capture? */
|
||||
s = start_capture(ms, s, p+2, CAP_POSITION);
|
||||
else
|
||||
s = start_capture(ms, s, p+1, CAP_UNFINISHED);
|
||||
break;
|
||||
case ')': /* end capture */
|
||||
s = end_capture(ms, s, p+1);
|
||||
break;
|
||||
case L_ESC:
|
||||
switch (*(p+1)) {
|
||||
case 'b': /* balanced string? */
|
||||
s = matchbalance(ms, s, p+2);
|
||||
if (s == NULL) break;
|
||||
p+=4;
|
||||
goto init; /* else s = match(ms, s, p+4); */
|
||||
case 'f': { /* frontier? */
|
||||
const char *ep; char previous;
|
||||
p += 2;
|
||||
if (*p != '[')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATB);
|
||||
ep = classend(ms, p); /* points to what is next */
|
||||
previous = (s == ms->src_init) ? '\0' : *(s-1);
|
||||
if (matchbracketclass(uchar(previous), p, ep-1) ||
|
||||
!matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; }
|
||||
p=ep;
|
||||
goto init; /* else s = match(ms, s, ep); */
|
||||
}
|
||||
default:
|
||||
if (lj_char_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
|
||||
s = match_capture(ms, s, uchar(*(p+1)));
|
||||
if (s == NULL) break;
|
||||
p+=2;
|
||||
goto init; /* else s = match(ms, s, p+2) */
|
||||
}
|
||||
goto dflt; /* case default */
|
||||
}
|
||||
break;
|
||||
case '\0': /* end of pattern */
|
||||
break; /* match succeeded */
|
||||
case '$':
|
||||
/* is the `$' the last char in pattern? */
|
||||
if (*(p+1) != '\0') goto dflt;
|
||||
if (s != ms->src_end) s = NULL; /* check end of string */
|
||||
break;
|
||||
default: dflt: { /* it is a pattern item */
|
||||
const char *ep = classend(ms, p); /* points to what is next */
|
||||
int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
|
||||
switch (*ep) {
|
||||
case '?': { /* optional */
|
||||
const char *res;
|
||||
if (m && ((res=match(ms, s+1, ep+1)) != NULL)) {
|
||||
s = res;
|
||||
break;
|
||||
}
|
||||
p=ep+1;
|
||||
goto init; /* else s = match(ms, s, ep+1); */
|
||||
}
|
||||
case '*': /* 0 or more repetitions */
|
||||
s = max_expand(ms, s, p, ep);
|
||||
break;
|
||||
case '+': /* 1 or more repetitions */
|
||||
s = (m ? max_expand(ms, s+1, p, ep) : NULL);
|
||||
break;
|
||||
case '-': /* 0 or more repetitions (minimum) */
|
||||
s = min_expand(ms, s, p, ep);
|
||||
break;
|
||||
default:
|
||||
if (m) { s++; p=ep; goto init; } /* else s = match(ms, s+1, ep); */
|
||||
s = NULL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ms->depth--;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
|
||||
{
|
||||
if (i >= ms->level) {
|
||||
if (i == 0) /* ms->level == 0, too */
|
||||
lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */
|
||||
else
|
||||
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
|
||||
} else {
|
||||
ptrdiff_t l = ms->capture[i].len;
|
||||
if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
|
||||
if (l == CAP_POSITION)
|
||||
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
|
||||
else
|
||||
lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
|
||||
}
|
||||
}
|
||||
|
||||
static int push_captures(MatchState *ms, const char *s, const char *e)
|
||||
{
|
||||
int i;
|
||||
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
|
||||
luaL_checkstack(ms->L, nlevels, "too many captures");
|
||||
for (i = 0; i < nlevels; i++)
|
||||
push_onecapture(ms, i, s, e);
|
||||
return nlevels; /* number of strings pushed */
|
||||
}
|
||||
|
||||
static int str_find_aux(lua_State *L, int find)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
GCstr *p = lj_lib_checkstr(L, 2);
|
||||
int32_t start = lj_lib_optint(L, 3, 1);
|
||||
MSize st;
|
||||
if (start < 0) start += (int32_t)s->len; else start--;
|
||||
if (start < 0) start = 0;
|
||||
st = (MSize)start;
|
||||
if (st > s->len) {
|
||||
#if LJ_52
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
#else
|
||||
st = s->len;
|
||||
#endif
|
||||
}
|
||||
if (find && ((L->base+3 < L->top && tvistruecond(L->base+3)) ||
|
||||
!lj_str_haspattern(p))) { /* Search for fixed string. */
|
||||
const char *q = lj_str_find(strdata(s)+st, strdata(p), s->len-st, p->len);
|
||||
if (q) {
|
||||
setintV(L->top-2, (int32_t)(q-strdata(s)) + 1);
|
||||
setintV(L->top-1, (int32_t)(q-strdata(s)) + (int32_t)p->len);
|
||||
return 2;
|
||||
}
|
||||
} else { /* Search for pattern. */
|
||||
MatchState ms;
|
||||
const char *pstr = strdata(p);
|
||||
const char *sstr = strdata(s) + st;
|
||||
int anchor = 0;
|
||||
if (*pstr == '^') { pstr++; anchor = 1; }
|
||||
ms.L = L;
|
||||
ms.src_init = strdata(s);
|
||||
ms.src_end = strdata(s) + s->len;
|
||||
do { /* Loop through string and try to match the pattern. */
|
||||
const char *q;
|
||||
ms.level = ms.depth = 0;
|
||||
q = match(&ms, sstr, pstr);
|
||||
if (q) {
|
||||
if (find) {
|
||||
setintV(L->top++, (int32_t)(sstr-(strdata(s)-1)));
|
||||
setintV(L->top++, (int32_t)(q-strdata(s)));
|
||||
return push_captures(&ms, NULL, NULL) + 2;
|
||||
} else {
|
||||
return push_captures(&ms, sstr, q);
|
||||
}
|
||||
}
|
||||
} while (sstr++ < ms.src_end && !anchor);
|
||||
}
|
||||
setnilV(L->top-1); /* Not found. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(string_find) LJLIB_REC(.)
|
||||
{
|
||||
return str_find_aux(L, 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(string_match)
|
||||
{
|
||||
return str_find_aux(L, 0);
|
||||
}
|
||||
|
||||
LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
|
||||
{
|
||||
const char *p = strVdata(lj_lib_upvalue(L, 2));
|
||||
GCstr *str = strV(lj_lib_upvalue(L, 1));
|
||||
const char *s = strdata(str);
|
||||
TValue *tvpos = lj_lib_upvalue(L, 3);
|
||||
const char *src = s + tvpos->u32.lo;
|
||||
MatchState ms;
|
||||
ms.L = L;
|
||||
ms.src_init = s;
|
||||
ms.src_end = s + str->len;
|
||||
for (; src <= ms.src_end; src++) {
|
||||
const char *e;
|
||||
ms.level = ms.depth = 0;
|
||||
if ((e = match(&ms, src, p)) != NULL) {
|
||||
int32_t pos = (int32_t)(e - s);
|
||||
if (e == src) pos++; /* Ensure progress for empty match. */
|
||||
tvpos->u32.lo = (uint32_t)pos;
|
||||
return push_captures(&ms, src, e);
|
||||
}
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
LJLIB_CF(string_gmatch)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
lj_lib_checkstr(L, 2);
|
||||
L->top = L->base+3;
|
||||
(L->top-1)->u64 = 0;
|
||||
lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
|
||||
{
|
||||
size_t l, i;
|
||||
const char *news = lua_tolstring(ms->L, 3, &l);
|
||||
for (i = 0; i < l; i++) {
|
||||
if (news[i] != L_ESC) {
|
||||
luaL_addchar(b, news[i]);
|
||||
} else {
|
||||
i++; /* skip ESC */
|
||||
if (!lj_char_isdigit(uchar(news[i]))) {
|
||||
luaL_addchar(b, news[i]);
|
||||
} else if (news[i] == '0') {
|
||||
luaL_addlstring(b, s, (size_t)(e - s));
|
||||
} else {
|
||||
push_onecapture(ms, news[i] - '1', s, e);
|
||||
luaL_addvalue(b); /* add capture to accumulated result */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_value(MatchState *ms, luaL_Buffer *b,
|
||||
const char *s, const char *e)
|
||||
{
|
||||
lua_State *L = ms->L;
|
||||
switch (lua_type(L, 3)) {
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TSTRING: {
|
||||
add_s(ms, b, s, e);
|
||||
return;
|
||||
}
|
||||
case LUA_TFUNCTION: {
|
||||
int n;
|
||||
lua_pushvalue(L, 3);
|
||||
n = push_captures(ms, s, e);
|
||||
lua_call(L, n, 1);
|
||||
break;
|
||||
}
|
||||
case LUA_TTABLE: {
|
||||
push_onecapture(ms, 0, s, e);
|
||||
lua_gettable(L, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!lua_toboolean(L, -1)) { /* nil or false? */
|
||||
lua_pop(L, 1);
|
||||
lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */
|
||||
} else if (!lua_isstring(L, -1)) {
|
||||
lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
|
||||
}
|
||||
luaL_addvalue(b); /* add result to accumulator */
|
||||
}
|
||||
|
||||
LJLIB_CF(string_gsub)
|
||||
{
|
||||
size_t srcl;
|
||||
const char *src = luaL_checklstring(L, 1, &srcl);
|
||||
const char *p = luaL_checkstring(L, 2);
|
||||
int tr = lua_type(L, 3);
|
||||
int max_s = luaL_optint(L, 4, (int)(srcl+1));
|
||||
int anchor = (*p == '^') ? (p++, 1) : 0;
|
||||
int n = 0;
|
||||
MatchState ms;
|
||||
luaL_Buffer b;
|
||||
if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
|
||||
tr == LUA_TFUNCTION || tr == LUA_TTABLE))
|
||||
lj_err_arg(L, 3, LJ_ERR_NOSFT);
|
||||
luaL_buffinit(L, &b);
|
||||
ms.L = L;
|
||||
ms.src_init = src;
|
||||
ms.src_end = src+srcl;
|
||||
while (n < max_s) {
|
||||
const char *e;
|
||||
ms.level = ms.depth = 0;
|
||||
e = match(&ms, src, p);
|
||||
if (e) {
|
||||
n++;
|
||||
add_value(&ms, &b, src, e);
|
||||
}
|
||||
if (e && e>src) /* non empty match? */
|
||||
src = e; /* skip it */
|
||||
else if (src < ms.src_end)
|
||||
luaL_addchar(&b, *src++);
|
||||
else
|
||||
break;
|
||||
if (anchor)
|
||||
break;
|
||||
}
|
||||
luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
|
||||
luaL_pushresult(&b);
|
||||
lua_pushinteger(L, n); /* number of substitutions */
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
LJLIB_CF(string_format) LJLIB_REC(.)
|
||||
{
|
||||
int retry = 0;
|
||||
SBuf *sb;
|
||||
do {
|
||||
sb = lj_buf_tmp_(L);
|
||||
retry = lj_strfmt_putarg(L, sb, 1, -retry);
|
||||
} while (retry > 0);
|
||||
setstrV(L, L->top-1, lj_buf_str(L, sb));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_string(lua_State *L)
|
||||
{
|
||||
GCtab *mt;
|
||||
global_State *g;
|
||||
LJ_LIB_REG(L, LUA_STRLIBNAME, string);
|
||||
mt = lj_tab_new(L, 0, 1);
|
||||
/* NOBARRIER: basemt is a GC root. */
|
||||
g = G(L);
|
||||
setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
|
||||
settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
|
||||
mt->nomm = (uint8_t)(~(1u<<MM_index));
|
||||
#if LJ_HASBUFFER
|
||||
lj_lib_prereg(L, LUA_STRLIBNAME ".buffer", luaopen_string_buffer, tabV(L->top-1));
|
||||
#endif
|
||||
#if LJ_SRB2LIB
|
||||
lua_getfield(L, -1, "gmatch");
|
||||
lua_setfield(L, -2, "gfind"); /* Ugh, can we get rid of this already? */
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
** Table library.
|
||||
** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_table_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_buf.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_table
|
||||
|
||||
LJLIB_LUA(table_foreachi) /*
|
||||
function(t, f)
|
||||
CHECK_tab(t)
|
||||
CHECK_func(f)
|
||||
for i=1,#t do
|
||||
local r = f(i, t[i])
|
||||
if r ~= nil then return r end
|
||||
end
|
||||
end
|
||||
*/
|
||||
|
||||
LJLIB_LUA(table_foreach) /*
|
||||
function(t, f)
|
||||
CHECK_tab(t)
|
||||
CHECK_func(f)
|
||||
for k, v in PAIRS(t) do
|
||||
local r = f(k, v)
|
||||
if r ~= nil then return r end
|
||||
end
|
||||
end
|
||||
*/
|
||||
|
||||
LJLIB_LUA(table_getn) /*
|
||||
function(t)
|
||||
CHECK_tab(t)
|
||||
return #t
|
||||
end
|
||||
*/
|
||||
|
||||
LJLIB_CF(table_maxn)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
TValue *array = tvref(t->array);
|
||||
Node *node;
|
||||
lua_Number m = 0;
|
||||
ptrdiff_t i;
|
||||
for (i = (ptrdiff_t)t->asize - 1; i >= 0; i--)
|
||||
if (!tvisnil(&array[i])) {
|
||||
m = (lua_Number)(int32_t)i;
|
||||
break;
|
||||
}
|
||||
node = noderef(t->node);
|
||||
for (i = (ptrdiff_t)t->hmask; i >= 0; i--)
|
||||
if (!tvisnil(&node[i].val) && tvisnumber(&node[i].key)) {
|
||||
lua_Number n = numberVnum(&node[i].key);
|
||||
if (n > m) m = n;
|
||||
}
|
||||
#if LJ_INTONLY
|
||||
setintV(L->top-1, lj_num2int(m));
|
||||
#else
|
||||
setnumV(L->top-1, m);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_insert) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n, i = (int32_t)lj_tab_len(t) + 1;
|
||||
int nargs = (int)((char *)L->top - (char *)L->base);
|
||||
if (nargs != 2*sizeof(TValue)) {
|
||||
if (nargs != 3*sizeof(TValue))
|
||||
lj_err_caller(L, LJ_ERR_TABINS);
|
||||
/* NOBARRIER: This just moves existing elements around. */
|
||||
for (n = lj_lib_checkint(L, 2); i > n; i--) {
|
||||
/* The set may invalidate the get pointer, so need to do it first! */
|
||||
TValue *dst = lj_tab_setint(L, t, i);
|
||||
cTValue *src = lj_tab_getint(t, i-1);
|
||||
if (src) {
|
||||
copyTV(L, dst, src);
|
||||
} else {
|
||||
setnilV(dst);
|
||||
}
|
||||
}
|
||||
i = n;
|
||||
}
|
||||
{
|
||||
TValue *dst = lj_tab_setint(L, t, i);
|
||||
copyTV(L, dst, L->top-1); /* Set new value. */
|
||||
lj_gc_barriert(L, t, dst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_LUA(table_remove) /*
|
||||
function(t, pos)
|
||||
CHECK_tab(t)
|
||||
local len = #t
|
||||
if pos == nil then
|
||||
if len ~= 0 then
|
||||
local old = t[len]
|
||||
t[len] = nil
|
||||
return old
|
||||
end
|
||||
else
|
||||
CHECK_int(pos)
|
||||
if pos >= 1 and pos <= len then
|
||||
local old = t[pos]
|
||||
for i=pos+1,len do
|
||||
t[i-1] = t[i]
|
||||
end
|
||||
t[len] = nil
|
||||
return old
|
||||
end
|
||||
end
|
||||
end
|
||||
*/
|
||||
|
||||
LJLIB_LUA(table_move) /*
|
||||
function(a1, f, e, t, a2)
|
||||
CHECK_tab(a1)
|
||||
CHECK_int(f)
|
||||
CHECK_int(e)
|
||||
CHECK_int(t)
|
||||
if a2 == nil then a2 = a1 end
|
||||
CHECK_tab(a2)
|
||||
if e >= f then
|
||||
local d = t - f
|
||||
if t > e or t <= f or a2 ~= a1 then
|
||||
for i=f,e do a2[i+d] = a1[i] end
|
||||
else
|
||||
for i=e,f,-1 do a2[i+d] = a1[i] end
|
||||
end
|
||||
end
|
||||
return a2
|
||||
end
|
||||
*/
|
||||
|
||||
LJLIB_CF(table_concat) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCstr *sep = lj_lib_optstr(L, 2);
|
||||
int32_t i = lj_lib_optint(L, 3, 1);
|
||||
int32_t e = (L->base+3 < L->top && !tvisnil(L->base+3)) ?
|
||||
lj_lib_checkint(L, 4) : (int32_t)lj_tab_len(t);
|
||||
SBuf *sb = lj_buf_tmp_(L);
|
||||
SBuf *sbx = lj_buf_puttab(sb, t, sep, i, e);
|
||||
if (LJ_UNLIKELY(!sbx)) { /* Error: bad element type. */
|
||||
int32_t idx = (int32_t)(intptr_t)sb->w;
|
||||
cTValue *o = lj_tab_getint(t, idx);
|
||||
lj_err_callerv(L, LJ_ERR_TABCAT,
|
||||
lj_obj_itypename[o ? itypemap(o) : ~LJ_TNIL], idx);
|
||||
}
|
||||
setstrV(L, L->top-1, lj_buf_str(L, sbx));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void set2(lua_State *L, int i, int j)
|
||||
{
|
||||
lua_rawseti(L, 1, i);
|
||||
lua_rawseti(L, 1, j);
|
||||
}
|
||||
|
||||
static int sort_comp(lua_State *L, int a, int b)
|
||||
{
|
||||
if (!lua_isnil(L, 2)) { /* function? */
|
||||
int res;
|
||||
lua_pushvalue(L, 2);
|
||||
lua_pushvalue(L, a-1); /* -1 to compensate function */
|
||||
lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
|
||||
lua_call(L, 2, 1);
|
||||
res = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
} else { /* a < b? */
|
||||
return lua_lessthan(L, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
static void auxsort(lua_State *L, int l, int u)
|
||||
{
|
||||
while (l < u) { /* for tail recursion */
|
||||
int i, j;
|
||||
/* sort elements a[l], a[(l+u)/2] and a[u] */
|
||||
lua_rawgeti(L, 1, l);
|
||||
lua_rawgeti(L, 1, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
|
||||
set2(L, l, u); /* swap a[l] - a[u] */
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
if (u-l == 1) break; /* only 2 elements */
|
||||
i = (l+u)/2;
|
||||
lua_rawgeti(L, 1, i);
|
||||
lua_rawgeti(L, 1, l);
|
||||
if (sort_comp(L, -2, -1)) { /* a[i]<a[l]? */
|
||||
set2(L, i, l);
|
||||
} else {
|
||||
lua_pop(L, 1); /* remove a[l] */
|
||||
lua_rawgeti(L, 1, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
|
||||
set2(L, i, u);
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
if (u-l == 2) break; /* only 3 elements */
|
||||
lua_rawgeti(L, 1, i); /* Pivot */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawgeti(L, 1, u-1);
|
||||
set2(L, i, u-1);
|
||||
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
|
||||
i = l; j = u-1;
|
||||
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
|
||||
/* repeat ++i until a[i] >= P */
|
||||
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (i>=u) lj_err_caller(L, LJ_ERR_TABSORT);
|
||||
lua_pop(L, 1); /* remove a[i] */
|
||||
}
|
||||
/* repeat --j until a[j] <= P */
|
||||
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (j<=l) lj_err_caller(L, LJ_ERR_TABSORT);
|
||||
lua_pop(L, 1); /* remove a[j] */
|
||||
}
|
||||
if (j<i) {
|
||||
lua_pop(L, 3); /* pop pivot, a[i], a[j] */
|
||||
break;
|
||||
}
|
||||
set2(L, i, j);
|
||||
}
|
||||
lua_rawgeti(L, 1, u-1);
|
||||
lua_rawgeti(L, 1, i);
|
||||
set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
|
||||
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */
|
||||
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
|
||||
if (i-l < u-i) {
|
||||
j=l; i=i-1; l=i+2;
|
||||
} else {
|
||||
j=i+1; i=u; u=j-2;
|
||||
}
|
||||
auxsort(L, j, i); /* call recursively the smaller one */
|
||||
} /* repeat the routine for the larger one */
|
||||
}
|
||||
|
||||
LJLIB_CF(table_sort)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n = (int32_t)lj_tab_len(t);
|
||||
lua_settop(L, 2);
|
||||
if (!tvisnil(L->base+1))
|
||||
lj_lib_checkfunc(L, 2);
|
||||
auxsort(L, 1, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_PUSH("n")
|
||||
LJLIB_CF(table_pack)
|
||||
{
|
||||
TValue *array, *base = L->base;
|
||||
MSize i, n = (uint32_t)(L->top - base);
|
||||
GCtab *t = lj_tab_new(L, n ? n+1 : 0, 1);
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
setintV(lj_tab_setstr(L, t, strV(lj_lib_upvalue(L, 1))), (int32_t)n);
|
||||
for (array = tvref(t->array) + 1, i = 0; i < n; i++)
|
||||
copyTV(L, &array[i], &base[i]);
|
||||
settabV(L, base, t);
|
||||
L->top = base+1;
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_NOREG LJLIB_CF(table_new) LJLIB_REC(.)
|
||||
{
|
||||
int32_t a = lj_lib_checkint(L, 1);
|
||||
int32_t h = lj_lib_checkint(L, 2);
|
||||
lua_createtable(L, a, h);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_NOREG LJLIB_CF(table_clear) LJLIB_REC(.)
|
||||
{
|
||||
lj_tab_clear(lj_lib_checktab(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luaopen_table_new(lua_State *L)
|
||||
{
|
||||
return lj_lib_postreg(L, lj_cf_table_new, FF_table_new, "new");
|
||||
}
|
||||
|
||||
static int luaopen_table_clear(lua_State *L)
|
||||
{
|
||||
return lj_lib_postreg(L, lj_cf_table_clear, FF_table_clear, "clear");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_table(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_TABLIBNAME, table);
|
||||
#if LJ_52
|
||||
lua_getglobal(L, "unpack");
|
||||
lua_setfield(L, -2, "unpack");
|
||||
#endif
|
||||
lj_lib_prereg(L, LUA_TABLIBNAME ".new", luaopen_table_new, tabV(L->top-1));
|
||||
lj_lib_prereg(L, LUA_TABLIBNAME ".clear", luaopen_table_clear, tabV(L->top-1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
** Bundled memory allocator.
|
||||
** Donated to the public domain.
|
||||
*/
|
||||
|
||||
#ifndef _LJ_ALLOC_H
|
||||
#define _LJ_ALLOC_H
|
||||
|
||||
#include "lj_def.h"
|
||||
|
||||
#ifndef LUAJIT_USE_SYSMALLOC
|
||||
LJ_FUNC void *lj_alloc_create(PRNGState *rs);
|
||||
LJ_FUNC void lj_alloc_setprng(void *msp, PRNGState *rs);
|
||||
LJ_FUNC void lj_alloc_destroy(void *msp);
|
||||
LJ_FUNC void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Loaded 100 of 421 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user