Compare commits
586
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8461a3da47 | ||
|
|
08fe5a7b6b | ||
|
|
37c15c9aa0 | ||
|
|
850f61dc22 | ||
|
|
82a735c367 | ||
|
|
42b094a575 | ||
|
|
3242560d02 | ||
|
|
6e17716a69 | ||
|
|
eff88dd591 | ||
|
|
bf0bbcc89b | ||
|
|
d46fdf152a | ||
|
|
3e1e1fc1b3 | ||
|
|
15952d3852 | ||
|
|
201c881b43 | ||
|
|
19feb40e8c | ||
|
|
2f4031b3d6 | ||
|
|
d8648176e9 | ||
|
|
e796314a24 | ||
|
|
7e9ed22dff | ||
|
|
c57c931099 | ||
|
|
832b0a5706 | ||
|
|
6196bdfe56 | ||
|
|
d06e665ebd | ||
|
|
5460d78ff1 | ||
|
|
9d0a43d5e6 | ||
|
|
15a210bede | ||
|
|
59c032f135 | ||
|
|
23236c88ea | ||
|
|
d1dd6de070 | ||
|
|
cbadc356f9 | ||
|
|
5dc483e256 | ||
|
|
6b86f846ca | ||
|
|
217f5f021d | ||
|
|
5ea0ce094e | ||
|
|
3e52baa209 | ||
|
|
508428bbc9 | ||
|
|
3a687db0bb | ||
|
|
38a277a914 | ||
|
|
dc4468593c | ||
|
|
a143c2de0c | ||
|
|
7da9d7cb0d | ||
|
|
d09b79995d | ||
|
|
61f23d99a1 | ||
|
|
813b59e25f | ||
|
|
ee3337f3c4 | ||
|
|
a060e1d76e | ||
|
|
3c6041c97c | ||
|
|
ca81c1185c | ||
|
|
40ea5d6513 | ||
|
|
4f45b391ea | ||
|
|
d6b87da063 | ||
|
|
0f2675e794 | ||
|
|
0678cc8c48 | ||
|
|
70c271d2a4 | ||
|
|
9d02a3ac0b | ||
|
|
6b180c8f59 | ||
|
|
a23c1bda3e | ||
|
|
dc24970af2 | ||
|
|
ea54d5685d | ||
|
|
99a021ad67 | ||
|
|
12da1cbfc1 | ||
|
|
bc1eeb6e38 | ||
|
|
c1e5a11962 | ||
|
|
d2138678ef | ||
|
|
1c3cdf3493 | ||
|
|
377c0db5d3 | ||
|
|
6cb1f525d9 | ||
|
|
a01e9280cc | ||
|
|
14d8c05a2a | ||
|
|
97c0a1c6fd | ||
|
|
d7c5af9b80 | ||
|
|
cb2dc4cb3a | ||
|
|
444e87dd7d | ||
|
|
d3997e0746 | ||
|
|
32599a08c1 | ||
|
|
37c4b0da3b | ||
|
|
5d15d01e21 | ||
|
|
3ca2165fd9 | ||
|
|
b859f8a136 | ||
|
|
22475e0fda | ||
|
|
c0ff5c9d96 | ||
|
|
c9562a6e04 | ||
|
|
d206abf3e1 | ||
|
|
ea2af739dd | ||
|
|
00bf7ebf38 | ||
|
|
e3743b185f | ||
|
|
54b239747e | ||
|
|
ae12ae8581 | ||
|
|
001f8afc37 | ||
|
|
e7b05c6a3d | ||
|
|
3d638927d5 | ||
|
|
4a66aa7fd2 | ||
|
|
9d02cc4684 | ||
|
|
65c72fd55c | ||
|
|
8453698f66 | ||
|
|
74fafb3b56 | ||
|
|
f044d94e90 | ||
|
|
87914b0a51 | ||
|
|
d7a5f7e68b | ||
|
|
7c11c589f7 | ||
|
|
3384941cd1 | ||
|
|
d0c7b9c77c | ||
|
|
9b93586eec | ||
|
|
023a22af08 | ||
|
|
4e46a25ab1 | ||
|
|
be4db7b5d1 | ||
|
|
985983391b | ||
|
|
3b9deb08d8 | ||
|
|
7468b007f7 | ||
|
|
3c0d3d13b5 | ||
|
|
966fc3b8f5 | ||
|
|
77f4a25055 | ||
|
|
82ad5da7c7 | ||
|
|
d023e9932d | ||
|
|
41772b4a0c | ||
|
|
58951a29a8 | ||
|
|
5bb6538703 | ||
|
|
9db3748f68 | ||
|
|
87e0bce5ee | ||
|
|
034293df59 | ||
|
|
eea8111e61 | ||
|
|
f2cbb658e1 | ||
|
|
c1d3f62bf0 | ||
|
|
5ec75b2d38 | ||
|
|
1784eaf906 | ||
|
|
948e1855a6 | ||
|
|
a3a627f513 | ||
|
|
ea57986312 | ||
|
|
5d1e604aac | ||
|
|
41674d125c | ||
|
|
79d1c86982 | ||
|
|
6da8e2e095 | ||
|
|
4907e64714 | ||
|
|
953c829e9f | ||
|
|
357a8bba05 | ||
|
|
bf5ecd7668 | ||
|
|
6000734704 | ||
|
|
71730d920c | ||
|
|
0f504a9010 | ||
|
|
960ecdfd74 | ||
|
|
5a10a2ea10 | ||
|
|
e82e2ac768 | ||
|
|
337be883d3 | ||
|
|
d0ffb094c2 | ||
|
|
79299ec47d | ||
|
|
2bd37e80e3 | ||
|
|
fc7c5d5560 | ||
|
|
8881dea2e2 | ||
|
|
2353dca8ec | ||
|
|
c98730f717 | ||
|
|
007943142a | ||
|
|
6769cf0335 | ||
|
|
c791bd5e46 | ||
|
|
fc797da542 | ||
|
|
14a8dcb98b | ||
|
|
ab30b264fc | ||
|
|
d926987d44 | ||
|
|
eb0c3dfd31 | ||
|
|
1a3ef521b3 | ||
|
|
aeb396831a | ||
|
|
6758ecbf69 | ||
|
|
5f92abb7bc | ||
|
|
dd24dc670f | ||
|
|
95b076581a | ||
|
|
054dcf0a0b | ||
|
|
07ff906338 | ||
|
|
a2e1c651fc | ||
|
|
9b97470c09 | ||
|
|
20e3f3694f | ||
|
|
400cc6e17b | ||
|
|
be2521fe46 | ||
|
|
8b63ae4ca2 | ||
|
|
b71ed43f10 | ||
|
|
efb4a54428 | ||
|
|
3209a7a633 | ||
|
|
301a9d3328 | ||
|
|
2b5ff215c7 | ||
|
|
8b2008031d | ||
|
|
8fb0e38f4c | ||
|
|
c02aa3a2eb | ||
|
|
b60ce06335 | ||
|
|
d921dcd9e7 | ||
|
|
97313e9e55 | ||
|
|
9ff5a3a03e | ||
|
|
b6a9b50f93 | ||
|
|
70390858d8 | ||
|
|
2178970ff7 | ||
|
|
78b2052445 | ||
|
|
934708b206 | ||
|
|
1dd53ebff5 | ||
|
|
09b5bf5ac3 | ||
|
|
9e55e909a8 | ||
|
|
8a98051759 | ||
|
|
c77d38e923 | ||
|
|
14920c0e1e | ||
|
|
64502471e1 | ||
|
|
cd68bb62ac | ||
|
|
9f98e683a9 | ||
|
|
b03e55bb7e | ||
|
|
1fb0f4a4ea | ||
|
|
52fd89decf | ||
|
|
4ce577d8fb | ||
|
|
a6e1957768 | ||
|
|
ec47401151 | ||
|
|
7070bc1608 | ||
|
|
2ab6342d3c | ||
|
|
c20bd4734b | ||
|
|
5c1b882169 | ||
|
|
2fb648d68a | ||
|
|
ce50daa87a | ||
|
|
3b4bd1c6dc | ||
|
|
edca6c3760 | ||
|
|
f3628a797a | ||
|
|
12b15958c6 | ||
|
|
5993affd9c | ||
|
|
fb3d3787e6 | ||
|
|
36d23d2f42 | ||
|
|
848dc0ab38 | ||
|
|
7a9bc5ae75 | ||
|
|
0cf80632c1 | ||
|
|
374921623b | ||
|
|
df2de5ecfa | ||
|
|
cd55a7e214 | ||
|
|
aeb4e8e54c | ||
|
|
fb53f88893 | ||
|
|
a7b8118ce8 | ||
|
|
87b24c2445 | ||
|
|
0f4aeeceff | ||
|
|
a2874a14e6 | ||
|
|
e79ea010b0 | ||
|
|
f2ef1bb240 | ||
|
|
4a685d478f | ||
|
|
f923dc4d8d | ||
|
|
30a3da4cd4 | ||
|
|
185a2196ca | ||
|
|
9717ce0107 | ||
|
|
4236127956 | ||
|
|
68cd5f2f90 | ||
|
|
58cfe2346e | ||
|
|
6a6cae2a44 | ||
|
|
b7e4861e6c | ||
|
|
94b174c8a0 | ||
|
|
3426139d03 | ||
|
|
d0434bf114 | ||
|
|
cbf76cd468 | ||
|
|
7b3e814524 | ||
|
|
1dcd29db9c | ||
|
|
cda4cca805 | ||
|
|
2ac412c11a | ||
|
|
f9ba391236 | ||
|
|
21436b2690 | ||
|
|
ddb1e6cc2a | ||
|
|
3a4323e288 | ||
|
|
da18dd39a4 | ||
|
|
96dad309ac | ||
|
|
28b7437e21 | ||
|
|
9a80f1df6c | ||
|
|
e196d6a28a | ||
|
|
c035a82394 | ||
|
|
bf2f38acd2 | ||
|
|
09ea7283f9 | ||
|
|
8af967db7f | ||
|
|
98ee53340f | ||
|
|
feb29fa7a3 | ||
|
|
df1ffa4c2a | ||
|
|
bcbc7142be | ||
|
|
ab0ee723b3 | ||
|
|
2868798f58 | ||
|
|
63acb2bf4d | ||
|
|
f2b65f57ec | ||
|
|
3b465121cd | ||
|
|
692998820c | ||
|
|
1fbebb2a62 | ||
|
|
18f4c2326f | ||
|
|
105456e56e | ||
|
|
8da4550d79 | ||
|
|
19d7a90c28 | ||
|
|
67667b10db | ||
|
|
4c114d8875 | ||
|
|
53ba4e48bd | ||
|
|
9bd3ac3dfb | ||
|
|
df9a5a9beb | ||
|
|
cc8ea01476 | ||
|
|
ec9e950185 | ||
|
|
324a22b6d1 | ||
|
|
3b0ed04e70 | ||
|
|
611f49cfb6 | ||
|
|
35f2070d3b | ||
|
|
78d3941f7d | ||
|
|
94d2bf5047 | ||
|
|
0ebe2e0752 | ||
|
|
4eec3f0908 | ||
|
|
a3bb62f2c3 | ||
|
|
fabd92adc7 | ||
|
|
38860a6313 | ||
|
|
d633a5786f | ||
|
|
fb4175736f | ||
|
|
43d25e58b9 | ||
|
|
0b7f7a50c7 | ||
|
|
8d01c01afd | ||
|
|
15b416b527 | ||
|
|
2c33effee1 | ||
|
|
4958fd2c14 | ||
|
|
87e2356eea | ||
|
|
c0d3231e02 | ||
|
|
d1c0f6754c | ||
|
|
01e95b51cf | ||
|
|
1ad7abf33d | ||
|
|
7e82ed6940 | ||
|
|
1d1efc6288 | ||
|
|
f865986675 | ||
|
|
981af07d5c | ||
|
|
57f8c655c8 | ||
|
|
acd6c9a615 | ||
|
|
3b8a2b225f | ||
|
|
96c110242e | ||
|
|
9994d2e983 | ||
|
|
b91dfef777 | ||
|
|
c3f554f1f5 | ||
|
|
3ccc37ca3f | ||
|
|
02b9429363 | ||
|
|
be14fa5f94 | ||
|
|
2f6db41b11 | ||
|
|
59865b2a3a | ||
|
|
e0bcf4bcef | ||
|
|
21d5cd1cd1 | ||
|
|
a714ec9d37 | ||
|
|
8e3811c490 | ||
|
|
93e983f74e | ||
|
|
0a81e67e89 | ||
|
|
f748cae562 | ||
|
|
dc4e06d88b | ||
|
|
d979cc8c1f | ||
|
|
25c1c9875b | ||
|
|
2bfcb6ef6e | ||
|
|
455c7608b1 | ||
|
|
875a9beb08 | ||
|
|
660427f759 | ||
|
|
3329c9beae | ||
|
|
f2f3e5b7bf | ||
|
|
79f044bc47 | ||
|
|
7b0c05b70b | ||
|
|
0f6e102e02 | ||
|
|
b289e93650 | ||
|
|
2b2b70e636 | ||
|
|
83baf5a4b6 | ||
|
|
f3c708613f | ||
|
|
849001ae9e | ||
|
|
a92b38e826 | ||
|
|
1cce6c4a47 | ||
|
|
2a798dd643 | ||
|
|
ef4421db20 | ||
|
|
d63787f8fc | ||
|
|
0e5ebd07ba | ||
|
|
6a287b955e | ||
|
|
c1a22411ca | ||
|
|
e1bb42fade | ||
|
|
31f341cf6f | ||
|
|
df1244d6f4 | ||
|
|
bbd0cdbf62 | ||
|
|
1b2324edab | ||
|
|
2b8cc2a69b | ||
|
|
868d48ae22 | ||
|
|
dddaba3254 | ||
|
|
65d7f7cc31 | ||
|
|
8e7d7feb57 | ||
|
|
c2d7480f3d | ||
|
|
07f2f2e9fc | ||
|
|
1f93550b6d | ||
|
|
a9e4aafae1 | ||
|
|
fddadf9d56 | ||
|
|
aa52cf157d | ||
|
|
13080f646f | ||
|
|
75ea6fb9b2 | ||
|
|
af5fba81c5 | ||
|
|
825fe6d326 | ||
|
|
d579d70113 | ||
|
|
70448f4590 | ||
|
|
29acf97dbc | ||
|
|
eb1065f387 | ||
|
|
23428b63a5 | ||
|
|
4807012bd8 | ||
|
|
9c427aa548 | ||
|
|
3bed3a70a6 | ||
|
|
be1acdb602 | ||
|
|
e2c4c7645e | ||
|
|
f03aea15c7 | ||
|
|
ee50ce61fe | ||
|
|
59a9fc5b1f | ||
|
|
dfae2f090c | ||
|
|
d06c4d2c0e | ||
|
|
f2d7b748ea | ||
|
|
5e100e2195 | ||
|
|
a79e723e8d | ||
|
|
bb864dff6f | ||
|
|
31898ab4a5 | ||
|
|
66e697e8df | ||
|
|
d82f24e17a | ||
|
|
e65f04f16b | ||
|
|
0279965074 | ||
|
|
beccb36b66 | ||
|
|
4255ab9d32 | ||
|
|
72a3c483e4 | ||
|
|
ca5473304f | ||
|
|
12bd7b7a16 | ||
|
|
dd56580855 | ||
|
|
424dbd7379 | ||
|
|
87f5b1026c | ||
|
|
9455db0eec | ||
|
|
a76cf55a2d | ||
|
|
c387926977 | ||
|
|
7c53a8744d | ||
|
|
cf815b17de | ||
|
|
7d28c9649e | ||
|
|
f084da7c01 | ||
|
|
78fc7313fb | ||
|
|
f0309e4904 | ||
|
|
bcf66edebe | ||
|
|
373a20c3e5 | ||
|
|
676c19c362 | ||
|
|
a7af34bd7e | ||
|
|
951b89be36 | ||
|
|
a5ceb3d4eb | ||
|
|
9910e05760 | ||
|
|
9e2ccbfab7 | ||
|
|
eb01a45024 | ||
|
|
cb0836de8a | ||
|
|
9a191daa8d | ||
|
|
7cba4cbc95 | ||
|
|
b28f20cce7 | ||
|
|
429de97083 | ||
|
|
a83d183abb | ||
|
|
31b8dccffd | ||
|
|
de86a1ed70 | ||
|
|
296f6a739e | ||
|
|
617d4ea911 | ||
|
|
5a59432733 | ||
|
|
6394ea30e2 | ||
|
|
b27633c86e | ||
|
|
813f0157e3 | ||
|
|
592400a1ea | ||
|
|
8312e653e8 | ||
|
|
31df298341 | ||
|
|
60100fa6af | ||
|
|
c1859a111c | ||
|
|
05d5bfbf37 | ||
|
|
82f16127c0 | ||
|
|
785741026f | ||
|
|
51daf28297 | ||
|
|
7fabf886fc | ||
|
|
a46391bf42 | ||
|
|
b918321f77 | ||
|
|
ce612c8e57 | ||
|
|
4ed7f3909a | ||
|
|
e7f47735ed | ||
|
|
cb430bf00a | ||
|
|
e8db102b9e | ||
|
|
16472a61d9 | ||
|
|
a9819e4123 | ||
|
|
76df3f0225 | ||
|
|
0dd7511a9b | ||
|
|
af2c66e7d7 | ||
|
|
eef84657a1 | ||
|
|
e0e024ee93 | ||
|
|
ea2abba574 | ||
|
|
17f109e204 | ||
|
|
626f79cbd1 | ||
|
|
3c45895878 | ||
|
|
590a6a2bbe | ||
|
|
b206e7d9c1 | ||
|
|
ced50bd78b | ||
|
|
967095df5b | ||
|
|
7e68e76ec4 | ||
|
|
471bedbcf4 | ||
|
|
82eea2719f | ||
|
|
7471cc64f9 | ||
|
|
679758c05b | ||
|
|
cc17a8f8db | ||
|
|
7fbf69ae63 | ||
|
|
1039969f69 | ||
|
|
ae63f4f6ab | ||
|
|
19fca19ca7 | ||
|
|
41c5908c5e | ||
|
|
db8220801c | ||
|
|
cc3359faea | ||
|
|
22c71388d4 | ||
|
|
a4bc5dc0ce | ||
|
|
e55ec5fde0 | ||
|
|
9e461d21cb | ||
|
|
95133d8483 | ||
|
|
8aad248c25 | ||
|
|
029a423f55 | ||
|
|
7028399366 | ||
|
|
7fedf8daad | ||
|
|
0a108e6289 | ||
|
|
a173ba0296 | ||
|
|
0e973d5f23 | ||
|
|
ff7caa57e8 | ||
|
|
f0deff931d | ||
|
|
b7100e9dc1 | ||
|
|
b1090abe70 | ||
|
|
b8470c1cae | ||
|
|
c71642dca0 | ||
|
|
cfb92e5da5 | ||
|
|
4ce55e3ea3 | ||
|
|
d3690f9143 | ||
|
|
e66d9e3adb | ||
|
|
751fbccb29 | ||
|
|
f5ee178407 | ||
|
|
e20a6f3d11 | ||
|
|
bd46b47b22 | ||
|
|
a5260f4441 | ||
|
|
76f03b5987 | ||
|
|
adfd202218 | ||
|
|
1d76280d0e | ||
|
|
bb9bb76653 | ||
|
|
7611d10072 | ||
|
|
3e4f3b88b0 | ||
|
|
82558bd86b | ||
|
|
709c3570fb | ||
|
|
ab814f55e6 | ||
|
|
a260b9316d | ||
|
|
1146719de1 | ||
|
|
4496738a24 | ||
|
|
bcedb15b23 | ||
|
|
41ed7176e9 | ||
|
|
ed373a3ef2 | ||
|
|
6b368b46a7 | ||
|
|
c35246f426 | ||
|
|
50d53d3ce6 | ||
|
|
8dda9f7c2d | ||
|
|
75db09e18a | ||
|
|
131f3a9a4d | ||
|
|
178ea401b7 | ||
|
|
c7c37e1007 | ||
|
|
60deee1d17 | ||
|
|
29667672e4 | ||
|
|
9b46e1da78 | ||
|
|
d14415a4e6 | ||
|
|
a0d6dced00 | ||
|
|
4db68ff5c4 | ||
|
|
23e7f5b1fb | ||
|
|
fa4c69f95d | ||
|
|
c2b9a26c62 | ||
|
|
1fe7878074 | ||
|
|
2b2c242d14 | ||
|
|
efc8f7948f | ||
|
|
b7eeb504ee | ||
|
|
a99db2a29b | ||
|
|
58d61badcb | ||
|
|
8d0241e0c3 | ||
|
|
9af7b2ead4 | ||
|
|
d3bdfd5205 | ||
|
|
25363120f8 | ||
|
|
99ddff2d87 | ||
|
|
0a84ca8db3 | ||
|
|
7b81e3c7cf | ||
|
|
6540e27844 | ||
|
|
20ba8e0297 | ||
|
|
0b0cf5ba66 | ||
|
|
f75a4d795a | ||
|
|
9e72393a11 | ||
|
|
ec4fb0bcf8 | ||
|
|
98efd9b1d2 | ||
|
|
8a69719acf | ||
|
|
fa90586a83 | ||
|
|
2610f361fd | ||
|
|
4415f1f38b | ||
|
|
4748915472 | ||
|
|
5551db3432 | ||
|
|
85e7011b6c | ||
|
|
8445af93d2 | ||
|
|
27ba74c720 | ||
|
|
a8eb7fe5fe | ||
|
|
24ae2dc966 | ||
|
|
d99b6268de | ||
|
|
b3a4be21d3 | ||
|
|
19171ee165 | ||
|
|
6daec5fa58 | ||
|
|
d971bae7cd | ||
|
|
54462320d2 | ||
|
|
c542442987 | ||
|
|
ce62546e90 | ||
|
|
907f7892f9 | ||
|
|
c54e87bd6d | ||
|
|
a603736f64 |
No files matched your search
+2
-1
@@ -10,6 +10,7 @@ __pycache__/
|
||||
# 构建产物(防御性)
|
||||
**/output/
|
||||
**/temp/
|
||||
**/output_dbg/
|
||||
**/*.deps.json
|
||||
**/*.doc.json
|
||||
**/*.pyi
|
||||
@@ -27,4 +28,4 @@ __pycache__/
|
||||
# 编译
|
||||
**/output/
|
||||
**/temp/
|
||||
**/.tpv_build/
|
||||
**/__vpcbuild__/
|
||||
+2605
-1124
File diff suppressed because it is too large.
Load diff
+682
-694
File diff suppressed because it is too large.
Load diff
+255
-353
@@ -1,115 +1,64 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import sys
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import win32.fileio as fileio
|
||||
import ast
|
||||
import llvmlite
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Gen.Generator as GenGenerator
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Stub.StubGen.Converter as StubConverter
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
|
||||
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
|
||||
import lib.Phase.Phase1 as Phase1
|
||||
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Phase1Scan - Phase1 拆分(阶段 E):Phase1Scan
|
||||
#
|
||||
# 直接移动模式:函数迁至此模块,原 Phase1.vp 仅保留常量/_mbuddy;
|
||||
# 调用方经本模块前缀访问。全局符号经 Phase1._xxx 跨模块访问。
|
||||
# ============================================================
|
||||
# Phase1Scan - scan class inheritance, generic-class markers and container usage.
|
||||
# Functions live in this module; globals are accessed cross-module via Phase1._xxx.
|
||||
|
||||
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
|
||||
# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。
|
||||
# 类名/父类名存储在 class_names_buf/parent_names_buf(每个 Phase1.CLASS_NAME_LEN 字节),
|
||||
# 定义 SHA1 存储在 def_sha1s_buf(每个 Phase1.SHA1_LEN 字节)。
|
||||
# Topological sort stores class info/deps in flat byte arrays (names in
|
||||
# class_names_buf/parent_names_buf, SHA1s in def_sha1s_buf) to avoid NULL str fields.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
|
||||
#
|
||||
# 遍历所有可达 includes 文件,解析 AST 收集 ClassDef 的类名和父类名。
|
||||
# 结果存入扁平字节数组,返回收集到的类数量。
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
# result: IncludesScanner 扫描结果
|
||||
# reachable_set: 可达 SHA1 集合
|
||||
# reachable_count: 可达 SHA1 数量
|
||||
# use_reachable: 是否使用可达集合过滤
|
||||
# sha1_set: SHA1 集合(use_reachable=0 时使用)
|
||||
# set_count: SHA1 集合数量
|
||||
# class_names_buf: 类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,连续内存)
|
||||
# parent_names_buf: 父类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,无父类填 '\0')
|
||||
# def_sha1s_buf: 定义 SHA1 扁平数组(每个 Phase1.SHA1_LEN 字节)
|
||||
#
|
||||
# Returns:
|
||||
# 收集到的类数量,-1 失败
|
||||
# ============================================================
|
||||
def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
|
||||
result: IncludesScanner.ScanResult | t.CPtr,
|
||||
reachable_set: str, reachable_count: int,
|
||||
use_reachable: int,
|
||||
sha1_set: str, set_count: int,
|
||||
class_names_buf: bytes,
|
||||
parent_names_buf: bytes,
|
||||
def_sha1s_buf: bytes,
|
||||
generic_file_indices: t.CPtr,
|
||||
generic_file_count_box: t.CPtr) -> int:
|
||||
"""预扫描 includes 文件,收集类继承关系"""
|
||||
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
|
||||
return -1
|
||||
# _ScanClassInheritance - pre-scan includes files, parse ASTs and collect ClassDef
|
||||
# names/parent names into flat byte arrays; returns the class count (-1 on failure).
|
||||
def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, generic_file_indices: rawptr, generic_file_count_box: rawptr) -> int:
|
||||
"""Pre-scan includes files and collect class inheritance info."""
|
||||
if result is None: return -1
|
||||
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
|
||||
# strcpy write the zero page, so fail-fast abort instead of returning -1 silently.
|
||||
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed (class_names_buf/parent_names_buf/def_sha1s_buf is NULL)", "Phase1")
|
||||
|
||||
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
class_count: int = 0
|
||||
|
||||
for i in range(result.Count):
|
||||
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes
|
||||
# 全量 parse 的 AST 若驻留到 mark1 → 1GB 主池耗尽 OOM 根因;
|
||||
# class_names_buf/parent_names_buf/def_sha1s_buf 在调用前分配,
|
||||
# 位于本 mark 之前,reset 不影响;generic_file_indices 同理)
|
||||
scan_mark: t.CSizeT = mb.mark()
|
||||
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
|
||||
if entry is None or entry.Sha1 is None:
|
||||
continue
|
||||
for i in range(int(result.Count)):
|
||||
# Per-file mark: reclaim this file's AST right after collecting inheritance
|
||||
# info (keeping all ASTs alive until mark1 would OOM the 1GB main pool).
|
||||
scan_mark: sizet = mb.mark()
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
|
||||
if entry is None or entry.Sha1 is None: continue
|
||||
|
||||
sha1_e: str = entry.Sha1
|
||||
file_has_generic: int = 0
|
||||
|
||||
# 按需翻译过滤
|
||||
# on-demand translation filter
|
||||
in_set: int = 0
|
||||
if use_reachable != 0:
|
||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else:
|
||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0:
|
||||
continue
|
||||
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0: continue
|
||||
|
||||
# 读取文件
|
||||
# read the file
|
||||
file_path: str = entry.Path
|
||||
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed:
|
||||
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed: continue
|
||||
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
if f.size() >= Phase1.SRC_BUF_SIZE:
|
||||
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path}", "Phase1")
|
||||
f.close()
|
||||
continue
|
||||
|
||||
src_buf: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
|
||||
@@ -122,177 +71,156 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
if bytes_read < Phase1.SRC_BUF_SIZE:
|
||||
src_buf[bytes_read] = 0
|
||||
else:
|
||||
src_buf[Phase1.SRC_BUF_SIZE - 1] = 0
|
||||
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
|
||||
|
||||
# 解析 AST
|
||||
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
|
||||
# parse the AST
|
||||
lx: ast.Lexer* = ast.new_lexer(mb)
|
||||
if lx is None:
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
ast._lexer_init(lx, src_buf, mb)
|
||||
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
|
||||
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
|
||||
tokens: ast.Token* = ast.tokenize(lx)
|
||||
tree: ast.AST* = ast.parse_tokens(mb, tokens)
|
||||
if tree is None:
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
|
||||
# 遍历 AST 顶层节点,收集 ClassDef
|
||||
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
|
||||
# walk top-level AST nodes, collect ClassDef
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn: t.CSizeT = len(ch)
|
||||
for ci in range(cn):
|
||||
child: ast.AST | t.CPtr = ch.get(ci)
|
||||
if child is None:
|
||||
continue
|
||||
cn: sizet = len(ch)
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd: int = child.kind()
|
||||
if kd != ast.ASTKind.ClassDef:
|
||||
continue
|
||||
if kd != ast.ASTKind.ClassDef: continue
|
||||
|
||||
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||||
if cd is None or cd.name is None:
|
||||
continue
|
||||
cd: ast.ClassDef* = ast.ClassDef*(child)
|
||||
if cd is None or cd.name is None: continue
|
||||
|
||||
# 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册
|
||||
# detect generic classes (type_params); record the file index for Phase 1a
|
||||
if file_has_generic == 0:
|
||||
tp_scan: list[str] | t.CPtr = cd.type_params
|
||||
tp_scan: list[str]* = cd.type_params
|
||||
if tp_scan is not None:
|
||||
file_has_generic = 1
|
||||
if generic_file_count_box is not None:
|
||||
gfc_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr))
|
||||
gfc_ptr_scan: int* = (int*)(generic_file_count_box)
|
||||
if gfc_ptr_scan is not None:
|
||||
gfc_scan: int = gfc_ptr_scan[0]
|
||||
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
|
||||
gi_addr_scan: t.CUInt64T = t.CUInt64T(generic_file_indices) + gfc_scan * 4
|
||||
gi_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_scan, t.CPtr))
|
||||
if gi_ptr_scan is not None:
|
||||
gi_ptr_scan[0] = i
|
||||
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
|
||||
gi_ptr_scan: int* = (int*)(gi_addr_scan)
|
||||
if gi_ptr_scan is not None: gi_ptr_scan[0] = i
|
||||
gfc_ptr_scan[0] = gfc_scan + 1
|
||||
|
||||
if class_count >= Phase1.MAX_CLASSES:
|
||||
break
|
||||
if class_count >= Phase1.MAX_CLASSES: break
|
||||
|
||||
# 填充扁平数组:类名(偏移 = class_count * Phase1.CLASS_NAME_LEN)
|
||||
# fill flat array: class name (offset = class_count * Phase1.CLASS_NAME_LEN)
|
||||
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
||||
nm_len: t.CSizeT = string.strlen(cd.name)
|
||||
nm_len: sizet = string.strlen(cd.name)
|
||||
if nm_len >= Phase1.CLASS_NAME_LEN:
|
||||
string.strncpy(name_dst, cd.name, Phase1.CLASS_NAME_LEN - 1)
|
||||
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
||||
else:
|
||||
string.strcpy(name_dst, cd.name)
|
||||
else: string.strcpy(name_dst, cd.name)
|
||||
|
||||
# 填充扁平数组:父类名(无父类填 '\0')
|
||||
# fill flat array: parent name (no parent -> '\0')
|
||||
parent_dst: str = parent_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
||||
parent_dst[0] = '\0'
|
||||
parent_nm: str = GenApi._get_parent_class(cd, None)
|
||||
# parent_nm 是 AST 节点内部指针,不需要释放
|
||||
# parent_nm is an internal AST pointer; no need to free
|
||||
if parent_nm is not None:
|
||||
pnm_len: t.CSizeT = string.strlen(parent_nm)
|
||||
pnm_len: sizet = string.strlen(parent_nm)
|
||||
if pnm_len >= Phase1.CLASS_NAME_LEN:
|
||||
string.strncpy(parent_dst, parent_nm, Phase1.CLASS_NAME_LEN - 1)
|
||||
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
||||
else:
|
||||
string.strcpy(parent_dst, parent_nm)
|
||||
else: string.strcpy(parent_dst, parent_nm)
|
||||
|
||||
# 填充扁平数组:定义 SHA1(偏移 = class_count * Phase1.SHA1_LEN)
|
||||
# fill flat array: defining SHA1 (offset = class_count * Phase1.SHA1_LEN)
|
||||
sha1_dst: str = def_sha1s_buf + class_count * Phase1.SHA1_LEN
|
||||
string.strcpy(sha1_dst, sha1_e)
|
||||
|
||||
class_count += 1
|
||||
|
||||
stdlib.free(src_buf)
|
||||
# 回收本文件 AST/lexer/token(类名/父类/SHA1 已拷入扁平数组)
|
||||
# reclaim this file's AST/lexer/token (names/SHA1 already copied into flat arrays)
|
||||
mb.reset_to(scan_mark)
|
||||
|
||||
return class_count
|
||||
# ============================================================
|
||||
# _BufHasGenericClassMarker - 文本扫描检测文件是否含泛型类标记
|
||||
#
|
||||
# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。
|
||||
# 逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。
|
||||
# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部
|
||||
# 98 个 includes 导致 AST 驻留 mark1 前把 pool 推到 OOM
|
||||
# (NewAstTest [52/68] pool 耗尽根因:1a-pre 已 parse 全部驻留,
|
||||
# 预注册再全量 parse 等于翻倍)。
|
||||
# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件);
|
||||
# 漏报有害(泛型模板未预注册 → 悬空复发),模式保守匹配行内单行形式
|
||||
# (TPV 源码泛型类均为单行 `class X[T]:`,grep 确认)。
|
||||
# ============================================================
|
||||
def _BufHasGenericClassMarker(buf: bytes, blen: t.CSizeT) -> int:
|
||||
"""文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0"""
|
||||
if buf is None or blen <= 6:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
# _BufHasGenericClassMarker - text scan for a generic-class marker (PEP 695 "class
|
||||
# Name["). Used to pre-filter files before registering generic templates.
|
||||
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
|
||||
"""Return 1 if buf contains a 'class identifier[' generic-class marker, else 0."""
|
||||
if buf is None or blen <= 6: return 0
|
||||
pos: sizet = 0
|
||||
while pos + 5 < blen:
|
||||
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' \
|
||||
and buf[pos + 3] == 's' and buf[pos + 4] == 's':
|
||||
# class 前是词边界(行首或非标识符字符)
|
||||
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' and buf[pos + 3] == 's' and buf[pos + 4] == 's':
|
||||
# 'class' preceded by a word boundary (line start or non-identifier)
|
||||
prev_ok: int = 1
|
||||
if pos > 0:
|
||||
pc: t.CChar = buf[pos - 1]
|
||||
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') \
|
||||
or(pc >= '0' and pc <= '9') or pc == '_':
|
||||
prev_ok = 0
|
||||
pc: char = buf[pos - 1]
|
||||
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
|
||||
if prev_ok != 0:
|
||||
p: t.CSizeT = pos + 5
|
||||
# 跳过空白
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
|
||||
p += 1
|
||||
# 读类名标识符
|
||||
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
|
||||
or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
|
||||
or(buf[p] >= 'A' and buf[p] <= 'Z')
|
||||
or(buf[p] >= '0' and buf[p] <= '9')
|
||||
or buf[p] == '_'):
|
||||
p: sizet = pos + 5
|
||||
# skip whitespace
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
# read the class-name identifier
|
||||
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
|
||||
p += 1
|
||||
# 类名后非空白是 '[' → 泛型类
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
|
||||
p += 1
|
||||
if p < blen and buf[p] == '[':
|
||||
return 1
|
||||
pos += 1
|
||||
else:
|
||||
# non-space after the name is '[' -> generic class
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
if p < blen and buf[p] == '[': return 1
|
||||
pos += 1
|
||||
else: pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前)
|
||||
#
|
||||
# 背景:泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/
|
||||
# GenericClassFiles + 模板节点)是懒分配的跨文件持久表,首次注册必须落在
|
||||
# mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2),
|
||||
# mark2 > mark1)存活。现有 1a-pre-generic 只注册"可达"泛型文件;FuncTest
|
||||
# 这类可达集合无泛型的项目,模板要等 Phase2 Phase A-pre-inc(mark2 之后)
|
||||
# 翻译全部 includes 时才注册 → reset 回收表与节点 → _find_generic_template
|
||||
# 遍历悬空内存 strcmp 崩溃。
|
||||
# 本函数先文本扫描(_BufHasGenericClassMarker,不 parse、不驻留)筛选出
|
||||
# 含泛型类的文件(全量 parse 会把 98 个 includes 的 AST 驻留 mark1 前 →
|
||||
# NewAstTest [52/68] pool 耗尽 OOM),仅对命中的文件用**永久池(perm_pool)**
|
||||
# 解析 → 模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool。
|
||||
# 重复注册由 _register_generic_template 内部查重跳过。
|
||||
# ============================================================
|
||||
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
|
||||
result: IncludesScanner.ScanResult | t.CPtr) -> int:
|
||||
"""预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量"""
|
||||
if result is None or mb is None:
|
||||
return 0
|
||||
perm: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool()
|
||||
if perm is None:
|
||||
return 0
|
||||
p1e_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
# _BufHasGenericFuncMarker - text scan for a generic-function marker (PEP 695
|
||||
# "def name["); cheap pre-filter before parsing for generic function templates.
|
||||
def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
|
||||
"""Return 1 if buf contains a 'def identifier[' generic-function marker, else 0."""
|
||||
if buf is None or blen <= 4: return 0
|
||||
pos: sizet = 0
|
||||
while pos + 3 < blen:
|
||||
if buf[pos] == 'd' and buf[pos + 1] == 'e' and buf[pos + 2] == 'f':
|
||||
# 'def' preceded by a word boundary (line start or non-identifier)
|
||||
prev_ok: int = 1
|
||||
if pos > 0:
|
||||
pc: char = buf[pos - 1]
|
||||
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
|
||||
if prev_ok != 0:
|
||||
p: sizet = pos + 3
|
||||
# skip whitespace
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
# read the function-name identifier
|
||||
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
|
||||
p += 1
|
||||
# non-space after the name is '[' -> generic function
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
if p < blen and buf[p] == '[': return 1
|
||||
pos += 1
|
||||
else: pos += 1
|
||||
return 0
|
||||
# _PreRegisterAllGenericTemplates - pre-register includes generic templates before
|
||||
# mark1 so the lazy tables survive later resets; parses only marker-matched files.
|
||||
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*) -> int:
|
||||
"""Pre-register generic templates from all includes files and return the count."""
|
||||
if result is None or mb is None: return 0
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if perm is None: return 0
|
||||
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
handled_count: int = 0
|
||||
for i in range(result.Count):
|
||||
entry_addr_pg: t.CUInt64T = t.CUInt64T(result.Entries) + i * p1e_size
|
||||
entry_pg: IncludesScanner.FileEntry | t.CPtr = (
|
||||
IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_pg, t.CPtr))
|
||||
if entry_pg is None or entry_pg.Sha1 is None:
|
||||
continue
|
||||
# 读取文件
|
||||
for i in range(int(result.Count)):
|
||||
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
|
||||
entry_pg: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(entry_addr_pg)
|
||||
if entry_pg is None or entry_pg.Sha1 is None: continue
|
||||
# read the file
|
||||
file_path_pg: str = entry_pg.Path
|
||||
f_pg: fileio.File | t.CPtr = fileio.File(file_path_pg, fileio.MODE.R)
|
||||
if f_pg.closed:
|
||||
f_pg: fileio.File* = fileio.File(file_path_pg, fileio.MODE.R)
|
||||
if f_pg.closed: continue
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
|
||||
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path_pg}", "Phase1")
|
||||
f_pg.close()
|
||||
continue
|
||||
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
|
||||
if src_buf_pg is None:
|
||||
@@ -303,210 +231,184 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
|
||||
if bytes_read_pg <= 0:
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
if bytes_read_pg < Phase1.SRC_BUF_SIZE:
|
||||
src_buf_pg[bytes_read_pg] = 0
|
||||
else:
|
||||
src_buf_pg[Phase1.SRC_BUF_SIZE - 1] = 0
|
||||
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)
|
||||
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0:
|
||||
if bytes_read_pg < Phase1.SRC_BUF_SIZE: src_buf_pg[bytes_read_pg] = 0
|
||||
# Text-scan filter: skip non-generic files (no parse, no residency, avoids
|
||||
# OOM). A class or function marker hit selects the file for parsing.
|
||||
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0 and _BufHasGenericFuncMarker(src_buf_pg, bytes_read_pg) == 0:
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
# 泛型文件:永久池解析 → 注册模板(节点/表进 perm_pool,跨 reset 存活)
|
||||
lx_pg: ast.Lexer | t.CPtr = ast.new_lexer(perm)
|
||||
# generic file: parse in perm_pool and register templates (survives reset)
|
||||
lx_pg: ast.Lexer* = ast.new_lexer(perm)
|
||||
if lx_pg is None:
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
ast._lexer_init(lx_pg, src_buf_pg, perm)
|
||||
tokens_pg: ast.Token | t.CPtr = ast.tokenize(lx_pg)
|
||||
tree_pg: ast.AST | t.CPtr = ast.parse_tokens(perm, tokens_pg)
|
||||
tokens_pg: ast.Token* = ast.tokenize(lx_pg)
|
||||
tree_pg: ast.AST* = ast.parse_tokens(perm, tokens_pg)
|
||||
if tree_pg is None:
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
|
||||
ch_pg: list[ast.AST | t.CPtr] | t.CPtr = tree_pg.children
|
||||
# walk top-level ClassDef/FunctionDef, register generic templates (type_params != None)
|
||||
ch_pg: list[ast.AST*]* = tree_pg.children
|
||||
if ch_pg is not None:
|
||||
cn_pg: t.CSizeT = len(ch_pg)
|
||||
for ci_pg in range(cn_pg):
|
||||
child_pg: ast.AST | t.CPtr = ch_pg.get(ci_pg)
|
||||
if child_pg is None or child_pg.kind() != ast.ASTKind.ClassDef:
|
||||
continue
|
||||
cd_pg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child_pg)
|
||||
if cd_pg is None or cd_pg.name is None:
|
||||
continue
|
||||
tp_pg: list[str] | t.CPtr = cd_pg.type_params
|
||||
if tp_pg is None:
|
||||
continue
|
||||
GenApi.set_current_file_type(file_path_pg)
|
||||
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
|
||||
handled_count += 1
|
||||
cn_pg: sizet = len(ch_pg)
|
||||
for ci_pg in range(int(cn_pg)):
|
||||
child_pg: ast.AST* = ch_pg.get(ci_pg)
|
||||
if child_pg is None: continue
|
||||
ck_pg: int = child_pg.kind()
|
||||
if ck_pg == ast.ASTKind.ClassDef:
|
||||
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
|
||||
if cd_pg is None or cd_pg.name is None: continue
|
||||
tp_pg: list[str]* = cd_pg.type_params
|
||||
if tp_pg is None: continue
|
||||
GenApi.set_current_file_type(file_path_pg)
|
||||
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
|
||||
handled_count += 1
|
||||
elif ck_pg == ast.ASTKind.FunctionDef:
|
||||
fd_pg: ast.FunctionDef* = ast.FunctionDef*(child_pg)
|
||||
if fd_pg is None or fd_pg.name is None: continue
|
||||
tf_pg: list[str]* = fd_pg.type_params
|
||||
if tf_pg is None: continue
|
||||
GenApi.set_current_file_type(file_path_pg)
|
||||
GenApi._register_generic_func_template(perm, fd_pg, entry_pg.Sha1)
|
||||
handled_count += 1
|
||||
stdlib.free(src_buf_pg)
|
||||
if handled_count > 0:
|
||||
fb_gpre2: str = VLogger.fmt_buf()
|
||||
if fb_gpre2 is not None:
|
||||
viperlib.snprintf(fb_gpre2, 1024, "mark1 前永久池预注册 %d 个 includes 泛型类模板", handled_count)
|
||||
VLogger.info(fb_gpre2, "Phase1")
|
||||
if handled_count > 0: VLogger.info(f"pre-registered {handled_count} includes generic templates (classes and functions) in the permanent pool before mark1", "Phase1")
|
||||
else: VLogger.info(f"pre-registered 0 generic templates in the permanent pool before mark1 (scanned {result.Count} files; the marker may not have matched or the full scan failed)", "Phase1")
|
||||
return handled_count
|
||||
# ============================================================
|
||||
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
|
||||
#
|
||||
# Args:
|
||||
# class_names_buf: 类名扁平数组
|
||||
# def_sha1s_buf: 定义 SHA1 扁平数组
|
||||
# class_count: 类数量
|
||||
# class_name: 要查找的类名
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到
|
||||
# ============================================================
|
||||
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
|
||||
class_count: int, class_name: str) -> str:
|
||||
"""查找类名对应的定义文件 SHA1"""
|
||||
if class_names_buf is None or def_sha1s_buf is None or class_name is None:
|
||||
return None
|
||||
for i in range(class_count):
|
||||
# _FindClassDefSha1 - look up a class name's defining file SHA1 in the flat arrays;
|
||||
# returns a pointer into def_sha1s_buf, or None if not found.
|
||||
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, class_name: str) -> str:
|
||||
"""Find a class name's defining file SHA1 in the flat arrays."""
|
||||
if class_names_buf is None or def_sha1s_buf is None or class_name is None: return None
|
||||
for i in range(int(class_count)):
|
||||
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
|
||||
if string.strcmp(name_ptr, class_name) == 0:
|
||||
return def_sha1s_buf + i * Phase1.SHA1_LEN
|
||||
if string.strcmp(name_ptr, class_name) == 0: return def_sha1s_buf + i * Phase1.SHA1_LEN
|
||||
return None
|
||||
# ============================================================
|
||||
# _ScanTreeForContainers - 递归扫描 AST 检测 list/dict 容器使用
|
||||
#
|
||||
# TPV 的 AST 中,语句块挂在 children,而表达式/注解树全部以结构体
|
||||
# 字段挂载(value/slice/func/args/annotation/targets/returns/bases/
|
||||
# left/right 等)。只递归 children 会漏掉 list[...] | t.CPtr(BinOp
|
||||
# 包裹)、list[...](...)(Call.func)、字段注解等一切非语句块位置。
|
||||
# 因此每个节点先递归其表达式承载字段,再递归 children 语句块。
|
||||
# 命中即置 need_list/need_dict;误报无害(最多多编译两个空壳模块)。
|
||||
# ============================================================
|
||||
def _ScanTreeForContainers(node: ast.AST | t.CPtr,
|
||||
need_list_box: t.CPtr,
|
||||
need_dict_box: t.CPtr,
|
||||
depth: int = 0):
|
||||
"""递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用"""
|
||||
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
|
||||
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
|
||||
if depth > 128:
|
||||
fb_stc: str = VLogger.fmt_buf()
|
||||
if fb_stc is not None:
|
||||
viperlib.snprintf(fb_stc, 256, "容器扫描递归过深(AST 环或嵌套超过 128 层)")
|
||||
VLogger.error(fb_stc, "Phase1")
|
||||
sys.exit(1)
|
||||
if node is None:
|
||||
return
|
||||
# _ScanTreeForContainers - recursively scan the AST for list/dict container usage:
|
||||
# recurse expression/annotation struct fields first, then statement-block children.
|
||||
|
||||
def _AnnIsContainer(ann: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is a list/dict container, else 0."""
|
||||
while ann is not None:
|
||||
ak_c: int = ann.kind()
|
||||
if ak_c == ast.ASTKind.Name:
|
||||
nm_c: ast.Name* = ast.Name*(ann)
|
||||
if nm_c.id is not None:
|
||||
if string.strcmp(nm_c.id, "list") == 0: return 1
|
||||
if string.strcmp(nm_c.id, "dict") == 0: return 1
|
||||
return 0
|
||||
elif ak_c == ast.ASTKind.Subscript:
|
||||
sub_c: ast.Subscript* = ast.Subscript*(ann)
|
||||
ann = sub_c.value
|
||||
else: return 0
|
||||
return 0
|
||||
|
||||
|
||||
def _ScanTreeForContainers(node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr, depth: int = 0):
|
||||
"""Recursively scan the AST for list/dict container usage."""
|
||||
# Recursion depth guard: a self-referential AST cycle would recurse until the
|
||||
# stack is exhausted; fail-fast abort.
|
||||
if depth > 128: VLogger.error("container scan recursion too deep (AST cycle or nesting over 128 levels)", "Phase1")
|
||||
if node is None: return
|
||||
k_s: int = node.kind()
|
||||
if k_s == ast.ASTKind.Name:
|
||||
nm_s: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||||
nm_s: ast.Name* = ast.Name*(node)
|
||||
if nm_s.id is not None:
|
||||
if string.strcmp(nm_s.id, "list") == 0:
|
||||
need_list_box[0] = 1
|
||||
elif string.strcmp(nm_s.id, "dict") == 0:
|
||||
need_dict_box[0] = 1
|
||||
if string.strcmp(nm_s.id, "list") == 0: need_list_box[0] = 1
|
||||
elif string.strcmp(nm_s.id, "dict") == 0: need_dict_box[0] = 1
|
||||
elif k_s == ast.ASTKind.Subscript:
|
||||
sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
|
||||
sub_s: ast.Subscript* = ast.Subscript*(node)
|
||||
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
_ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.BinOp:
|
||||
bop_s: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
|
||||
bop_s: ast.BinOp* = ast.BinOp*(node)
|
||||
_ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box, depth + 1)
|
||||
_ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Call:
|
||||
cl_s2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
|
||||
cl_s2: ast.Call* = ast.Call*(node)
|
||||
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1)
|
||||
cl_args_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.args
|
||||
cl_args_s: list[ast.AST*]* = cl_s2.args
|
||||
if cl_args_s is not None:
|
||||
na_s: t.CSizeT = len(cl_args_s)
|
||||
i_a2: t.CSizeT
|
||||
for i_a2 in range(na_s):
|
||||
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
|
||||
cl_kws_s: list[ast.AST | t.CPtr] | t.CPtr = cl_s2.keywords
|
||||
na_s: sizet = len(cl_args_s)
|
||||
i_a2: sizet
|
||||
for i_a2 in range(int(na_s)): _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
|
||||
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
|
||||
if cl_kws_s is not None:
|
||||
nk_s: t.CSizeT = len(cl_kws_s)
|
||||
i_k2: t.CSizeT
|
||||
for i_k2 in range(nk_s):
|
||||
kw_s: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(cl_kws_s.get(i_k2))
|
||||
if kw_s is not None:
|
||||
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
nk_s: sizet = len(cl_kws_s)
|
||||
i_k2: sizet
|
||||
for i_k2 in range(int(nk_s)):
|
||||
kw_s: ast.Keyword* = ast.Keyword*(cl_kws_s.get(i_k2))
|
||||
if kw_s is not None: _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Assign:
|
||||
asn_s: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
|
||||
asn_s: ast.Assign* = ast.Assign*(node)
|
||||
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
tgts_s: list[ast.AST | t.CPtr] | t.CPtr = asn_s.targets
|
||||
tgts_s: list[ast.AST*]* = asn_s.targets
|
||||
if tgts_s is not None:
|
||||
nt_s: t.CSizeT = len(tgts_s)
|
||||
i_t2: t.CSizeT
|
||||
for i_t2 in range(nt_s):
|
||||
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
|
||||
nt_s: sizet = len(tgts_s)
|
||||
i_t2: sizet
|
||||
for i_t2 in range(int(nt_s)): _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.AnnAssign:
|
||||
aa_s2: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
|
||||
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
|
||||
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
|
||||
_ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.FunctionDef:
|
||||
fn_s: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node)
|
||||
fn_s: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1)
|
||||
fn_args_s: ast.Arguments | t.CPtr = fn_s.args
|
||||
fn_args_s: ast.Arguments* = ast.Arguments*(fn_s.args)
|
||||
if fn_args_s is not None:
|
||||
fa_s: list[ast.AST | t.CPtr] | t.CPtr = fn_args_s.args
|
||||
fa_s: list[ast.AST*]* = fn_args_s.args
|
||||
if fa_s is not None:
|
||||
nf_s: t.CSizeT = len(fa_s)
|
||||
i_f2: t.CSizeT
|
||||
for i_f2 in range(nf_s):
|
||||
arg_s: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(fa_s.get(i_f2))
|
||||
if arg_s is not None:
|
||||
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
|
||||
nf_s: sizet = len(fa_s)
|
||||
i_f2: sizet
|
||||
for i_f2 in range(int(nf_s)):
|
||||
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
|
||||
if arg_s is not None: _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.ClassDef:
|
||||
cd_s: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(node)
|
||||
bases_s: list[ast.AST | t.CPtr] | t.CPtr = cd_s.bases
|
||||
cd_s: ast.ClassDef* = ast.ClassDef*(node)
|
||||
bases_s: list[ast.AST*]* = cd_s.bases
|
||||
if bases_s is not None:
|
||||
nb_s: t.CSizeT = len(bases_s)
|
||||
i_b2: t.CSizeT
|
||||
for i_b2 in range(nb_s):
|
||||
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
|
||||
nb_s: sizet = len(bases_s)
|
||||
i_b2: sizet
|
||||
for i_b2 in range(int(nb_s)): _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Attribute:
|
||||
at_s: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||||
at_s: ast.Attribute* = ast.Attribute*(node)
|
||||
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Tuple:
|
||||
tu_s: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
|
||||
elts_s: list[ast.AST | t.CPtr] | t.CPtr = tu_s.elts
|
||||
tu_s: ast.Tuple* = ast.Tuple*(node)
|
||||
elts_s: list[ast.AST*]* = tu_s.elts
|
||||
if elts_s is not None:
|
||||
ne_s: t.CSizeT = len(elts_s)
|
||||
i_e2: t.CSizeT
|
||||
for i_e2 in range(ne_s):
|
||||
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
|
||||
ne_s: sizet = len(elts_s)
|
||||
i_e2: sizet
|
||||
for i_e2 in range(int(ne_s)): _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.UnaryOp:
|
||||
uop_s: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
|
||||
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Expr:
|
||||
ex_s: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node)
|
||||
ex_s: ast.Expr* = ast.Expr*(node)
|
||||
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Return:
|
||||
rt_s: ast.Return | t.CPtr = (ast.Return | t.CPtr)(node)
|
||||
rt_s: ast.Return* = ast.Return*(node)
|
||||
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Compare:
|
||||
cp_s: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
|
||||
cp_s: ast.Compare* = ast.Compare*(node)
|
||||
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
|
||||
cps_s: list[ast.AST | t.CPtr] | t.CPtr = cp_s.comparators
|
||||
cps_s: list[ast.AST*]* = cp_s.comparators
|
||||
if cps_s is not None:
|
||||
nc_s: t.CSizeT = len(cps_s)
|
||||
i_c2: t.CSizeT
|
||||
for i_c2 in range(nc_s):
|
||||
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
|
||||
nc_s: sizet = len(cps_s)
|
||||
i_c2: sizet
|
||||
for i_c2 in range(int(nc_s)): _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.BoolOp:
|
||||
bo_s: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
|
||||
bvs_s: list[ast.AST | t.CPtr] | t.CPtr = bo_s.values
|
||||
bo_s: ast.BoolOp* = ast.BoolOp*(node)
|
||||
bvs_s: list[ast.AST*]* = bo_s.values
|
||||
if bvs_s is not None:
|
||||
nv_s: t.CSizeT = len(bvs_s)
|
||||
i_v2: t.CSizeT
|
||||
for i_v2 in range(nv_s):
|
||||
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.List:
|
||||
# [] 字面量
|
||||
need_list_box[0] = 1
|
||||
elif k_s == ast.ASTKind.Dict:
|
||||
# {} 字面量
|
||||
need_dict_box[0] = 1
|
||||
# 递归语句块子节点
|
||||
nv_s: sizet = len(bvs_s)
|
||||
i_v2: sizet
|
||||
for i_v2 in range(int(nv_s)): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
||||
# `[...]`/`{}` literals do not trigger _list/_dict imports (compile-time constant lists, array init);
|
||||
# recurse into statement-block children.
|
||||
if node.children is not None:
|
||||
ch_s: list[ast.AST | t.CPtr] | t.CPtr = node.children
|
||||
n_s: t.CSizeT = len(ch_s)
|
||||
ci_s: t.CSizeT
|
||||
for ci_s in range(n_s):
|
||||
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
||||
ch_s: list[ast.AST*]* = node.children
|
||||
n_s: sizet = len(ch_s)
|
||||
ci_s: sizet
|
||||
for ci_s in range(int(n_s)): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
||||
+154
-266
@@ -1,181 +1,109 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import ast
|
||||
import llvmlite
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Gen.Generator as GenGenerator
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Stub.StubGen.Converter as StubConverter
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
|
||||
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
|
||||
import lib.Phase.Phase1 as Phase1
|
||||
|
||||
# ============================================================
|
||||
# Phase1Topo - Phase1 拆分(阶段 E):Phase1Topo
|
||||
#
|
||||
# 直接移动模式:函数迁至此模块,原 Phase1.vp 仅保留常量/_mbuddy;
|
||||
# 调用方经本模块前缀访问。全局符号经 Phase1._xxx 跨模块访问。
|
||||
# ============================================================
|
||||
# Phase1Topo - topologically sort source files by class inheritance and import dependencies.
|
||||
# Functions live in this module (accessed via its prefix); globals go through Phase1._xxx.
|
||||
|
||||
# ============================================================
|
||||
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序
|
||||
#
|
||||
# 算法(Kahn):
|
||||
# 1. 从扁平类信息数组构建"文件 SHA1 → 依赖文件 SHA1 集合"映射
|
||||
# 2. 计算每个文件的入度(依赖数量)
|
||||
# 3. 入度为 0 的文件先处理,处理后减少依赖它的文件入度
|
||||
# 4. 重复直到所有文件处理完成
|
||||
#
|
||||
# 注意:依赖方向是"父类所在文件 → 子类所在文件",
|
||||
# 即父类文件必须先处理。Kahn 用 DepCount 作为剩余依赖计数,
|
||||
# 依赖为 0 的先处理,初始化 InDegree = DepCount。
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
# result: IncludesScanner 扫描结果
|
||||
# reachable_set: 可达 SHA1 集合
|
||||
# reachable_count: 可达 SHA1 数量
|
||||
# use_reachable: 是否使用可达集合过滤
|
||||
# sha1_set: SHA1 集合
|
||||
# set_count: SHA1 集合数量
|
||||
# class_names_buf: 类名扁平数组
|
||||
# parent_names_buf: 父类名扁平数组
|
||||
# def_sha1s_buf: 定义 SHA1 扁平数组
|
||||
# class_count: 类数量
|
||||
# out_order: 输出拓扑顺序的文件索引数组(调用者分配,int 数组)
|
||||
# out_count: 输出文件数量
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功,-1 失败
|
||||
# ============================================================
|
||||
def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
|
||||
result: IncludesScanner.ScanResult | t.CPtr,
|
||||
reachable_set: str, reachable_count: int,
|
||||
use_reachable: int,
|
||||
sha1_set: str, set_count: int,
|
||||
class_names_buf: bytes, parent_names_buf: bytes,
|
||||
def_sha1s_buf: bytes, class_count: int,
|
||||
out_order: t.CPtr, out_count: t.CPtr,
|
||||
temp_dir: str, td_len_ts: t.CSizeT) -> int:
|
||||
"""根据类继承关系对文件进行拓扑排序"""
|
||||
if result is None or out_order is None or out_count is None:
|
||||
return -1
|
||||
# _TopoSortFiles - topologically sort files by class inheritance (Kahn).
|
||||
# Builds a "file SHA1 -> dependency SHA1 set" map, computes in-degrees (parent-file first), then outputs the order; returns 0 or -1.
|
||||
def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: rawptr, out_count: rawptr, temp_dir: str, td_len_ts: sizet) -> int:
|
||||
"""Topologically sort files by class inheritance and import dependencies."""
|
||||
if result is None or out_order is None or out_count is None: return -1
|
||||
|
||||
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
|
||||
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
|
||||
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
|
||||
# 1. Allocate flat arrays: file SHA1, dependency SHA1 list, dep counts, in-degrees, processed flags.
|
||||
# Flat arrays replace a FileDepEntry struct to avoid NULL str-pointer fields crashing.
|
||||
file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||
file_deps_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
file_dep_counts: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
file_indegrees: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
file_processed: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
# 取消整数提升后,模块属性 CDefine(Phase1.MAX_*) 按最小位宽物化为 i16/i8;三段乘积 256*16*17=69632 会在 i16 回绕,
|
||||
# 首乘数显式 sizet() 加宽到 64 位。上一行 256*17=4352 未超 i16,故无需改。
|
||||
file_deps_buf: bytes = mb.alloc(sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
file_dep_counts: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
file_indegrees: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
file_processed: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
|
||||
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
|
||||
if file_sha1s_buf is not None:
|
||||
mb.free(file_sha1s_buf)
|
||||
if file_deps_buf is not None:
|
||||
mb.free(file_deps_buf)
|
||||
if file_dep_counts is not None:
|
||||
mb.free(file_dep_counts)
|
||||
if file_indegrees is not None:
|
||||
mb.free(file_indegrees)
|
||||
if file_processed is not None:
|
||||
mb.free(file_processed)
|
||||
if file_sha1s_buf is not None: mb.free(file_sha1s_buf)
|
||||
if file_deps_buf is not None: mb.free(file_deps_buf)
|
||||
if file_dep_counts is not None: mb.free(file_dep_counts)
|
||||
if file_indegrees is not None: mb.free(file_indegrees)
|
||||
if file_processed is not None: mb.free(file_processed)
|
||||
return -1
|
||||
|
||||
string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||
string.memset(file_deps_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
string.memset(file_deps_buf, 0, sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
string.memset(file_dep_counts, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
string.memset(file_indegrees, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
|
||||
file_count: int = 0
|
||||
# sha1→file_index 映射(简单线性查找,文件数不多)
|
||||
for i in range(result.Count):
|
||||
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
|
||||
if entry is None or entry.Sha1 is None:
|
||||
continue
|
||||
# sha1 -> file_index map (simple linear search; file count is small)
|
||||
for i in range(int(result.Count)):
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
|
||||
if entry is None or entry.Sha1 is None: continue
|
||||
|
||||
sha1_e: str = entry.Sha1
|
||||
in_set: int = 0
|
||||
if use_reachable != 0:
|
||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else:
|
||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0:
|
||||
continue
|
||||
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0: continue
|
||||
|
||||
if file_count >= Phase1.MAX_FILE_DEPS:
|
||||
break
|
||||
if file_count >= Phase1.MAX_FILE_DEPS: break
|
||||
|
||||
# 初始化扁平数组:文件 SHA1(偏移 = file_count * Phase1.SHA1_LEN)
|
||||
# DepCount/InDegree/Processed 已由 memset 清零
|
||||
# Initialize the flat array: file SHA1 (offset = file_count * Phase1.SHA1_LEN).
|
||||
# DepCount/InDegree/Processed are already zeroed by memset.
|
||||
sha1_dst: str = file_sha1s_buf + file_count * Phase1.SHA1_LEN
|
||||
string.strcpy(sha1_dst, sha1_e)
|
||||
|
||||
file_count += 1
|
||||
|
||||
# 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖
|
||||
for ci in range(class_count):
|
||||
# 读取父类名(偏移 = ci * Phase1.CLASS_NAME_LEN)
|
||||
# 2. Build dependency edges: for each class, find the parent's defining file and add a child->parent edge
|
||||
for ci in range(int(class_count)):
|
||||
# Read parent class name (offset = ci * Phase1.CLASS_NAME_LEN)
|
||||
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
|
||||
if parent_ptr[0] == '\0':
|
||||
continue
|
||||
if parent_ptr[0] == '\0': continue
|
||||
|
||||
# 读取子类定义 SHA1(偏移 = ci * Phase1.SHA1_LEN)
|
||||
# Read child class definition SHA1 (offset = ci * Phase1.SHA1_LEN)
|
||||
child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN
|
||||
|
||||
# 查找父类定义所在文件的 SHA1
|
||||
# Find the SHA1 of the file defining the parent class
|
||||
parent_sha1: str = Phase1Scan._FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
|
||||
if parent_sha1 is None:
|
||||
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
|
||||
continue # parent not in includes (e.g. Object, CEnum marker base classes)
|
||||
|
||||
# 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖)
|
||||
if string.strcmp(parent_sha1, child_def_sha1) == 0:
|
||||
continue
|
||||
# The parent file SHA1 must differ from the child's (same-file inheritance needs no dependency)
|
||||
if string.strcmp(parent_sha1, child_def_sha1) == 0: continue
|
||||
|
||||
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
|
||||
for fi in range(file_count):
|
||||
# Find the child file's index and add the parent file SHA1 to its dependency list
|
||||
for fi in range(int(file_count)):
|
||||
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||
if string.strcmp(fsha1, child_def_sha1) != 0:
|
||||
continue
|
||||
if string.strcmp(fsha1, child_def_sha1) != 0: continue
|
||||
|
||||
# 读取当前依赖数量
|
||||
cnt_addr: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4
|
||||
cnt_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr, t.CPtr))
|
||||
if cnt_ptr is None:
|
||||
break
|
||||
# Read the current dependency count
|
||||
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
|
||||
cnt_ptr: int* = (int*)(cnt_addr)
|
||||
if cnt_ptr is None: break
|
||||
cur_cnt: int = cnt_ptr[0]
|
||||
|
||||
# 检查依赖是否已添加(去重)
|
||||
# Check whether the dependency is already present (dedupe)
|
||||
already: int = 0
|
||||
for di in range(cur_cnt):
|
||||
for di in range(int(cur_cnt)):
|
||||
dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot, parent_sha1) == 0:
|
||||
already = 1
|
||||
break
|
||||
if already != 0:
|
||||
break
|
||||
if already != 0: break
|
||||
|
||||
if cur_cnt < Phase1.MAX_DEP_EDGES:
|
||||
dep_dst: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt * Phase1.SHA1_LEN
|
||||
@@ -183,65 +111,44 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
|
||||
cnt_ptr[0] = cur_cnt + 1
|
||||
break
|
||||
|
||||
# 2.5 补充 import 依赖边:被 import 的模块先完整翻译。
|
||||
#
|
||||
# 仅按类继承建边不够:跨模块全局变量 stub(lexer.vp from-import
|
||||
# tokens.vp 的 _op_head)、跨模块 vtable TypeStr(astaux 继承 base 的
|
||||
# AST)、泛型模板(base.vp 用 list[...])都要求依赖模块先翻译,而它们
|
||||
# 之间可能无类继承关系。deps(_includes_deps_*)由 Phase 1a-pre 填充
|
||||
# (含容器按需注入的 _list/_dict)。
|
||||
#
|
||||
# 用 _BuildIncludesSha1Map 复制局部 sha1→模块名 数组(与
|
||||
# _BuildReachableSha1Set 的 BFS 一致),避免直接使用全局 store 返回值
|
||||
# 跨函数传参(bytes|t.CPtr 联合转 str 参数)的地址异常。
|
||||
# 2.5 Add import dependency edges: imported modules must be fully translated first.
|
||||
# Class inheritance alone is insufficient (cross-module globals/vtable TypeStr/generics need deps); deps come from Phase 1a-pre, mapped via _BuildIncludesSha1Map.
|
||||
sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
map_count_ts: int = 0
|
||||
if sha1_arr_ts is not None and mod_arr_ts is not None:
|
||||
map_count_ts = StubMergerIR._BuildIncludesSha1Map(
|
||||
temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
|
||||
for fi_imp in range(file_count):
|
||||
if map_count_ts <= 0:
|
||||
break
|
||||
map_count_ts = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
|
||||
for fi_imp in range(int(file_count)):
|
||||
if map_count_ts <= 0: break
|
||||
fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN
|
||||
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
|
||||
if deps_imp is None:
|
||||
continue
|
||||
dl_imp: t.CSizeT = string.strlen(deps_imp)
|
||||
dpos_imp: t.CSizeT = 0
|
||||
if deps_imp is None: continue
|
||||
dl_imp: sizet = string.strlen(deps_imp)
|
||||
dpos_imp: sizet = 0
|
||||
while dpos_imp < dl_imp:
|
||||
dstart_imp: t.CSizeT = dpos_imp
|
||||
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ':
|
||||
dpos_imp += 1
|
||||
dlen_imp: t.CSizeT = dpos_imp - dstart_imp
|
||||
if dpos_imp < dl_imp:
|
||||
dpos_imp += 1
|
||||
if dlen_imp == 0:
|
||||
continue
|
||||
dstart_imp: sizet = dpos_imp
|
||||
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ': dpos_imp += 1
|
||||
dlen_imp: sizet = dpos_imp - dstart_imp
|
||||
if dpos_imp < dl_imp: dpos_imp += 1
|
||||
if dlen_imp == 0: continue
|
||||
dname_imp: bytes = stdlib.malloc(dlen_imp + 1)
|
||||
if dname_imp is None:
|
||||
continue
|
||||
if dname_imp is None: continue
|
||||
string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp)
|
||||
dname_imp[dlen_imp] = '\0'
|
||||
dsha1_imp: str = StubMergerIR._FindSha1ByModName(
|
||||
sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
|
||||
dsha1_imp: str = StubMergerIR._FindSha1ByModName(sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
|
||||
stdlib.free(dname_imp)
|
||||
if dsha1_imp is None:
|
||||
continue
|
||||
# 找依赖文件在列表中的索引(跳过自身)
|
||||
for fj_imp in range(file_count):
|
||||
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0:
|
||||
continue
|
||||
if fj_imp == fi_imp:
|
||||
break
|
||||
# 去重 + 加边:fi_imp 依赖 fj_imp(InDegree[fi_imp]++)
|
||||
cnt_addr_imp: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi_imp * 4
|
||||
cnt_ptr_imp: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr_imp, t.CPtr))
|
||||
if cnt_ptr_imp is None:
|
||||
break
|
||||
if dsha1_imp is None: continue
|
||||
# Find the dependency file's index in the list (skip self)
|
||||
for fj_imp in range(int(file_count)):
|
||||
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue
|
||||
if fj_imp == fi_imp: break
|
||||
# Dedupe and add the edge: fi_imp depends on fj_imp (InDegree[fi_imp]++)
|
||||
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
|
||||
cnt_ptr_imp: int* = (int*)(cnt_addr_imp)
|
||||
if cnt_ptr_imp is None: break
|
||||
cur_cnt_imp: int = cnt_ptr_imp[0]
|
||||
already_imp: int = 0
|
||||
for di_imp in range(cur_cnt_imp):
|
||||
for di_imp in range(int(cur_cnt_imp)):
|
||||
dep_slot_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_imp * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot_imp, dsha1_imp) == 0:
|
||||
already_imp = 1
|
||||
@@ -252,120 +159,104 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
|
||||
cnt_ptr_imp[0] = cur_cnt_imp + 1
|
||||
break
|
||||
|
||||
# 释放 import 边用的局部映射数组
|
||||
if sha1_arr_ts is not None:
|
||||
stdlib.free(sha1_arr_ts)
|
||||
if mod_arr_ts is not None:
|
||||
stdlib.free(mod_arr_ts)
|
||||
# Free the local mapping arrays used for import edges
|
||||
if sha1_arr_ts is not None: stdlib.free(sha1_arr_ts)
|
||||
if mod_arr_ts is not None: stdlib.free(mod_arr_ts)
|
||||
|
||||
# 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件)
|
||||
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
|
||||
for fi in range(file_count):
|
||||
cnt_addr3: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4
|
||||
cnt_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr3, t.CPtr))
|
||||
ind_addr3: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4
|
||||
ind_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr3, t.CPtr))
|
||||
if cnt_ptr3 is not None and ind_ptr3 is not None:
|
||||
ind_ptr3[0] = cnt_ptr3[0]
|
||||
# 3. Kahn: InDegree = DepCount (number of parent files depended on).
|
||||
# Process zero-dependency files first, then decrement the in-degree of dependents.
|
||||
for fi in range(int(file_count)):
|
||||
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
|
||||
cnt_ptr3: int* = (int*)(cnt_addr3)
|
||||
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
|
||||
ind_ptr3: int* = (int*)(ind_addr3)
|
||||
if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0]
|
||||
|
||||
# 4. 拓扑排序输出
|
||||
# 4. Topological sort output
|
||||
out_idx: int = 0
|
||||
changed: int = 1
|
||||
while changed != 0:
|
||||
changed = 0
|
||||
for fi in range(file_count):
|
||||
# 检查处理状态
|
||||
proc_addr4: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
|
||||
proc_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr4, t.CPtr))
|
||||
if proc_ptr4 is None or proc_ptr4[0] != 0:
|
||||
continue
|
||||
# 检查入度
|
||||
ind_addr4: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4
|
||||
ind_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr4, t.CPtr))
|
||||
if ind_ptr4 is None or ind_ptr4[0] > 0:
|
||||
continue
|
||||
for fi in range(int(file_count)):
|
||||
# Check processed flag
|
||||
proc_addr4: uint64 = uint64(file_processed) + fi * 4
|
||||
proc_ptr4: int* = (int*)(proc_addr4)
|
||||
if proc_ptr4 is None or proc_ptr4[0] != 0: continue
|
||||
# Check in-degree
|
||||
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
|
||||
ind_ptr4: int* = (int*)(ind_addr4)
|
||||
if ind_ptr4 is None or ind_ptr4[0] > 0: continue
|
||||
|
||||
# 入度为 0,可以处理
|
||||
# 找到对应的 result.Entries 索引
|
||||
# In-degree 0: can be processed
|
||||
# Find the matching result.Entries index
|
||||
target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
ea_r: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size
|
||||
ent_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r, t.CPtr))
|
||||
for ri in range(int(result.Count)):
|
||||
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
|
||||
ent_r: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r)
|
||||
if ent_r is not None and ent_r.Sha1 is not None:
|
||||
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
|
||||
# 写入输出索引数组
|
||||
out_idx_addr: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4
|
||||
out_int_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr, t.CPtr))
|
||||
if out_int_ptr is not None:
|
||||
out_int_ptr[0] = ri
|
||||
# Write into the output index array
|
||||
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
|
||||
out_int_ptr: int* = (int*)(out_idx_addr)
|
||||
if out_int_ptr is not None: out_int_ptr[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
|
||||
# 标记已处理
|
||||
# Mark as processed
|
||||
proc_ptr4[0] = 1
|
||||
changed = 1
|
||||
|
||||
# 减少依赖该文件的其他文件的入度
|
||||
for fj in range(file_count):
|
||||
proc_addr5: t.CUInt64T = t.CUInt64T(file_processed) + fj * 4
|
||||
proc_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr5, t.CPtr))
|
||||
if proc_ptr5 is None or proc_ptr5[0] != 0:
|
||||
continue
|
||||
# 读取 fj 的依赖数量
|
||||
cnt_addr5: t.CUInt64T = t.CUInt64T(file_dep_counts) + fj * 4
|
||||
cnt_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr5, t.CPtr))
|
||||
if cnt_ptr5 is None:
|
||||
continue
|
||||
# Decrement the in-degree of files that depend on this one
|
||||
for fj in range(int(file_count)):
|
||||
proc_addr5: uint64 = uint64(file_processed) + fj * 4
|
||||
proc_ptr5: int* = (int*)(proc_addr5)
|
||||
if proc_ptr5 is None or proc_ptr5[0] != 0: continue
|
||||
# Read fj's dependency count
|
||||
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
|
||||
cnt_ptr5: int* = (int*)(cnt_addr5)
|
||||
if cnt_ptr5 is None: continue
|
||||
fj_dep_cnt: int = cnt_ptr5[0]
|
||||
for di in range(fj_dep_cnt):
|
||||
for di in range(int(fj_dep_cnt)):
|
||||
dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot5, target_sha1) == 0:
|
||||
ind_addr5: t.CUInt64T = t.CUInt64T(file_indegrees) + fj * 4
|
||||
ind_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr5, t.CPtr))
|
||||
if ind_ptr5 is not None:
|
||||
ind_ptr5[0] = ind_ptr5[0] - 1
|
||||
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
|
||||
ind_ptr5: int* = (int*)(ind_addr5)
|
||||
if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1
|
||||
break
|
||||
|
||||
# 处理循环依赖:环内优先输出"被依赖者"(被其余剩余文件依赖数量多者先)。
|
||||
#
|
||||
# 背景:容器按需注入(string 用 list 泛型 → 依赖 _list)与真实 import
|
||||
# (_list import string)形成伪环,Kahn 无法完全归零。若按扫描顺序追加,
|
||||
# 依赖者(astaux)会先于被依赖者(base)翻译 → 跨模块 vtable TypeStr
|
||||
# 未注册 → 误杀。被依赖者先翻译,其 stub/TypeStr/模板先就绪,依赖者
|
||||
# 翻译时可引用(函数/全局声明由 1a-pre 的 vpi 兜底)。
|
||||
# Handle cycles: within a cycle emit the more-depended-upon files first.
|
||||
# On-demand container injection (e.g. string -> _list) plus real imports form pseudo-cycles Kahn cannot fully drain; scan order would translate dependents first -> unregistered cross-module vtable TypeStr.
|
||||
if out_idx < file_count:
|
||||
rem_idx: t.CPtr = stdlib.malloc(4 * file_count)
|
||||
rem_depcnt: t.CPtr = stdlib.malloc(4 * file_count)
|
||||
rem_idx: int* = (int*)(stdlib.malloc(4 * file_count))
|
||||
rem_depcnt: int* = (int*)(stdlib.malloc(4 * file_count))
|
||||
rem_count: int = 0
|
||||
if rem_idx is not None and rem_depcnt is not None:
|
||||
for fi in range(file_count):
|
||||
proc_addr7: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
|
||||
proc_ptr7: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr7, t.CPtr))
|
||||
for fi in range(int(file_count)):
|
||||
proc_addr7: uint64 = uint64(file_processed) + fi * 4
|
||||
proc_ptr7: int* = (int*)(proc_addr7)
|
||||
if proc_ptr7 is not None and proc_ptr7[0] == 0:
|
||||
rem_idx[rem_count] = fi
|
||||
rem_depcnt[rem_count] = 0
|
||||
rem_count += 1
|
||||
# 计算每个剩余文件的被依赖数(被其余剩余文件依赖)
|
||||
for i_a in range(rem_count):
|
||||
# Compute each remaining file's dependents count (among the remaining files)
|
||||
for i_a in range(int(rem_count)):
|
||||
fi_a: int = rem_idx[i_a]
|
||||
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
|
||||
for i_b in range(rem_count):
|
||||
for i_b in range(int(rem_count)):
|
||||
fi_b: int = rem_idx[i_b]
|
||||
if fi_b == fi_a:
|
||||
continue
|
||||
cnt_addr_b: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi_b * 4
|
||||
cnt_ptr_b: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr_b, t.CPtr))
|
||||
if cnt_ptr_b is None:
|
||||
continue
|
||||
if fi_b == fi_a: continue
|
||||
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
|
||||
cnt_ptr_b: int* = (int*)(cnt_addr_b)
|
||||
if cnt_ptr_b is None: continue
|
||||
cnt_b: int = cnt_ptr_b[0]
|
||||
for di_b in range(cnt_b):
|
||||
for di_b in range(int(cnt_b)):
|
||||
dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot_b, target_sha1_a) == 0:
|
||||
rem_depcnt[i_a] = rem_depcnt[i_a] + 1
|
||||
break
|
||||
# 冒泡降序(被依赖数多者在前)
|
||||
for i_a in range(rem_count):
|
||||
for j_a in range(rem_count - 1):
|
||||
# Bubble sort descending (more dependents first)
|
||||
for i_a in range(int(rem_count)):
|
||||
for j_a in range(int(rem_count - 1)):
|
||||
if rem_depcnt[j_a] < rem_depcnt[j_a + 1]:
|
||||
tmp_i: int = rem_idx[j_a]
|
||||
rem_idx[j_a] = rem_idx[j_a + 1]
|
||||
@@ -373,35 +264,32 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
|
||||
tmp_d: int = rem_depcnt[j_a]
|
||||
rem_depcnt[j_a] = rem_depcnt[j_a + 1]
|
||||
rem_depcnt[j_a + 1] = tmp_d
|
||||
# 按序输出剩余文件
|
||||
for i_a in range(rem_count):
|
||||
# Emit the remaining files in order
|
||||
for i_a in range(int(rem_count)):
|
||||
fi_a2: int = rem_idx[i_a]
|
||||
proc_addr8: t.CUInt64T = t.CUInt64T(file_processed) + fi_a2 * 4
|
||||
proc_ptr8: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr8, t.CPtr))
|
||||
if proc_ptr8 is None or proc_ptr8[0] != 0:
|
||||
continue
|
||||
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
|
||||
proc_ptr8: int* = (int*)(proc_addr8)
|
||||
if proc_ptr8 is None or proc_ptr8[0] != 0: continue
|
||||
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
ea_r2: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size
|
||||
ent_r2: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r2, t.CPtr))
|
||||
for ri in range(int(result.Count)):
|
||||
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
|
||||
ent_r2: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r2)
|
||||
if ent_r2 is not None and ent_r2.Sha1 is not None:
|
||||
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
|
||||
out_idx_addr2: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4
|
||||
out_int_ptr2: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr2, t.CPtr))
|
||||
if out_int_ptr2 is not None:
|
||||
out_int_ptr2[0] = ri
|
||||
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
|
||||
out_int_ptr2: int* = (int*)(out_idx_addr2)
|
||||
if out_int_ptr2 is not None: out_int_ptr2[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
proc_ptr8[0] = 1
|
||||
stdlib.free(rem_idx)
|
||||
stdlib.free(rem_depcnt)
|
||||
|
||||
# 写入输出数量
|
||||
out_count_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_count, t.CPtr))
|
||||
if out_count_ptr is not None:
|
||||
out_count_ptr[0] = out_idx
|
||||
# Write the output count
|
||||
out_count_ptr: int* = (int*)(out_count)
|
||||
if out_count_ptr is not None: out_count_ptr[0] = out_idx
|
||||
|
||||
# 释放扁平数组
|
||||
# Free the flat arrays
|
||||
mb.free(file_sha1s_buf)
|
||||
mb.free(file_deps_buf)
|
||||
mb.free(file_dep_counts)
|
||||
|
||||
@@ -1,41 +1,19 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import ast
|
||||
import llvmlite
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Gen.Generator as GenGenerator
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Stub.StubGen.Converter as StubConverter
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# 源代码缓冲区大小(1MB)
|
||||
SRC_BUF_SIZE: t.CDefine = 1048576
|
||||
# Source buffer size (1MB)
|
||||
define SRC_BUF_SIZE = 1048576
|
||||
|
||||
# vpi 缓冲区大小(256KB)
|
||||
PYI_BUF_SIZE: t.CSizeT = 262144
|
||||
# vpi buffer size (256KB).
|
||||
# Compile-time constant via define: a module-level global would emit a strong symbol per module, causing llvm-link errors.
|
||||
define PYI_BUF_SIZE = 262144
|
||||
|
||||
# 拓扑排序相关常量
|
||||
MAX_CLASSES: t.CDefine = 512
|
||||
MAX_FILE_DEPS: t.CDefine = 256
|
||||
MAX_DEP_EDGES: t.CDefine = 16
|
||||
SHA1_LEN: t.CDefine = 17
|
||||
CLASS_NAME_LEN: t.CDefine = 64
|
||||
# Topological sort constants
|
||||
define MAX_CLASSES = 512
|
||||
define MAX_FILE_DEPS = 256
|
||||
define MAX_DEP_EDGES = 16
|
||||
define SHA1_LEN = 17
|
||||
define CLASS_NAME_LEN = 64
|
||||
+1530
-1725
File diff suppressed because it is too large.
Load diff
+346
-372
@@ -1,166 +1,169 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
from json.__parser import parse as json_parse
|
||||
from json import JsonValue, JSON_OBJECT, JSON_INT
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Config - 工程配置
|
||||
# ============================================================
|
||||
# Config - project configuration
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
_mbuddy: memhub.MemManager | t.CPtr
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemManager*
|
||||
|
||||
# 配置值(从 project.vpj 加载)
|
||||
# 默认 None/0:未加载 project.vpj 时字段为 null,调用方 is not None
|
||||
# 检查才能正确兜底(否则垃圾指针通过检查 → 用非法路径 → 崩溃,
|
||||
# 尤其非工作区执行无配置时)。
|
||||
# Config values (loaded from project.vpj); default None/0: callers must guard with `is not None` (else garbage pointer crash)
|
||||
SourceDir: str = None
|
||||
BuildDir: str = None
|
||||
TempDir: str = None # = {BuildDir}/temp(自动计算)
|
||||
OutputDir: str = None # = {BuildDir}/output(自动计算)
|
||||
TempDir: str = None # = {BuildDir}/temp (auto-computed)
|
||||
OutputDir: str = None # = {BuildDir}/output (auto-computed)
|
||||
ProjectName: str = None
|
||||
ProjectVersion: str = None
|
||||
CompilerCmd: str = None
|
||||
CompilerFlags: str = None # 编译器参数(空格分隔,从 compiler.flags 数组拼接)
|
||||
CompilerFlags: str = None # compiler flags (space-separated, joined from compiler.flags array)
|
||||
LinkerCmd: str = None
|
||||
LinkerFlags: str = None # 链接器参数(空格分隔,从 linker.flags 数组拼接)
|
||||
LinkerFlags: str = None # linker flags (space-separated, joined from linker.flags array)
|
||||
LinkerOutput: str = None
|
||||
LinkerStaticLibs: str = None # explicit static libs (space-separated abs paths, from linker.static_libs array)
|
||||
LinkerDynamicLibs: str = None # explicit dynamic libs (space-separated abs paths, from linker.dynamic_libs array, .dll/.so)
|
||||
TargetTriple: str = None
|
||||
TargetDataLayout: str = None
|
||||
ProjectDir: str = None # project.vpj 所在目录(resolve_paths 设置)
|
||||
Sha1SliceLevel: int = 0 # SHA1 切片层数(目录分散等级)
|
||||
ProjectDir: str = None # directory containing project.vpj (set by resolve_paths)
|
||||
Sha1SliceLevel: int = 0 # SHA1 slice level (directory dispersion depth)
|
||||
StrictMode: int = 0
|
||||
IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
|
||||
IncludesDirs: str = None # 全部 includes 目录(空格分隔,多 includes 目录支持)
|
||||
IncludesDir: str = None # includes dir path (first element of includes array, single-dir compat)
|
||||
IncludesDirs: str = None # all includes dirs (space-separated, multi-dir support)
|
||||
Qt6Path: str = None # runtime.qt6_path: Qt6 install root (optional)
|
||||
Qt6Dlls: str = None # runtime.qt6_dlls joined (space-separated DLL names; optional)
|
||||
Qt6Plugins: str = None # runtime.qt6_plugins joined (space-separated paths relative to <qt6_path>/plugins; optional)
|
||||
|
||||
# includes 预编译缓存目录后缀(asan 模式用 .binary_asan 隔离,避免
|
||||
# asan 插桩 obj 与非 asan obj 混链:asan obj 引用 __asan_init 等符号,
|
||||
# 非 asan 链接不提供 asan 库 → undefined reference)
|
||||
# includes prebuilt cache dir suffix (asan mode uses .binary_asan to isolate instrumented obj)
|
||||
_inc_bin_suffix: str = ".binary"
|
||||
|
||||
# Emission backend: single-track MLIR (since 2026-10-03).
|
||||
# The tree now uses includes/mlirlite; BuildPipeline always runs mlir-opt -> mlir-translate to lower back to LLVM IR.
|
||||
|
||||
|
||||
def Load_project_config(path: str) -> int:
|
||||
"""加载工程配置文件(project.vpj)
|
||||
|
||||
Args:
|
||||
path: 配置文件路径
|
||||
|
||||
Returns:
|
||||
0 表示成功,非 0 表示失败
|
||||
"""
|
||||
"""Load the project config file (project.vpj); 0=success, non-zero=failure."""
|
||||
global SourceDir, BuildDir, TempDir, OutputDir, ProjectName, ProjectVersion
|
||||
global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput
|
||||
global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput, LinkerStaticLibs, LinkerDynamicLibs
|
||||
global TargetTriple, TargetDataLayout, Sha1SliceLevel, StrictMode
|
||||
global IncludesDir, IncludesDirs
|
||||
global Qt6Path, Qt6Dlls, Qt6Plugins
|
||||
|
||||
if path is None: return 1
|
||||
if _mbuddy is None: return 1
|
||||
|
||||
# 打开文件
|
||||
f: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R)
|
||||
# Open file
|
||||
f: fileio.File* = fileio.File(path, fileio.MODE.R)
|
||||
if f is None: return 1
|
||||
if f.closed: return 1
|
||||
|
||||
# 分配读取缓冲区
|
||||
CFG_BUF_SIZE: t.CSizeT = 8192
|
||||
# Allocate read buffer
|
||||
CFG_BUF_SIZE: sizet = 8192
|
||||
buf: bytes = _mbuddy.alloc(CFG_BUF_SIZE)
|
||||
if buf is None:
|
||||
f.close()
|
||||
return 1
|
||||
|
||||
bytes_read: t.CInt64T = f.read_all(buf, CFG_BUF_SIZE)
|
||||
bytes_read: int64 = f.read_all(buf, CFG_BUF_SIZE)
|
||||
f.close()
|
||||
|
||||
if bytes_read <= 0: return 1
|
||||
|
||||
# 解析 JSON
|
||||
root: JsonValue | t.CPtr = json_parse(_mbuddy, buf)
|
||||
# Parse JSON
|
||||
root: JsonValue* = json_parse(_mbuddy, buf)
|
||||
if root is None: return 1
|
||||
if not root.is_object(): return 1
|
||||
|
||||
# 读取顶层字段(使用显式 __getitem__ 调用,兼容旧编译器二进制)
|
||||
sd_val: JsonValue | t.CPtr = root["source_dir"]
|
||||
# Read top-level fields (explicit __getitem__ calls for old compiler binaries)
|
||||
sd_val: JsonValue* = root["source_dir"]
|
||||
SourceDir = sd_val.as_string() if sd_val is not None else None
|
||||
# build_dir: 统一构建目录(默认 ./.tpv_build),temp 和 output 作为其子目录
|
||||
bd_val: JsonValue | t.CPtr = root["build_dir"]
|
||||
if bd_val is not None and bd_val.is_string():
|
||||
BuildDir = bd_val.as_string()
|
||||
else:
|
||||
BuildDir = "./.tpv_build"
|
||||
name_val: JsonValue | t.CPtr = root["name"]
|
||||
# build_dir: unified build dir (default ./__vpcbuild__); temp and output are subdirs
|
||||
bd_val: JsonValue* = root["build_dir"]
|
||||
if bd_val is not None and bd_val.is_string(): BuildDir = bd_val.as_string()
|
||||
else: BuildDir = "./__vpcbuild__"
|
||||
name_val: JsonValue* = root["name"]
|
||||
ProjectName = name_val.as_string() if name_val is not None else None
|
||||
ver_val: JsonValue | t.CPtr = root["version"]
|
||||
ver_val: JsonValue* = root["version"]
|
||||
ProjectVersion = ver_val.as_string() if ver_val is not None else None
|
||||
|
||||
# 读取 compiler 子对象
|
||||
compiler: JsonValue | t.CPtr = root["compiler"]
|
||||
# Read compiler sub-object
|
||||
compiler: JsonValue* = root["compiler"]
|
||||
if compiler is not None and compiler.is_object():
|
||||
cc_cmd_val: JsonValue | t.CPtr = compiler["cmd"]
|
||||
cc_cmd_val: JsonValue* = compiler["cmd"]
|
||||
CompilerCmd = cc_cmd_val.as_string() if cc_cmd_val is not None else None
|
||||
# 读取 compiler.flags 数组,拼接成空格分隔的字符串
|
||||
cc_flags_val: JsonValue | t.CPtr = compiler["flags"]
|
||||
# Read compiler.flags array, join into a space-separated string
|
||||
cc_flags_val: JsonValue* = compiler["flags"]
|
||||
CompilerFlags = _join_flags_array(cc_flags_val)
|
||||
|
||||
# 读取 linker 子对象
|
||||
linker: JsonValue | t.CPtr = root["linker"]
|
||||
# Read linker sub-object
|
||||
linker: JsonValue* = root["linker"]
|
||||
if linker is not None and linker.is_object():
|
||||
ld_cmd_val: JsonValue | t.CPtr = linker["cmd"]
|
||||
ld_cmd_val: JsonValue* = linker["cmd"]
|
||||
LinkerCmd = ld_cmd_val.as_string() if ld_cmd_val is not None else None
|
||||
ld_out_val: JsonValue | t.CPtr = linker["output"]
|
||||
ld_out_val: JsonValue* = linker["output"]
|
||||
LinkerOutput = ld_out_val.as_string() if ld_out_val is not None else None
|
||||
# 读取 linker.flags 数组,拼接成空格分隔的字符串
|
||||
ld_flags_val: JsonValue | t.CPtr = linker["flags"]
|
||||
# Read linker.flags array, join into a space-separated string
|
||||
ld_flags_val: JsonValue* = linker["flags"]
|
||||
LinkerFlags = _join_flags_array(ld_flags_val)
|
||||
# mingw-w64:同时链接 -lmsvcrt 与 -lucrt 时,GNU ld 会从两个 import 库各拉出一个都定义
|
||||
# __imp_<name> 的成员(现场:__imp_ldexp 同时出现在 libmsvcrt 与
|
||||
# libapi-ms-win-crt-math),直接报 "multiple definition"。lld 按需解析 import 库不会冲突,
|
||||
# 仓库默认链接命令也一直是 lld;项目配置漏写 -fuse-ld 时在这里补齐,避免逐个测试改配置。
|
||||
if LinkerCmd is not None and LinkerFlags is not None and string.strstr(LinkerCmd, "clang") is not None:
|
||||
if string.strstr(LinkerFlags, "-fuse-ld") is None and string.strstr(LinkerFlags, "-lucrt") is not None:
|
||||
lf_cap: sizet = string.strlen(LinkerFlags) + 32
|
||||
lf_new: str = _mbuddy.alloc(lf_cap)
|
||||
if lf_new is not None:
|
||||
viperlib.snprintf(lf_new, lf_cap, "-fuse-ld=lld %s", LinkerFlags)
|
||||
LinkerFlags = lf_new
|
||||
# Read linker.static_libs array: explicit static libs (.a/.obj); joined raw here,
|
||||
# then resolved to abs paths against project_dir in resolve_paths.
|
||||
sl_val: JsonValue* = linker["static_libs"]
|
||||
LinkerStaticLibs = _join_flags_array(sl_val)
|
||||
# Read linker.dynamic_libs array: explicit dynamic libs (.dll/.so); joined raw here,
|
||||
# then resolved to abs paths against project_dir in resolve_paths.
|
||||
dl_val: JsonValue* = linker["dynamic_libs"]
|
||||
LinkerDynamicLibs = _join_flags_array(dl_val)
|
||||
|
||||
# 读取 target 子对象
|
||||
target: JsonValue | t.CPtr = root["target"]
|
||||
# Read target sub-object
|
||||
target: JsonValue* = root["target"]
|
||||
if target is not None and target.is_object():
|
||||
tgt_triple_val: JsonValue | t.CPtr = target["triple"]
|
||||
tgt_triple_val: JsonValue* = target["triple"]
|
||||
TargetTriple = tgt_triple_val.as_string() if tgt_triple_val is not None else None
|
||||
tgt_dl_val: JsonValue | t.CPtr = target["datalayout"]
|
||||
tgt_dl_val: JsonValue* = target["datalayout"]
|
||||
TargetDataLayout = tgt_dl_val.as_string() if tgt_dl_val is not None else None
|
||||
|
||||
# 读取 options 子对象
|
||||
# Sha1SliceLevel: SHA1 切片层数(默认 1,每层取 SHA1 前 2 字符作为子目录)
|
||||
# level=0 → 不切片,文件直接放 base_dir
|
||||
# level=1 → b7/{sha1}.ext
|
||||
# level=2 → b7/90/{sha1}.ext
|
||||
options: JsonValue | t.CPtr = root["options"]
|
||||
# Read options sub-object
|
||||
# Sha1SliceLevel: SHA1 slice level (default 1; each level uses 2 SHA1 chars: 0=none, 1=b7/, 2=b7/90/)
|
||||
options: JsonValue* = root["options"]
|
||||
Sha1SliceLevel = 1
|
||||
StrictMode = 1
|
||||
if options is not None and options.is_object():
|
||||
sl: JsonValue | t.CPtr = options["sha1_slice_level"]
|
||||
if sl is not None and sl.is_int():
|
||||
Sha1SliceLevel = sl.as_int()
|
||||
sm: JsonValue | t.CPtr = options["strict_mode"]
|
||||
if sm is not None and sm.is_bool():
|
||||
StrictMode = 1 if sm.as_bool() else 0
|
||||
sl: JsonValue* = options["sha1_slice_level"]
|
||||
if sl is not None and sl.is_int(): Sha1SliceLevel = int(sl.as_int())
|
||||
sm: JsonValue* = options["strict_mode"]
|
||||
if sm is not None and sm.is_bool(): StrictMode = 1 if sm.as_bool() else 0
|
||||
|
||||
# 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs)
|
||||
includes_val: JsonValue | t.CPtr = root["includes"]
|
||||
# Read includes array (multi-dir: first element -> IncludesDir, all joined -> IncludesDirs)
|
||||
includes_val: JsonValue* = root["includes"]
|
||||
if includes_val is not None and includes_val.is_array():
|
||||
arr_len: t.CSizeT = len(includes_val)
|
||||
arr_len: sizet = len(includes_val)
|
||||
inc_buf: bytes = stdlib.malloc(2048)
|
||||
if inc_buf is not None:
|
||||
inc_buf[0] = '\0'
|
||||
inc_pos: t.CSizeT = 0
|
||||
for inc_i in range(arr_len):
|
||||
inc_item: JsonValue | t.CPtr = includes_val.get_item(inc_i)
|
||||
if inc_item is None or not inc_item.is_string():
|
||||
continue
|
||||
inc_pos: sizet = 0
|
||||
for inc_i in range(int(arr_len)):
|
||||
inc_item: JsonValue* = includes_val.get_item(inc_i)
|
||||
if inc_item is None or not inc_item.is_string(): continue
|
||||
inc_str: str = inc_item.as_string()
|
||||
inc_slen: t.CSizeT = string.strlen(inc_str)
|
||||
if inc_i == 0:
|
||||
IncludesDir = inc_str
|
||||
# 拼接进 IncludesDirs(空格分隔)
|
||||
inc_slen: sizet = string.strlen(inc_str)
|
||||
if inc_i == 0: IncludesDir = inc_str
|
||||
# Append into IncludesDirs (space-separated)
|
||||
if inc_pos + inc_slen + 1 < 2048:
|
||||
if inc_pos > 0:
|
||||
inc_buf[inc_pos] = ' '
|
||||
@@ -169,56 +172,53 @@ def Load_project_config(path: str) -> int:
|
||||
inc_pos += inc_slen
|
||||
inc_buf[inc_pos] = '\0'
|
||||
IncludesDirs = inc_buf
|
||||
if IncludesDir is None:
|
||||
IncludesDir = inc_buf
|
||||
if IncludesDir is None: IncludesDir = inc_buf
|
||||
|
||||
# Read runtime sub-object (optional): declared runtime deps deployed next to the exe.
|
||||
# qt6_dlls / qt6_plugins join via _join_flags_array (space-separated, same口径 as linker.static_libs).
|
||||
runtime: JsonValue* = root["runtime"]
|
||||
if runtime is not None and runtime.is_object():
|
||||
qp_val: JsonValue* = runtime["qt6_path"]
|
||||
Qt6Path = qp_val.as_string() if qp_val is not None else None
|
||||
qd_val: JsonValue* = runtime["qt6_dlls"]
|
||||
Qt6Dlls = _join_flags_array(qd_val)
|
||||
qpl_val: JsonValue* = runtime["qt6_plugins"]
|
||||
Qt6Plugins = _join_flags_array(qpl_val)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
|
||||
"""将 JSON 字符串数组拼接成空格分隔的字符串
|
||||
|
||||
用于 linker.flags 和 compiler.flags 数组。
|
||||
用 while + 索引遍历,避免 list 迭代器 bug。
|
||||
|
||||
Args:
|
||||
flags_val: JsonValue,应为字符串数组
|
||||
|
||||
Returns:
|
||||
空格分隔的字符串,或 None 如果 flags_val 为 None 或非数组或空数组
|
||||
"""
|
||||
def _join_flags_array(flags_val: JsonValue*) -> str:
|
||||
"""Join a JSON string array into a space-separated string; None if empty/invalid."""
|
||||
if flags_val is None: return None
|
||||
if not flags_val.is_array(): return None
|
||||
|
||||
arr_len: t.CSizeT = len(flags_val)
|
||||
if arr_len == 0:
|
||||
return None
|
||||
arr_len: sizet = len(flags_val)
|
||||
if arr_len == 0: return None
|
||||
|
||||
# 第一遍:计算总长度(所有 flag 长度 + 空格分隔符)
|
||||
total_len: t.CSizeT = 0
|
||||
for i in range(arr_len):
|
||||
item: JsonValue | t.CPtr = flags_val.get_item(i)
|
||||
# First pass: compute total length (all flags + space separators)
|
||||
total_len: sizet = 0
|
||||
for i in range(int(arr_len)):
|
||||
item: JsonValue* = flags_val.get_item(i)
|
||||
if item is not None and item.is_string():
|
||||
s: str = item.as_string()
|
||||
if s is not None:
|
||||
total_len += string.strlen(s) + 1 # +1 for space
|
||||
|
||||
if total_len == 0:
|
||||
return None
|
||||
if total_len == 0: return None
|
||||
|
||||
# 分配缓冲区(total_len 已含每个 flag 后的空格,最后一个是 '\0')
|
||||
# Allocate buffer (total_len includes a trailing space per flag; last one becomes '\0')
|
||||
buf: str = _mbuddy.alloc(total_len + 1)
|
||||
if buf is None:
|
||||
return None
|
||||
if buf is None: return None
|
||||
|
||||
# 第二遍:拼接(flag 之间用空格分隔)
|
||||
pos: t.CSizeT = 0
|
||||
for i in range(arr_len):
|
||||
# Second pass: join (flags separated by spaces)
|
||||
pos: sizet = 0
|
||||
for i in range(int(arr_len)):
|
||||
item = flags_val.get_item(i)
|
||||
if item is not None and item.is_string():
|
||||
s = item.as_string()
|
||||
if s is not None:
|
||||
slen: t.CSizeT = string.strlen(s)
|
||||
slen: sizet = string.strlen(s)
|
||||
if pos > 0:
|
||||
buf[pos] = ' '
|
||||
pos += 1
|
||||
@@ -229,138 +229,118 @@ def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
|
||||
|
||||
|
||||
def _join_path(rel: str, project_dir: str) -> str:
|
||||
"""将相对路径与 project_dir 拼接为绝对路径
|
||||
"""Join a relative path with project_dir, expanding env vars and normalizing it."""
|
||||
if rel is None: return None
|
||||
# Env var expansion: only when '%' present (fileio.ExpandEnvironmentPath returns None on
|
||||
# failure/no '%'); on failure keep the original string unchanged.
|
||||
ep: str = fileio.ExpandEnvironmentPath(rel)
|
||||
if ep is not None: rel = ep
|
||||
# Absolute paths (starting with / or \) stay unchanged
|
||||
if rel[0] == '/' or rel[0] == '\\': return rel
|
||||
# Skip joining when rel already has the project_dir prefix (avoids double paths); boundary at separator or end
|
||||
if project_dir is not None:
|
||||
pdl: sizet = string.strlen(project_dir)
|
||||
if pdl > 0 and string.strncmp(rel, project_dir, pdl) == 0:
|
||||
if rel[pdl] == '/' or rel[pdl] == '\\' or rel[pdl] == '\0': return rel
|
||||
|
||||
- ./App + Test → Test/App
|
||||
- ./temp + Test → Test/temp
|
||||
- ../includes + Test → Test/../includes(OS 解析为 includes)
|
||||
- 绝对路径保持不变
|
||||
- None 返回 None
|
||||
rel_len: sizet = string.strlen(rel)
|
||||
dir_len: sizet = string.strlen(project_dir)
|
||||
|
||||
拼接结果经 _normalize_path 规范化:./Test/../includes → ./includes,
|
||||
消除 .. 段,保证路径显示干净且相对寻址正确。
|
||||
"""
|
||||
if rel is None:
|
||||
return None
|
||||
# 绝对路径(以 / 或 \ 开头)保持不变
|
||||
if rel[0] == '/' or rel[0] == '\\':
|
||||
return rel
|
||||
|
||||
rel_len: t.CSizeT = string.strlen(rel)
|
||||
dir_len: t.CSizeT = string.strlen(project_dir)
|
||||
|
||||
# 处理 ../ 或 ..\\ 前缀:拼接 project_dir/../...
|
||||
# Handle ../ or ..\ prefix: join project_dir/../...
|
||||
if rel_len >= 3 and rel[0] == '.' and rel[1] == '.':
|
||||
if rel[2] == '/' or rel[2] == '\\':
|
||||
if dir_len == 0:
|
||||
return rel
|
||||
total_len: t.CSizeT = dir_len + 1 + rel_len + 1
|
||||
if dir_len == 0: return rel
|
||||
total_len: sizet = dir_len + 1 + rel_len + 1
|
||||
buf: str = _mbuddy.alloc(total_len)
|
||||
if buf is None:
|
||||
return rel
|
||||
if buf is None: return rel
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
|
||||
return _normalize_path(buf)
|
||||
|
||||
# 处理 ./ 或 .\\ 前缀:拼接 project_dir + rel[2:]
|
||||
rel_off: t.CSizeT = 0
|
||||
# Handle ./ or .\ prefix: join project_dir + rel[2:]
|
||||
rel_off: sizet = 0
|
||||
if rel_len >= 2 and rel[0] == '.':
|
||||
if rel[1] == '/' or rel[1] == '\\':
|
||||
rel_off = 2
|
||||
if rel[1] == '/' or rel[1] == '\\': rel_off = 2
|
||||
else:
|
||||
return rel # 不是 ./ 开头,保持不变
|
||||
return rel # not starting with ./: keep unchanged
|
||||
else:
|
||||
return rel # 不是 . 开头,保持不变
|
||||
return rel # not starting with .: keep unchanged
|
||||
|
||||
if dir_len == 0:
|
||||
# project_dir 为空(当前目录),去掉 ./ 前缀即可
|
||||
# project_dir empty (current dir): just strip the ./ prefix
|
||||
return rel + rel_off
|
||||
# 拼接: project_dir + "/" + rel[rel_off:]
|
||||
# Join: project_dir + "/" + rel[rel_off:]
|
||||
total_len = dir_len + 1 + (rel_len - rel_off) + 1
|
||||
buf = _mbuddy.alloc(total_len)
|
||||
if buf is None:
|
||||
return rel # 分配失败,返回原路径
|
||||
return rel # allocation failed: return original path
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel + rel_off)
|
||||
return _normalize_path(buf)
|
||||
|
||||
|
||||
def _normalize_path(path: str) -> str:
|
||||
"""规范化路径:解析 . 和 .. 段,合并多余分隔符
|
||||
|
||||
例: ./test/BuddyTest/../../includes → ./includes
|
||||
./test/BuddyTest/App → ./test/BuddyTest/App
|
||||
绝对路径(/ 或 \\ 开头,含盘符 C:)保持不变。
|
||||
返回 _mbuddy 分配的字符串;失败时返回原路径。
|
||||
"""
|
||||
if path is None:
|
||||
return None
|
||||
plen: t.CSizeT = string.strlen(path)
|
||||
if plen == 0:
|
||||
return path
|
||||
# 绝对路径(/ 或 \ 开头,或盘符如 C:)不规范化
|
||||
if path[0] == '/' or path[0] == '\\':
|
||||
return path
|
||||
if plen >= 2 and path[1] == ':':
|
||||
return path
|
||||
# 输出缓冲(最坏情况 = 输入长度 + 可能的前缀 ./)
|
||||
"""Normalize a path by resolving . and .. segments; absolute paths pass through."""
|
||||
if path is None: return None
|
||||
plen: sizet = string.strlen(path)
|
||||
if plen == 0: return path
|
||||
# Absolute paths (/ or \ start, or drive letter like C:) are not normalized
|
||||
if path[0] == '/' or path[0] == '\\': return path
|
||||
if plen >= 2 and path[1] == ':': return path
|
||||
# Output buffer (worst case = input length + possible ./ prefix)
|
||||
out: str = _mbuddy.alloc(plen + 3)
|
||||
if out is None:
|
||||
return path
|
||||
seg_start: t.CSizeT = 0
|
||||
out_len: t.CSizeT = 0
|
||||
if out is None: return path
|
||||
seg_start: sizet = 0
|
||||
out_len: sizet = 0
|
||||
has_seg: int = 0
|
||||
i: t.CSizeT = 0
|
||||
while i <= plen:
|
||||
ch: t.CChar = '\0'
|
||||
if i < plen:
|
||||
ch = path[i]
|
||||
for i in range(int(plen + 1)):
|
||||
ch: char = '\0'
|
||||
if i < plen: ch = path[i]
|
||||
if ch == '/' or ch == '\\' or ch == '\0':
|
||||
seg_len: t.CSizeT = i - seg_start
|
||||
seg_len: sizet = i - seg_start
|
||||
if seg_len > 0:
|
||||
# 单段 ".":跳过
|
||||
if seg_len == 1 and path[seg_start] == '.':
|
||||
pass
|
||||
# 单段 "..":弹出上一段(无段可弹时保留,如 ../includes)
|
||||
# Single "." segment: skip
|
||||
if seg_len == 1 and path[seg_start] == '.': pass
|
||||
# Single ".." segment: pop previous; leading ".." with nothing to pop must be kept (like Python normpath)
|
||||
elif seg_len == 2 and path[seg_start] == '.' and path[seg_start + 1] == '.':
|
||||
if out_len > 0:
|
||||
# 回退到上一个分隔符
|
||||
k: t.CSizeT = out_len
|
||||
while k > 0 and out[k - 1] != '/' and out[k - 1] != '\\':
|
||||
k -= 1
|
||||
if k > 0:
|
||||
out_len = k - 1
|
||||
else:
|
||||
out_len = 0
|
||||
# 仍有剩余段时保持分隔符逻辑
|
||||
if out_len > 0:
|
||||
has_seg = 1
|
||||
else:
|
||||
has_seg = 0
|
||||
# Whether the last segment is already a leading ".." (standalone: at start or preceded by a separator)
|
||||
last_is_dotdot: int = 0
|
||||
if out_len >= 2 and out[out_len - 1] == '.' and out[out_len - 2] == '.':
|
||||
if out_len == 2: last_is_dotdot = 1
|
||||
elif out[out_len - 3] == '/' or out[out_len - 3] == '\\': last_is_dotdot = 1
|
||||
if out_len > 0 and last_is_dotdot == 0:
|
||||
# Back up to the previous separator
|
||||
k: sizet = out_len
|
||||
while k > 0 and out[k - 1] != '/' and out[k - 1] != '\\': k -= 1
|
||||
if k > 0: out_len = k - 1
|
||||
else: out_len = 0
|
||||
# Keep separator logic when segments remain
|
||||
if out_len > 0: has_seg = 1
|
||||
else: has_seg = 0
|
||||
else:
|
||||
# 无段可弹:保留 ..
|
||||
out[0] = '.'
|
||||
out[1] = '.'
|
||||
out_len = 2
|
||||
# No normal segment to pop: append ".." as a normal segment (keep leading ..)
|
||||
if has_seg > 0:
|
||||
out[out_len] = '/'
|
||||
out_len += 1
|
||||
out[out_len] = '.'
|
||||
out[out_len + 1] = '.'
|
||||
out_len += 2
|
||||
has_seg = 1
|
||||
else:
|
||||
# 普通段:追加分隔符 + 段
|
||||
# Normal segment: append separator + segment
|
||||
if has_seg > 0:
|
||||
out[out_len] = '/'
|
||||
out_len += 1
|
||||
jj: t.CSizeT = 0
|
||||
while jj < seg_len:
|
||||
for jj in range(int(seg_len)):
|
||||
out[out_len] = path[seg_start + jj]
|
||||
out_len += 1
|
||||
jj += 1
|
||||
has_seg = 1
|
||||
seg_start = i + 1
|
||||
i += 1
|
||||
if has_seg == 0:
|
||||
# 全部段被消掉(如 ./..):返回当前目录
|
||||
# All segments consumed (e.g. ./..): return current dir
|
||||
return "./"
|
||||
# 相对路径补回 ./ 前缀(与原路径风格一致;以 .. 开头的除外)
|
||||
# Re-add ./ prefix for relative paths (matches original style; except those starting with ..)
|
||||
if out[0] != '.' and out[0] != '/':
|
||||
# 右移 2 位留出 "./"(从后往前拷贝,避免覆盖源字符)
|
||||
mv: t.CSizeT = out_len
|
||||
# Shift right by 2 to make room for "./" (copy back-to-front to avoid overwriting)
|
||||
mv: sizet = out_len
|
||||
while mv > 0:
|
||||
mv -= 1
|
||||
out[mv + 2] = out[mv]
|
||||
@@ -372,32 +352,20 @@ def _normalize_path(path: str) -> str:
|
||||
|
||||
|
||||
def resolve_paths(project_dir: str) -> int:
|
||||
"""解析并规范化工程路径(src/build/includes)
|
||||
|
||||
将 project.vpj 中的相对路径(以 ./ 开头)转换为基于 project_dir 的路径。
|
||||
BuildDir 解析后,TempDir = {BuildDir}/temp,OutputDir = {BuildDir}/output。
|
||||
|
||||
Args:
|
||||
project_dir: 工程根目录(project.vpj 所在目录)
|
||||
|
||||
Returns:
|
||||
0 表示成功,非 0 表示失败
|
||||
"""
|
||||
"""Resolve and normalize project paths (src/build/includes); 0=success."""
|
||||
global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs
|
||||
global ProjectDir
|
||||
if project_dir is None:
|
||||
return 1
|
||||
if _mbuddy is None:
|
||||
return 1
|
||||
global ProjectDir, LinkerStaticLibs, LinkerDynamicLibs
|
||||
if project_dir is None: return 1
|
||||
if _mbuddy is None: return 1
|
||||
|
||||
ProjectDir = project_dir
|
||||
|
||||
SourceDir = _join_path(SourceDir, project_dir)
|
||||
BuildDir = _join_path(BuildDir, project_dir)
|
||||
|
||||
# TempDir = {BuildDir}/temp
|
||||
# TempDir = {BuildDir}/temp; OutputDir = {BuildDir}/output
|
||||
if BuildDir is not None:
|
||||
bd_len: t.CSizeT = string.strlen(BuildDir)
|
||||
bd_len: sizet = string.strlen(BuildDir)
|
||||
temp_buf: str = _mbuddy.alloc(bd_len + 8)
|
||||
if temp_buf is not None:
|
||||
viperlib.snprintf(temp_buf, bd_len + 8, "%s/temp", BuildDir)
|
||||
@@ -408,24 +376,21 @@ def resolve_paths(project_dir: str) -> int:
|
||||
viperlib.snprintf(output_buf, bd_len + 8, "%s/output", BuildDir)
|
||||
OutputDir = output_buf
|
||||
|
||||
# includes 路径可能是 ../includes 形式,需要基于 project_dir 解析
|
||||
# IncludesDir(首元素)与 IncludesDirs(全部,空格分隔)逐项规范化
|
||||
# includes paths may be of the form ../includes, resolved against project_dir.
|
||||
# Normalize IncludesDir (first element) and IncludesDirs (all, space-separated) item by item.
|
||||
if IncludesDirs is not None:
|
||||
id_total: t.CSizeT = string.strlen(IncludesDirs)
|
||||
id_total: sizet = string.strlen(IncludesDirs)
|
||||
id_out: str = _mbuddy.alloc(id_total * 3 + 64)
|
||||
if id_out is not None:
|
||||
id_out[0] = '\0'
|
||||
id_pos: t.CSizeT = 0
|
||||
id_i: t.CSizeT = 0
|
||||
id_pos: sizet = 0
|
||||
id_i: sizet = 0
|
||||
while id_i < id_total:
|
||||
while id_i < id_total and IncludesDirs[id_i] == ' ':
|
||||
id_i += 1
|
||||
if id_i >= id_total:
|
||||
break
|
||||
id_start: t.CSizeT = id_i
|
||||
while id_i < id_total and IncludesDirs[id_i] != ' ':
|
||||
id_i += 1
|
||||
seg_len: t.CSizeT = id_i - id_start
|
||||
while id_i < id_total and IncludesDirs[id_i] == ' ': id_i += 1
|
||||
if id_i >= id_total: break
|
||||
id_start: sizet = id_i
|
||||
while id_i < id_total and IncludesDirs[id_i] != ' ': id_i += 1
|
||||
seg_len: sizet = id_i - id_start
|
||||
if seg_len > 0:
|
||||
seg_buf: str = _mbuddy.alloc(seg_len + 1)
|
||||
if seg_buf is not None:
|
||||
@@ -433,7 +398,7 @@ def resolve_paths(project_dir: str) -> int:
|
||||
seg_buf[seg_len] = '\0'
|
||||
joined: str = _join_path(seg_buf, project_dir)
|
||||
if joined is not None:
|
||||
jl: t.CSizeT = string.strlen(joined)
|
||||
jl: sizet = string.strlen(joined)
|
||||
if id_pos > 0:
|
||||
id_out[id_pos] = ' '
|
||||
id_pos += 1
|
||||
@@ -441,74 +406,108 @@ def resolve_paths(project_dir: str) -> int:
|
||||
id_pos += jl
|
||||
id_out[id_pos] = '\0'
|
||||
IncludesDirs = id_out
|
||||
if IncludesDir is not None:
|
||||
IncludesDir = _join_path(IncludesDir, project_dir)
|
||||
# linker.flags 中 `-T <脚本>` 的链接脚本路径同样基于 project_dir 解析,
|
||||
# 否则 subprocess 按进程 cwd(TransPyC/)找 linker.ld → ld.lld 报
|
||||
# "cannot find linker script linker.ld"
|
||||
if IncludesDir is not None: IncludesDir = _join_path(IncludesDir, project_dir)
|
||||
# linker.static_libs explicit paths resolved segment-by-segment to abs paths against project_dir
|
||||
if LinkerStaticLibs is not None:
|
||||
sl_total: sizet = string.strlen(LinkerStaticLibs)
|
||||
sl_out: str = _mbuddy.alloc(sl_total * 3 + 64)
|
||||
if sl_out is not None:
|
||||
sl_out[0] = '\0'
|
||||
sl_pos: sizet = 0
|
||||
sl_i: sizet = 0
|
||||
while sl_i < sl_total:
|
||||
while sl_i < sl_total and LinkerStaticLibs[sl_i] == ' ': sl_i += 1
|
||||
if sl_i >= sl_total: break
|
||||
sl_start: sizet = sl_i
|
||||
while sl_i < sl_total and LinkerStaticLibs[sl_i] != ' ': sl_i += 1
|
||||
seg_len: sizet = sl_i - sl_start
|
||||
if seg_len > 0:
|
||||
seg_buf: str = _mbuddy.alloc(seg_len + 1)
|
||||
if seg_buf is not None:
|
||||
string.strncpy(seg_buf, LinkerStaticLibs + sl_start, seg_len)
|
||||
seg_buf[seg_len] = '\0'
|
||||
joined: str = _join_path(seg_buf, project_dir)
|
||||
if joined is not None:
|
||||
jl: sizet = string.strlen(joined)
|
||||
if sl_pos > 0:
|
||||
sl_out[sl_pos] = ' '
|
||||
sl_pos += 1
|
||||
string.strcpy(sl_out + sl_pos, joined)
|
||||
sl_pos += jl
|
||||
sl_out[sl_pos] = '\0'
|
||||
LinkerStaticLibs = sl_out
|
||||
# linker.dynamic_libs explicit files (.dll/.so) likewise resolved to abs paths against project_dir.
|
||||
if LinkerDynamicLibs is not None:
|
||||
dl_total: sizet = string.strlen(LinkerDynamicLibs)
|
||||
dl_out: str = _mbuddy.alloc(dl_total * 3 + 64)
|
||||
if dl_out is not None:
|
||||
dl_out[0] = '\0'
|
||||
dl_pos: sizet = 0
|
||||
dl_i: sizet = 0
|
||||
while dl_i < dl_total:
|
||||
while dl_i < dl_total and LinkerDynamicLibs[dl_i] == ' ': dl_i += 1
|
||||
if dl_i >= dl_total: break
|
||||
dl_start: sizet = dl_i
|
||||
while dl_i < dl_total and LinkerDynamicLibs[dl_i] != ' ': dl_i += 1
|
||||
seg_len: sizet = dl_i - dl_start
|
||||
if seg_len > 0:
|
||||
seg_buf: str = _mbuddy.alloc(seg_len + 1)
|
||||
if seg_buf is not None:
|
||||
string.strncpy(seg_buf, LinkerDynamicLibs + dl_start, seg_len)
|
||||
seg_buf[seg_len] = '\0'
|
||||
joined: str = _join_path(seg_buf, project_dir)
|
||||
if joined is not None:
|
||||
jl: sizet = string.strlen(joined)
|
||||
if dl_pos > 0:
|
||||
dl_out[dl_pos] = ' '
|
||||
dl_pos += 1
|
||||
string.strcpy(dl_out + dl_pos, joined)
|
||||
dl_pos += jl
|
||||
dl_out[dl_pos] = '\0'
|
||||
LinkerDynamicLibs = dl_out
|
||||
# -T <script> paths in linker.flags also resolved against project_dir, else ld.lld misses them
|
||||
_resolve_linker_t_paths(project_dir)
|
||||
return 0
|
||||
|
||||
|
||||
def _join_path_abs(rel: str, project_dir: str) -> str:
|
||||
"""将相对路径基于 project_dir 拼接为绝对路径(用于 -T 链接脚本等)
|
||||
|
||||
已为绝对路径(/ \\ 开头)或盘符形式(x:)时原样返回。
|
||||
"""
|
||||
if rel is None:
|
||||
return None
|
||||
rel_len: t.CSizeT = string.strlen(rel)
|
||||
if rel_len == 0:
|
||||
return rel
|
||||
if rel[0] == '/' or rel[0] == '\\':
|
||||
return rel
|
||||
if rel_len >= 2 and rel[1] == ':':
|
||||
return rel
|
||||
dir_len: t.CSizeT = string.strlen(project_dir)
|
||||
if dir_len == 0:
|
||||
return rel
|
||||
total_len: t.CSizeT = dir_len + 1 + rel_len + 1
|
||||
"""Join a relative path under project_dir into an absolute path; absolutes unchanged."""
|
||||
if rel is None: return None
|
||||
rel_len: sizet = string.strlen(rel)
|
||||
if rel_len == 0: return rel
|
||||
if rel[0] == '/' or rel[0] == '\\': return rel
|
||||
if rel_len >= 2 and rel[1] == ':': return rel
|
||||
dir_len: sizet = string.strlen(project_dir)
|
||||
if dir_len == 0: return rel
|
||||
total_len: sizet = dir_len + 1 + rel_len + 1
|
||||
buf: str = _mbuddy.alloc(total_len)
|
||||
if buf is None:
|
||||
return rel
|
||||
if buf is None: return rel
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
|
||||
return buf
|
||||
|
||||
|
||||
def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
"""将 LinkerFlags 中 `-T <相对路径>` 的链接脚本路径解析为绝对路径
|
||||
|
||||
ld.lld 的 -T 参数若是相对路径,将按 subprocess 进程 cwd 解析而找不到
|
||||
脚本。逐 token 扫描 LinkerFlags,遇 -T 其后 token 若非绝对路径则基于
|
||||
project_dir 拼接(与 resolve_paths 对其他相对路径的处理保持一致)。
|
||||
"""
|
||||
"""Resolve relative `-T <script>` paths in LinkerFlags against project_dir."""
|
||||
global LinkerFlags
|
||||
if LinkerFlags is None or project_dir is None:
|
||||
return
|
||||
fl_total: t.CSizeT = string.strlen(LinkerFlags)
|
||||
if fl_total == 0:
|
||||
return
|
||||
if LinkerFlags is None or project_dir is None: return
|
||||
fl_total: sizet = string.strlen(LinkerFlags)
|
||||
if fl_total == 0: return
|
||||
out: str = _mbuddy.alloc(fl_total * 2 + 512)
|
||||
if out is None:
|
||||
return
|
||||
if out is None: return
|
||||
out[0] = '\0'
|
||||
out_pos: t.CSizeT = 0
|
||||
i: t.CSizeT = 0
|
||||
prev_is_t: int = 0 # 前一 token 是否为 -T
|
||||
out_pos: sizet = 0
|
||||
i: sizet = 0
|
||||
prev_is_t: int = 0 # whether the previous token is -T
|
||||
while i < fl_total:
|
||||
# 跳过前导空格
|
||||
while i < fl_total and LinkerFlags[i] == ' ':
|
||||
i += 1
|
||||
if i >= fl_total:
|
||||
break
|
||||
start: t.CSizeT = i
|
||||
while i < fl_total and LinkerFlags[i] != ' ':
|
||||
i += 1
|
||||
tok_len: t.CSizeT = i - start
|
||||
# Skip leading spaces
|
||||
while i < fl_total and LinkerFlags[i] == ' ': i += 1
|
||||
if i >= fl_total: break
|
||||
start: sizet = i
|
||||
while i < fl_total and LinkerFlags[i] != ' ': i += 1
|
||||
tok_len: sizet = i - start
|
||||
is_t_flag: int = 0
|
||||
if tok_len == 2 and LinkerFlags[start] == '-' and LinkerFlags[start + 1] == 'T':
|
||||
is_t_flag = 1
|
||||
# 输出分隔符(前一 token 之后)
|
||||
if tok_len == 2 and LinkerFlags[start] == '-' and LinkerFlags[start + 1] == 'T': is_t_flag = 1
|
||||
# Output separator (after the previous token)
|
||||
if out_pos > 0:
|
||||
out[out_pos] = ' '
|
||||
out_pos += 1
|
||||
@@ -516,14 +515,14 @@ def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
string.strcpy(out + out_pos, "-T")
|
||||
out_pos += 2
|
||||
elif prev_is_t != 0:
|
||||
# -T 后的脚本路径:非绝对路径则基于 project_dir 拼接
|
||||
# Script path after -T: join against project_dir if not absolute
|
||||
tok_buf: str = _mbuddy.alloc(tok_len + 1)
|
||||
if tok_buf is not None:
|
||||
string.memcpy(tok_buf, LinkerFlags + start, tok_len)
|
||||
tok_buf[tok_len] = '\0'
|
||||
abs_path: str = _join_path_abs(tok_buf, project_dir)
|
||||
if abs_path is not None:
|
||||
p_len: t.CSizeT = string.strlen(abs_path)
|
||||
p_len: sizet = string.strlen(abs_path)
|
||||
string.strcpy(out + out_pos, abs_path)
|
||||
out_pos += p_len
|
||||
else:
|
||||
@@ -541,96 +540,71 @@ def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
|
||||
|
||||
def get_includes_binary_dir() -> str:
|
||||
"""获取 includes.binary 目录路径(includes 目录 + 缓存后缀)
|
||||
|
||||
includes 目录如 ../includes,默认返回 ../includes.binary;
|
||||
asan 模式(set_inc_bin_suffix(".binary_asan"))返回 ../includes.binary_asan,
|
||||
隔离 asan 插桩 obj,避免与非 asan obj 混链。
|
||||
"""
|
||||
if IncludesDir is None:
|
||||
return None
|
||||
inc_len: t.CSizeT = string.strlen(IncludesDir)
|
||||
suf_len: t.CSizeT = string.strlen(_inc_bin_suffix)
|
||||
buf: str = _mbuddy.alloc(inc_len + suf_len + 1)
|
||||
if buf is None:
|
||||
return None
|
||||
viperlib.snprintf(buf, inc_len + suf_len + 1, "%s%s", IncludesDir, _inc_bin_suffix)
|
||||
"""Return the includes binary cache dir (includes dir + cache and MLIR suffixes)."""
|
||||
if IncludesDir is None: return None
|
||||
inc_len: sizet = string.strlen(IncludesDir)
|
||||
suf_len: sizet = string.strlen(_inc_bin_suffix)
|
||||
be_suf: str = mlir_dir_suffix()
|
||||
be_suf_len: sizet = string.strlen(be_suf)
|
||||
buf: str = _mbuddy.alloc(inc_len + suf_len + be_suf_len + 1)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, inc_len + suf_len + be_suf_len + 1, "%s%s%s", IncludesDir, _inc_bin_suffix, be_suf)
|
||||
return buf
|
||||
|
||||
|
||||
# Directory isolation suffix for the includes binary cache only.
|
||||
# TempDir/OutputDir are plain {BuildDir}/temp and {BuildDir}/output (same layout as TPC and as
|
||||
# the project scripts expect). The includes cache keeps "_mlir": it holds MLIR-era artifacts
|
||||
# (sha1 subdirectories) while TPC's includes cache at {IncludesDir}.binary/ holds flat
|
||||
# "<sha1>.<triple>.pic.obj" files, so sharing one directory would mix the two layouts.
|
||||
def mlir_dir_suffix() -> str:
|
||||
"""Directory isolation suffix for the includes cache of the MLIR backend (always "_mlir")."""
|
||||
return "_mlir"
|
||||
|
||||
|
||||
def asan_runtime_declared() -> int:
|
||||
"""Whether project.vpj declares an ASan runtime; 1=declared."""
|
||||
if LinkerStaticLibs is not None:
|
||||
if string.strstr(LinkerStaticLibs, "asan") is not None: return 1
|
||||
if LinkerDynamicLibs is not None:
|
||||
if string.strstr(LinkerDynamicLibs, "asan") is not None: return 1
|
||||
if LinkerFlags is not None:
|
||||
if string.strstr(LinkerFlags, "asan") is not None: return 1
|
||||
return 0
|
||||
|
||||
|
||||
def print_config():
|
||||
"""打印当前配置"""
|
||||
buf: str = VLogger.fmt_buf()
|
||||
if buf is None:
|
||||
return
|
||||
if ProjectName is not None:
|
||||
viperlib.snprintf(buf, 1024, "project: %s v%s", ProjectName,
|
||||
ProjectVersion if ProjectVersion is not None else "?")
|
||||
VLogger.info(buf, "config")
|
||||
if SourceDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "source_dir: %s", SourceDir)
|
||||
VLogger.info(buf, "config")
|
||||
if BuildDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "build_dir: %s", BuildDir)
|
||||
VLogger.info(buf, "config")
|
||||
if TempDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "temp_dir: %s", TempDir)
|
||||
VLogger.info(buf, "config")
|
||||
if OutputDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "output_dir: %s", OutputDir)
|
||||
VLogger.info(buf, "config")
|
||||
if CompilerCmd is not None:
|
||||
viperlib.snprintf(buf, 1024, "compiler: %s", CompilerCmd)
|
||||
VLogger.info(buf, "config")
|
||||
if LinkerCmd is not None:
|
||||
viperlib.snprintf(buf, 1024, "linker: %s -> %s", LinkerCmd,
|
||||
LinkerOutput if LinkerOutput is not None else "?")
|
||||
VLogger.info(buf, "config")
|
||||
if IncludesDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "includes: %s", IncludesDir)
|
||||
VLogger.info(buf, "config")
|
||||
viperlib.snprintf(buf, 1024, "sha1_slice_level: %d", Sha1SliceLevel)
|
||||
VLogger.info(buf, "config")
|
||||
"""Print the current configuration."""
|
||||
if ProjectName is not None: VLogger.info(f"project: {ProjectName} v{ProjectVersion if ProjectVersion is not None else "?"}", "config")
|
||||
if SourceDir is not None: VLogger.info(f"source_dir: {SourceDir}", "config")
|
||||
if BuildDir is not None: VLogger.info(f"build_dir: {BuildDir}", "config")
|
||||
if TempDir is not None: VLogger.info(f"temp_dir: {TempDir}", "config")
|
||||
if OutputDir is not None: VLogger.info(f"output_dir: {OutputDir}", "config")
|
||||
if CompilerCmd is not None: VLogger.info(f"compiler: {CompilerCmd}", "config")
|
||||
if LinkerCmd is not None: VLogger.info(f"linker: {LinkerCmd} -> {LinkerOutput if LinkerOutput is not None else "?"}", "config")
|
||||
if IncludesDir is not None: VLogger.info(f"includes: {IncludesDir}", "config")
|
||||
VLogger.info(f"sha1_slice_level: {Sha1SliceLevel}", "config")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# slice_subdir - 根据 SHA1 和切片层数返回子目录路径
|
||||
#
|
||||
# 每层取 SHA1 的 2 个字符作为子目录名:
|
||||
# level=0 → None(不切片)
|
||||
# level=1 → "b7"
|
||||
# level=2 → "b7/90"
|
||||
# level=3 → "b7/90/23"
|
||||
#
|
||||
# Args:
|
||||
# sha1: SHA1 字符串(至少 2*level 字符)
|
||||
# level: 切片层数
|
||||
#
|
||||
# Returns:
|
||||
# 子目录路径字符串(mbuddy 分配,无需释放),level<=0 时返回 None
|
||||
# ============================================================
|
||||
# slice_subdir - use 2 SHA1 chars per level as subdir names (level=0 none, 1->"b7", 2->"b7/90").
|
||||
# Args: sha1 (>= 2*level chars) / level. Returns mbuddy-allocated subdir path, or None if level<=0.
|
||||
def slice_subdir(sha1: str, level: int) -> str:
|
||||
"""根据 SHA1 和切片层数返回子目录路径"""
|
||||
if level <= 0 or sha1 is None:
|
||||
return None
|
||||
if _mbuddy is None:
|
||||
return None
|
||||
# 每层 2 字符 + 分隔符,最后无分隔符 + null
|
||||
# buf 大小: level * 2 + (level - 1) + 1 = level * 3
|
||||
buf_len: t.CSizeT = t.CSizeT(level * 3)
|
||||
"""Return the SHA1 slice subdir path for the given level."""
|
||||
if level <= 0 or sha1 is None: return None
|
||||
if _mbuddy is None: return None
|
||||
# 2 chars per level + separators; no trailing separator, then null
|
||||
# buf size: level * 2 + (level - 1) + 1 = level * 3
|
||||
buf_len: sizet = sizet(level * 3)
|
||||
buf: str = _mbuddy.alloc(buf_len)
|
||||
if buf is None:
|
||||
return None
|
||||
if buf is None: return None
|
||||
pos: int = 0
|
||||
i: int = 0
|
||||
while i < level:
|
||||
for i in range(int(level)):
|
||||
if i > 0:
|
||||
buf[pos] = '/'
|
||||
pos += 1
|
||||
idx: int = i * 2
|
||||
idx: int = int(i * 2)
|
||||
buf[pos] = sha1[idx]
|
||||
buf[pos + 1] = sha1[idx + 1]
|
||||
pos += 2
|
||||
i += 1
|
||||
buf[pos] = '\0'
|
||||
return buf
|
||||
+189
-267
@@ -1,267 +1,189 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import hashlib
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import w32.fileio as fileio
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Utils - 工程工具函数
|
||||
#
|
||||
# 提供 SHA1 计算和目录清理等通用工具函数。
|
||||
# ============================================================
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
|
||||
|
||||
# ============================================================
|
||||
# nibble_to_hex_char - 半字节转十六进制字符
|
||||
# ============================================================
|
||||
def nibble_to_hex_char(n: int, uppercase: int) -> t.CChar:
|
||||
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
|
||||
if n < 10: return '0' + n
|
||||
if uppercase != 0: return 'A' + (n - 10)
|
||||
return 'a' + (n - 10)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# bytes_to_hex - 字节序列转十六进制字符串
|
||||
# ============================================================
|
||||
def bytes_to_hex(pool: memhub.MemBuddy | t.CPtr,
|
||||
digest: bytes,
|
||||
nbytes: t.CSizeT,
|
||||
uppercase: int,
|
||||
use_malloc: int) -> str:
|
||||
"""将 digest 前 nbytes 字节转为 2*nbytes 个十六进制字符(含 null 终止)
|
||||
|
||||
use_malloc=1 时用 stdlib.malloc(调用方负责 free),否则 pool.alloc。
|
||||
合并自 Utils.compute_sha1 / IncludesScanner.compute_file_sha1 的重复 hex 转换。
|
||||
"""
|
||||
if digest is None or nbytes <= 0:
|
||||
return None
|
||||
hex_len: t.CSizeT = nbytes * 2 + 1
|
||||
hex_buf: str = None
|
||||
if use_malloc != 0:
|
||||
hex_buf = stdlib.malloc(hex_len)
|
||||
else:
|
||||
hex_buf = pool.alloc(hex_len)
|
||||
if hex_buf is None:
|
||||
return None
|
||||
for i in range(nbytes):
|
||||
hi: int = (digest[i] >> 4) & 0xF
|
||||
lo: int = digest[i] & 0xF
|
||||
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
|
||||
hex_buf[i * 2 + 1] = nibble_to_hex_char(lo, uppercase)
|
||||
hex_buf[nbytes * 2] = '\0'
|
||||
return hex_buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# compute_sha1 - 计算字符串的 SHA1,返回前 16 个十六进制字符
|
||||
#
|
||||
# 使用 includes/hashlib 库的 sha1 类计算摘要,转为十六进制字符串。
|
||||
# 参考 Projectrans.py 的 compute_sha1(hashlib.sha1().hexdigest()[:16])。
|
||||
# ============================================================
|
||||
def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str:
|
||||
"""计算字符串的 SHA1,返回前 16 个十六进制字符(用于文件命名)"""
|
||||
if content is None or pool is None:
|
||||
return None
|
||||
|
||||
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
|
||||
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
|
||||
ctx: hashlib.sha1 | t.CPtr = hashlib.sha1()
|
||||
if ctx is None: return None
|
||||
|
||||
# 计算摘要
|
||||
ctx.update(content)
|
||||
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
|
||||
if digest is None: return None
|
||||
ctx.final(digest)
|
||||
|
||||
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
|
||||
return bytes_to_hex(pool, digest, 8, 0, 0)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
|
||||
# ============================================================
|
||||
def is_vp_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int:
|
||||
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
|
||||
|
||||
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
|
||||
"""
|
||||
if p is None or plen <= 3:
|
||||
return 0
|
||||
base: t.CSizeT = start + plen - 3
|
||||
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p':
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# JoinPath - 拼接两个路径段为 a/b
|
||||
# ============================================================
|
||||
def JoinPath(a: str, b: str) -> str:
|
||||
"""拼接路径段为 "a/b"(stdlib.malloc 分配,调用方负责 free),失败返回 None"""
|
||||
if a is None or b is None:
|
||||
return None
|
||||
a_len: t.CSizeT = string.strlen(a)
|
||||
b_len: t.CSizeT = string.strlen(b)
|
||||
total_len: t.CSizeT = a_len + b_len + 2
|
||||
buf: str = stdlib.malloc(total_len)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", a, b)
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# read_file_to_buf - 读取整个文件到缓冲区(null 终止)
|
||||
# ============================================================
|
||||
def read_file_to_buf(path: str, buf_size: t.CSizeT) -> bytes:
|
||||
"""读取文件到 stdlib.malloc 缓冲区并保证 null 终止
|
||||
|
||||
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
|
||||
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
|
||||
"""
|
||||
if path is None or buf_size <= 0: return None
|
||||
sf: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R)
|
||||
if sf.closed: return None
|
||||
buf: bytes = stdlib.malloc(buf_size)
|
||||
if buf is None:
|
||||
sf.close()
|
||||
return None
|
||||
br: t.CInt64T = sf.read_all(buf, buf_size)
|
||||
sf.close()
|
||||
if br <= 0:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
if br < buf_size:
|
||||
buf[br] = 0
|
||||
else:
|
||||
buf[buf_size - 1] = 0
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PathToModuleName - 文件路径转模块名
|
||||
# ============================================================
|
||||
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||
"""将文件路径转换为模块名(stdlib.malloc 分配,调用方负责 free)
|
||||
|
||||
strip_includes_prefix=1 时跳过 includes/ 前缀(如 includes/ast/__init__.vp → ast)。
|
||||
特殊处理 __init__.vp:pkg/__init__.vp → pkg(而非 pkg.__init__)。
|
||||
合并自 StubMerger._PathToModuleName / IncludesScanner 内联模块名计算。
|
||||
"""
|
||||
if path is None:
|
||||
return None
|
||||
plen: t.CSizeT = string.strlen(path)
|
||||
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
|
||||
prefix_len: t.CSizeT = 9
|
||||
path_start: t.CSizeT = 0
|
||||
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
|
||||
psep_ch: t.CChar = path[8]
|
||||
if psep_ch == '/' or psep_ch == '\\':
|
||||
path_start = prefix_len
|
||||
plen = plen - prefix_len
|
||||
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
|
||||
if is_vp_at(path, path_start, plen) != 0:
|
||||
plen -= 3
|
||||
if plen == 0:
|
||||
return None
|
||||
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
|
||||
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
|
||||
if plen > 9:
|
||||
tail_off: t.CSizeT = path_start + plen - 9
|
||||
sep_char: t.CChar = path[tail_off]
|
||||
if (sep_char == '/' or sep_char == '\\') \
|
||||
and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' \
|
||||
and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' \
|
||||
and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
|
||||
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
|
||||
plen = tail_off - path_start
|
||||
if plen == 0:
|
||||
return None
|
||||
# 复制并替换 / 为 .
|
||||
result: str = stdlib.malloc(plen + 1)
|
||||
if result is None:
|
||||
return None
|
||||
for ri in range(plen):
|
||||
ch: t.CChar = path[path_start + ri]
|
||||
if ch == '/' or ch == '\\':
|
||||
result[ri] = '.'
|
||||
else:
|
||||
result[ri] = ch
|
||||
result[plen] = '\0'
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身)
|
||||
#
|
||||
# 用于 --clean 选项:清理 temp_dir 和 output_dir 中的旧文件。
|
||||
# SHA1 分片目录结构(如 temp/1c/、temp/7a/)需要递归清理,
|
||||
# 否则旧的截断文件(如 256KB text.ll)会被 Phase1 跳过逻辑复用。
|
||||
# ============================================================
|
||||
def CleanDir(dir_path: str) -> int:
|
||||
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
|
||||
if dir_path is None:
|
||||
return -1
|
||||
dir_len: t.CSizeT = string.strlen(dir_path)
|
||||
pattern: bytes = stdlib.malloc(dir_len + 8)
|
||||
if pattern is None:
|
||||
return -1
|
||||
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
|
||||
|
||||
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
if find_data is None:
|
||||
stdlib.free(pattern)
|
||||
return -1
|
||||
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
|
||||
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
return 0
|
||||
|
||||
deleted: int = 0
|
||||
while True:
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
# 跳过 . 和 ..
|
||||
if fname[0] == '.':
|
||||
if fname[1] == '\0':
|
||||
fname = None
|
||||
elif fname[1] == '.' and fname[2] == '\0':
|
||||
fname = None
|
||||
if fname is not None:
|
||||
full_path: bytes = JoinPath(dir_path, fname)
|
||||
if full_path is not None:
|
||||
# 检查是否为目录(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
is_dir: t.CUInt32T = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
if is_dir != 0:
|
||||
# 递归清理子目录中的文件
|
||||
sub_deleted: int = CleanDir(full_path)
|
||||
if sub_deleted > 0:
|
||||
deleted += sub_deleted
|
||||
# 删除空子目录(RemoveDirectoryA 要求目录为空)
|
||||
w32.win32file.RemoveDirectoryA(full_path)
|
||||
else:
|
||||
# 删除文件
|
||||
if w32.win32file.DeleteFileA(full_path) != 0:
|
||||
deleted += 1
|
||||
stdlib.free(full_path)
|
||||
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0:
|
||||
break
|
||||
|
||||
w32.win32file.FindClose(handle)
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
return deleted
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import hashlib
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import win32.fileio as fileio
|
||||
|
||||
|
||||
# Utils - project utility functions (SHA1 hashing, directory cleanup)
|
||||
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
|
||||
# nibble_to_hex_char - convert a nibble to a hex character
|
||||
def nibble_to_hex_char(n: int, uppercase: int) -> char:
|
||||
"""Convert a 0-15 nibble to a hex character (uppercase=1 uses 'A'-'F')."""
|
||||
if n < 10: return char('0' + n)
|
||||
if uppercase != 0: return char('A' + (n - 10))
|
||||
return char('a' + (n - 10))
|
||||
|
||||
|
||||
# bytes_to_hex - convert a byte sequence to a hex string
|
||||
def bytes_to_hex(pool: memhub.MemBuddy*, digest: bytes, nbytes: sizet, uppercase: int, use_malloc: int) -> str:
|
||||
"""Convert the first nbytes of digest to a null-terminated hex string."""
|
||||
if digest is None or nbytes <= 0: return None
|
||||
hex_len: sizet = nbytes * 2 + 1
|
||||
hex_buf: str = None
|
||||
if use_malloc != 0: hex_buf = stdlib.malloc(hex_len)
|
||||
else: hex_buf = pool.alloc(hex_len)
|
||||
if hex_buf is None: return None
|
||||
for i in range(int(nbytes)):
|
||||
hi: int = (digest[i] >> 4) & 0xF
|
||||
lo: int = digest[i] & 0xF
|
||||
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
|
||||
hex_buf[i * 2 + 1] = nibble_to_hex_char(lo, uppercase)
|
||||
hex_buf[nbytes * 2] = '\0'
|
||||
return hex_buf
|
||||
|
||||
|
||||
# compute_sha1 - compute a string SHA1, return the first 16 hex chars
|
||||
def compute_sha1(pool: memhub.MemBuddy*, content: str) -> str:
|
||||
"""Compute the SHA1 of a string, returning its first 16 hex chars."""
|
||||
if content is None or pool is None: return None
|
||||
|
||||
# ctx is stack-allocated (alloca); no heap allocation needed
|
||||
ctx: hashlib.sha1* = hashlib.sha1()
|
||||
if ctx is None: return None
|
||||
|
||||
# Compute the digest
|
||||
ctx.update(content)
|
||||
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
|
||||
if digest is None: return None
|
||||
ctx.final(digest)
|
||||
|
||||
# Convert to a hex string (first 8 bytes = 16 hex chars)
|
||||
return bytes_to_hex(pool, digest, 8, 0, 0)
|
||||
|
||||
|
||||
# is_vp_at - check whether a path segment ends with .vp
|
||||
def is_vp_at(p: str, start: sizet, plen: sizet) -> int:
|
||||
"""Return 1 if p[start, start+plen) ends with the .vp suffix."""
|
||||
if p is None or plen <= 3: return 0
|
||||
base: sizet = start + plen - 3
|
||||
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p': return 1
|
||||
return 0
|
||||
|
||||
|
||||
# JoinPath - join two path segments as a/b
|
||||
def JoinPath(a: str, b: str) -> str:
|
||||
"""Join two path segments as "a/b"; caller frees the malloc'd result."""
|
||||
if a is None or b is None: return None
|
||||
a_len: sizet = string.strlen(a)
|
||||
b_len: sizet = string.strlen(b)
|
||||
total_len: sizet = a_len + b_len + 2
|
||||
buf: str = stdlib.malloc(total_len)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", a, b)
|
||||
return buf
|
||||
|
||||
|
||||
# read_file_to_buf - read a whole file into a null-terminated buffer
|
||||
def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
|
||||
"""Read a whole file into a null-terminated malloc buffer, or None."""
|
||||
if path is None or buf_size <= 0: return None
|
||||
sf: fileio.File* = fileio.File(path, fileio.MODE.R)
|
||||
if sf.closed: return None
|
||||
buf: bytes = stdlib.malloc(buf_size)
|
||||
if buf is None:
|
||||
sf.close()
|
||||
return None
|
||||
br: int64 = sf.read_all(buf, buf_size)
|
||||
sf.close()
|
||||
if br <= 0:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
if br < buf_size: buf[br] = 0
|
||||
return buf
|
||||
|
||||
|
||||
# PathToModuleName - convert a file path to a module name
|
||||
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||
"""Convert a file path to a dotted module name; caller frees the result."""
|
||||
if path is None: return None
|
||||
plen: sizet = string.strlen(path)
|
||||
# Skip the includes/ or includes\ prefix
|
||||
prefix_len: sizet = 9
|
||||
path_start: sizet = 0
|
||||
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
|
||||
psep_ch: char = path[8]
|
||||
if psep_ch == '/' or psep_ch == '\\':
|
||||
path_start = prefix_len
|
||||
plen = plen - prefix_len
|
||||
# Strip the .vp suffix
|
||||
if is_vp_at(path, path_start, plen) != 0: plen -= 3
|
||||
if plen == 0: return None
|
||||
# Check for a trailing /__init__ or \__init__ (package __init__.vp)
|
||||
if plen > 9:
|
||||
tail_off: sizet = path_start + plen - 9
|
||||
sep_char: char = path[tail_off]
|
||||
if (sep_char == '/' or sep_char == '\\') and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
|
||||
# Drop the separator + __init__ suffix
|
||||
plen = tail_off - path_start
|
||||
if plen == 0: return None
|
||||
# Copy and replace / with .
|
||||
result: str = stdlib.malloc(plen + 1)
|
||||
if result is None: return None
|
||||
for ri in range(int(plen)):
|
||||
ch: char = path[path_start + ri]
|
||||
if ch == '/' or ch == '\\': result[ri] = '.'
|
||||
else: result[ri] = ch
|
||||
result[plen] = '\0'
|
||||
return result
|
||||
|
||||
|
||||
# CleanDir - recursively delete all files/subdirs under dir_path (for --clean)
|
||||
def CleanDir(dir_path: str) -> int:
|
||||
"""Recursively delete files/subdirs under dir_path, returning the count."""
|
||||
if dir_path is None: return -1
|
||||
dir_len: sizet = string.strlen(dir_path)
|
||||
pattern: bytes = stdlib.malloc(dir_len + 8)
|
||||
if pattern is None: return -1
|
||||
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
|
||||
|
||||
find_data: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
if find_data is None:
|
||||
stdlib.free(pattern)
|
||||
return -1
|
||||
string.memset(find_data, 0, win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
|
||||
handle: win32.win32base.HANDLE = win32.win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32.win32base.INVALID_HANDLE_VALUE:
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
return 0
|
||||
|
||||
deleted: int = 0
|
||||
while True:
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
# Skip . and ..
|
||||
if fname[0] == '.':
|
||||
if fname[1] == '\0': fname = None
|
||||
elif fname[1] == '.' and fname[2] == '\0': fname = None
|
||||
if fname is not None:
|
||||
full_path: bytes = JoinPath(dir_path, fname)
|
||||
if full_path is not None:
|
||||
# Check whether this is a directory
|
||||
is_dir: uint32 = find_data.dwFileAttributes & win32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
if is_dir != 0:
|
||||
# Recurse into the subdirectory
|
||||
sub_deleted: int = CleanDir(full_path)
|
||||
if sub_deleted > 0: deleted += sub_deleted
|
||||
# Remove the now-empty subdirectory
|
||||
win32.win32file.RemoveDirectoryA(full_path)
|
||||
else:
|
||||
# Delete the file
|
||||
if win32.win32file.DeleteFileA(full_path) != 0: deleted += 1
|
||||
stdlib.free(full_path)
|
||||
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
|
||||
win32.win32file.FindClose(handle)
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
return deleted
|
||||
@@ -1,12 +1,6 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
|
||||
from .Config import Load_project_config, resolve_paths
|
||||
from .Utils import compute_sha1, CleanDir
|
||||
|
||||
__all__ = [
|
||||
'Load_project_config',
|
||||
'resolve_paths',
|
||||
'compute_sha1',
|
||||
'CleanDir',
|
||||
]
|
||||
__all__ = ['Load_project_config', 'resolve_paths', 'compute_sha1', 'CleanDir',]
|
||||
+1022
-1007
File diff suppressed because it is too large.
Load diff
+4018
-2108
File diff suppressed because it is too large.
Load diff
@@ -1,3 +1 @@
|
||||
# ============================================================
|
||||
# StubGen 包入口
|
||||
# ============================================================
|
||||
# StubGen
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,284 +1,124 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import llvmlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# ============================================================
|
||||
# StubMergerCollect - StubMerger 拆分(阶段 E):StubMergerCollect
|
||||
#
|
||||
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
|
||||
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
|
||||
# StubMerger._xxx 访问(main.vp 先例)。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
||||
#
|
||||
# 根据 Config.Sha1SliceLevel 将文件分散到 SHA1 前缀子目录:
|
||||
# level=0 → {temp_dir}/{sha1}.{ext}
|
||||
# level=1 → {temp_dir}/b7/{sha1}.{ext}
|
||||
# level=2 → {temp_dir}/b7/90/{sha1}.{ext}
|
||||
#
|
||||
# 自动调用 BuildPipeline.ensure_dir 创建子目录(level>0 时)。
|
||||
#
|
||||
# Args:
|
||||
# temp_dir: 基础目录
|
||||
# td_len: temp_dir 长度(避免重复 strlen)
|
||||
# sha1: SHA1 字符串
|
||||
# ext: 文件扩展名(如 "stub.ll")
|
||||
#
|
||||
# Returns:
|
||||
# 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
|
||||
# ============================================================
|
||||
def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
|
||||
"""构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
||||
|
||||
level>0 时自动调用 ensure_dir 创建子目录。
|
||||
"""
|
||||
if temp_dir is None or sha1 is None or ext is None:
|
||||
return None
|
||||
sha1_len: t.CSizeT = string.strlen(sha1)
|
||||
ext_len: t.CSizeT = string.strlen(ext)
|
||||
|
||||
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
|
||||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||||
sub_len: t.CSizeT = 0
|
||||
if subdir_buf is not None:
|
||||
sub_len = string.strlen(subdir_buf)
|
||||
|
||||
# 确保子目录存在: {temp_dir}/{subdir}
|
||||
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
|
||||
# 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。
|
||||
dir_path_len: t.CSizeT = td_len + sub_len + 2
|
||||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||||
if dir_path is not None:
|
||||
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
||||
BuildPipeline.ensure_dir(dir_path)
|
||||
stdlib.free(dir_path)
|
||||
|
||||
# 构建完整路径
|
||||
path: str = None
|
||||
path_len: t.CSizeT = 0
|
||||
if sub_len > 0:
|
||||
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None:
|
||||
viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
||||
else:
|
||||
path_len = td_len + sha1_len + ext_len + 3
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None:
|
||||
viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
||||
|
||||
return path
|
||||
# ============================================================
|
||||
# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1
|
||||
#
|
||||
# 根据 level 递归扫描子目录:
|
||||
# level=0 → 扫描 {base_dir}/*.stub.ll,从文件名提取 SHA1
|
||||
# level>0 → 扫描 {base_dir}/* 的子目录,对每个子目录递归调用
|
||||
#
|
||||
# Args:
|
||||
# base_dir: 当前扫描目录
|
||||
# bd_len: base_dir 长度
|
||||
# level: 剩余递归层数
|
||||
# out_arr: 输出数组(每个 SHA1 17 字节 = 16字符 + null)
|
||||
# out_count: 当前已收集数量
|
||||
# max_count: 最大数量
|
||||
#
|
||||
# Returns:
|
||||
# 收集后的总数量
|
||||
# ============================================================
|
||||
def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
|
||||
out_arr: str, out_count: int, max_count: int) -> int:
|
||||
"""递归扫描子目录,收集 .stub.ll 文件的 SHA1"""
|
||||
if base_dir is None or out_arr is None:
|
||||
return out_count
|
||||
if out_count >= max_count:
|
||||
return out_count
|
||||
|
||||
find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
if find_data is None:
|
||||
return out_count
|
||||
string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
|
||||
if level <= 0:
|
||||
# 扫描 {base_dir}/*.stub.ll
|
||||
pattern: bytes = stdlib.malloc(bd_len + 16)
|
||||
if pattern is None:
|
||||
stdlib.free(find_data)
|
||||
return out_count
|
||||
viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir)
|
||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||
stdlib.free(pattern)
|
||||
if handle != win32base.INVALID_HANDLE_VALUE:
|
||||
while True:
|
||||
if out_count >= max_count:
|
||||
break
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
if fname[0] != '\0':
|
||||
# 从文件名提取 SHA1(前 16 字符)
|
||||
string.strncpy(out_arr + out_count * 17, fname, 16)
|
||||
out_arr[out_count * 17 + 16] = '\0'
|
||||
out_count += 1
|
||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
||||
break
|
||||
win32file.FindClose(handle)
|
||||
else:
|
||||
# 扫描 {base_dir}/* 的子目录
|
||||
pattern = stdlib.malloc(bd_len + 4)
|
||||
if pattern is None:
|
||||
stdlib.free(find_data)
|
||||
return out_count
|
||||
viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir)
|
||||
handle = win32file.FindFirstFileA(pattern, find_data)
|
||||
stdlib.free(pattern)
|
||||
if handle != win32base.INVALID_HANDLE_VALUE:
|
||||
while True:
|
||||
# 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16)
|
||||
attrs: t.CUInt32T = find_data.dwFileAttributes
|
||||
if (attrs & 16) != 0:
|
||||
fname = find_data.cFileName
|
||||
if fname is not None:
|
||||
# 跳过 "." 和 ".."
|
||||
is_dot: int = 0
|
||||
if fname[0] == '.':
|
||||
if fname[1] == '\0':
|
||||
is_dot = 1
|
||||
elif fname[1] == '.' and fname[2] == '\0':
|
||||
is_dot = 1
|
||||
if is_dot == 0:
|
||||
# 构建子目录路径并递归扫描
|
||||
sub_dir: str = Utils.JoinPath(base_dir, fname)
|
||||
if sub_dir is not None:
|
||||
sub_dir_len: t.CSizeT = string.strlen(sub_dir)
|
||||
out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count)
|
||||
stdlib.free(sub_dir)
|
||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
||||
break
|
||||
win32file.FindClose(handle)
|
||||
|
||||
stdlib.free(find_data)
|
||||
return out_count
|
||||
# ============================================================
|
||||
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
|
||||
#
|
||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||||
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
|
||||
# ============================================================
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr,
|
||||
temp_dir: str,
|
||||
sha1_set: str) -> int:
|
||||
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
|
||||
|
||||
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
|
||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||||
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null)
|
||||
返回找到的 includes SHA1 数量,-1 表示错误"""
|
||||
if sha1_set is None:
|
||||
return -1
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
return -1
|
||||
count: int = 0
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
if count >= StubMerger.MAX_INCLUDES_SHA1:
|
||||
break
|
||||
idx: t.CSizeT = t.CSizeT(i) * 17
|
||||
string.strcpy(sha1_set + t.CSizeT(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
# ============================================================
|
||||
# _is_in_sha1_set - 检查 SHA1 是否在集合中
|
||||
# ============================================================
|
||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
"""检查 sha1 是否在集合中"""
|
||||
if sha1 is None or sha1_set is None:
|
||||
return 0
|
||||
for i in range(set_count):
|
||||
if string.strcmp(sha1_set + i * 17, sha1) == 0:
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出)
|
||||
#
|
||||
# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。
|
||||
# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
# temp_dir: 临时目录(_sha1_map.txt 写入位置)
|
||||
# scan_result: IncludesScanner 扫描结果
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功,非 0 失败
|
||||
# ============================================================
|
||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
|
||||
scan_result: IncludesScanner.ScanResult | t.CPtr,
|
||||
filter_set: str,
|
||||
filter_count: int) -> int:
|
||||
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
|
||||
|
||||
若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。
|
||||
"""
|
||||
if temp_dir is None or scan_result is None:
|
||||
return 1
|
||||
|
||||
# 构造路径 temp_dir/_sha1_map.txt
|
||||
dir_len: t.CSizeT = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
if map_path is None:
|
||||
return 1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# 打开文件写入(CREATE_ALWAYS)
|
||||
f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.W)
|
||||
if f.closed:
|
||||
return 1
|
||||
|
||||
# 写入每个 include 条目: {sha1}:includes/{rel_path}\n
|
||||
entry_size_w: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
line_buf: bytes = stdlib.malloc(512)
|
||||
if line_buf is None:
|
||||
f.close()
|
||||
return 1
|
||||
|
||||
written_count: int = 0
|
||||
for i in range(scan_result.Count):
|
||||
ea_w: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_w
|
||||
ent_w: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_w, t.CPtr))
|
||||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None:
|
||||
continue
|
||||
# 若提供过滤集合,只写入集合中的条目
|
||||
if filter_set is not None and filter_count > 0:
|
||||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0:
|
||||
continue
|
||||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||||
line_len_w: t.CSizeT = string.strlen(line_buf)
|
||||
f.write(line_buf, line_len_w)
|
||||
written_count += 1
|
||||
|
||||
f.close()
|
||||
return 0
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Gen.Translator as HandlesTranslator
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# StubMergerCollect - slice-path construction and stub-file SHA1 collection.
|
||||
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||
|
||||
# _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0).
|
||||
# Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure.
|
||||
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
"""Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir)."""
|
||||
if temp_dir is None or sha1 is None or ext is None: return None
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
ext_len: sizet = string.strlen(ext)
|
||||
|
||||
# Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free)
|
||||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||||
sub_len: sizet = 0
|
||||
if subdir_buf is not None:
|
||||
sub_len = string.strlen(subdir_buf)
|
||||
|
||||
# Ensure subdir {temp_dir}/{subdir} exists.
|
||||
# Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0.
|
||||
dir_path_len: sizet = td_len + sub_len + 2
|
||||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||||
if dir_path is not None:
|
||||
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
||||
BuildPipeline.ensure_dir(dir_path)
|
||||
stdlib.free(dir_path)
|
||||
|
||||
# Build the full path
|
||||
path: str = None
|
||||
path_len: sizet = 0
|
||||
if sub_len > 0:
|
||||
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None: viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
||||
else:
|
||||
path_len = td_len + sha1_len + ext_len + 3
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
||||
|
||||
return path
|
||||
|
||||
# _load_includes_sha1_set - collect includes SHA1 from the global in-memory store.
|
||||
# Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking ViperC's own lib/* stubs.
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
|
||||
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
|
||||
if sha1_set is None: return -1
|
||||
if StubMerger._sha1_store_arr is None: return -1
|
||||
count: int = 0
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
if count >= StubMerger.MAX_INCLUDES_SHA1: break
|
||||
idx: sizet = sizet(i) * 17
|
||||
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
# _is_in_sha1_set - check whether a SHA1 is in the set.
|
||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
"""Whether sha1 is present in the set."""
|
||||
if sha1 is None or sha1_set is None: return 0
|
||||
for i in range(int(set_count)):
|
||||
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
|
||||
return 0
|
||||
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
|
||||
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
|
||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int:
|
||||
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
|
||||
if temp_dir is None or scan_result is None: return 1
|
||||
|
||||
# Build the path temp_dir/_sha1_map.txt
|
||||
dir_len: sizet = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
if map_path is None: return 1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# Open the file for writing (CREATE_ALWAYS)
|
||||
f: fileio.File* = fileio.File(map_path, fileio.MODE.W)
|
||||
if f.closed: return 1
|
||||
|
||||
# Write each include entry: {sha1}:includes/{rel_path}\n
|
||||
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
line_buf: bytes = stdlib.malloc(512)
|
||||
if line_buf is None:
|
||||
f.close()
|
||||
return 1
|
||||
|
||||
written_count: int = 0
|
||||
for i in range(int(scan_result.Count)):
|
||||
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
|
||||
ent_w: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(ea_w)
|
||||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue
|
||||
# When a filter set is provided, write only its entries
|
||||
if filter_set is not None and filter_count > 0:
|
||||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue
|
||||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||||
line_len_w: sizet = string.strlen(line_buf)
|
||||
f.write(line_buf, line_len_w)
|
||||
written_count += 1
|
||||
|
||||
f.close()
|
||||
return 0
|
||||
@@ -1,19 +1,18 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import llvmlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Gen.Translator as HandlesTranslator
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.Projectrans.Utils as Utils
|
||||
@@ -26,385 +25,211 @@ import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StubMergerIR - StubMerger 拆分(阶段 E):StubMergerIR
|
||||
# ============================================================
|
||||
# StubMergerIR - module structured sidecar ({sha1}.meta) write/read, submodule checks and module-name/SHA1 mapping helpers
|
||||
|
||||
# ============================================================
|
||||
# IsDeclOnlyModule - 判断模块是否为纯声明文件
|
||||
#
|
||||
# 读取 {temp_dir}/{sha1}.text.ll,扫描是否含 "define " 行:
|
||||
# 返回 1 = 纯声明(无函数体,如 stdio.vp 全 pass),无需 .obj
|
||||
# 返回 0 = 实现文件(含 define 函数体)
|
||||
# 返回 -1 = text.ll 不存在/无法判断
|
||||
#
|
||||
# 与 Phase B+(Phase2.vp 编译缺失 includes 时)的内联判断逻辑一致,
|
||||
# 供 Phase C 链接时区分"声明文件无需 .obj"与"实现文件缺失"。
|
||||
# ============================================================
|
||||
def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
|
||||
"""判断模块是否为纯声明文件(text.ll 无 define 行)"""
|
||||
if temp_dir is None or sha1 is None:
|
||||
return -1
|
||||
tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll")
|
||||
if tpath is None:
|
||||
return -1
|
||||
tf_d: fileio.File = fileio.File(tpath, fileio.MODE.R)
|
||||
stdlib.free(tpath)
|
||||
if tf_d.closed:
|
||||
return -1
|
||||
tbuf_d: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||||
tbr_d: t.CInt64T = 0
|
||||
if tbuf_d is not None:
|
||||
tbr_d = tf_d.read_all(tbuf_d, StubMerger.STUB_READ_BUF_SIZE)
|
||||
tf_d.close()
|
||||
if tbuf_d is None:
|
||||
return -1
|
||||
if tbr_d <= 0:
|
||||
# 空 text.ll:无函数体,纯声明
|
||||
stdlib.free(tbuf_d)
|
||||
# WriteModuleMeta - write {sha1}.meta (structured sidecar: funcs + struct layout + vtables).
|
||||
# Lines: FUNC <mangled> DEF|DECL, STRUCT <sha1>.<name> FIELDS <n> PACKED <0|1>|OPAQUE, VTABLE <sha1>.<name> SLOTS <n>.
|
||||
def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
"""Write the module's func defines, struct layouts and vtables to {sha1}.meta."""
|
||||
if mod is None or temp_dir is None or sha1 is None:
|
||||
VLogger.error("meta write: null argument", "Meta")
|
||||
return 1
|
||||
if tbr_d < StubMerger.STUB_READ_BUF_SIZE:
|
||||
tbuf_d[tbr_d] = '\0'
|
||||
else:
|
||||
tbuf_d[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||||
# 逐行扫描:找 "define " 行即为实现文件
|
||||
tpos_d: t.CSizeT = 0
|
||||
while tpos_d < tbr_d:
|
||||
tls_d: t.CSizeT = tpos_d
|
||||
while tpos_d < tbr_d:
|
||||
if tbuf_d[tpos_d] == '\n':
|
||||
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||||
if mpath is None:
|
||||
VLogger.error("meta write: path construction failed", "Meta")
|
||||
return 1
|
||||
mf: fileio.File = fileio.File(mpath, fileio.MODE.W)
|
||||
if mf.closed:
|
||||
VLogger.error(f"meta write: cannot open {mpath}", "Meta")
|
||||
return 1
|
||||
cur: mlirlite.Function* = mod.FuncHead
|
||||
steps: sizet = 0
|
||||
while cur is not None:
|
||||
steps += 1
|
||||
if steps > 100000:
|
||||
VLogger.error("meta write: function list has a cycle or exceeded the limit", "Meta")
|
||||
break
|
||||
nm: str = mlirlite.function_get_name(cur)
|
||||
if nm is not None:
|
||||
nlen: sizet = string.strlen(nm)
|
||||
line: bytes = stdlib.malloc(nlen + 16)
|
||||
if line is None:
|
||||
VLogger.error("meta write: line buffer allocation failed", "Meta")
|
||||
break
|
||||
tpos_d += 1
|
||||
tll_d: t.CSizeT = tpos_d - tls_d
|
||||
if tpos_d < tbr_d:
|
||||
tpos_d += 1
|
||||
if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0:
|
||||
stdlib.free(tbuf_d)
|
||||
return 0
|
||||
stdlib.free(tbuf_d)
|
||||
mark: str = "DECL"
|
||||
if mlirlite.function_is_declared(cur) == 0: mark = "DEF"
|
||||
viperlib.snprintf(line, nlen + 16, "FUNC %s %s\n", nm, mark)
|
||||
mf.write(line, string.strlen(line))
|
||||
stdlib.free(line)
|
||||
cur = mlirlite.function_get_next(cur)
|
||||
# 2) Struct layout: STRUCT <name> FIELDS <n> PACKED <0|1> (opaque -> STRUCT <name> OPAQUE)
|
||||
nt: mlirlite.NamedTypeNode* = mod.NamedTypeHead
|
||||
nt_steps: sizet = 0
|
||||
while nt is not None:
|
||||
nt_steps += 1
|
||||
if nt_steps > 100000:
|
||||
VLogger.error("meta write: named-type list has a cycle or exceeded the limit", "Meta")
|
||||
break
|
||||
tn: str = mlirlite.get_struct_name(nt.Ty)
|
||||
if tn is not None and tn[0] != '\0':
|
||||
tl: sizet = string.strlen(tn)
|
||||
lb: bytes = stdlib.malloc(tl + 48)
|
||||
if lb is None:
|
||||
VLogger.error("meta write: line buffer allocation failed", "Meta")
|
||||
break
|
||||
fc: int = mlirlite.get_struct_fcount(nt.Ty)
|
||||
if fc < 0: viperlib.snprintf(lb, tl + 48, "STRUCT %s OPAQUE\n", tn)
|
||||
else: viperlib.snprintf(lb, tl + 48, "STRUCT %s FIELDS %d PACKED %d\n", tn, fc, nt.IsPacked)
|
||||
mf.write(lb, string.strlen(lb))
|
||||
stdlib.free(lb)
|
||||
nt = nt.Next
|
||||
# 3) Vtable slots: VTABLE <class> SLOTS <n> (global <class>.vtable; Ty anonymous struct fcount = slots)
|
||||
gv: mlirlite.GlobalVariable* = mod.GlobalHead
|
||||
gv_steps: sizet = 0
|
||||
while gv is not None:
|
||||
gv_steps += 1
|
||||
if gv_steps > 100000:
|
||||
VLogger.error("meta write: global-variable list has a cycle or exceeded the limit", "Meta")
|
||||
break
|
||||
gn: str = gv.Name
|
||||
if gn is not None:
|
||||
gnl: sizet = string.strlen(gn)
|
||||
if gnl > 7 and string.strcmp(gn + gnl - 7, ".vtable") == 0:
|
||||
base_len: sizet = gnl - 7
|
||||
base: bytes = stdlib.malloc(base_len + 1)
|
||||
if base is not None:
|
||||
string.strncpy(base, gn, base_len)
|
||||
base[base_len] = '\0'
|
||||
ar: int = mlirlite.get_struct_fcount(gv.Ty)
|
||||
if ar < 0: ar = 0
|
||||
lb2: bytes = stdlib.malloc(base_len + 40)
|
||||
if lb2 is None: VLogger.error("meta write: line buffer allocation failed", "Meta")
|
||||
else:
|
||||
viperlib.snprintf(lb2, base_len + 40, "VTABLE %s SLOTS %d\n", base, ar)
|
||||
mf.write(lb2, string.strlen(lb2))
|
||||
stdlib.free(lb2)
|
||||
stdlib.free(base)
|
||||
gv = gv.Next
|
||||
mf.close()
|
||||
stdlib.free(mpath)
|
||||
return 0
|
||||
|
||||
|
||||
# _MetaHasDef - read {sha1}.meta to check for a function definition ("FUNC ... DEF" line).
|
||||
# Returns: 1=has DEF (impl file); 0=readable but no DEF (decl-only); -1=missing/unreadable.
|
||||
def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
"""Read {sha1}.meta: 1 if it has a FUNC ... DEF line, 0 if none, -1 if unreadable."""
|
||||
if temp_dir is None or sha1 is None: return -1
|
||||
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||||
if mpath is None: return -1
|
||||
mf: fileio.File = fileio.File(mpath, fileio.MODE.R)
|
||||
stdlib.free(mpath)
|
||||
if mf.closed: return -1
|
||||
mbuf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||||
mbr: int64 = 0
|
||||
if mbuf is not None: mbr = mf.read_all(mbuf, StubMerger.STUB_READ_BUF_SIZE)
|
||||
mf.close()
|
||||
if mbuf is None: return -1
|
||||
if mbr <= 0:
|
||||
# Empty meta: no DEF, decl-only
|
||||
stdlib.free(mbuf)
|
||||
return 0
|
||||
if mbr < StubMerger.STUB_READ_BUF_SIZE: mbuf[mbr] = '\0'
|
||||
# Scan line by line: a trailing " DEF" marks an impl file
|
||||
mpos: sizet = 0
|
||||
while mpos < mbr:
|
||||
mls: sizet = mpos
|
||||
while mpos < mbr:
|
||||
if mbuf[mpos] == '\n': break
|
||||
mpos += 1
|
||||
mll: sizet = mpos - mls
|
||||
if mpos < mbr: mpos += 1
|
||||
if mll > 0 and mbuf[mls + mll - 1] == '\r': mll -= 1
|
||||
if mll >= 10 and string.strncmp(mbuf + mls, "FUNC ", 5) == 0:
|
||||
if mbuf[mls + mll - 4] == ' ' and string.strncmp(mbuf + mls + mll - 3, "DEF", 3) == 0:
|
||||
stdlib.free(mbuf)
|
||||
return 1
|
||||
stdlib.free(mbuf)
|
||||
return 0
|
||||
|
||||
|
||||
# IsDeclOnlyModule - whether a module is decl-only (reads the structured definition list in {sha1}.meta).
|
||||
# 1=decl-only (no FUNC ... DEF, e.g. all-pass modules, no .obj needed); 0=impl; -1=unknown/missing.
|
||||
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
"""Whether the module is decl-only (its meta has no FUNC ... DEF line)."""
|
||||
h: int = _MetaHasDef(temp_dir, td_len, sha1)
|
||||
if h < 0: return -1
|
||||
if h == 1: return 0
|
||||
return 1
|
||||
# ============================================================
|
||||
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
|
||||
#
|
||||
# 逐字节strncmp搜索 func_sig(如 @"sha1.func"(/ @printf( ),每次
|
||||
# 找到匹配后检查行首是否以 "declare " 或 "define " 开头,跳过 call 语句。
|
||||
# 避免 strstr 子串匹配误匹配 call ... @"func"(...) 中的 @"func"( 子串。
|
||||
#
|
||||
# Args:
|
||||
# out_buf: 已有的 IR 文本缓冲区
|
||||
# func_sig: 函数签名片段,如 @"sha1.func"(/ @printf(
|
||||
#
|
||||
# Returns:
|
||||
# 1=已存在 declare/define, 0=不存在
|
||||
# ============================================================
|
||||
def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
|
||||
"""检查 out_buf 中是否已有函数的 declare 或 define 行"""
|
||||
if out_buf is None or func_sig is None:
|
||||
return 0
|
||||
sig_len: t.CSizeT = string.strlen(func_sig)
|
||||
out_len: t.CSizeT = string.strlen(out_buf)
|
||||
if sig_len == 0 or out_len == 0 or sig_len > out_len:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
for pos in range(0, out_len - sig_len + 1):
|
||||
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
|
||||
ls: t.CSizeT = pos
|
||||
while ls > 0 and out_buf[ls - 1] != '\n':
|
||||
ls -= 1
|
||||
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
|
||||
return 1
|
||||
if string.strncmp(out_buf + ls, "define ", 7) == 0:
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉
|
||||
#
|
||||
# 用于解决 declare/define 冲突:当本地 text.ll 包含 define 而依赖
|
||||
# text.ll 已生成了同函数的 declare 时,将 declare 行首字符改为 ';'
|
||||
# 使其成为注释,然后追加 define 行。
|
||||
# ============================================================
|
||||
def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int:
|
||||
"""在 out_buf 中搜索 declare 行(包含 func_sig),将其注释掉"""
|
||||
if out_buf is None or func_sig is None:
|
||||
return 0
|
||||
sig_len: t.CSizeT = string.strlen(func_sig)
|
||||
out_len: t.CSizeT = string.strlen(out_buf)
|
||||
if sig_len == 0 or out_len == 0 or sig_len > out_len:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
while pos + sig_len <= out_len:
|
||||
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
|
||||
ls: t.CSizeT = pos
|
||||
while ls > 0 and out_buf[ls - 1] != '\n':
|
||||
ls -= 1
|
||||
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
|
||||
out_buf[ls] = ';'
|
||||
return 1
|
||||
pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _CommentOutGlobalInBuf - 将 out_buf 中匹配全局变量名的行注释掉
|
||||
#
|
||||
# 解决 stub/text 全局变量重复定义问题:stub 中可能是 external 声明
|
||||
# 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。
|
||||
# ============================================================
|
||||
def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int:
|
||||
"""在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';'
|
||||
|
||||
注释掉**所有**匹配行(而非仅第一个):跨模块同名裸全局(如 main.vp 与
|
||||
argparse.vp 都声明 StubMerger._mbuddy)时,本地 stub external + 依赖 stub external +
|
||||
依赖 text 定义会同时存在。只注释第一个会导致依赖 text 的定义残留 →
|
||||
合成 IR 重复定义 → llc "redefinition of global '@StubMerger._mbuddy'"(ArgparseTest
|
||||
回归根因)。"以 text 中定义为准"的语义:后追加者注释掉全部旧行,最终
|
||||
只保留最后一个定义。
|
||||
"""
|
||||
if out_buf is None or global_name is None:
|
||||
return 0
|
||||
name_len: t.CSizeT = string.strlen(global_name)
|
||||
out_len: t.CSizeT = string.strlen(out_buf)
|
||||
if name_len == 0 or out_len == 0 or name_len > out_len:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
commented: int = 0
|
||||
while pos + name_len <= out_len:
|
||||
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
|
||||
# 确认是行首(前一个是换行或 pos==0)
|
||||
if pos == 0 or out_buf[pos - 1] == '\n':
|
||||
out_buf[pos] = ';'
|
||||
commented = 1
|
||||
pos += 1
|
||||
return commented
|
||||
def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int:
|
||||
"""检测 out_buf 中是否已有同名全局变量行(行首匹配,未注释)
|
||||
|
||||
用于跳过重复追加依赖全局强定义:stub 注入已提供 external 声明,
|
||||
依赖 text.ll 的强定义只需在定义模块存在一份,复制进每个依赖方
|
||||
会导致 ld.lld duplicate symbol(如 _op_head、MemBuddy.vtable)。
|
||||
"""
|
||||
if out_buf is None or global_name is None:
|
||||
return 0
|
||||
name_len: t.CSizeT = string.strlen(global_name)
|
||||
out_len: t.CSizeT = string.strlen(out_buf)
|
||||
if name_len == 0 or out_len == 0 or name_len > out_len:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
while pos + name_len <= out_len:
|
||||
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
|
||||
# 确认是行首(前一个是换行或 pos==0)
|
||||
if pos == 0 or out_buf[pos - 1] == '\n':
|
||||
return 1
|
||||
pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉
|
||||
#
|
||||
# 解决跨模块类型定义冲突:本地 stub 可能包含自动生成的 opaque 声明
|
||||
#(如 %"sha1.Widget" = type opaque),而依赖 stub 包含完整定义
|
||||
#(如 %"sha1.Widget" = type { i8*, i32 })。去重时需要将 opaque 声明
|
||||
# 注释掉,让完整定义被追加,否则 LLC 报错 "Cannot allocate unsized type"。
|
||||
# ============================================================
|
||||
def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
|
||||
"""在 out_buf 中搜索 type_name_prefix 对应的 opaque 声明行,将行首改为 ';'
|
||||
|
||||
type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=' 和后续内容)
|
||||
返回 1=已注释, 0=未找到
|
||||
"""
|
||||
if out_buf is None or type_name_prefix is None:
|
||||
return 0
|
||||
prefix_len: t.CSizeT = string.strlen(type_name_prefix)
|
||||
out_len: t.CSizeT = string.strlen(out_buf)
|
||||
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
while pos + prefix_len <= out_len:
|
||||
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
|
||||
# 确认是行首(前一个是换行或 pos==0)
|
||||
if pos == 0 or out_buf[pos - 1] == '\n':
|
||||
# 检查后面是否是 " = type opaque"
|
||||
p: t.CSizeT = pos + prefix_len
|
||||
# 跳过空格
|
||||
while p < out_len and out_buf[p] == ' ':
|
||||
p += 1
|
||||
if p < out_len and out_buf[p] == '=':
|
||||
p += 1
|
||||
while p < out_len and out_buf[p] == ' ':
|
||||
p += 1
|
||||
if p + 11 <= out_len:
|
||||
if string.strncmp(out_buf + p, "type opaque", 11) == 0:
|
||||
# 找到 opaque 声明,将行首改为 ';'
|
||||
out_buf[pos] = ';'
|
||||
return 1
|
||||
pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
|
||||
#
|
||||
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp
|
||||
# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。
|
||||
# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。
|
||||
#
|
||||
# 例如:
|
||||
# deps_buf = "ast stdio string"
|
||||
# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
|
||||
# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
|
||||
# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0
|
||||
# ============================================================
|
||||
# _is_submodule_of_imported - check whether a module name is a submodule of an imported package.
|
||||
# deps_buf holds only direct imports; a name with '.' is matched by its top-level package (e.g. "ast.lexer" -> 1).
|
||||
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
||||
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
|
||||
if deps_buf is None or mod_name is None:
|
||||
return 0
|
||||
# 查找第一个 '.',提取顶层包名
|
||||
"""Whether mod_name is a submodule of a package imported in deps_buf."""
|
||||
if deps_buf is None or mod_name is None: return 0
|
||||
# Find the first '.' and extract the top-level package name
|
||||
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
||||
if dot_pos is None:
|
||||
return 0
|
||||
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name))
|
||||
if pkg_len == 0 or pkg_len >= 256:
|
||||
return 0
|
||||
# 构造顶层包名(截断到 '.' 之前)
|
||||
# 直接在 mod_name 上临时截断检查,避免分配
|
||||
saved_ch: t.CChar = mod_name[pkg_len]
|
||||
if dot_pos is None: return 0
|
||||
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
||||
if pkg_len == 0 or pkg_len >= 256: return 0
|
||||
# Build the top-level package name (truncated before '.') by temporary in-place
|
||||
# truncation to avoid an allocation
|
||||
saved_ch: char = mod_name[pkg_len]
|
||||
mod_name[pkg_len] = '\0'
|
||||
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
||||
mod_name[pkg_len] = saved_ch
|
||||
return result
|
||||
# ============================================================
|
||||
# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque)
|
||||
#
|
||||
# type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=')
|
||||
# 完整定义: %"name" = type { ... } 或 %"name" = type < { ... } >
|
||||
# 返回 1=已有完整定义, 0=无完整定义
|
||||
# ============================================================
|
||||
def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
|
||||
"""检查 out_buf 中是否已有 type_name_prefix 的完整定义"""
|
||||
if out_buf is None or type_name_prefix is None:
|
||||
return 0
|
||||
prefix_len: t.CSizeT = string.strlen(type_name_prefix)
|
||||
out_len: t.CSizeT = string.strlen(out_buf)
|
||||
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
|
||||
return 0
|
||||
pos: t.CSizeT = 0
|
||||
while pos + prefix_len <= out_len:
|
||||
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
|
||||
# 确认是行首(前一个是换行或 pos==0),且未被注释(行首不是 ';')
|
||||
is_line_start: int = 0
|
||||
if pos == 0:
|
||||
is_line_start = 1
|
||||
elif out_buf[pos - 1] == '\n':
|
||||
is_line_start = 1
|
||||
if is_line_start == 1:
|
||||
# 检查后面是否是 " = type {" 或 " = type <"
|
||||
p: t.CSizeT = pos + prefix_len
|
||||
while p < out_len and out_buf[p] == ' ':
|
||||
p += 1
|
||||
if p < out_len and out_buf[p] == '=':
|
||||
p += 1
|
||||
while p < out_len and out_buf[p] == ' ':
|
||||
p += 1
|
||||
if p + 5 < out_len:
|
||||
if string.strncmp(out_buf + p, "type ", 5) == 0:
|
||||
p += 5
|
||||
if p < out_len:
|
||||
if out_buf[p] == '{' or out_buf[p] == '<':
|
||||
return 1
|
||||
pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
|
||||
#
|
||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||||
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
|
||||
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
|
||||
# ============================================================
|
||||
def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
|
||||
sha1_list: str,
|
||||
mod_list: str) -> int:
|
||||
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
|
||||
|
||||
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
|
||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||||
"""
|
||||
if sha1_list is None or mod_list is None:
|
||||
return 0
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
||||
return 0
|
||||
# _BuildIncludesSha1Map - build the sha1 -> module_name map from the global in-memory store.
|
||||
# Source is the store filled by PopulateSha1MapStore (no _sha1_map.txt read); returns the entry count.
|
||||
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list: str) -> int:
|
||||
"""Copy the sha1 to module_name map from the global store into caller arrays."""
|
||||
if sha1_list is None or mod_list is None: return 0
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
||||
count: int = 0
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
if count >= StubMerger.MAX_INCLUDES:
|
||||
break
|
||||
idx: t.CSizeT = t.CSizeT(i) * 17
|
||||
idx2: t.CSizeT = t.CSizeT(i) * 64
|
||||
string.strcpy(sha1_list + t.CSizeT(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
string.strcpy(mod_list + t.CSizeT(count) * 64, StubMerger._sha1_store_mod + idx2)
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
if count >= StubMerger.MAX_INCLUDES: break
|
||||
idx: sizet = sizet(i) * 17
|
||||
idx2: sizet = sizet(i) * 64
|
||||
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
|
||||
count += 1
|
||||
return count
|
||||
# ============================================================
|
||||
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配)
|
||||
#
|
||||
# 在 sha1_arr/mod_arr 映射中查找模块名对应的 SHA1。
|
||||
# 用于依赖图分析:模块名 → SHA1。
|
||||
#
|
||||
# Args:
|
||||
# sha1_arr: SHA1 数组(每个 17 字节)
|
||||
# mod_arr: 模块名数组(每个 64 字节)
|
||||
# count: 映射条目数
|
||||
# mod_name: 要查找的模块名
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
|
||||
# ============================================================
|
||||
def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
|
||||
count: int, mod_name: str) -> str:
|
||||
"""从模块名查找 SHA1(精确匹配 + 包名回退)
|
||||
|
||||
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
||||
支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
||||
"""
|
||||
if sha1_arr is None or mod_arr is None or mod_name is None:
|
||||
return None
|
||||
if count <= 0:
|
||||
return None
|
||||
# 第一遍:精确匹配
|
||||
for i in range(count):
|
||||
idx: t.CSizeT = t.CSizeT(i) * 64
|
||||
if string.strcmp(mod_arr + idx, mod_name) == 0:
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
# _FindSha1ByModName - look up a SHA1 by module name (exact match) for dependency graph analysis.
|
||||
# sha1_arr holds 17-byte entries, mod_arr 64-byte names; returns a pointer into sha1_arr, or None.
|
||||
def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -> str:
|
||||
"""Look up a SHA1 by module name, falling back to the .__init__ package form."""
|
||||
if sha1_arr is None or mod_arr is None or mod_name is None: return None
|
||||
if count <= 0: return None
|
||||
# Pass 1: exact match
|
||||
for i in range(int(count)):
|
||||
idx: sizet = sizet(i) * 64
|
||||
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs
|
||||
# (hot spot: this function strcmp-scans the whole table per lookup).
|
||||
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
|
||||
sidx: sizet = sizet(i) * 17
|
||||
return sha1_arr + sidx
|
||||
# 第二遍:包名回退("ast" → "ast.__init__")
|
||||
nm_len: t.CSizeT = string.strlen(mod_name)
|
||||
# Pass 2: package fallback ("ast" -> "ast.__init__")
|
||||
nm_len: sizet = string.strlen(mod_name)
|
||||
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
||||
if init_buf is not None:
|
||||
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
||||
for i in range(count):
|
||||
idx2: t.CSizeT = t.CSizeT(i) * 64
|
||||
if string.strcmp(mod_arr + idx2, init_buf) == 0:
|
||||
sidx2: t.CSizeT = t.CSizeT(i) * 17
|
||||
for i in range(int(count)):
|
||||
idx2: sizet = sizet(i) * 64
|
||||
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
|
||||
sidx2: sizet = sizet(i) * 17
|
||||
stdlib.free(init_buf)
|
||||
return sha1_arr + sidx2
|
||||
stdlib.free(init_buf)
|
||||
return None
|
||||
# ============================================================
|
||||
# _AddSha1ToSet - 将 SHA1 加入集合(去重)
|
||||
#
|
||||
# 检查 SHA1 是否已在集合中,若不在则加入。返回新的 count。
|
||||
#
|
||||
# Args:
|
||||
# set_buf: SHA1 集合缓冲区(每个 17 字节)
|
||||
# count: 当前集合中的 SHA1 数量
|
||||
# sha1: 要加入的 SHA1
|
||||
#
|
||||
# Returns:
|
||||
# 新的 count(如果已存在则不变)
|
||||
# ============================================================
|
||||
# _AddSha1ToSet - add a SHA1 to a set (deduped; count unchanged if already present).
|
||||
# set_buf holds 17-byte entries; returns the new count.
|
||||
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
||||
"""将 SHA1 加入集合,返回新 count"""
|
||||
if set_buf is None or sha1 is None:
|
||||
return count
|
||||
for i in range(count):
|
||||
idx: t.CSizeT = t.CSizeT(i) * 17
|
||||
if string.strcmp(set_buf + idx, sha1) == 0:
|
||||
return count
|
||||
"""Add a SHA1 to the set (deduped) and return the new count."""
|
||||
if set_buf is None or sha1 is None: return count
|
||||
for i in range(int(count)):
|
||||
idx: sizet = sizet(i) * 17
|
||||
if string.strcmp(set_buf + idx, sha1) == 0: return count
|
||||
if count < StubMerger.MAX_INCLUDES_SHA1:
|
||||
idx: t.CSizeT = t.CSizeT(count) * 17
|
||||
idx: sizet = sizet(count) * 17
|
||||
string.strcpy(set_buf + idx, sha1)
|
||||
return count + 1
|
||||
return count
|
||||
@@ -1,18 +1,17 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import llvmlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Gen.Translator as HandlesTranslator
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.Projectrans.Utils as Utils
|
||||
@@ -26,63 +25,137 @@ import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StubMergerStore - StubMerger 拆分(阶段 E):StubMergerStore
|
||||
# ============================================================
|
||||
# StubMergerStore - global SHA1 map store (sha1 -> module name / rel path) read/write
|
||||
|
||||
# Import-context accounting, single source of truth: AST import statements map to
|
||||
# imported_modules / from_imports; both translate-time and vpi-injection use these helpers.
|
||||
|
||||
class ImportCtx:
|
||||
"""Import context container: two space-separated lists (ImportedModules / FromImports)"""
|
||||
ImportedModules: str
|
||||
FromImports: str
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器
|
||||
#
|
||||
# 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
|
||||
# 模块名通过 _PathToModuleName(includes/rel_path) 计算。
|
||||
# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
||||
#
|
||||
# Args:
|
||||
# scan_result: IncludesScanner 扫描结果
|
||||
#
|
||||
# Returns:
|
||||
# 填充的条目数,-1 表示失败
|
||||
# ============================================================
|
||||
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int:
|
||||
"""从扫描结果填充全局 SHA1 映射存储器
|
||||
# AppendImportedModule - append module name to space-separated list (pure bookkeeping)
|
||||
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
||||
"""Append a module name to a space-separated list (pool-allocated); returns the new list"""
|
||||
if name is None: return cur
|
||||
if cur is None:
|
||||
nlen: sizet = string.strlen(name)
|
||||
buf: str = pool.alloc(nlen + 1)
|
||||
if buf is not None: string.strcpy(buf, name)
|
||||
return buf
|
||||
old_len: sizet = string.strlen(cur)
|
||||
name_len: sizet = string.strlen(name)
|
||||
buf2: str = pool.alloc(old_len + 1 + name_len + 1)
|
||||
if buf2 is not None:
|
||||
string.strcpy(buf2, cur)
|
||||
buf2[old_len] = ' '
|
||||
string.strcpy(buf2 + old_len + 1, name)
|
||||
return buf2
|
||||
|
||||
遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
|
||||
模块名通过 _PathToModuleName(includes/rel_path) 计算。
|
||||
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
||||
"""
|
||||
if scan_result is None:
|
||||
return -1
|
||||
# 释放旧存储器
|
||||
if StubMerger._sha1_store_arr is not None:
|
||||
stdlib.free(StubMerger._sha1_store_arr)
|
||||
if StubMerger._sha1_store_mod is not None:
|
||||
stdlib.free(StubMerger._sha1_store_mod)
|
||||
if StubMerger._sha1_store_rel is not None:
|
||||
stdlib.free(StubMerger._sha1_store_rel)
|
||||
if StubMerger._sha1_store_incdir is not None:
|
||||
stdlib.free(StubMerger._sha1_store_incdir)
|
||||
|
||||
# AppendFromImport - append a from-import mapping ("local:mod[:orig]") to a space-separated list.
|
||||
# Exact-size allocation (no fixed 256 buffer) so overlong entries are never silently truncated.
|
||||
def AppendFromImport(pool: memhub.MemBuddy*, cur: str, local: str, mod: str, orig: str) -> str:
|
||||
"""Append a from-import mapping (pool-allocated); returns the new list"""
|
||||
if local is None or mod is None: return cur
|
||||
llen: sizet = string.strlen(local)
|
||||
mlen: sizet = string.strlen(mod)
|
||||
use_orig: int = 0
|
||||
olen: sizet = 0
|
||||
if orig is not None and string.strcmp(orig, local) != 0:
|
||||
use_orig = 1
|
||||
olen = string.strlen(orig)
|
||||
# local ':' mod [':' orig] '\0'
|
||||
need: sizet = llen + 1 + mlen + 1
|
||||
if use_orig != 0: need += 1 + olen
|
||||
entry: str = pool.alloc(need)
|
||||
if entry is None: return cur
|
||||
string.strcpy(entry, local)
|
||||
entry[llen] = ':'
|
||||
string.strcpy(entry + llen + 1, mod)
|
||||
if use_orig != 0:
|
||||
entry[llen + 1 + mlen] = ':'
|
||||
string.strcpy(entry + llen + 1 + mlen + 1, orig)
|
||||
if cur is None: return entry
|
||||
old_len: sizet = string.strlen(cur)
|
||||
entry_len: sizet = string.strlen(entry)
|
||||
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
|
||||
if buf is not None:
|
||||
string.strcpy(buf, cur)
|
||||
buf[old_len] = ' '
|
||||
string.strcpy(buf + old_len + 1, entry)
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AllocImportCtx - build the accounting container from two existing context strings
|
||||
def AllocImportCtx(pool: memhub.MemBuddy*, imported_modules: str, from_imports: str) -> ImportCtx*:
|
||||
"""Build an import context container (pool-allocated); either list may be None"""
|
||||
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
|
||||
if ctx is None: return None
|
||||
ctx.ImportedModules = imported_modules
|
||||
ctx.FromImports = from_imports
|
||||
return ctx
|
||||
|
||||
|
||||
# NotePlainImport - record `import X` / `import X as Y` in place on ctx.
|
||||
# Aliased: append alias Y + from-import "Y:X" (sole source for reverse module lookup).
|
||||
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, name: str, asname: str) -> int:
|
||||
"""Record a single import in place on ctx; returns 0"""
|
||||
if ctx is None or name is None: return 1
|
||||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
|
||||
if asname is not None:
|
||||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname)
|
||||
ctx.FromImports = AppendFromImport(pool, ctx.FromImports, asname, name, None)
|
||||
else:
|
||||
dot_ptr: str = string.strstr(name, ".")
|
||||
if dot_ptr is not None:
|
||||
pkg_len: sizet = sizet(uint64(dot_ptr) - uint64(name))
|
||||
if pkg_len > 0 and pkg_len < 256:
|
||||
pkg_buf: str = pool.alloc(pkg_len + 1)
|
||||
if pkg_buf is not None:
|
||||
string.strncpy(pkg_buf, name, pkg_len)
|
||||
pkg_buf[pkg_len] = '\0'
|
||||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, pkg_buf)
|
||||
return 0
|
||||
|
||||
|
||||
# PopulateSha1MapStore - build sha1 -> module_name map from ScanResult.Entries into the global store.
|
||||
# Memory via stdlib.malloc (global, not freed with mbuddy). Returns entry count, -1 on failure.
|
||||
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
|
||||
"""Populate the global SHA1 store from a scan result; returns the entry count or -1"""
|
||||
if scan_result is None: return -1
|
||||
# free the old store
|
||||
if StubMerger._sha1_store_arr is not None: stdlib.free(StubMerger._sha1_store_arr)
|
||||
if StubMerger._sha1_store_mod is not None: stdlib.free(StubMerger._sha1_store_mod)
|
||||
if StubMerger._sha1_store_rel is not None: stdlib.free(StubMerger._sha1_store_rel)
|
||||
if StubMerger._sha1_store_incdir is not None: stdlib.free(StubMerger._sha1_store_incdir)
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
|
||||
return -1
|
||||
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 CDefine 按最小位宽物化为 i16,
|
||||
# 两侧都 i16 的乘积(1024*256 / 1024*512)会静默回绕成 0;显式 sizet() 把乘数加宽到 64 位,结果与旧「提升到 int」一致。
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return -1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
p_bad: int = 0
|
||||
entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
for i in range(scan_result.Count):
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
|
||||
VLogger.error("includes SHA1 存储超上限,终止", "StubMerger")
|
||||
ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p
|
||||
ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
|
||||
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
for i in range(int(scan_result.Count)):
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: VLogger.error("includes SHA1 store exceeded the limit; aborting", "StubMerger")
|
||||
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
|
||||
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea)
|
||||
if ent is None or ent.Sha1 is None or ent.RelPath is None:
|
||||
p_bad += 1
|
||||
continue
|
||||
# 构建完整路径 includes/{RelPath}(_PathToModuleName 需要)
|
||||
rp_len: t.CSizeT = string.strlen(ent.RelPath)
|
||||
# build full path includes/{RelPath} (needed by _PathToModuleName)
|
||||
rp_len: sizet = string.strlen(ent.RelPath)
|
||||
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
|
||||
if full_path_buf is None:
|
||||
p_bad += 1
|
||||
@@ -91,235 +164,270 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
|
||||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||||
stdlib.free(full_path_buf)
|
||||
if mod_name is not None:
|
||||
idx: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17
|
||||
idx: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||
string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1)
|
||||
idx2: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64
|
||||
mn_len: t.CSizeT = string.strlen(mod_name)
|
||||
if mn_len < 64:
|
||||
string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
|
||||
idx2: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63)
|
||||
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
# 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用)
|
||||
idx3: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN:
|
||||
string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
|
||||
# save rel_path (used by Phase2 3.5 to compile missing includes)
|
||||
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||
# 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用)
|
||||
idx_inc: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
# save owning includes root dir (Phase2 pre-register / on-demand path building)
|
||||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
if ent.IncDir is not None:
|
||||
icd_len: t.CSizeT = string.strlen(ent.IncDir)
|
||||
if icd_len < StubMerger.MAX_INC_DIR_LEN:
|
||||
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
|
||||
icd_len: sizet = string.strlen(ent.IncDir)
|
||||
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir, StubMerger.MAX_INC_DIR_LEN - 1)
|
||||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||||
StubMerger._sha1_store_count += 1
|
||||
if p_bad > 0:
|
||||
fb_pbad: str = VLogger.fmt_buf()
|
||||
if fb_pbad is not None:
|
||||
viperlib.snprintf(fb_pbad, 1024, "includes SHA1 存储有 %d 个条目失败", p_bad)
|
||||
VLogger.error(fb_pbad, "StubMerger")
|
||||
if p_bad > 0: VLogger.error(f"includes SHA1 store has {p_bad} failed entries", "StubMerger")
|
||||
return StubMerger._sha1_store_count
|
||||
# ============================================================
|
||||
# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器
|
||||
#
|
||||
# Phase1 调用 PopulateSha1MapStore 填充 includes 文件,
|
||||
# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目,
|
||||
# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块
|
||||
# (如 StubMerger.StubMerger.MAX_INCLUDES 在编译 Phase2 时通过
|
||||
# _build_source_path_from_sha1 查找 StubMerger 的源路径)。
|
||||
#
|
||||
# 若存储器未初始化(PopulateSha1MapStore 未调用),自动初始化。
|
||||
# 若 SHA1 已存在(去重),跳过。
|
||||
#
|
||||
# Args:
|
||||
# sha1: SHA1 字符串(16 字符 + null)
|
||||
# rel_path: 相对路径(如 "lib/core/StubMerger.vp",
|
||||
# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功(含去重跳过),1 失败
|
||||
# ============================================================
|
||||
# AppendToSha1MapStore - append an App module entry to the global SHA1 store (Phase2 use);
|
||||
# auto-initializes if empty, skips on duplicate SHA1. Returns 0 ok, 1 failed.
|
||||
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
"""追加条目到全局 SHA1 映射存储器(Phase2 用)"""
|
||||
if sha1 is None or rel_path is None:
|
||||
return 1
|
||||
# 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用)
|
||||
"""Append an entry to the global SHA1 store (Phase2)"""
|
||||
if sha1 is None or rel_path is None: return 1
|
||||
# init store if empty (Phase2 may run before Phase1)
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
|
||||
return 1
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
|
||||
return 1
|
||||
# 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger"
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# module name: rel_path "lib/core/StubMerger.vp" -> "lib.core.StubMerger"
|
||||
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
|
||||
if full_path_buf is None:
|
||||
return 1
|
||||
if full_path_buf is None: return 1
|
||||
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
|
||||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||||
stdlib.free(full_path_buf)
|
||||
if mod_name is None:
|
||||
return 1
|
||||
# 去重:SHA1 与模块名都相同才跳过。同一 SHA1 可能被 includes 扫描先以
|
||||
# 全路径模块名注册(如 App 源文件在 includes 目录下 → "Apps.Scene3D.gfx"),
|
||||
# App 追加的短模块名("gfx")必须保留——否则 import gfx 的精确查找
|
||||
# LookupSha1ByModName("gfx") 失败 → 后缀匹配劫持到 VKernel 同名 gfx
|
||||
# (VKernel/Kernel/drivers/video/vesafb/gfx.vp,类 Scene3D 归属错乱根因)。
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
sidx_d: t.CSizeT = t.CSizeT(i) * 17
|
||||
if mod_name is None: return 1
|
||||
# Dedup only when both SHA1 and module name match: the same SHA1 may be registered first
|
||||
# under a full-path name; the App short name must stay or exact lookup breaks.
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx_d: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||
midx_d: t.CSizeT = t.CSizeT(i) * 64
|
||||
midx_d: sizet = sizet(i) * 64
|
||||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
|
||||
stdlib.free(mod_name)
|
||||
return 0
|
||||
# 写入 SHA1
|
||||
idx_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17
|
||||
# write SHA1
|
||||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||||
# 写入模块名
|
||||
idx_m: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64
|
||||
mn_len: t.CSizeT = string.strlen(mod_name)
|
||||
if mn_len < 64:
|
||||
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||
# write module name
|
||||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
# 写入相对路径
|
||||
idx_r: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
rp_len: t.CSizeT = string.strlen(rel_path)
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN:
|
||||
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||
# write rel path
|
||||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
rp_len: sizet = string.strlen(rel_path)
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||
# App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数)
|
||||
idx_inc_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
# App sources belong to no includes dir: leave IncDir empty (Phase2 falls back to includes_dir)
|
||||
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
|
||||
StubMerger._sha1_store_count += 1
|
||||
return 0
|
||||
# AddIncludeToSha1MapStore - register one includes entry into the global SHA1 store (on-demand).
|
||||
# Unreferenced includes stay out; App sources still use AppendToSha1MapStore (empty inc_dir).
|
||||
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
|
||||
"""Register one includes entry in the global SHA1 store (on-demand scan)"""
|
||||
if sha1 is None or rel_path is None or mod_name is None: return 1
|
||||
# init store if empty
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# dedup when both SHA1 and module name match (aligned with AppendToSha1MapStore)
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx_d: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||
midx_d: sizet = sizet(i) * 64
|
||||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0: return 0
|
||||
# write SHA1
|
||||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||||
# write module name
|
||||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||||
# write rel path
|
||||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
rp_len: sizet = string.strlen(rel_path)
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||
# write owning includes root (non-empty = includes entry; App entries empty)
|
||||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
if inc_dir is not None:
|
||||
icd_len: sizet = string.strlen(inc_dir)
|
||||
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1)
|
||||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||||
else: StubMerger._sha1_store_incdir[idx_inc] = '\0'
|
||||
StubMerger._sha1_store_count += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# GetSha1StoreCount - 获取全局存储器条目数
|
||||
# ============================================================
|
||||
# GetSha1StoreCount - return the global store entry count
|
||||
def GetSha1StoreCount() -> int:
|
||||
"""返回全局 SHA1 存储器的条目数"""
|
||||
"""Return the global SHA1 store entry count"""
|
||||
return StubMerger._sha1_store_count
|
||||
# ============================================================
|
||||
# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器)
|
||||
#
|
||||
# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
||||
# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
|
||||
# ============================================================
|
||||
# LookupSha1ByModName - find SHA1 by module name; falls back to "{mod_name}.__init__".
|
||||
# Returns a pointer into the store (do not free), None if not found.
|
||||
def LookupSha1ByModName(mod_name: str) -> str:
|
||||
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
|
||||
"""Look up a SHA1 by module name (exact then package fallback); delegates to _FindSha1ByModName"""
|
||||
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
|
||||
|
||||
|
||||
# Generic-class owner table (generic class name -> defining module SHA1), REPL-session only;
|
||||
# needed because REPL skips generic templates, so generic subscript resolution would otherwise miss.
|
||||
define GENERIC_OWNER_MAX = 32
|
||||
_generic_owner_names: bytes = None
|
||||
_generic_owner_sha1s: bytes = None
|
||||
_generic_owner_count: int = 0
|
||||
|
||||
|
||||
def ResetGenericOwners() -> None:
|
||||
"""Clear the generic-class owner table (called once at REPL session start)"""
|
||||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||||
if _generic_owner_names is not None:
|
||||
stdlib.free(_generic_owner_names)
|
||||
_generic_owner_names = None
|
||||
if _generic_owner_sha1s is not None:
|
||||
stdlib.free(_generic_owner_sha1s)
|
||||
_generic_owner_sha1s = None
|
||||
_generic_owner_count = 0
|
||||
|
||||
|
||||
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
||||
"""Register generic-class-name to defining-module SHA1; 1 = registered, 0 = failed"""
|
||||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||||
if cls_name is None or mod_sha1 is None: return 0
|
||||
if cls_name[0] == '\0' or mod_sha1[0] == '\0': return 0
|
||||
if _generic_owner_names is None:
|
||||
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
|
||||
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
|
||||
if _generic_owner_names is None or _generic_owner_sha1s is None: return 0
|
||||
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
|
||||
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
|
||||
_generic_owner_count = 0
|
||||
i_go: int
|
||||
for i_go in range(int(_generic_owner_count)):
|
||||
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
|
||||
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0: return 1
|
||||
return 0
|
||||
if _generic_owner_count >= GENERIC_OWNER_MAX: return 0
|
||||
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
|
||||
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
|
||||
_generic_owner_count += 1
|
||||
return 1
|
||||
|
||||
|
||||
def LookupGenericOwnerSha1(cls_name: str) -> str:
|
||||
"""Look up the defining-module SHA1 by generic class name (internal pointer, no free)"""
|
||||
if cls_name is None or _generic_owner_names is None: return None
|
||||
i_lg: int
|
||||
for i_lg in range(int(_generic_owner_count)):
|
||||
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0: return _generic_owner_sha1s + sizet(i_lg) * 17
|
||||
return None
|
||||
# ============================================================
|
||||
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1
|
||||
#
|
||||
# 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
|
||||
# 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
|
||||
# 包含三种匹配方式:精确、后缀、反向后缀。
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
|
||||
# ============================================================
|
||||
# LookupSha1ByModNameSuffix - find SHA1 by suffix; tries exact, suffix, reverse-suffix. None if absent.
|
||||
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||||
"""后缀匹配查找模块 SHA1"""
|
||||
if mod_suffix is None:
|
||||
return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
||||
return None
|
||||
if StubMerger._sha1_store_count <= 0:
|
||||
return None
|
||||
suffix_len_sf: t.CSizeT = string.strlen(mod_suffix)
|
||||
if suffix_len_sf == 0:
|
||||
return None
|
||||
"""Look up a module SHA1 by suffix match"""
|
||||
if mod_suffix is None: return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||
if StubMerger._sha1_store_count <= 0: return None
|
||||
suffix_len_sf: sizet = string.strlen(mod_suffix)
|
||||
if suffix_len_sf == 0: return None
|
||||
i_sf: int
|
||||
for i_sf in range(StubMerger._sha1_store_count):
|
||||
idx_sf: t.CSizeT = t.CSizeT(i_sf) * 64
|
||||
for i_sf in range(int(StubMerger._sha1_store_count)):
|
||||
idx_sf: sizet = sizet(i_sf) * 64
|
||||
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
|
||||
mod_len_sf: t.CSizeT = string.strlen(mod_name_sf)
|
||||
if mod_len_sf == 0:
|
||||
continue
|
||||
# 精确匹配
|
||||
mod_len_sf: sizet = string.strlen(mod_name_sf)
|
||||
if mod_len_sf == 0: continue
|
||||
# exact match
|
||||
if mod_len_sf == suffix_len_sf:
|
||||
if string.strcmp(mod_name_sf, mod_suffix) == 0:
|
||||
sidx_sf: t.CSizeT = t.CSizeT(i_sf) * 17
|
||||
sidx_sf: sizet = sizet(i_sf) * 17
|
||||
return StubMerger._sha1_store_arr + sidx_sf
|
||||
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
|
||||
# suffix match: mod_name ends with "." + mod_suffix
|
||||
if mod_len_sf > suffix_len_sf:
|
||||
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
|
||||
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
|
||||
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
|
||||
sidx2_sf: t.CSizeT = t.CSizeT(i_sf) * 17
|
||||
sidx2_sf: sizet = sizet(i_sf) * 17
|
||||
return StubMerger._sha1_store_arr + sidx2_sf
|
||||
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
|
||||
# reverse suffix match: mod_suffix ends with "." + mod_name
|
||||
if suffix_len_sf > mod_len_sf:
|
||||
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
|
||||
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
|
||||
if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0:
|
||||
sidx_rev_sf: t.CSizeT = t.CSizeT(i_sf) * 17
|
||||
sidx_rev_sf: sizet = sizet(i_sf) * 17
|
||||
return StubMerger._sha1_store_arr + sidx_rev_sf
|
||||
return None
|
||||
# ============================================================
|
||||
# LookupSha1ByAlias - 从 from_imports 别名映射解析模块 SHA1
|
||||
#
|
||||
# from_imports 条目格式: "local:module" 或 "local:module:orig"(空格分隔),
|
||||
# 由 ImportsGenMixin.add_from_import 写入(import X as Y → "Y:X")。
|
||||
# 命中 local==alias 时,对 module 执行 LookupSha1ByModName 返回 SHA1。
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向全局存储器内部),None 未找到
|
||||
# ============================================================
|
||||
# LookupSha1ByAlias - resolve SHA1 from from_imports alias "local:module[:orig]" entries.
|
||||
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||
"""从 from_imports 别名映射解析模块 SHA1(改名别名 import X as Y 场景)"""
|
||||
if from_imports is None or alias is None:
|
||||
return None
|
||||
alias_len: t.CSizeT = string.strlen(alias)
|
||||
if alias_len == 0:
|
||||
return None
|
||||
total_len: t.CSizeT = string.strlen(from_imports)
|
||||
ci: t.CSizeT = 0
|
||||
"""Resolve a module SHA1 from a from-import alias mapping (import X as Y)"""
|
||||
if from_imports is None or alias is None: return None
|
||||
alias_len: sizet = string.strlen(alias)
|
||||
if alias_len == 0: return None
|
||||
total_len: sizet = string.strlen(from_imports)
|
||||
ci: sizet = 0
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
while ci < total_len and from_imports[ci] == ' ':
|
||||
ci += 1
|
||||
if ci >= total_len:
|
||||
break
|
||||
# 条目起点 ci,找 ':'(local_name 结束)
|
||||
colon: t.CSizeT = ci
|
||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
|
||||
colon += 1
|
||||
local_len: t.CSizeT = colon - ci
|
||||
# skip leading spaces
|
||||
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# entry starts at ci; find ':' (end of local_name)
|
||||
colon: sizet = ci
|
||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||||
local_len: sizet = colon - ci
|
||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||
# 比较 local_name == alias
|
||||
match: int = 1
|
||||
li: t.CSizeT = 0
|
||||
while li < alias_len:
|
||||
# compare local_name == alias
|
||||
Matched: int = 1
|
||||
for li in range(int(alias_len)):
|
||||
if from_imports[ci + li] != alias[li]:
|
||||
match = 0
|
||||
Matched = 0
|
||||
break
|
||||
li += 1
|
||||
if match == 1:
|
||||
# 提取 module(colon+1 到下一个 ':' 或空格)
|
||||
m_start: t.CSizeT = colon + 1
|
||||
m_end: t.CSizeT = m_start
|
||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
|
||||
m_end += 1
|
||||
if Matched == 1:
|
||||
# extract module (from colon+1 to next ':' or space)
|
||||
m_start: sizet = colon + 1
|
||||
m_end: sizet = m_start
|
||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||||
if m_end > m_start:
|
||||
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
||||
if mbuf is not None:
|
||||
@@ -329,55 +437,38 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||
stdlib.free(mbuf)
|
||||
return res
|
||||
return None
|
||||
# 跳到下一条目
|
||||
while ci < total_len and from_imports[ci] != ' ':
|
||||
ci += 1
|
||||
# advance to next entry
|
||||
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||||
return None
|
||||
# ============================================================
|
||||
# LookupModNameByAlias - 从 from_imports 别名映射解析模块真名
|
||||
#
|
||||
# import X as Y 时 ImportsGenMixin.add_from_import 写入 "Y:X"(Y=别名,
|
||||
# X=模块真名)。本函数返回别名 Y 对应的模块真名 X(供限定名解析
|
||||
# "Y.Class" → 按 X 模块归属匹配,如 "types.fileinfo" → "fat32_types")。
|
||||
#
|
||||
# Returns:
|
||||
# 模块名字符串(stdlib 分配,调用方负责 free),None 未找到
|
||||
# ============================================================
|
||||
# LookupModNameByAlias - return real module name X for alias Y (from_imports "Y:X"; caller frees).
|
||||
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||
"""从 from_imports 别名映射解析模块真名(import X as Y → Y:X)"""
|
||||
if from_imports is None or alias is None:
|
||||
return None
|
||||
alias_len: t.CSizeT = string.strlen(alias)
|
||||
if alias_len == 0:
|
||||
return None
|
||||
total_len: t.CSizeT = string.strlen(from_imports)
|
||||
ci: t.CSizeT = 0
|
||||
"""Resolve the real module name for an alias from from_imports (import X as Y -> Y:X)"""
|
||||
if from_imports is None or alias is None: return None
|
||||
alias_len: sizet = string.strlen(alias)
|
||||
if alias_len == 0: return None
|
||||
total_len: sizet = string.strlen(from_imports)
|
||||
ci: sizet = 0
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
while ci < total_len and from_imports[ci] == ' ':
|
||||
ci += 1
|
||||
if ci >= total_len:
|
||||
break
|
||||
# 条目起点 ci,找 ':'(local_name 结束)
|
||||
colon: t.CSizeT = ci
|
||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
|
||||
colon += 1
|
||||
local_len: t.CSizeT = colon - ci
|
||||
# skip leading spaces
|
||||
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# entry starts at ci; find ':' (end of local_name)
|
||||
colon: sizet = ci
|
||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||||
local_len: sizet = colon - ci
|
||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||
# 比较 local_name == alias
|
||||
match: int = 1
|
||||
li: t.CSizeT = 0
|
||||
while li < alias_len:
|
||||
# compare local_name == alias
|
||||
Matched: int = 1
|
||||
for li in range(int(alias_len)):
|
||||
if from_imports[ci + li] != alias[li]:
|
||||
match = 0
|
||||
Matched = 0
|
||||
break
|
||||
li += 1
|
||||
if match == 1:
|
||||
# 提取 module(colon+1 到下一个 ':' 或空格)
|
||||
m_start: t.CSizeT = colon + 1
|
||||
m_end: t.CSizeT = m_start
|
||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
|
||||
m_end += 1
|
||||
if Matched == 1:
|
||||
# extract module (from colon+1 to next ':' or space)
|
||||
m_start: sizet = colon + 1
|
||||
m_end: sizet = m_start
|
||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||||
if m_end > m_start:
|
||||
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
||||
if mbuf is not None:
|
||||
@@ -385,141 +476,101 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||
mbuf[m_end - m_start] = '\0'
|
||||
return mbuf
|
||||
return None
|
||||
# 跳到下一条目
|
||||
while ci < total_len and from_imports[ci] != ' ':
|
||||
ci += 1
|
||||
# advance to next entry
|
||||
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||||
return None
|
||||
# ============================================================
|
||||
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
|
||||
#
|
||||
# Phase B+ 前用 declare_only 翻译 includes 文件,提取 _imported_modules
|
||||
# 后调用此函数存入内存。_BuildReachableSha1Set 从内存读取,不读 deps.txt。
|
||||
#
|
||||
# Args:
|
||||
# sha1: includes 文件的 SHA1(16字符)
|
||||
# deps_str: 依赖模块名字符串(空格分隔,如 "t c stdint string")
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功,非 0 失败
|
||||
# ============================================================
|
||||
# PopulateIncludesDeps - store includes deps in memory (deps_str space-separated). 0 ok, non-0 fail.
|
||||
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||||
"""将 includes 文件的依赖存入内存"""
|
||||
if sha1 is None:
|
||||
return 1
|
||||
# 延迟初始化
|
||||
"""Store includes-file dependencies in memory"""
|
||||
if sha1 is None: return 1
|
||||
# lazy init
|
||||
if StubMerger._includes_deps_sha1 is None:
|
||||
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._includes_deps_str = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
||||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None:
|
||||
return 1
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(MAX_DEPS_STR_LEN) 会回绕成 0,显式加宽到 64 位
|
||||
StubMerger._includes_deps_str = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 1
|
||||
string.memset(StubMerger._includes_deps_sha1, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._includes_deps_str, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
||||
string.memset(StubMerger._includes_deps_str, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||||
StubMerger._includes_deps_count = 0
|
||||
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES:
|
||||
return 1
|
||||
# 去重:若 SHA1 已存在则跳过
|
||||
for i in range(StubMerger._includes_deps_count):
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
||||
return 0
|
||||
# 写入 SHA1
|
||||
idx: t.CSizeT = t.CSizeT(StubMerger._includes_deps_count) * 17
|
||||
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# dedup: skip if SHA1 already present
|
||||
for i in range(int(StubMerger._includes_deps_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: return 0
|
||||
# write SHA1
|
||||
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
|
||||
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
|
||||
# 写入依赖字符串
|
||||
idx2: t.CSizeT = t.CSizeT(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
|
||||
# write deps string
|
||||
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
|
||||
if deps_str is not None:
|
||||
dl: t.CSizeT = string.strlen(deps_str)
|
||||
if dl < StubMerger.MAX_DEPS_STR_LEN:
|
||||
string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
|
||||
else:
|
||||
string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
|
||||
else:
|
||||
StubMerger._includes_deps_str[idx2] = '\0'
|
||||
dl: sizet = string.strlen(deps_str)
|
||||
if dl < StubMerger.MAX_DEPS_STR_LEN: string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
|
||||
else: string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
|
||||
else: StubMerger._includes_deps_str[idx2] = '\0'
|
||||
StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# LookupIncludesDeps - 从内存查找 includes 文件的依赖
|
||||
#
|
||||
# Args:
|
||||
# sha1: includes 文件的 SHA1(16字符)
|
||||
#
|
||||
# Returns:
|
||||
# 依赖模块名字符串指针(空格分隔),None 表示未找到
|
||||
# ============================================================
|
||||
# LookupIncludesDeps - look up includes deps by SHA1 in memory; returns space-separated deps or None.
|
||||
def LookupIncludesDeps(sha1: str) -> str:
|
||||
"""从内存查找 includes 文件的依赖"""
|
||||
if sha1 is None or StubMerger._includes_deps_sha1 is None:
|
||||
return None
|
||||
if StubMerger._includes_deps_count <= 0:
|
||||
return None
|
||||
for i in range(StubMerger._includes_deps_count):
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
"""Look up includes-file dependencies in memory"""
|
||||
if sha1 is None or StubMerger._includes_deps_sha1 is None: return None
|
||||
if StubMerger._includes_deps_count <= 0: return None
|
||||
for i in range(int(StubMerger._includes_deps_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
||||
idx2: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_DEPS_STR_LEN
|
||||
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
|
||||
return StubMerger._includes_deps_str + idx2
|
||||
return None
|
||||
def GetSha1StoreArrPtr() -> bytes:
|
||||
"""返回 SHA1 数组指针(每个条目 17 字节)"""
|
||||
"""Return the SHA1 array pointer (17 bytes per entry)"""
|
||||
return StubMerger._sha1_store_arr
|
||||
def GetSha1StoreModArrPtr() -> bytes:
|
||||
"""返回模块名数组指针(每个条目 64 字节)"""
|
||||
"""Return the module-name array pointer (64 bytes per entry)"""
|
||||
return StubMerger._sha1_store_mod
|
||||
def GetSha1StoreRelArrPtr() -> bytes:
|
||||
"""返回相对路径数组指针(每个条目 StubMerger.MAX_REL_PATH_LEN 字节)"""
|
||||
"""Return the rel-path array pointer (StubMerger.MAX_REL_PATH_LEN bytes per entry)"""
|
||||
return StubMerger._sha1_store_rel
|
||||
def GetSha1StoreIncdirArrPtr() -> bytes:
|
||||
"""返回所属 includes 根目录数组指针(每个条目 StubMerger.MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)"""
|
||||
"""Return the owning includes-root array pointer (empty string = not in any includes dir)"""
|
||||
return StubMerger._sha1_store_incdir
|
||||
# ============================================================
|
||||
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
|
||||
#
|
||||
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
|
||||
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*StubMerger.MAX_REL_PATH_LEN]
|
||||
# 内存归全局存储器所有,调用者不要 free
|
||||
# ============================================================
|
||||
def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int:
|
||||
"""获取全局存储器数组指针
|
||||
|
||||
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针,
|
||||
函数将存储器数组指针写入这些位置。
|
||||
返回条目数,-1 失败
|
||||
"""
|
||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
|
||||
return -1
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None:
|
||||
return -1
|
||||
sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr)) # 存疑
|
||||
if sp is not None:
|
||||
sp[0] = StubMerger._sha1_store_arr
|
||||
mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr))
|
||||
if mp is not None:
|
||||
mp[0] = StubMerger._sha1_store_mod
|
||||
rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr))
|
||||
if rp is not None:
|
||||
rp[0] = StubMerger._sha1_store_rel
|
||||
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
|
||||
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
||||
"""Get the global store array pointers and entry count; returns -1 on failure"""
|
||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
|
||||
sp: rawptr[t.CPtr] = out_sha1_arr
|
||||
if sp is not None: sp[0] = StubMerger._sha1_store_arr
|
||||
mp: rawptr[t.CPtr] = out_mod_arr
|
||||
if mp is not None: mp[0] = StubMerger._sha1_store_mod
|
||||
rp: rawptr[t.CPtr] = out_rel_arr
|
||||
if rp is not None: rp[0] = StubMerger._sha1_store_rel
|
||||
return StubMerger._sha1_store_count
|
||||
|
||||
# ============================================================
|
||||
# LookupSha1RelPath - 通过 SHA1 查找相对路径
|
||||
#
|
||||
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free)。
|
||||
# 用于错误提示:将 sha1 转换为人类可读的文件路径。
|
||||
#
|
||||
# Args:
|
||||
# sha1: SHA1 字符串(16 字符)
|
||||
#
|
||||
# Returns:
|
||||
# 相对路径字符串指针(只读),None=未找到
|
||||
# ============================================================
|
||||
# LookupSha1RelPath - return the read-only rel_path pointer for a SHA1 (no free), None if missing.
|
||||
def LookupSha1RelPath(sha1: str) -> str:
|
||||
"""通过 SHA1 查找相对路径,返回只读指针,None=未找到"""
|
||||
if sha1 is None:
|
||||
return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None:
|
||||
return None
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
|
||||
if sha1 is None: return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: return None
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
|
||||
ridx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
return StubMerger._sha1_store_rel + ridx
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# LookupModNameBySha1 - reverse lookup module name by SHA1 (read-only ptr, no free); first hit wins.
|
||||
def LookupModNameBySha1(sha1: str) -> str:
|
||||
"""Return the read-only module-name pointer for a SHA1, or None if missing"""
|
||||
if sha1 is None or sha1[0] == '\0': return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx_mb: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
|
||||
midx_mb: sizet = sizet(i) * 64
|
||||
return StubMerger._sha1_store_mod + midx_mb
|
||||
return None
|
||||
@@ -1,182 +1,116 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import llvmlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# ============================================================
|
||||
# StubMergerTopo - StubMerger 拆分(阶段 E):StubMergerTopo
|
||||
#
|
||||
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
|
||||
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
|
||||
# StubMerger._xxx 访问(main.vp 先例)。
|
||||
# ============================================================
|
||||
# StubMergerTopo - reverse-topological sort of reachable includes (dependencies first).
|
||||
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||
|
||||
# ============================================================
|
||||
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
|
||||
#
|
||||
# 背景:跨模块 vtable 生成要求被依赖模块先完整翻译(如 astaux.vp 继承
|
||||
# base.vp 的 AST,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表)。
|
||||
# Phase B+ 原先按 store 扫描顺序编译,依赖顺序不保证 → fail-fast 暴露。
|
||||
#
|
||||
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中)。
|
||||
# indegree[m] = m 在可达集合内的直接依赖数;
|
||||
# 入度为 0(不依赖任何可达模块)者先输出,输出后对依赖它的模块
|
||||
# 减入度,入度归 0 者入队。结果即"被依赖者先编译"。
|
||||
# reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
|
||||
#
|
||||
# Args:
|
||||
# store_count / store_sha1_arr / store_mod_arr: store 元数据
|
||||
# reachable_set / reachable_count: 可达集合(_BuildReachableSha1Set 输出)
|
||||
# sorted_idx: 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
|
||||
#
|
||||
# Returns:
|
||||
# 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
|
||||
# ============================================================
|
||||
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
|
||||
store_mod_arr: bytes, reachable_set: bytes,
|
||||
reachable_count: int,
|
||||
sorted_idx: t.CSizeT | t.CPtr) -> int:
|
||||
"""对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx"""
|
||||
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None:
|
||||
return 0
|
||||
indegree: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
# BuildTopoSortedIndices - reverse-topological sort of reachable includes (dependencies first).
|
||||
# Uses Kahn's algorithm; reachable_count <= 0 sorts all stores; cycles are appended in store order.
|
||||
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_arr: bytes, reachable_set: bytes, reachable_count: int, sorted_idx: sizet*) -> int:
|
||||
"""Reverse-topologically sort reachable includes (dependencies first); store indices go to sorted_idx."""
|
||||
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: return 0
|
||||
indegree: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
|
||||
queue: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
|
||||
rel_idx: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
|
||||
if indegree is None or queue is None or rel_idx is None:
|
||||
if indegree is not None:
|
||||
stdlib.free(indegree)
|
||||
if queue is not None:
|
||||
stdlib.free(queue)
|
||||
if rel_idx is not None:
|
||||
stdlib.free(rel_idx)
|
||||
if indegree is not None: stdlib.free(indegree)
|
||||
if queue is not None: stdlib.free(queue)
|
||||
if rel_idx is not None: stdlib.free(rel_idx)
|
||||
return 0
|
||||
# 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0)
|
||||
# Zero indegree/queue/rel_idx (malloc memory is uninitialized)
|
||||
zi: int = 0
|
||||
for zi in range(store_count + 1):
|
||||
for zi in range(int(store_count + 1)):
|
||||
indegree[zi] = 0
|
||||
queue[zi] = 0
|
||||
rel_idx[zi] = 0
|
||||
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
|
||||
# Collect reachable store indices (all stores when reachable_count<=0)
|
||||
rel_count: int = 0
|
||||
i_t: int
|
||||
if reachable_set is None or reachable_count <= 0:
|
||||
for i_t in range(store_count):
|
||||
for i_t in range(int(store_count)):
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
else:
|
||||
for i_t in range(store_count):
|
||||
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0:
|
||||
for i_t in range(int(store_count)):
|
||||
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + sizet(i_t) * 17, reachable_set, reachable_count) != 0:
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
# 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行)
|
||||
fb_rel: str = VLogger.fmt_buf()
|
||||
if fb_rel is not None:
|
||||
viperlib.snprintf(fb_rel, 1024, "诊断 topo rel_count=%d", rel_count)
|
||||
VLogger.debug(fb_rel, "StubMergerTopo")
|
||||
# 计算入度:m 在可达集合内的直接依赖数
|
||||
i_r: int
|
||||
for i_r in range(rel_count):
|
||||
si: int = rel_idx[i_r]
|
||||
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si) * 17)
|
||||
if deps_si is None:
|
||||
continue
|
||||
dl_si: t.CSizeT = string.strlen(deps_si)
|
||||
dpos: t.CSizeT = 0
|
||||
|
||||
# Compute indegree: number of direct deps of m within the reachable set
|
||||
for i_r in range(int(rel_count)):
|
||||
si: int = int(rel_idx[i_r])
|
||||
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
|
||||
if deps_si is None: continue
|
||||
dl_si: sizet = string.strlen(deps_si)
|
||||
dpos: sizet = 0
|
||||
while dpos < dl_si:
|
||||
dstart: t.CSizeT = dpos
|
||||
while dpos < dl_si and deps_si[dpos] != ' ':
|
||||
dpos += 1
|
||||
dlen: t.CSizeT = dpos - dstart
|
||||
if dpos < dl_si:
|
||||
dpos += 1
|
||||
if dlen == 0:
|
||||
continue
|
||||
dstart: sizet = dpos
|
||||
while dpos < dl_si and deps_si[dpos] != ' ': dpos += 1
|
||||
dlen: sizet = dpos - dstart
|
||||
if dpos < dl_si: dpos += 1
|
||||
if dlen == 0: continue
|
||||
dname_buf: bytes = stdlib.malloc(dlen + 1)
|
||||
if dname_buf is None:
|
||||
continue
|
||||
if dname_buf is None: continue
|
||||
string.strncpy(dname_buf, deps_si + dstart, dlen)
|
||||
dname_buf[dlen] = '\0'
|
||||
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
|
||||
stdlib.free(dname_buf)
|
||||
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
|
||||
# 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug
|
||||
indg: int = indegree[si]
|
||||
# Use an intermediate int to work around a union-pointer arithmetic codegen bug
|
||||
indg: int = int(indegree[si])
|
||||
indg = indg + 1
|
||||
indegree[si] = indg
|
||||
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
|
||||
# Kahn: nodes with indegree 0 (no reachable deps) are output first
|
||||
q_tail: int = 0
|
||||
for i_r in range(rel_count):
|
||||
si2: int = rel_idx[i_r]
|
||||
for i_r in range(int(rel_count)):
|
||||
si2: int = int(rel_idx[i_r])
|
||||
if indegree[si2] == 0:
|
||||
queue[q_tail] = si2
|
||||
q_tail += 1
|
||||
out_count: int = 0
|
||||
# Kahn 主循环必须用 while(不能用 for range):循环体内会动态入队
|
||||
# (queue[q_tail]=si3; q_tail+=1),for range 上界只求值一次,
|
||||
# 动态入队的模块永远不会被处理且兜底(indegree>0)也捞不回 → 拓扑漏项
|
||||
# The Kahn main loop must use while, not for range: the body enqueues dynamically,
|
||||
# and a for range bound is evaluated once, so newly queued modules would be skipped.
|
||||
q_head: int = 0
|
||||
while q_head < q_tail:
|
||||
m: int = queue[q_head]
|
||||
m: int = int(queue[q_head])
|
||||
sorted_idx[out_count] = m
|
||||
out_count += 1
|
||||
# 对依赖 m 的可达模块减入度
|
||||
mod_m: str = store_mod_arr + t.CSizeT(m) * 64
|
||||
mod_m_len: t.CSizeT = string.strlen(mod_m)
|
||||
for i_r in range(rel_count):
|
||||
si3: int = rel_idx[i_r]
|
||||
if indegree[si3] == 0:
|
||||
continue
|
||||
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si3) * 17)
|
||||
if deps_si3 is None:
|
||||
continue
|
||||
dl_si3: t.CSizeT = string.strlen(deps_si3)
|
||||
dpos3: t.CSizeT = 0
|
||||
# Decrement indegree of reachable modules that depend on m
|
||||
mod_m: str = store_mod_arr + sizet(m) * 64
|
||||
mod_m_len: sizet = string.strlen(mod_m)
|
||||
for i_r in range(int(rel_count)):
|
||||
si3: int = int(rel_idx[i_r])
|
||||
if indegree[si3] == 0: continue
|
||||
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si3) * 17)
|
||||
if deps_si3 is None: continue
|
||||
dl_si3: sizet = string.strlen(deps_si3)
|
||||
dpos3: sizet = 0
|
||||
found_dep: int = 0
|
||||
while dpos3 < dl_si3 and found_dep == 0:
|
||||
dstart3: t.CSizeT = dpos3
|
||||
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ':
|
||||
dpos3 += 1
|
||||
dlen3: t.CSizeT = dpos3 - dstart3
|
||||
if dpos3 < dl_si3:
|
||||
dpos3 += 1
|
||||
if dlen3 == 0:
|
||||
continue
|
||||
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0:
|
||||
found_dep = 1
|
||||
dstart3: sizet = dpos3
|
||||
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ': dpos3 += 1
|
||||
dlen3: sizet = dpos3 - dstart3
|
||||
if dpos3 < dl_si3: dpos3 += 1
|
||||
if dlen3 == 0: continue
|
||||
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: found_dep = 1
|
||||
if found_dep == 1:
|
||||
# 中间 int 变量递减:绕过联合类型指针算术编译 bug
|
||||
indg3: int = indegree[si3]
|
||||
# Use an intermediate int decrement to work around a union-pointer arithmetic codegen bug
|
||||
indg3: int = int(indegree[si3])
|
||||
indg3 = indg3 - 1
|
||||
indegree[si3] = indg3
|
||||
if indegree[si3] == 0:
|
||||
queue[q_tail] = si3
|
||||
q_tail += 1
|
||||
q_head += 1
|
||||
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
|
||||
for i_r in range(rel_count):
|
||||
si4: int = rel_idx[i_r]
|
||||
# On cycles, append remaining entries in store order to avoid skipping compilation
|
||||
for i_r in range(int(rel_count)):
|
||||
si4: int = int(rel_idx[i_r])
|
||||
if indegree[si4] > 0:
|
||||
sorted_idx[out_count] = si4
|
||||
out_count += 1
|
||||
|
||||
@@ -1,78 +1,42 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import llvmlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.Phase.BuildPipeline as BuildPipeline
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StubMerger - stub 声明合并模块
|
||||
#
|
||||
# 从 temp 目录加载所有 .stub.ll 文件,将声明内容合并到主 IR。
|
||||
# 这是 Phase2 的核心功能:翻译主文件时,将所有依赖的 stub
|
||||
# 声明合并到主 IR 前面,使链接器能解析跨模块符号引用。
|
||||
#
|
||||
# 合并策略:
|
||||
# 1. 扫描 temp 目录,收集所有 {sha1}.stub.ll 文件
|
||||
# 2. 读取每个 stub 文件内容
|
||||
# 3. 过滤去重(避免重复声明)
|
||||
# 4. 将声明插入到主 IR 的 target triple 之后
|
||||
# ============================================================
|
||||
# StubMerger - merges dependency stub declarations into the main IR (Phase2 core):
|
||||
# collect {sha1}.stub.ll from temp, dedup, then insert after the target triple so cross-module symbols link.
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# 最大 includes SHA1 数(用于过滤)
|
||||
MAX_INCLUDES_SHA1: t.CDefine = 1024
|
||||
# stub 读取缓冲区大小
|
||||
STUB_READ_BUF_SIZE: t.CDefine = 1048576
|
||||
# 最大 includes 条目数
|
||||
MAX_INCLUDES: t.CDefine = 1024
|
||||
# 可达性分析 worklist/processed 缓冲区大小(8192 不够:递归扫描 source_dir
|
||||
# 后 worklist 含全部 App 文件 imports 的拼接 + BFS 依赖展开,追加82 溢出)
|
||||
REACH_WORKLIST_BUF: t.CDefine = 65536
|
||||
# Max includes SHA1 entries (for filtering)
|
||||
define MAX_INCLUDES_SHA1 = 1024
|
||||
# Stub/text read buffer size. 1MB is too small: text.ll files exceed 1MB, so a single
|
||||
# truncated read drops trailing data (opt: "end of file in global variable name").
|
||||
define STUB_READ_BUF_SIZE = 8388608
|
||||
# Max includes entries
|
||||
define MAX_INCLUDES = 1024
|
||||
# Reachability worklist/processed buffer size. 8192 is too small: after scanning
|
||||
# source_dir it holds all App imports plus BFS expansion, overflowing on append 82.
|
||||
define REACH_WORKLIST_BUF = 65536
|
||||
|
||||
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
|
||||
# Phase1 扫描完成后填充,供所有函数使用
|
||||
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_incdir: bytes # 所属 includes 根目录数组(每个 512 字节,MAX_INCLUDES 个,多 includes 支持)
|
||||
_sha1_store_count: int # 条目数
|
||||
# Global SHA1 map store (in memory, no dependency on _sha1_map.txt).
|
||||
# Filled after the Phase1 scan and shared by all functions.
|
||||
_sha1_store_arr: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
|
||||
_sha1_store_mod: bytes # Module-name array (64 bytes each, MAX_INCLUDES entries)
|
||||
_sha1_store_rel: bytes # Relative-path array (256 bytes each, MAX_INCLUDES entries)
|
||||
_sha1_store_incdir: bytes # Owning includes-root array (512 bytes each, MAX_INCLUDES entries, multi-includes)
|
||||
_sha1_store_count: int # Entry count
|
||||
|
||||
# includes 依赖内存存储(不依赖 deps.txt 过程文件)
|
||||
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处
|
||||
# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt
|
||||
_includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
||||
_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个)
|
||||
_includes_deps_count: int # 条目数
|
||||
# In-memory includes dependency store (no dependency on the deps.txt file).
|
||||
# Before Phase B+ all includes are translated in declare_only mode; _BuildReachableSha1Set reads deps from here.
|
||||
_includes_deps_sha1: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
|
||||
_includes_deps_str: bytes # Dependency-string array (512 bytes each, MAX_INCLUDES entries)
|
||||
_includes_deps_count: int # Entry count
|
||||
|
||||
# BuildCombinedIR 全目录扫描缓存:Phase B 对每个源文件调用一次 BuildCombinedIR,
|
||||
# 每次 _collect_stub_sha1s 递归扫描 temp_dir 全部切片子目录(O(N×S) 目录枚举,
|
||||
# 90 文件 × 全目录是 Phase B 慢的根因之一)。按 temp_dir 缓存扫描结果,
|
||||
# 首次扫描后复用,temp_dir 变化时自动重建。
|
||||
_stub_scan_cache_dir: str # 缓存对应的 temp_dir(None=未初始化)
|
||||
_stub_scan_cache_arr: str # 扫描结果数组(每 17 字节一个 SHA1,MAX_INCLUDES_SHA1 个)
|
||||
_stub_scan_cache_count: int # 收集数量
|
||||
# Max length of a single dependency string
|
||||
define MAX_DEPS_STR_LEN = 512
|
||||
|
||||
# 单条依赖字符串最大长度
|
||||
MAX_DEPS_STR_LEN: t.CDefine = 512
|
||||
|
||||
# rel_path 最大长度
|
||||
MAX_REL_PATH_LEN: t.CDefine = 256
|
||||
# includes 根目录最大长度(绝对路径,多 includes 支持)
|
||||
MAX_INC_DIR_LEN: t.CDefine = 512
|
||||
# Max rel_path length
|
||||
define MAX_REL_PATH_LEN = 256
|
||||
# Max includes-root path length (absolute path, multi-includes)
|
||||
define MAX_INC_DIR_LEN = 512
|
||||
+8
-13
@@ -1,13 +1,8 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# lib 包入口 - 声明全局 _mbuddy 内存池指针
|
||||
# ============================================================
|
||||
|
||||
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
|
||||
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
import memhub
|
||||
|
||||
|
||||
# lib package entry - declares the global _mbuddy memory-pool pointer.
|
||||
|
||||
# Global mbuddy memory-pool pointer (injected by App/main.vp after init).
|
||||
# Uses MemBuddy, not MemManager, to avoid returning None when virtual dispatch is unavailable.
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
@@ -1,68 +1,31 @@
|
||||
import t, c
|
||||
import memhub
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式)
|
||||
#
|
||||
# TPC 的 Translator 在 __init__ 集中创建编译器各子对象(Handler/
|
||||
# SymbolTable/Exportable 等),是编译器的"装配器/工厂";TPV 的 main
|
||||
# 目前手动创建 mb/gen 并逐模块注入(_mbuddy/set_global_gen),装配
|
||||
# 逻辑散落。本工厂集中持有编译器级共享内存对象:
|
||||
#
|
||||
# - Pool :主内存池(MemBuddy,瞬时数据,受 mark/reset_to 回收)
|
||||
# - PermPool :永久内存池(MemBuddy,跨 mark 持久数据专用,从不 reset)
|
||||
#
|
||||
# 各模块经模块级委托 CompilerFactory.get_perm_pool() 获取永久池,
|
||||
# 避免把编译器专属单例塞进通用内存库 memhub(memhub 保持通用)。
|
||||
# includes 层(llvmlite/__types.vp 的 DeepCopyType)不依赖本工厂:
|
||||
# 深拷贝的分配源由调用方以 pool 参数传入(传 PermPool 即持久)。
|
||||
# 类名与模块名同名(均为 CompilerFactory):合法场景,TPC 已支持
|
||||
# 模块别名优先解析——`X.func()` 中 X 是 import 别名(IsModuleAlias)
|
||||
# 时按模块函数处理,`X.X(args)` 的类构造按 FuncAttr==类名 走 struct
|
||||
# 路径,二者互不冲突(勿再改类名规避,见用户明确要求)。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class CompilerFactory:
|
||||
"""编译器结构体工厂(持有主池/永久池等共享对象)"""
|
||||
|
||||
Pool: memhub.MemBuddy | t.CPtr # 主内存池(瞬时数据,mark/reset 回收)
|
||||
PermPool: memhub.MemBuddy | t.CPtr # 永久内存池(持久数据,从不 reset)
|
||||
|
||||
def __init__(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
perm_pool: memhub.MemBuddy | t.CPtr):
|
||||
if self is None:
|
||||
return
|
||||
self.Pool = pool
|
||||
self.PermPool = perm_pool
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局工厂单例
|
||||
# ============================================================
|
||||
_g_factory: CompilerFactory | t.CPtr = None
|
||||
|
||||
|
||||
def set_factory(factory: CompilerFactory | t.CPtr) -> None:
|
||||
"""注册全局工厂单例(main 创建后注册)"""
|
||||
global _g_factory
|
||||
_g_factory = factory
|
||||
|
||||
|
||||
def get_factory() -> CompilerFactory | t.CPtr:
|
||||
"""获取全局工厂单例(未注册返回 None)"""
|
||||
return _g_factory
|
||||
|
||||
|
||||
def get_pool() -> memhub.MemBuddy | t.CPtr:
|
||||
"""获取主内存池(瞬时数据分配源)"""
|
||||
if _g_factory is not None:
|
||||
return _g_factory.Pool
|
||||
return None
|
||||
|
||||
|
||||
def get_perm_pool() -> memhub.MemBuddy | t.CPtr:
|
||||
"""获取永久内存池(跨 mark 持久数据分配源)"""
|
||||
if _g_factory is not None:
|
||||
return _g_factory.PermPool
|
||||
return None
|
||||
import memhub
|
||||
|
||||
|
||||
# CompilerFactory - compiler struct factory (aligned with TPC Translator assembly).
|
||||
novirtual class CompilerFactory:
|
||||
"""Compiler struct factory holding shared objects such as the main and permanent pools."""
|
||||
Pool: memhub.MemBuddy* # Main memory pool (transient data; reclaimed by mark/reset)
|
||||
PermPool: memhub.MemBuddy* # Permanent memory pool (persistent data; never reset)
|
||||
|
||||
# ---- REnum variant registry (compile-time singleton; same lifetime as perm_pool struct entries)----
|
||||
RenumClassNames: sizet*
|
||||
RenumClassVariantCounts: sizet*
|
||||
RenumVariantNames: sizet*
|
||||
RenumVariantTags: sizet*
|
||||
RenumClassCount: int
|
||||
|
||||
# ---- REnum variant field alias table (same lifetime; _bind_variant_fields resolves real field types)----
|
||||
RenumAliasClass: sizet*
|
||||
RenumAliasField: sizet*
|
||||
RenumAliasIndex: sizet*
|
||||
RenumAliasTy: sizet*
|
||||
RenumAliasVariant: sizet*
|
||||
RenumAliasCount: int
|
||||
|
||||
def __init__(self, pool: memhub.MemBuddy*, perm_pool: memhub.MemBuddy*):
|
||||
if self is None: return
|
||||
self.Pool = pool
|
||||
self.PermPool = perm_pool
|
||||
|
||||
# Global factory singleton
|
||||
factory: CompilerFactory* = None
|
||||
@@ -0,0 +1,334 @@
|
||||
from stdint import *
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import win32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Diag - 编译期诊断(traceback 系统):源码行读取 + 统一打印 + 终止策略
|
||||
# ============================================================
|
||||
# 抽出来的原因:格式化 / 源码行读取 / 终止策略原先都散在 TypeGenErr 里(连同它的
|
||||
# TraceCount 调用栈遍历),导致"多错误栈"(错误收集 + 每单元去重 + 上限)无处安放。
|
||||
# 现在 TypeGenErr / GenApi 的 `fatal_error` / `dump_parse_traceback` 全部**委托**到这里;
|
||||
# 保留在 TypeGenErr 里的只有两件"它才知道"的东西:
|
||||
# ① 调用栈帧的遍历(`TypeGenBase._get_trace_frame`);
|
||||
# ② 当前文件名(`TypeGenResolve.get_current_file`)。
|
||||
#
|
||||
# **行为与抽离前逐字一致**:报文顺序、缩进、caret 规则、fflush、repl_abort 全不变。
|
||||
# 诊断模型:**单条 + 立即终止**(不收集多条错误)—— 但单条自带**整条调用链**(DiagEntry),
|
||||
# 由调用方(TypeGenErr)填好 entry 后 emit,方向由 DIAG_DIR_* 决定。所有打印都拆成独立的
|
||||
# emit_*,便于在不改调用点的前提下调整格式。
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ---- traceback 打印方向 ----
|
||||
define DIAG_DIR_UP = 0 # 外→内:最外层触发点在上,错误点最后(Python "most recent call last")
|
||||
define DIAG_DIR_DOWN = 1 # 内→外:错误点最先,越往外越下
|
||||
define DIAG_MAX_FRAMES = 32 # 调用链上限(与 TypeGenMixin.TRACE_MAX 对齐)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# DiagEntry - 单条编译期诊断(= 一次 traceback 要打印的全部内容)
|
||||
# ============================================================
|
||||
# **不是**错误收集容器:任何时刻只处理"出错的那一条"(打印完即终止),
|
||||
# 但这一条自带**整条调用链** —— 编译器逐层下潜进各源码上下文
|
||||
# (import 触发依赖加载 / 泛型特化 / 函数体翻译)时的触发点,
|
||||
# 用于"从错误发生点向外展示触发过程"。
|
||||
# 所有字符串都用 stdlib 深拷贝:编译期池会回绕,不能借用池内指针。
|
||||
struct DiagEntry:
|
||||
# 字段名不要与"本模块 import 的类名"重名:TPC 的类型/符号表按**非限定名**索引
|
||||
# (Gen.structs / SymbolTable / _mangle_func_name),曾把本字段的 `File` 与
|
||||
# `win32.fileio.File` 撞在一起,导致同模块 `fileio.File(...)` 的构造调用把
|
||||
# `File.__before_init__` 的 self 形参解析成 `i8**`(llvmlite: "Type of #1 arg
|
||||
# mismatch: i8** != %"...fileio.File"*")。故此处用 FileName 而非 File。
|
||||
FileName: str # 当前帧(错误点)文件
|
||||
Line: int # 当前帧行号
|
||||
ColStart: int # caret 起始列(1-based;0 = 不画)
|
||||
ColEnd: int # caret 结束列
|
||||
Category: str # LifetimeError / SyntaxError / TypeError ...
|
||||
Msg: str # 消息
|
||||
Hint: str # 可选提示(可 None)
|
||||
FrameCount: int # 调用链帧数
|
||||
FrameFiles: sizet* # 调用链每帧文件名(深拷贝)
|
||||
FrameLines: sizet* # 调用链每帧行号(与 FrameFiles 同容量)
|
||||
# 泄漏点(可选):被调者那边**造成逃逸的写入点**。只有逃逸类 LifetimeError 会有值;
|
||||
# 打印成独立一帧 `File lifetime leak "<file>", line N`,与调用栈帧(`File "..."`)区分。
|
||||
LeakFileName: str
|
||||
LeakLine: int
|
||||
Cap: int # 内部:FrameFiles/FrameLines 容量(不参与打印)
|
||||
|
||||
|
||||
# dup_str - 深拷贝到 stdlib 堆(诊断需跨池回绕点存活)
|
||||
def dup_str(s: str) -> str:
|
||||
if s is None: return None
|
||||
n: sizet = string.strlen(s)
|
||||
b: str = str(stdlib.malloc(n + 1))
|
||||
if b is None: return None
|
||||
string.strncpy(b, s, n)
|
||||
b[n] = '\0'
|
||||
return b
|
||||
|
||||
|
||||
# new_entry - 分配一条诊断(调用方持有;打印完用 free_entry 释放)
|
||||
def new_entry(max_frames: int) -> DiagEntry*:
|
||||
ent: DiagEntry* = (DiagEntry*)(stdlib.malloc(DiagEntry.__sizeof__()))
|
||||
if ent is None: return None
|
||||
if max_frames <= 0: max_frames = 1
|
||||
ent.FileName = None
|
||||
ent.Line = 0
|
||||
ent.ColStart = 0
|
||||
ent.ColEnd = 0
|
||||
ent.Category = None
|
||||
ent.Msg = None
|
||||
ent.Hint = None
|
||||
ent.FrameCount = 0
|
||||
ent.LeakFileName = None
|
||||
ent.LeakLine = 0
|
||||
ent.Cap = max_frames
|
||||
ent.FrameFiles = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
|
||||
ent.FrameLines = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
|
||||
return ent
|
||||
|
||||
|
||||
# begin - 初始化一条诊断(随后用 add_frame/set_current 填调用链与当前位置)
|
||||
def begin(ent: DiagEntry*, category: str, msg: str, hint: str = None) -> int:
|
||||
if ent is None: return 0
|
||||
ent.Category = dup_str(category)
|
||||
ent.Msg = dup_str(msg)
|
||||
ent.Hint = dup_str(hint)
|
||||
ent.FileName = None
|
||||
ent.Line = 0
|
||||
ent.ColStart = 0
|
||||
ent.ColEnd = 0
|
||||
ent.FrameCount = 0
|
||||
ent.LeakFileName = None
|
||||
ent.LeakLine = 0
|
||||
return 0
|
||||
|
||||
|
||||
# set_current - 设置当前帧(错误点)位置
|
||||
def set_current(ent: DiagEntry*, file_name: str, lineno: int, col_start: int, col_end: int) -> int:
|
||||
if ent is None: return 0
|
||||
ent.FileName = dup_str(file_name)
|
||||
ent.Line = lineno
|
||||
ent.ColStart = col_start
|
||||
ent.ColEnd = col_end
|
||||
return 0
|
||||
|
||||
|
||||
# set_leak - 设置"泄漏点"帧(File lifetime leak);仅逃逸类 LifetimeError 使用
|
||||
def set_leak(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||
if ent is None or lineno <= 0: return 0
|
||||
ent.LeakFileName = dup_str(file_name)
|
||||
ent.LeakLine = lineno
|
||||
return 0
|
||||
|
||||
|
||||
# add_frame - 追加调用链一帧(按"自外向内"顺序追加)
|
||||
def add_frame(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||
if ent is None or ent.FrameFiles is None or ent.FrameLines is None: return 0
|
||||
if ent.FrameCount >= ent.Cap: return 0
|
||||
ent.FrameFiles[ent.FrameCount] = sizet(dup_str(file_name))
|
||||
ent.FrameLines[ent.FrameCount] = sizet(lineno)
|
||||
ent.FrameCount += 1
|
||||
return 0
|
||||
|
||||
|
||||
# emit - 打印一条诊断:头 → 调用链(按 direction) → 当前帧(caret) → 类别: 消息
|
||||
# 去重:① 连续两个位置完全相同的栈帧只印一次;② 与“错误点帧”位置相同的栈帧不再重复印
|
||||
#(此前常见 `_start` 定义行被打印两遍:栈内最内层帧与错误点帧落在同一行)。
|
||||
def emit(ent: DiagEntry*, direction: int) -> int:
|
||||
if ent is None: return 0
|
||||
emit_header()
|
||||
last_file: str = ""
|
||||
last_line: int = -1
|
||||
if ent.FrameFiles is not None and ent.FrameLines is not None:
|
||||
if direction == DIAG_DIR_DOWN:
|
||||
i_d: int = ent.FrameCount - 1
|
||||
while i_d >= 0:
|
||||
ff: str = str(ent.FrameFiles[i_d])
|
||||
ll: int = int(ent.FrameLines[i_d])
|
||||
dup: int = 1 if last_file != "" and string.strcmp(ff, last_file) == 0 and ll == last_line else 0
|
||||
if dup == 0:
|
||||
emit_frame(ff, ll)
|
||||
last_file = ff
|
||||
last_line = ll
|
||||
i_d -= 1
|
||||
else:
|
||||
i_u: int = 0
|
||||
while i_u < ent.FrameCount:
|
||||
ff2: str = str(ent.FrameFiles[i_u])
|
||||
ll2: int = int(ent.FrameLines[i_u])
|
||||
dup2: int = 1 if last_file != "" and string.strcmp(ff2, last_file) == 0 and ll2 == last_line else 0
|
||||
cur_same: int = 1 if ent.FileName is not None and string.strcmp(ff2, ent.FileName) == 0 and ll2 == ent.Line else 0
|
||||
if dup2 == 0 and cur_same == 0:
|
||||
emit_frame(ff2, ll2)
|
||||
last_file = ff2
|
||||
last_line = ll2
|
||||
i_u += 1
|
||||
emit_current(ent.FileName, ent.Line, ent.ColStart, ent.ColEnd)
|
||||
# 泄漏点帧紧跟在"错误点"之后(往下再一行),再印类别: 消息
|
||||
if ent.LeakLine > 0: emit_leak_frame(ent.LeakFileName, ent.LeakLine)
|
||||
emit_category(ent.Category, ent.Msg)
|
||||
return 0
|
||||
|
||||
|
||||
# free_entry - 释放一条诊断(含深拷贝的字符串)
|
||||
def free_entry(ent: DiagEntry*) -> int:
|
||||
if ent is None: return 0
|
||||
if ent.FileName is not None: stdlib.free(ent.FileName)
|
||||
if ent.LeakFileName is not None: stdlib.free(ent.LeakFileName)
|
||||
if ent.Category is not None: stdlib.free(ent.Category)
|
||||
if ent.Msg is not None: stdlib.free(ent.Msg)
|
||||
if ent.Hint is not None: stdlib.free(ent.Hint)
|
||||
if ent.FrameFiles is not None:
|
||||
i_f: int = 0
|
||||
while i_f < ent.FrameCount:
|
||||
if ent.FrameFiles[i_f] != 0: stdlib.free(str(ent.FrameFiles[i_f]))
|
||||
i_f += 1
|
||||
stdlib.free(ent.FrameFiles)
|
||||
if ent.FrameLines is not None: stdlib.free(ent.FrameLines)
|
||||
stdlib.free(ent)
|
||||
return 0
|
||||
|
||||
|
||||
# read_source_line - 读源文件第 lineno 行,供 traceback 展示
|
||||
def read_source_line(file_path: str, lineno: int) -> str:
|
||||
"""读第 lineno 行;返回 stdlib 分配的缓冲(调用方 free),读不到返回 None"""
|
||||
if file_path is None or lineno <= 0: return None
|
||||
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed: return None
|
||||
# Allocate by actual file size: a fixed 256KB buffer truncated large files so late line
|
||||
# numbers were unreadable. The 64MB cap guards against reading abnormally huge files.
|
||||
fsize: LONGLONG = f.size()
|
||||
if fsize <= 0 or fsize > 67108864:
|
||||
f.close()
|
||||
return None
|
||||
src_size: sizet = sizet(fsize)
|
||||
buf: bytes = stdlib.malloc(src_size + 1)
|
||||
if buf is None:
|
||||
f.close()
|
||||
return None
|
||||
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
|
||||
f.close()
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
# Scan line by line for the target lineno
|
||||
cur_line: int = 1
|
||||
line_start: sizet = 0
|
||||
for i in range(int(bytes_read)):
|
||||
if buf[i] == '\n':
|
||||
if cur_line == lineno:
|
||||
line_len: sizet = i - line_start
|
||||
if line_len > 0 and buf[line_start + line_len - 1] == '\r': line_len -= 1
|
||||
line_buf: bytes = stdlib.malloc(line_len + 1)
|
||||
if line_buf is not None:
|
||||
string.strncpy(line_buf, buf + line_start, line_len)
|
||||
line_buf[line_len] = '\0'
|
||||
stdlib.free(buf)
|
||||
return line_buf
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
cur_line += 1
|
||||
line_start = i + 1
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
|
||||
|
||||
# emit_header - traceback 头(解析失败与语义错误共用同一行文案)
|
||||
def emit_header() -> int:
|
||||
stdio.printf("Traceback (most recent call last, compile-time error):\n")
|
||||
return 0
|
||||
|
||||
|
||||
# emit_frame - 调用栈里的一帧(只印位置与源码行,不印 caret)
|
||||
def emit_frame(file_name: str, lineno: int) -> int:
|
||||
if file_name is None: return 0
|
||||
if lineno > 0:
|
||||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||
src: str = read_source_line(file_name, lineno)
|
||||
if src is not None:
|
||||
stdio.printf(" %s\n", src)
|
||||
stdlib.free(src)
|
||||
else: stdio.printf(' File "%s"\n', file_name)
|
||||
return 0
|
||||
|
||||
|
||||
# emit_leak_frame - "泄漏点"帧:标签用 `File lifetime leak`,与调用栈帧(`File "..."`)区分
|
||||
def emit_leak_frame(file_name: str, lineno: int) -> int:
|
||||
if file_name is None: return 0
|
||||
if lineno > 0:
|
||||
stdio.printf(' File lifetime leak "%s", line %d\n', file_name, lineno)
|
||||
src: str = read_source_line(file_name, lineno)
|
||||
if src is not None:
|
||||
stdio.printf(" %s\n", src)
|
||||
stdlib.free(src)
|
||||
else: stdio.printf(' File lifetime leak "%s"\n', file_name)
|
||||
return 0
|
||||
|
||||
|
||||
# emit_current - 当前帧(带 caret 列范围;行读不出时印警告)
|
||||
def emit_current(file_name: str, lineno: int, col_start: int, col_end: int) -> int:
|
||||
# Sanity defense: read the line first to verify lineno belongs to the current file.
|
||||
# If unreadable the file/line is untrustworthy (e.g. reused AST); print a warning instead.
|
||||
src_line: str = None
|
||||
line_ok: int = 1
|
||||
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
|
||||
src_line = read_source_line(file_name, lineno)
|
||||
if src_line is None: line_ok = 0
|
||||
if line_ok == 0:
|
||||
stdio.printf(' File "%s", line %d (!! cannot read that source line: file does not exist or line number out of range; the error may come from a reused AST tree, rely on the error message)\n', file_name, lineno)
|
||||
else:
|
||||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||
if src_line is not None:
|
||||
stdio.printf(" %s\n", src_line)
|
||||
# Column-range marker: use ^ under the source line to mark the error range
|
||||
if col_start > 0 and col_end >= col_start:
|
||||
stdio.printf(" ")
|
||||
j: int
|
||||
for j in range(int(col_end)):
|
||||
if j >= col_start - 1 and j < col_end: stdio.printf("^")
|
||||
else: stdio.printf(" ")
|
||||
stdio.printf("\n")
|
||||
stdlib.free(src_line)
|
||||
return 0
|
||||
|
||||
|
||||
# emit_category - `类别: 消息`,随后立即 fflush
|
||||
def emit_category(err_type: str, msg: str) -> int:
|
||||
if err_type is None or err_type[0] == 0: err_type = "SyntaxError"
|
||||
stdio.printf("%s: %s\n", err_type, msg)
|
||||
# Force flush before exiting: stdout redirected to a file is fully buffered, so terminating
|
||||
# directly drops the whole traceback (observed: 2.log kept only one VLogger line).
|
||||
stdio.fflush(None)
|
||||
return 0
|
||||
|
||||
|
||||
# emit_parse - 解析失败的单帧 traceback(Python 风格,单个 ^ 指向 token 起始列)
|
||||
def emit_parse(file_path: str, err_line: int, err_col: int, err_msg: str) -> int:
|
||||
emit_header()
|
||||
if file_path is None or file_path[0] == 0: file_path = "<unknown>"
|
||||
if err_line > 0:
|
||||
stdio.printf(' File "%s", line %d\n', file_path, err_line)
|
||||
src: str = read_source_line(file_path, err_line)
|
||||
if src is not None:
|
||||
stdio.printf(" %s\n", src)
|
||||
# Column marker: put ^ under the error token's start column (parser col_offset is
|
||||
# 0-based, so 4-space indent + err_col spaces lands on the token's first char)
|
||||
if err_col > 0:
|
||||
stdio.printf(" ")
|
||||
for _ in range(int(err_col)): stdio.printf(" ")
|
||||
stdio.printf("^\n")
|
||||
stdlib.free(src)
|
||||
else: stdio.printf(' File "%s"\n', file_path)
|
||||
if err_msg is None or err_msg[0] == 0: err_msg = "syntax error"
|
||||
stdio.printf("SyntaxError: %s\n", err_msg)
|
||||
return 0
|
||||
|
||||
|
||||
# finish - 终止策略:REPL 里 longjmp 回循环(报文已印、进程存活),批处理里 sys.exit(1)
|
||||
def finish() -> None:
|
||||
VLogger.repl_abort()
|
||||
+3800
-2236
File diff suppressed because it is too large.
Load diff
+1248
-1999
File diff suppressed because it is too large.
Load diff
@@ -1,50 +1,35 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import llvmlite
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import mlirlite
|
||||
|
||||
|
||||
# ============================================================
|
||||
# BaseGenMixin - 生成器基座(状态容器)
|
||||
#
|
||||
# 对象化重构 P1(方案见 wiki/14-oo-refactor.md):
|
||||
# 将 Translator/Handles 的共享状态集中于此。后续 Mixin
|
||||
# (TypeGen/StructGen/ExprGen/CallGen/FuncGen/ClassGen/StmtGen/
|
||||
# ImportsGen)单继承本类,逐步把 Handles* 模块的函数迁移为
|
||||
# Generator 的方法,消除 (pool, builder, mod, node, ...) 六参连传。
|
||||
#
|
||||
# 注意:@t.NoVTable(静态绑定,骨架无多态需求;若未来需要
|
||||
# 运行时多态再改 @t.CVTable,字段布局由编译器统一管理)。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class BaseGenMixin:
|
||||
"""生成器状态容器(基座)"""
|
||||
# BaseGenMixin - generator base (state container).
|
||||
novirtual class BaseGenMixin:
|
||||
"""Generator state container (base)."""
|
||||
|
||||
# 内存/IR 状态
|
||||
Gen: Generator.Generator | t.CPtr
|
||||
Pool: memhub.MemBuddy | t.CPtr # 内存池
|
||||
Module: llvmlite.LLVMModule | t.CPtr # 当前 LLVM 模块
|
||||
Builder: llvmlite.IRBuilder | t.CPtr # 当前 IRBuilder
|
||||
# 符号表/函数表
|
||||
SymTab: GenTypes.SymbolTable | t.CPtr
|
||||
Funcs: GenTypes.FuncEntry | t.CPtr
|
||||
# Memory / IR state
|
||||
Gen: Generator.Generator*
|
||||
Pool: memhub.MemBuddy* # Memory pool
|
||||
Module: mlirlite.LLVMModule* # Current LLVM module
|
||||
Builder: mlirlite.IRBuilder* # Current IRBuilder
|
||||
# Symbol / function tables
|
||||
SymTab: GenTypes.SymbolTable*
|
||||
Funcs: GenTypes.FuncEntry*
|
||||
FuncCount: int
|
||||
# 导入/模块信息
|
||||
# Import / module info
|
||||
ImportedModules: str
|
||||
FromImports: str
|
||||
ModuleSha1: str
|
||||
# 当前翻译上下文
|
||||
CurFunc: llvmlite.Function | t.CPtr
|
||||
CurBuilder: llvmlite.IRBuilder | t.CPtr
|
||||
# Current translation context
|
||||
CurFunc: mlirlite.Function*
|
||||
CurBuilder: mlirlite.IRBuilder*
|
||||
CurClassName: str
|
||||
|
||||
def InitBaseGen(self, pool: memhub.MemBuddy | t.CPtr) -> int:
|
||||
"""初始化生成器状态(默认值;子类构造时调用)"""
|
||||
if self is None:
|
||||
return 1
|
||||
def InitBaseGen(self, pool: memhub.MemBuddy*) -> int:
|
||||
"""Initialize generator state to defaults; called from subclass constructors."""
|
||||
if self is None: return 1
|
||||
self.Pool = pool
|
||||
self.Module = None
|
||||
self.Builder = None
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,246 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# CallGenFd - split from CallGenMixin (stage B): FakeDuck / requires field injection / provider stack
|
||||
novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
"""FakeDuck / requires field injection / provider stack"""
|
||||
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""FakeDuck: resolve a str variable's method call to extension class _Type/_str, return the call result or None"""
|
||||
if pool is None or builder is None or cl is None or trans is None: return None
|
||||
if method_name is None: return None
|
||||
# Only trigger for str (AnnotClassName == "str", object is char*);
|
||||
# also try when cls_name is None (receiver resolution failed).
|
||||
if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None
|
||||
fd_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if fd_at is None or fd_at.value is None or fd_at.attr is None: return None
|
||||
if string.strcmp(fd_at.attr, method_name) != 0: return None
|
||||
# Candidate extension classes: _Type first (a.type_id()), then _str (a.upper() etc.)
|
||||
fd_func: mlirlite.Function* = None
|
||||
fd_cls: str = None
|
||||
fd_key: str = pool.alloc(160)
|
||||
if fd_key is not None:
|
||||
for fd_ci in range(2):
|
||||
fd_cn: str = "_Type"
|
||||
if fd_ci == 1: fd_cn = "_str"
|
||||
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
|
||||
fd_func = self.find_func_in_module(mod, fd_key)
|
||||
if fd_func is not None:
|
||||
fd_cls = fd_cn
|
||||
break
|
||||
if fd_func is None or fd_cls is None: return None
|
||||
# The extension class struct must be registered (GEP/temp shadow needs a type)
|
||||
fd_sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
|
||||
if fd_sty is None: return None
|
||||
# Extension class field count: with fields (_str) self uses a shadow struct pointer;
|
||||
# without fields (_Type) self bitcasts the object value to the extension pointer (matches TPC L2856-2876).
|
||||
fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty)
|
||||
fd_has_fields: int = 0
|
||||
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0: fd_has_fields = 1
|
||||
# self arg: prefer an existing shadow {var}__meta__ (created by fd2, only when the extension class has fields)
|
||||
fd_self: mlirlite.Value* = None
|
||||
if fd_at.value.kind() == ast.ASTKind.Name:
|
||||
fd_onm: ast.Name* = ast.Name*(fd_at.value)
|
||||
if fd_onm.id is not None:
|
||||
# Pool-persistent buffer (an f-string stack buffer would expire after return)
|
||||
fd_mn: str = pool.alloc(256)
|
||||
if fd_mn is not None: viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
|
||||
fd_meta: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
|
||||
if fd_meta is not None and fd_has_fields != 0: fd_self = fd_meta
|
||||
if fd_self is None:
|
||||
if fd_has_fields != 0:
|
||||
# Fielded extension class with no shadow (string literal etc.): create and init a temp
|
||||
# shadow at entry; first ensure full _str definition (cross-module structs default to opaque and alloca would report unsized).
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
|
||||
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
|
||||
if fd_self is None: return None
|
||||
fd_obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
||||
if fd_obj_val is not None:
|
||||
fd_dfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__data__")
|
||||
if fd_dfe is not None and fd_dfe.Ty is not None:
|
||||
fd_dp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
|
||||
if fd_dp is not None: mlirlite.build_store(builder, fd_obj_val, fd_dp)
|
||||
fd_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
|
||||
if fd_bfe is not None and fd_bfe.Ty is not None:
|
||||
fd_bp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
|
||||
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, fd_bfe.Ty, "null")
|
||||
if fd_bp is not None and fd_null is not None: mlirlite.build_store(builder, fd_null, fd_bp)
|
||||
# FakeDuck: auto-inject requires fields from the with context (e.g. __mbuddy__)
|
||||
self._inject_requires_fields(trans, pool, builder, mod, fd_self, fd_cls)
|
||||
else:
|
||||
# Fieldless extension class (_Type): bitcast the object value to the extension pointer
|
||||
fd_obj_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
||||
if fd_obj_v is None: return None
|
||||
fd_self = mlirlite.build_bitcast(builder, fd_obj_v, mlirlite.Ptr(pool, fd_sty))
|
||||
if fd_self is None: return None
|
||||
# Build the argument list: self -> remaining args
|
||||
mlirlite.value_set_next(fd_self, None)
|
||||
fd_head: mlirlite.Value* = fd_self
|
||||
fd_tail: mlirlite.Value* = fd_self
|
||||
fd_args: list[ast.AST*]* = cl.args
|
||||
fd_can: int = 0
|
||||
if fd_args is not None: fd_can = int(len(fd_args))
|
||||
for fd_ai in range(int(fd_can)):
|
||||
fd_arg: ast.AST* = fd_args.get(fd_ai)
|
||||
if fd_arg is not None:
|
||||
fd_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
|
||||
# char constant arg -> single-char string (implicit char->str, e.g. s1.split(',') where
|
||||
# ',' is i32 44); passing it raw to a str param makes strlen/strncmp deref 44 and crash.
|
||||
if fd_arg.kind() == ast.ASTKind.Constant:
|
||||
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
|
||||
if fd_ac.const_kind == ast.CONST_CHAR:
|
||||
ch_buf: str = pool.alloc(2)
|
||||
if ch_buf is not None:
|
||||
ch_buf[0] = char(fd_ac.int_val)
|
||||
ch_buf[1] = '\0'
|
||||
fd_av = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, ch_buf, trans)
|
||||
if fd_av is not None:
|
||||
mlirlite.value_set_next(fd_av, None)
|
||||
mlirlite.value_set_next(fd_tail, fd_av)
|
||||
fd_tail = fd_av
|
||||
# Return type and call name (from the function definition hit by find_func_in_module)
|
||||
fd_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(fd_func)
|
||||
if fd_ret_ty is None: fd_ret_ty = mlirlite.Int32(pool)
|
||||
fd_fname: str = mlirlite.function_get_name(fd_func)
|
||||
if fd_fname is None: return None
|
||||
# Refine return type: cross-module declare may simplify a list[T] specialization return type to Ptr(i8), degrading the caller's alloca type -> parts[0] takes the char-index path instead of list inline __getitem__ (printf %s crash). Authority is the defining module's vpi (fallback text.ll); extract the module sha1 only when the name is "{16hex}.{func}" (via GenApi.sha1_prefix_len)
|
||||
if GenApi.sha1_prefix_len(fd_fname) != 0:
|
||||
fd_sha1: str = pool.alloc(17)
|
||||
if fd_sha1 is not None:
|
||||
string.strncpy(fd_sha1, fd_fname, 16)
|
||||
fd_sha1[16] = '\0'
|
||||
fd_exact: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, fd_sha1, fd_cls, method_name)
|
||||
if fd_exact is None: fd_exact = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
|
||||
if fd_exact is not None: fd_ret_ty = fd_exact
|
||||
fd_total: int = 1 + fd_can
|
||||
# Value-reference (&T / &temp T) params of the resolved extension method: auto-address the
|
||||
# user args (the shadow self pointer occupies slot 0).
|
||||
fd_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, fd_fname, fd_args, fd_head, fd_total, 1)
|
||||
return mlirlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
|
||||
# _inject_requires_fields - FakeDuck: inject requires fields from the with context.
|
||||
# For each class __requires__ field (registered by fd1), call _find_provider(field) at runtime; if non-NULL bitcast and store it (conditional inject); skip when _find_provider is unregistered.
|
||||
|
||||
# ============================================================
|
||||
def _inject_requires_fields(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, meta_ptr: mlirlite.Value*, class_name: str) -> int:
|
||||
"""Inject requires fields from the with context, return the injected field count"""
|
||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||
if meta_ptr is None or class_name is None: return 0
|
||||
# _withcontent._find_provider (cross-module): inject only when visible in this module
|
||||
# (matches TPC L4048-4050); otherwise non-with projects would emit undefined {sha1}._find_provider calls.
|
||||
fd_fn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_find_provider")
|
||||
if fd_fn_probe is None: return 0
|
||||
fd_fname: str = mlirlite.function_get_name(fd_fn_probe)
|
||||
if fd_fname is None: return 0
|
||||
cnt: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_REQUIRES, class_name)
|
||||
if cnt <= 0: return 0
|
||||
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
||||
if sty is None: return 0
|
||||
i8_ty_j: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if i8_ty_j is None: return 0
|
||||
i8_ptr_ty_j: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_j)
|
||||
if i8_ptr_ty_j is None: return 0
|
||||
cur_func: mlirlite.Function* = trans._cur_func
|
||||
if cur_func is None: return 0
|
||||
injected: int = 0
|
||||
for ji in range(int(cnt)):
|
||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
|
||||
if fname is None: continue
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
|
||||
if fe is None or fe.Ty is None: continue
|
||||
# Arg: field name string (i8*)
|
||||
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
||||
if fd_fstr is None: continue
|
||||
mlirlite.value_set_next(fd_fstr, None)
|
||||
self._ensure_method_declare(pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
|
||||
fd_ptr: mlirlite.Value* = mlirlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
|
||||
if fd_ptr is None: continue
|
||||
# Conditional inject: store only when ptr != NULL (preserve user-set values)
|
||||
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty_j, "null")
|
||||
if fd_null is None: continue
|
||||
fd_cmp: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, fd_ptr, fd_null)
|
||||
if fd_cmp is None: continue
|
||||
# Unique block names (create_block does not auto-number; duplicate labels in textual IR
|
||||
# conflict -> llc "Terminator found in the middle of a basic block").
|
||||
vtc2: int = trans._label_counter
|
||||
trans._label_counter = vtc2 + 1
|
||||
fd_bb_buf2: str = pool.alloc(64)
|
||||
if fd_bb_buf2 is None: continue
|
||||
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
|
||||
fd_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
|
||||
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
|
||||
fd_inject_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
|
||||
if fd_skip_bb is None or fd_inject_bb is None: continue
|
||||
mlirlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
|
||||
# inject block: bitcast + GEP + store, then jump back to skip
|
||||
mlirlite.position_at_end(builder, fd_inject_bb)
|
||||
fd_cast: mlirlite.Value* = mlirlite.build_bitcast(builder, fd_ptr, fe.Ty)
|
||||
if fd_cast is not None:
|
||||
fd_fp: mlirlite.Value* = mlirlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
|
||||
if fd_fp is not None: mlirlite.build_store(builder, fd_cast, fd_fp)
|
||||
mlirlite.build_br(builder, fd_skip_bb)
|
||||
# skip block: restore CurBlock and continue codegen
|
||||
mlirlite.position_at_end(builder, fd_skip_bb)
|
||||
injected += 1
|
||||
return injected
|
||||
# _emit_provider_push / _emit_provider_pop - FakeDuck: with-context provider push/pop (__provides__ metadata).
|
||||
# Matches TPC HandlesWith.py L138-174: push _push_provider(field, ctx_i8) on entry and equal _pop_provider() on exit.
|
||||
|
||||
# ============================================================
|
||||
def _emit_provider_push(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, ctx_val: mlirlite.Value*) -> int:
|
||||
"""Emit a _push_provider(field, ctx_i8) call per provided field, return the field count"""
|
||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||
if cls_name is None or ctx_val is None: return 0
|
||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||
if fd_pc <= 0: return 0
|
||||
# _withcontent._push_provider (cross-module, same visibility guard as _find_provider:
|
||||
# skip when this module does not import _withcontent, to avoid undefined symbol).
|
||||
fd_pfn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_push_provider")
|
||||
if fd_pfn_probe is None: return 0
|
||||
fd_pfn: str = mlirlite.function_get_name(fd_pfn_probe)
|
||||
if fd_pfn is None: return 0
|
||||
i8_ty_p: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if i8_ty_p is None: return 0
|
||||
i8_ptr_ty_p: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_p)
|
||||
if i8_ptr_ty_p is None: return 0
|
||||
ret_i32_p: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if ret_i32_p is None: return 0
|
||||
# Bitcast the ctx value to i8* (provider stack stores void*)
|
||||
fd_ctx_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
|
||||
if fd_ctx_i8 is None: return 0
|
||||
for pi in range(int(fd_pc)):
|
||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
|
||||
if fname is None: continue
|
||||
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
||||
if fd_fstr is None: continue
|
||||
mlirlite.value_set_next(fd_fstr, fd_ctx_i8)
|
||||
mlirlite.value_set_next(fd_ctx_i8, None)
|
||||
self._ensure_method_declare(pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
|
||||
mlirlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
|
||||
return fd_pc
|
||||
def _emit_provider_pop(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str) -> int:
|
||||
"""Emit a _pop_provider() call per provided field, return the field count"""
|
||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||
if cls_name is None: return 0
|
||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||
if fd_pc <= 0: return 0
|
||||
# _withcontent._pop_provider (cross-module, same visibility guard as _find_provider:
|
||||
# skip when this module does not import _withcontent, to avoid undefined symbol).
|
||||
fd_pon_probe: mlirlite.Function* = self.find_func_in_module(mod, "_pop_provider")
|
||||
if fd_pon_probe is None: return 0
|
||||
fd_pon: str = mlirlite.function_get_name(fd_pon_probe)
|
||||
if fd_pon is None: return 0
|
||||
ret_i32_q: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if ret_i32_q is None: return 0
|
||||
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
|
||||
for _pi in range(int(fd_pc)): mlirlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
|
||||
return fd_pc
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,594 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenLib - CallGenMixin split (stage C): C-lib declares / return-type inference
|
||||
novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
"""C-lib declarations / return-type inference / kwargs call helpers (extends Base)"""
|
||||
|
||||
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str) -> mlirlite.Function*:
|
||||
"""Create a declare for a known C-lib function"""
|
||||
if pool is None or mod is None or name is None: return None
|
||||
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
|
||||
ret_ty: mlirlite.LLVMType* = None
|
||||
params: int = 0 # parameter count
|
||||
|
||||
if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0:
|
||||
ret_ty = i64_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "memset") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "memcpy") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "memmove") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "memcmp") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "strcmp") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 2
|
||||
elif string.strcmp(name, "strncmp") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "strcpy") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 2
|
||||
elif string.strcmp(name, "strncpy") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "strcat") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 2
|
||||
elif string.strcmp(name, "puts") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "atoi") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "atol") == 0:
|
||||
ret_ty = i64_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "abs") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "labs") == 0:
|
||||
ret_ty = i64_ty
|
||||
params = 1
|
||||
else: return None
|
||||
|
||||
# create the declare
|
||||
fn: mlirlite.Function* = mlirlite.create_declare(pool, mod, name, ret_ty)
|
||||
if fn is None: return None
|
||||
# add parameters
|
||||
if params == 1:
|
||||
if string.strcmp(name, "abs") == 0: mlirlite.add_param(pool, fn, i32_ty, "n")
|
||||
elif string.strcmp(name, "labs") == 0: mlirlite.add_param(pool, fn, i64_ty, "n")
|
||||
else: mlirlite.add_param(pool, fn, i8_ptr_ty, "s")
|
||||
elif params == 2:
|
||||
mlirlite.add_param(pool, fn, i8_ptr_ty, "dst")
|
||||
mlirlite.add_param(pool, fn, i8_ptr_ty, "src")
|
||||
elif params == 3:
|
||||
if string.strcmp(name, "memset") == 0:
|
||||
mlirlite.add_param(pool, fn, i8_ptr_ty, "dst")
|
||||
mlirlite.add_param(pool, fn, i32_ty, "c")
|
||||
mlirlite.add_param(pool, fn, i64_ty, "n")
|
||||
else:
|
||||
mlirlite.add_param(pool, fn, i8_ptr_ty, "dst")
|
||||
mlirlite.add_param(pool, fn, i8_ptr_ty, "src")
|
||||
mlirlite.add_param(pool, fn, i64_ty, "n")
|
||||
return fn
|
||||
|
||||
def _promote_clib_call_args(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, count: int) -> mlirlite.Value*:
|
||||
"""Promote bare-name C-lib call arguments to their standard signature widths"""
|
||||
if pool is None or builder is None or call_name is None or head is None: return head
|
||||
# bare names only (no '.' -> no sha1 prefix); prefixed ones use vpi definition-side parsing
|
||||
if string.strchr(call_name, 46) is not None: return head
|
||||
# whitelist: index of the size_t (i64) length param (matches _ensure_c_lib_declare)
|
||||
size_idx: int = -1
|
||||
if string.strcmp(call_name, "memset") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "memcpy") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "memmove") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "memcmp") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "strncmp") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "strncpy") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "strncat") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "vsnprintf") == 0: size_idx = 1
|
||||
if size_idx < 0 or count <= size_idx: return head
|
||||
# locate the length param (index size_idx)
|
||||
cur: mlirlite.Value* = head
|
||||
idx: int = 0
|
||||
while cur is not None and idx < size_idx:
|
||||
cur = cur.Next
|
||||
idx += 1
|
||||
if cur is None or cur.Ty is None: return head
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
promoted: mlirlite.Value* = None
|
||||
match cur.Ty:
|
||||
case mlirlite.LLVMType.Int(bits):
|
||||
if bits >= 64: return head
|
||||
if cur.IsConst != 0:
|
||||
# constant: use an i64 constant directly (bit pattern kept, unsigned length)
|
||||
promoted = mlirlite.const_int(pool, 64, cur.IntVal)
|
||||
else:
|
||||
# SSA value: zext to i64 (size_t is an unsigned length)
|
||||
promoted = mlirlite.build_zext(builder, cur, i64_ty)
|
||||
case _:
|
||||
return head
|
||||
if promoted is None: return head
|
||||
# rebuild the arg list: replace index size_idx with the promoted value
|
||||
new_head: mlirlite.Value* = None
|
||||
new_tail: mlirlite.Value* = None
|
||||
cur2: mlirlite.Value* = head
|
||||
i2: int = 0
|
||||
while cur2 is not None:
|
||||
node_v: mlirlite.Value* = cur2
|
||||
if i2 == size_idx: node_v = promoted
|
||||
if new_head is None: new_head = node_v
|
||||
else: new_tail.Next = node_v
|
||||
new_tail = node_v
|
||||
cur2 = cur2.Next
|
||||
i2 += 1
|
||||
return new_head
|
||||
|
||||
# _coerce_args_to_def_params: coerce cross-module call args to vpi definition-side param
|
||||
# types so call/declare/define widths match; bare C-lib names use the C-lib path instead.
|
||||
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, call_name: str, head: mlirlite.Value*, count: int, cargs: list[ast.AST*]* = None, self_off: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Coerce cross-module call args to vpi definition-side param types; returns the rebuilt chain"""
|
||||
if pool is None or builder is None or call_name is None or head is None: return head
|
||||
if self._is_sha1_mangled_call(call_name) == 0: return head
|
||||
# split {sha1}.{rest}: if rest has '.', it is {cls}.{method}, else {func}
|
||||
rest: str = call_name + 17
|
||||
cls_nm: str = None
|
||||
meth_nm: str = rest
|
||||
# take the last '.': the method has none, and the first '.' would truncate qualified type args
|
||||
dot_r: str = string.strrchr(rest, 46)
|
||||
if dot_r is not None:
|
||||
cls_len: sizet = uint64(dot_r) - uint64(rest)
|
||||
if cls_len <= 0: return head
|
||||
cls_buf: bytes = stdlib.malloc(cls_len + 1)
|
||||
if cls_buf is None: return head
|
||||
string.strncpy(cls_buf, rest, cls_len)
|
||||
cls_buf[cls_len] = '\0'
|
||||
cls_nm = cls_buf
|
||||
meth_nm = dot_r + 1
|
||||
if meth_nm is None or meth_nm[0] == '\0':
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return head
|
||||
sha1_buf: bytes = stdlib.malloc(17)
|
||||
if sha1_buf is None:
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return head
|
||||
string.strncpy(sha1_buf, call_name, 16)
|
||||
sha1_buf[16] = '\0'
|
||||
# ensure-load + direct cache lookup (not _vpi_method_param_annot, which misses module funcs)
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir)
|
||||
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
|
||||
# generic-specialized class: annotations live on the template class, so fall back to template lookup
|
||||
tpl_src: int = 0
|
||||
if prm_annots is None:
|
||||
if cls_nm is not None and string.strchr(cls_nm, 91) is not None: tpl_src = 1
|
||||
else:
|
||||
stdlib.free(sha1_buf)
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return head
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
new_head: mlirlite.Value* = None
|
||||
new_tail: mlirlite.Value* = None
|
||||
cur: mlirlite.Value* = head
|
||||
idx: int = 0
|
||||
while cur is not None and idx < count:
|
||||
node_v: mlirlite.Value* = cur
|
||||
tgt_ty: mlirlite.LLVMType* = None
|
||||
if tpl_src != 0: tgt_ty = VpiReader._resolve_vpi_param_annot_seg_template(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty)
|
||||
else: tgt_ty = VpiReader._resolve_vpi_param_annot_seg(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty)
|
||||
if tgt_ty is not None and cur.Ty is not None:
|
||||
# Argument AST (for source-symbolic checks): AST index = chain index idx - self offset
|
||||
arg_idx: int = idx - self_off
|
||||
arg_node: ast.AST* = None
|
||||
if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs): arg_node = cargs.get(arg_idx)
|
||||
# Value-reference params (&T / &temp T): auto-address the argument (cross-module callee).
|
||||
# The mode comes from the definition-side vpi annotation segment (shared resolver); if the
|
||||
# arg is not a pointer it is addressed (ref: lvalue) or copied into a temp slot (temp).
|
||||
vpi_cur_sha: str = None
|
||||
if trans is not None: vpi_cur_sha = trans.ModuleSha1
|
||||
mode_cm: int = self.Gen.CallGenBase._vpi_auto_addr_mode(call_name, vpi_cur_sha, idx)
|
||||
if mode_cm != 0 and trans is not None and trans.Module is not None:
|
||||
node_v = self.Gen.CallGenBase._apply_auto_addr_arg(builder, pool, trans.Module, trans, mode_cm, arg_node, node_v)
|
||||
# "str / bytes are not generic pointer wildcards": void* / t.CPtr / rawptr may
|
||||
# convert to any pointer, but str/bytes may not, even though both are i8* at LLVM level.
|
||||
if self.Gen.ExprGenBase._str_rejects_ptr_kind(cur) != 0:
|
||||
if VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "str") != 0 or VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "bytes") != 0:
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_agg_ptr(cur, 1, arg_node)
|
||||
node_v = self._coerce_arg_value_to_ty(pool, builder, node_v, tgt_ty, arg_node, trans, tpl_src)
|
||||
if new_head is None: new_head = node_v
|
||||
else: new_tail.Next = node_v
|
||||
new_tail = node_v
|
||||
cur = cur.Next
|
||||
idx += 1
|
||||
stdlib.free(sha1_buf)
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return new_head
|
||||
|
||||
# _coerce_arg_value_to_ty: convert one arg Value to the target param type -- same-width ints
|
||||
# pass through, narrow/wide ints trunc/extend, mismatched pointers bitcast; else unchanged.
|
||||
def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, tgt_ty: mlirlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None, tpl_src: int = 0) -> mlirlite.Value*:
|
||||
"""Convert one argument Value to the target param type; leaves it unchanged when unsafe"""
|
||||
if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None: return val
|
||||
match val.Ty:
|
||||
case mlirlite.LLVMType.Int(bits_a):
|
||||
match tgt_ty:
|
||||
case mlirlite.LLVMType.Int(bits_b):
|
||||
if bits_b == bits_a: return val
|
||||
if bits_b < bits_a:
|
||||
if val.IsConst != 0:
|
||||
nv_c: mlirlite.Value* = mlirlite.const_int(pool, bits_b, val.IntVal)
|
||||
if nv_c is not None: return nv_c
|
||||
else:
|
||||
nv_t: mlirlite.Value* = mlirlite.build_trunc(builder, val, tgt_ty)
|
||||
if nv_t is not None: return nv_t
|
||||
# widening (arg narrower than param): rebuild constants by decimal value
|
||||
if val.IsConst != 0:
|
||||
nv_w: mlirlite.Value* = mlirlite.const_int(pool, bits_b, val.IntVal)
|
||||
if nv_w is not None: return nv_w
|
||||
return val
|
||||
# SSA widening: direction from source signedness (i1/i8/unsigned -> zext, else sext)
|
||||
if bits_b > bits_a:
|
||||
if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, node, trans) != 0:
|
||||
nv_zw: mlirlite.Value* = mlirlite.build_zext(builder, val, tgt_ty)
|
||||
if nv_zw is not None: return nv_zw
|
||||
else:
|
||||
nv_sw: mlirlite.Value* = mlirlite.build_sext(builder, val, tgt_ty)
|
||||
if nv_sw is not None: return nv_sw
|
||||
return val
|
||||
case _:
|
||||
pass
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
match tgt_ty:
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
# same-level pointers: bitcast when the arg type differs from the definition
|
||||
# side (aligned so the first call site's declare matches; llc otherwise errors).
|
||||
if self.Gen.AssignGen._stg_ty_equal(pool, val.Ty, tgt_ty) == 0:
|
||||
nv_p: mlirlite.Value* = mlirlite.build_bitcast(builder, val, tgt_ty)
|
||||
if nv_p is not None: return nv_p
|
||||
case _:
|
||||
pass
|
||||
case _:
|
||||
# aggregate -> pointer: spill rvalue struct/array and take its address (params lower to Ptr)
|
||||
match tgt_ty:
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
vk_a: int = val.Ty.kind()
|
||||
if vk_a == mlirlite.LLVMType.Struct or vk_a == mlirlite.LLVMType.Array:
|
||||
tmp_a: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty)
|
||||
if tmp_a is not None:
|
||||
mlirlite.build_store(builder, val, tmp_a)
|
||||
return tmp_a
|
||||
case _:
|
||||
pass
|
||||
return val
|
||||
|
||||
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy*, func_name: str, node: ast.AST*, fatal_if_unknown: int) -> mlirlite.LLVMType*:
|
||||
"""Infer the return type of an external includes function; a whitelist miss may fail fast"""
|
||||
if pool is None or func_name is None: return mlirlite.Int32(pool)
|
||||
|
||||
# extract the bare function name by stripping the SHA1 prefix
|
||||
# (16 hex digits + '.'), e.g. "240a9a4157959a9f.parse" -> "parse".
|
||||
bare_name: str = func_name
|
||||
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
|
||||
if dot_pos is not None:
|
||||
# the part after '.' is the bare function name
|
||||
bare_name = dot_pos + 1
|
||||
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# functions returning i8* (truncating the pointer would be wrong)
|
||||
if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strcpy") == 0 or string.strcmp(func_name, "strcpy") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strncpy") == 0 or string.strcmp(func_name, "strncpy") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memset") == 0 or string.strcmp(func_name, "memset") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memmove") == 0 or string.strcmp(func_name, "memmove") == 0: return i8_ptr_ty
|
||||
# JSON parse functions return JsonValue* (pointer); include the json_parse alias
|
||||
# (from json.__parser import parse as json_parse).
|
||||
if string.strcmp(bare_name, "parse") == 0 or string.strcmp(func_name, "parse") == 0 or string.strcmp(func_name, "json_parse") == 0: return i8_ptr_ty
|
||||
# MemBuddy methods (may use the external-function path when type info is lost).
|
||||
# malloc: bare-name CExport cross-module call (uhci/desktop mm.malloc -> @malloc), else ret is inferred i32 and the 64-bit pointer truncates.
|
||||
if string.strcmp(bare_name, "malloc") == 0 or string.strcmp(func_name, "malloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "alloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "calloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "realloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "alloc_buf") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# VLogger module funcs (stubs not injected cross-module, pointers truncated to i32)
|
||||
if string.strcmp(func_name, "get_logger") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "fmt_buf") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Config module funcs (stubs not injected cross-module, i8* truncated to i32)
|
||||
if string.strcmp(func_name, "get_includes_binary_dir") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# HandlesType module func (returns ast.AST* union; inferred as i32 cross-module)
|
||||
if string.strcmp(bare_name, "_parse_type_string") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# Functions returning i64
|
||||
if string.strcmp(func_name, "strlen") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "atoll") == 0: return mlirlite.Int64(pool)
|
||||
# mlirlite name-length accessors return sizet (i64); the default i32 clashes with the declare
|
||||
if string.strcmp(bare_name, "function_get_name_len") == 0 or string.strcmp(func_name, "function_get_name_len") == 0: return mlirlite.Int64(pool)
|
||||
|
||||
# Functions returning double
|
||||
if string.strcmp(func_name, "atof") == 0: return mlirlite.Double(pool)
|
||||
|
||||
# Functions returning i8
|
||||
if string.strcmp(func_name, "samestr") == 0: return mlirlite.Int8(pool)
|
||||
|
||||
# Win32 APIs returning a pointer (VOIDPTR/HANDLE); no stubs are injected, so infer
|
||||
# i8* to keep the pointer from being truncated to i32 and crashing on dereference.
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "VirtualAlloc") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapAlloc") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapReAlloc") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapCreate") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetProcessHeap") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GlobalAlloc") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GlobalFree") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GlobalLock") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LocalAlloc") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LocalFree") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetProcAddress") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetModuleHandleA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetModuleHandleW") == 0: return i8_ptr_ty
|
||||
# GetCommandLineA/W return LPSTR (i8*); without it a 64-bit pointer is truncated to i32
|
||||
if string.strcmp(func_name, "GetCommandLineA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetCommandLineW") == 0: return i8_ptr_ty
|
||||
# gethostbyname returns hostent* (i8*)
|
||||
if string.strcmp(func_name, "gethostbyname") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LoadLibraryA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LoadLibraryW") == 0: return i8_ptr_ty
|
||||
# Win32 handle-returning functions (HANDLE = void* = i8*)
|
||||
if string.strcmp(func_name, "GetStdHandle") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "CreateFileA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "CreateFileW") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "FindFirstFileA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "FindFirstFileW") == 0: return i8_ptr_ty
|
||||
|
||||
# Win32 APIs returning i64 (SIZE_T)
|
||||
if string.strcmp(func_name, "VirtualQuery") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "HeapSize") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "HeapCompact") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "GlobalSize") == 0: return mlirlite.Int64(pool)
|
||||
|
||||
# mlirlite build_* return-type inference: most return Value*, terminators return void
|
||||
if string.strncmp(func_name, "build_", 6) == 0:
|
||||
# build_* returning void (terminators and void-returning ops)
|
||||
if string.strcmp(func_name, "build_ret") == 0 or string.strcmp(func_name, "build_ret_void") == 0 or string.strcmp(func_name, "build_store") == 0 or string.strcmp(func_name, "build_br") == 0 or string.strcmp(func_name, "build_cond_br") == 0 or string.strcmp(func_name, "build_unreachable") == 0 or string.strcmp(func_name, "build_switch") == 0:
|
||||
return mlirlite.Void(pool)
|
||||
# other build_* return Value* (pointer)
|
||||
return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# mlirlite type factories / const constructors / accessors return pointers (LLVMType* / Value*)
|
||||
if string.strcmp(func_name, "Int1") == 0 or string.strcmp(func_name, "Int8") == 0 or string.strcmp(func_name, "Int16") == 0 or string.strcmp(func_name, "Int32") == 0 or string.strcmp(func_name, "Int64") == 0 or string.strcmp(func_name, "Ptr") == 0 or string.strcmp(func_name, "Func") == 0 or string.strcmp(func_name, "Array") == 0 or string.strcmp(func_name, "Struct") == 0 or string.strcmp(func_name, "Void") == 0 or string.strcmp(func_name, "Half") == 0 or string.strcmp(func_name, "Float") == 0 or string.strcmp(func_name, "Double") == 0 or string.strcmp(func_name, "FP128") == 0 or string.strcmp(func_name, "Label") == 0 or string.strcmp(func_name, "new_param_node") == 0 or string.strcmp(func_name, "new_phi_incoming") == 0 or string.strcmp(func_name, "const_int32") == 0 or string.strcmp(func_name, "const_int64") == 0 or string.strcmp(func_name, "ConstFloat") == 0 or string.strcmp(func_name, "ConstZero") == 0 or string.strcmp(func_name, "ConstNull") == 0 or string.strcmp(func_name, "ConstInt") == 0 or string.strcmp(func_name, "function_get_name") == 0 or string.strcmp(func_name, "param_get_ty") == 0 or string.strcmp(func_name, "value_get_ty") == 0 or string.strcmp(func_name, "value_get_next") == 0 or string.strcmp(func_name, "function_get_ret_ty") == 0 or string.strcmp(func_name, "function_get_params") == 0 or string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0:
|
||||
return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# Default i32 placeholder: fatal_if_unknown=1 fails fast; =0 (Phase A mid-pass) stays silent.
|
||||
if fatal_if_unknown != 0: self.Gen.TypeGenErr.fatal_error(node, f"external function return type is unregistered and cannot be inferred conclusively (defaulting to i32 would truncate pointers/i64; provide a stub declaration or add it to the whitelist): {func_name}", "TypeError")
|
||||
return mlirlite.Int32(pool)
|
||||
def _get_external_func_param_ty(self, pool: memhub.MemBuddy*, func_name: str, param_idx: int) -> mlirlite.LLVMType*:
|
||||
"""Return the expected type of an external function's param_idx-th parameter, or None (fallback only)"""
|
||||
if pool is None or func_name is None: return None
|
||||
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
|
||||
# Win32 API list: every non-pointer parameter is i64 in declare
|
||||
# (ULONG/DWORD/SIZE_T are all mapped to i64 during LLVM stub translation)
|
||||
win32_apis: list[str]* = ["VirtualAlloc", "VirtualFree", "VirtualProtect", "VirtualQuery", "VirtualLock", "VirtualUnlock", "HeapCreate", "HeapDestroy", "HeapAlloc", "HeapReAlloc", "HeapFree", "HeapSize", "HeapValidate", "HeapCompact", "GlobalAlloc", "GlobalFree", "GlobalLock", "GlobalUnlock", "GlobalSize", "LocalAlloc", "LocalFree", "GetLastError", "SetLastError", "Sleep", "SleepEx", "GetTickCount", "GetProcessHeap"]
|
||||
|
||||
# list iteration is unsupported; use range + get instead
|
||||
api_count: sizet = len(win32_apis)
|
||||
for ai in range(int(api_count)):
|
||||
api_name: str = win32_apis.get(ai)
|
||||
if string.strcmp(api_name, func_name) == 0: return i64_ty
|
||||
|
||||
# Allocators: size params are declared i64 (size_t); without promotion on bare-name fallback call/define widths mismatch; calloc/realloc size is idx 1
|
||||
if string.strcmp(func_name, "malloc") == 0 or string.strcmp(func_name, "alloc") == 0 or string.strcmp(func_name, "alloc_buf") == 0:
|
||||
if param_idx == 0: return i64_ty
|
||||
if string.strcmp(func_name, "calloc") == 0:
|
||||
if param_idx == 0 or param_idx == 1: return i64_ty
|
||||
if string.strcmp(func_name, "realloc") == 0:
|
||||
if param_idx == 1: return i64_ty
|
||||
|
||||
return None
|
||||
def _is_sha1_mangled_call(self, call_name: str) -> int:
|
||||
"""Return whether call_name is a cross-module Viper function ("{16 hex}.func")"""
|
||||
if call_name is None: return 0
|
||||
if GenApi.sha1_prefix_len(call_name) != 0: return 1
|
||||
return 0
|
||||
def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, dst_val: mlirlite.Value*, src_val: mlirlite.Value*, num_val: mlirlite.Value*) -> mlirlite.Value*:
|
||||
"""Map a memcpy call to the @llvm.memcpy intrinsic"""
|
||||
if pool is None or builder is None or mod is None: return None
|
||||
if dst_val is None or src_val is None or num_val is None: return None
|
||||
|
||||
# Declare void_ty at the top of the function to avoid later shadowing
|
||||
void_ty: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||
|
||||
# Declare @llvm.memcpy (if not already declared)
|
||||
memcpy_func: mlirlite.Function* = self.find_func_in_module(mod, "llvm.memcpy")
|
||||
if memcpy_func is None:
|
||||
memcpy_func = mlirlite.create_declare(pool, mod, "llvm.memcpy", void_ty)
|
||||
if memcpy_func is not None:
|
||||
i8_ptr_ty_d: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
i64_ty_d: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
i1_ty_d: mlirlite.LLVMType* = mlirlite.Int1(pool)
|
||||
mlirlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "dst")
|
||||
mlirlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "src")
|
||||
mlirlite.add_param(pool, memcpy_func, i64_ty_d, "len")
|
||||
mlirlite.add_param(pool, memcpy_func, i1_ty_d, "isvolatile")
|
||||
|
||||
# Coercions: dst, src -> i8*; num -> i64
|
||||
# Use the value_get_ty accessor for Ty to avoid local Value field-index offset
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
|
||||
# dst → i8*
|
||||
dst_coerced: mlirlite.Value* = dst_val
|
||||
dst_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(dst_val)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(dst_ty) != 0: dst_coerced = mlirlite.build_bitcast(builder, dst_val, i8_ptr_ty)
|
||||
else:
|
||||
dst_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dst_ty)
|
||||
if dst_bits != 0:
|
||||
if dst_bits < 64: dst_coerced = mlirlite.build_zext(builder, dst_val, i64_ty)
|
||||
dst_coerced = mlirlite.build_inttoptr(builder, dst_coerced, i8_ptr_ty)
|
||||
|
||||
# src → i8*
|
||||
src_coerced: mlirlite.Value* = src_val
|
||||
src_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(src_val)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(src_ty) != 0: src_coerced = mlirlite.build_bitcast(builder, src_val, i8_ptr_ty)
|
||||
else:
|
||||
src_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(src_ty)
|
||||
if src_bits != 0:
|
||||
if src_bits < 64: src_coerced = mlirlite.build_zext(builder, src_val, i64_ty)
|
||||
src_coerced = mlirlite.build_inttoptr(builder, src_coerced, i8_ptr_ty)
|
||||
|
||||
# num → i64
|
||||
num_coerced: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, num_val, i64_ty)
|
||||
|
||||
if dst_coerced is None or src_coerced is None or num_coerced is None: return None
|
||||
|
||||
# Append the i1 false arg (isvolatile) - llvm.memcpy needs 4 parameters
|
||||
false_val: mlirlite.Value* = mlirlite.ConstInt(pool, mlirlite.Int1(pool), 0, "false")
|
||||
|
||||
# Build the arg list via include accessors to avoid local Value field-index offset
|
||||
# (f26cd4c78b4d554c.Value lacks GSListNode.Next; direct .Next access corrupts .Ty)
|
||||
mlirlite.value_set_next(dst_coerced, src_coerced)
|
||||
mlirlite.value_set_next(src_coerced, num_coerced)
|
||||
if false_val is not None:
|
||||
mlirlite.value_set_next(num_coerced, false_val)
|
||||
mlirlite.value_set_next(false_val, None)
|
||||
else: mlirlite.value_set_next(num_coerced, None)
|
||||
|
||||
# Emit the call via build_call (it accesses include Value fields with correct indices)
|
||||
# _ll_name_needs_quote already exempts the llvm. prefix from quoting
|
||||
result: mlirlite.Value* = mlirlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0)
|
||||
|
||||
# Return dst (matches C memcpy semantics: returns the dest pointer)
|
||||
return dst_coerced
|
||||
# _translate_call_with_kwargs: keyword-arg call; builds args in found_func.Params order (positional first, else match by param name)
|
||||
|
||||
# ============================================================
|
||||
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, func_name: str, found_func: mlirlite.Function*, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Translate a call with keyword arguments, building args in parameter order"""
|
||||
head: mlirlite.Value* = None
|
||||
tail: mlirlite.Value* = None
|
||||
actual_count: sizet = 0
|
||||
# memcpy-only: collect independent refs of the first three Values (avoid shared alloca Next pollution)
|
||||
mc_dst1: mlirlite.Value* = None
|
||||
mc_src1: mlirlite.Value* = None
|
||||
mc_num1: mlirlite.Value* = None
|
||||
|
||||
# Default-arg lookup for omitted params (same source as CallGenMixin non-kwargs path):
|
||||
# module SHA1 is the "{sha1}.func" prefix or current module; the name+SHA1-keyed snapshot survives resets.
|
||||
kw_callee_sha: str = None
|
||||
kw_found_name: str = mlirlite.function_get_name(found_func)
|
||||
if kw_found_name is not None:
|
||||
if GenApi.sha1_prefix_len(kw_found_name) != 0:
|
||||
kw_sha_buf: str = pool.alloc(17)
|
||||
if kw_sha_buf is not None:
|
||||
string.strncpy(kw_sha_buf, kw_found_name, 16)
|
||||
kw_sha_buf[16] = '\0'
|
||||
kw_callee_sha = kw_sha_buf
|
||||
if kw_callee_sha is None and self.Gen.TypeGenBase is not None: kw_callee_sha = self.Gen.TypeGenBase.CurrentModuleSha1
|
||||
kw_gd: GenTypes.FuncDefaultsEntry* = None
|
||||
if kw_callee_sha is not None: kw_gd = self.Gen.CallGenLeafKit.find_func_defaults(func_name, kw_callee_sha)
|
||||
kw_default_count: int = 0
|
||||
kw_default_off: int = 0
|
||||
if kw_gd is not None:
|
||||
kw_default_count = kw_gd.DefaultCount
|
||||
kw_default_off = kw_gd.ParamCount - kw_gd.DefaultCount
|
||||
if kw_default_off < 0: kw_default_off = 0
|
||||
|
||||
# Walk the function parameter list (accessor gets Head)
|
||||
param_idx: sizet = 0
|
||||
cur_param: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
|
||||
|
||||
while cur_param is not None:
|
||||
arg_val: mlirlite.Value* = None
|
||||
param_ty: mlirlite.LLVMType* = mlirlite.param_get_ty(cur_param)
|
||||
if param_idx < can:
|
||||
# Prefer the positional argument
|
||||
arg: ast.AST* = cargs.get(param_idx)
|
||||
if arg is not None:
|
||||
# Pass the param type (char literal -> str param materializes a string; see translate_constant)
|
||||
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans, param_ty)
|
||||
else:
|
||||
# Find the matching keyword argument
|
||||
param_name: str = mlirlite.param_get_name(cur_param)
|
||||
if param_name is not None:
|
||||
for ki in range(int(kw_count)):
|
||||
kw: ast.Keyword* = ast.Keyword*(keywords.get(ki))
|
||||
if kw is None or kw.arg is None or kw.value is None: continue
|
||||
if self.Gen.CallGenCall._kwarg_name_matches(kw.arg, param_name) == 1:
|
||||
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, kw.value, None, 0, trans, param_ty)
|
||||
break
|
||||
|
||||
# Param supplied by neither position nor keyword: fill with the definition-site default
|
||||
# (index = param_idx - param_count + default_count); else fail-fast, never silently drop/zero.
|
||||
if arg_val is None:
|
||||
if kw_default_count > 0 and int(param_idx) >= kw_default_off:
|
||||
kw_di: int = int(param_idx) - kw_default_off
|
||||
if kw_di >= 0 and kw_di < kw_default_count:
|
||||
arg_val = self.Gen.CallGenLeafKit._build_const_from_snapshot(pool, mod, kw_gd, kw_di, trans, builder)
|
||||
if arg_val is None:
|
||||
kw_node: ast.AST* = None
|
||||
if keywords is not None and kw_count > 0: kw_node = keywords.get(0)
|
||||
self.Gen.TypeGenErr.fatal_error(kw_node, f"a parameter of call '{func_name}' is passed neither positionally nor by keyword, " f"and the definition has no default value to fill in (refusing to silently drop/zero a parameter)", "TypeError")
|
||||
|
||||
if arg_val is not None:
|
||||
# Coerce by parameter type (via accessor)
|
||||
if param_ty is not None: arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, param_ty)
|
||||
arg_val.Next = None
|
||||
if head is None: head = arg_val
|
||||
else: tail.Next = arg_val
|
||||
tail = arg_val
|
||||
# memcpy-only: save independent refs of the first three args
|
||||
if actual_count == 0: mc_dst1 = arg_val
|
||||
elif actual_count == 1: mc_src1 = arg_val
|
||||
elif actual_count == 2: mc_num1 = arg_val
|
||||
actual_count += 1
|
||||
|
||||
param_idx += 1
|
||||
cur_param = mlirlite.param_get_next(cur_param)
|
||||
|
||||
# declare and define both use found_func.Name: normal funcs carry the SHA1 prefix, t.CExtern/t.State/t.CExport use bare names (bare names desync call/define symbols)
|
||||
call_name: str = func_name
|
||||
found_name: str = mlirlite.function_get_name(found_func)
|
||||
if found_name is not None: call_name = found_name
|
||||
|
||||
# Return type (via accessor)
|
||||
call_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(found_func)
|
||||
if call_ret_ty is None:
|
||||
# found_func has a declare but return type is unreadable (accessor anomaly, rare guard):
|
||||
# even if the whitelist misses, do not fail-fast; keep the default i32.
|
||||
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name, None, 0)
|
||||
|
||||
# memcpy -> @llvm.memcpy intrinsic (avoid a missing cross-module @memcpy declare under monomorphization)
|
||||
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3: return self._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1)
|
||||
return mlirlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,315 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# CallGenProp - split from CallGenMixin (stage B): property access (getter/setter/deleter / static class calls / list builtins)
|
||||
# ============================================================
|
||||
novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
"""Property access (getter/setter/deleter / static class calls / list builtins)"""
|
||||
|
||||
# Property methods (@property/@x.setter/@x.deleter) and static/class call helpers.
|
||||
# Translate obj.prop / obj.prop=v / del obj.prop / Class.method() into the matching {sha1}.ClassName.prop call.
|
||||
|
||||
# _translate_property_call - translate a property/static method call.
|
||||
# Resolve the call name like _translate_method_call; call_head/call_count is the prebuilt arg list.
|
||||
def _translate_property_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, call_head: mlirlite.Value*, call_count: int, trans: HT.Translator*, arg_nodes: list[ast.AST*]* = None, start_idx: int = 0) -> mlirlite.Value*:
|
||||
"""Translate a property/static method call, return the call result Value"""
|
||||
if pool is None or builder is None or class_name is None or method_name is None: return None
|
||||
|
||||
# Build the lookup name: ClassName.method_name
|
||||
lookup_name: str = pool.alloc(128)
|
||||
if lookup_name is None: return None
|
||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
|
||||
|
||||
# Look up the function (exact/suffix match)
|
||||
found_func: mlirlite.Function* = self.find_func_in_module(mod, lookup_name)
|
||||
|
||||
# Determine the call name
|
||||
call_name: str = lookup_name
|
||||
if found_func is not None:
|
||||
found_name: str = mlirlite.function_get_name(found_func)
|
||||
if found_name is not None: call_name = found_name
|
||||
else:
|
||||
# Not found: build the call name from the class's owning module SHA1
|
||||
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||
if cls_sha1_pc is not None:
|
||||
mlen_pc: sizet = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
|
||||
mangled_pc: str = pool.alloc(mlen_pc)
|
||||
if mangled_pc is not None:
|
||||
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
|
||||
call_name = mangled_pc
|
||||
|
||||
# Determine the return type
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
call_ret_ty: mlirlite.LLVMType* = i32_ty
|
||||
if found_func is not None:
|
||||
frt: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(found_func)
|
||||
if frt is not None: call_ret_ty = frt
|
||||
else:
|
||||
sp_ty1: mlirlite.LLVMType* = self._special_method_ret_ty(pool, class_name, method_name)
|
||||
if sp_ty1 is not None: call_ret_ty = sp_ty1
|
||||
else:
|
||||
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||
# Authoritative source: vpi stub return-annotation cache (in place, no hardcoded whitelist)
|
||||
vpi_ret_ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
|
||||
if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty
|
||||
else:
|
||||
# Fallback: parse the module text.ll define line (legacy form not covered by vpi)
|
||||
looked_up_ty: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
|
||||
if looked_up_ty is not None: call_ret_ty = looked_up_ty
|
||||
else:
|
||||
# Phase A deps not ready (cls_sha1_rt None / the defining module's text.ll missing):
|
||||
# keep the i32 default (build_call(Void) returning None would break expression translation).
|
||||
if cls_sha1_rt is None or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: call_ret_ty = mlirlite.Int32(pool)
|
||||
else: call_ret_ty = self._fail_method_ret_ty(method_name, None, class_name, None, cls_sha1_rt)
|
||||
|
||||
# Value-reference (&T / &temp T) params: auto-address the user args before param-type
|
||||
# coercion (self/obj or the classmethod cls occupies index 0, skipped via start_idx).
|
||||
call_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, call_head, call_count, start_idx)
|
||||
|
||||
# Coerce args to the declared param types (same as _translate_method_call); target func: found_func,
|
||||
# else the mangled module declare. Otherwise call(i32) vs declare(i64) mismatches and llc errors.
|
||||
tgt_func_pc: mlirlite.Function* = found_func
|
||||
if tgt_func_pc is None: tgt_func_pc = self.find_func_in_module(mod, call_name)
|
||||
if tgt_func_pc is not None:
|
||||
prev_pc: mlirlite.Value* = None
|
||||
cur_arg_pc: mlirlite.Value* = call_head
|
||||
cur_param_pc: mlirlite.Param* = mlirlite.function_get_param_head(tgt_func_pc)
|
||||
while cur_arg_pc is not None and cur_param_pc is not None:
|
||||
param_ty_pc: mlirlite.LLVMType* = mlirlite.param_get_ty(cur_param_pc)
|
||||
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
|
||||
coerced_pc: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
|
||||
if coerced_pc is not None and coerced_pc != cur_arg_pc:
|
||||
# Keep the list intact: the new Value inherits the node's Next; predecessor points to it
|
||||
mlirlite.value_set_next(coerced_pc, cur_arg_pc.Next)
|
||||
if prev_pc is None: call_head = coerced_pc
|
||||
else: mlirlite.value_set_next(prev_pc, coerced_pc)
|
||||
cur_arg_pc = coerced_pc
|
||||
prev_pc = cur_arg_pc
|
||||
cur_arg_pc = cur_arg_pc.Next
|
||||
cur_param_pc = mlirlite.param_get_next(cur_param_pc)
|
||||
|
||||
# Ensure the declare exists, then call
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
|
||||
return mlirlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
|
||||
# _resolve_obj_class_name - resolve an object expression's class short name.
|
||||
# Prefer the variable annotation (AnnotClassName); fall back to the LLVM type (Ptr(Struct)).
|
||||
|
||||
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, obj_node: ast.AST*) -> str:
|
||||
"""Resolve an object expression's class short name, None on failure"""
|
||||
if pool is None or trans is None or obj_node is None: return None
|
||||
if obj_node.kind() == ast.ASTKind.Name:
|
||||
obj_nm: ast.Name* = ast.Name*(obj_node)
|
||||
if obj_nm.id is not None:
|
||||
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, obj_nm.id)
|
||||
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
|
||||
if annot_val is not None:
|
||||
# [D32] Split the qualified name only on '.' outside brackets: the old strrchr over the
|
||||
# whole string picked the '.' inside "list[sha1.AST*]", yielding a bogus short name.
|
||||
n_av: sizet = string.strlen(annot_val)
|
||||
last_dot_av: sizet = n_av
|
||||
depth_av: int = 0
|
||||
for ai_av in range(int(n_av)):
|
||||
ch_av: int = annot_val[ai_av]
|
||||
if ch_av == '[': depth_av += 1
|
||||
elif ch_av == ']': depth_av -= 1
|
||||
elif ch_av == '.' and depth_av == 0: last_dot_av = ai_av
|
||||
if last_dot_av < n_av: return annot_val + last_dot_av + 1
|
||||
return annot_val
|
||||
return None
|
||||
# _try_property_getter_attr - try to translate obj.attr into a property getter call.
|
||||
# If the class has an @property/@x.getter for attr, emit call Class.attr(obj); else return None.
|
||||
|
||||
def _try_property_getter_attr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, at_node: ast.Attribute*, obj_ptr: mlirlite.Value*, trans: HT.Translator*, fallback_class_name: str = None) -> mlirlite.Value*:
|
||||
"""Try a property getter access, return the call result Value; None if not a getter."""
|
||||
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None: return None
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, at_node.value)
|
||||
if class_name is None: class_name = fallback_class_name
|
||||
if class_name is None: return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
|
||||
if meta & GenTypes.META_PROP_GETTER == 0: return None
|
||||
# Single-arg call: obj -> getter (no user args to auto-address; obj is slot 0)
|
||||
mlirlite.value_set_next(obj_ptr, None)
|
||||
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans, None, 1)
|
||||
# _try_property_setter - try to translate obj.attr = v into a property setter call.
|
||||
# If the class has an @x.setter for attr, emit call Class.attr$set(obj, v) and return 1; else 0.
|
||||
|
||||
def _try_property_setter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, rhs_val: mlirlite.Value*, trans: HT.Translator*, rhs_node: ast.AST* = None) -> int:
|
||||
"""Try a property setter assignment, return 1=handled (call emitted)"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||
if class_name is None: return 0
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||
if meta & GenTypes.META_PROP_SETTER == 0: return 0
|
||||
# Translate the object value (self argument)
|
||||
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
||||
if obj_val is None: return 0
|
||||
# Build the argument list: obj -> rhs
|
||||
mlirlite.value_set_next(obj_val, None)
|
||||
mlirlite.value_set_next(rhs_val, None)
|
||||
mlirlite.value_set_next(obj_val, rhs_val)
|
||||
# The setter function name carries the $set suffix
|
||||
setter_name_buf: str = pool.alloc(128)
|
||||
if setter_name_buf is None: return 0
|
||||
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
|
||||
# Value-reference (&T / &temp T) setter value param: expose the rhs AST as user arg 0
|
||||
# (obj is slot 0) so the helper can take the lvalue address / build a temp copy.
|
||||
set_arg_nodes: list[ast.AST*]* = None
|
||||
if rhs_node is not None:
|
||||
set_arg_nodes = list[ast.AST*](pool, 1)
|
||||
if set_arg_nodes is not None: set_arg_nodes.append(rhs_node)
|
||||
self._translate_property_call(pool, builder, mod, class_name, setter_name_buf, obj_val, 2, trans, set_arg_nodes, 1)
|
||||
return 1
|
||||
# _try_property_deleter - try to translate del obj.attr into a property deleter call.
|
||||
# If the class has an @x.deleter for attr, emit call Class.attr$del(obj) and return 1; else 0.
|
||||
|
||||
def _try_property_deleter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, trans: HT.Translator*) -> int:
|
||||
"""Try a property deleter, return 1=handled (call emitted)"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if target_attr is None or target_attr.attr is None: return 0
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||
if class_name is None: return 0
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||
if meta & GenTypes.META_PROP_DELETER == 0: return 0
|
||||
# Translate the object value (self argument)
|
||||
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
||||
if obj_val is None: return 0
|
||||
mlirlite.value_set_next(obj_val, None)
|
||||
# The deleter function name carries the $del suffix
|
||||
del_name_buf: str = pool.alloc(128)
|
||||
if del_name_buf is None: return 0
|
||||
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name, del_name_buf, obj_val, 1, trans, None, 1)
|
||||
return 1
|
||||
# _translate_static_class_call - translate ClassName.method(args) for static/class methods.
|
||||
# @staticmethod: call Class.method(args...). @classmethod: prepend a stack-allocated class pointer.
|
||||
|
||||
def _translate_static_class_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Translate a ClassName static/class method call, return the call result Value; None otherwise"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return None
|
||||
if class_name is None or method_name is None or cl is None: return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
|
||||
is_static: int = 0
|
||||
is_class: int = 0
|
||||
if meta & GenTypes.META_STATIC != 0: is_static = 1
|
||||
if meta & GenTypes.META_CLASS != 0: is_class = 1
|
||||
if is_static == 0 and is_class == 0: return None
|
||||
|
||||
# Build the argument list
|
||||
head: mlirlite.Value* = None
|
||||
total_count: int = 0
|
||||
|
||||
# @classmethod: prepend a stack-allocated class pointer as the cls argument
|
||||
if is_class != 0:
|
||||
struct_ty_cc: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
||||
if struct_ty_cc is None: return None
|
||||
cls_ptr: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
|
||||
if cls_ptr is None: return None
|
||||
mlirlite.value_set_next(cls_ptr, None)
|
||||
head = cls_ptr
|
||||
total_count = 1
|
||||
|
||||
# Translate the call arguments
|
||||
cargs: list[ast.AST*]* = cl.args
|
||||
can: sizet = 0
|
||||
if cargs is not None: can = len(cargs)
|
||||
tail: mlirlite.Value* = head
|
||||
for ai in range(int(can)):
|
||||
arg_node: ast.AST* = cargs.get(ai)
|
||||
if arg_node is None: continue
|
||||
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
|
||||
if arg_val is None: continue
|
||||
mlirlite.value_set_next(arg_val, None)
|
||||
if head is None:
|
||||
head = arg_val
|
||||
tail = arg_val
|
||||
else:
|
||||
mlirlite.value_set_next(tail, arg_val)
|
||||
tail = arg_val
|
||||
total_count += 1
|
||||
|
||||
# Zero-arg @staticmethod (e.g. PyListObj.new()): head=None is a valid no-arg call, so still route
|
||||
# through _translate_property_call; returning None would fall back to bare-name lookup and fail.
|
||||
# Value-reference (&T / &temp T) user args: static methods have no self slot (start_idx=0),
|
||||
# classmethods carry the cls pointer at slot 0 (start_idx=1).
|
||||
sc_start_idx: int = 0
|
||||
if is_class != 0: sc_start_idx = 1
|
||||
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans, cargs, sc_start_idx)
|
||||
# _is_list_builtin_method - test whether (class_name, method_name) is a list generic builtin.
|
||||
# list[T] methods are inlined by translate_call's list branch, not registered as functions.
|
||||
|
||||
def _is_list_builtin_method(self, class_name: str, method_name: str) -> int:
|
||||
"""Test whether (class_name, method_name) is a list generic builtin method"""
|
||||
if class_name is None or method_name is None: return 0
|
||||
# Class name must start with "list" (list / list[AST*] specializations)
|
||||
if string.strncmp(class_name, "list", 4) != 0: return 0
|
||||
# Method name must be one of the list builtin methods
|
||||
if string.strcmp(method_name, "append") == 0: return 1
|
||||
if string.strcmp(method_name, "get") == 0: return 1
|
||||
if string.strcmp(method_name, "set") == 0: return 1
|
||||
if string.strcmp(method_name, "pop") == 0: return 1
|
||||
if string.strcmp(method_name, "clear") == 0: return 1
|
||||
if string.strcmp(method_name, "__iter__") == 0: return 1
|
||||
if string.strcmp(method_name, "__len__") == 0: return 1
|
||||
if string.strcmp(method_name, "__getitem__") == 0: return 1
|
||||
if string.strcmp(method_name, "__setitem__") == 0: return 1
|
||||
return 0
|
||||
|
||||
# _strip_module_sha1_prefix - strip the cross-module specialization sha1 prefix.
|
||||
# Receiver struct names often carry a "{16 hex}." prefix; use GenApi.sha1_prefix_len (C10).
|
||||
def _strip_module_sha1_prefix(self, name: str) -> str:
|
||||
"""Strip the "{16hex}." prefix (return the input unchanged if absent; returns a substring of the input)"""
|
||||
if name is None: return None
|
||||
pfx: int = GenApi.sha1_prefix_len(name)
|
||||
return name + pfx if pfx != 0 else name
|
||||
|
||||
# _resolve_generic_receiver_name - resolve the receiver's actual generic specialization name (IR truth).
|
||||
# Strip the sha1 prefix, take the base before '['; bare generic base => fail-fast (D25), else None.
|
||||
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*, struct_ty: mlirlite.LLVMType*, method_name: str, cl: ast.Call*) -> str:
|
||||
"""Return the actual generic specialization name (sha1 stripped) or None; a bare generic base fails fast"""
|
||||
if struct_ty is None: return None
|
||||
ty_nm: str = mlirlite.get_struct_name(struct_ty)
|
||||
if ty_nm is None or ty_nm[0] == '\0': return None
|
||||
short_nm: str = self._strip_module_sha1_prefix(ty_nm)
|
||||
lb: str = string.strchr(short_nm, '[')
|
||||
bare_len: sizet = 0
|
||||
if lb is not None: bare_len = sizet(uint64(lb) - uint64(short_nm))
|
||||
else: bare_len = string.strlen(short_nm)
|
||||
if bare_len == 0: return None
|
||||
base_nm: str = pool.alloc(bare_len + 1)
|
||||
if base_nm is None: return None
|
||||
string.strncpy(base_nm, short_nm, bare_len)
|
||||
base_nm[bare_len] = '\0'
|
||||
# Base is not a registered generic template (plain class receiver) => not generic; return None
|
||||
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None: return None
|
||||
if lb is not None: return short_nm
|
||||
# Bare generic base (no type args): element type unknown -> fail-fast (D25)
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"the actual struct type of the receiver of method '{method_name}' is a bare generic base " f"'{short_nm}*' (type arguments lost), so the element type is unknown; " f"silently using the default element type is forbidden (treating elements as i32 would truncate pointers); " f"make the variable annotation/type carry type arguments (e.g. list[ast.AST*])", "TypeError")
|
||||
return None
|
||||
|
||||
# _is_list_builtin_receiver - test list builtin methods from the receiver's actual type.
|
||||
# Prefer the actual LLVM struct name (sha1 stripped); fall back to the annotation/registered name.
|
||||
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*, recv_val: mlirlite.Value*, fallback_name: str, method_name: str, cl: ast.Call*) -> int:
|
||||
"""Test list builtin methods from the receiver's actual type (fall back to the annotation name if unavailable)"""
|
||||
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
|
||||
if sty is not None:
|
||||
ty_nm: str = mlirlite.get_struct_name(sty)
|
||||
if ty_nm is not None and ty_nm[0] != '\0':
|
||||
# Generic template receiver: bare base fail-fast; with type args use the actual specialization
|
||||
act_nm: str = self._resolve_generic_receiver_name(pool, sty, method_name, cl)
|
||||
if act_nm is not None: return self._is_list_builtin_method(act_nm, method_name)
|
||||
# Non-generic template receiver: judge by the actual short name (sha1 stripped)
|
||||
return self._is_list_builtin_method(self._strip_module_sha1_prefix(ty_nm), method_name)
|
||||
# Actual type unavailable -> fall back to the annotation/registered name
|
||||
return self._is_list_builtin_method(fallback_name, method_name)
|
||||
@@ -0,0 +1,257 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# CallGenVa - CallGenMixin split (stage B): va_arg variadics (start/arg/end / variadic call promotion)
|
||||
# ============================================================
|
||||
novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
"""va_arg variadics (start/arg/end and variadic call promotion)."""
|
||||
|
||||
# _func_has_eh_params - whether callee signature has try-except EH out params (main excluded)
|
||||
# ============================================================
|
||||
|
||||
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, arg_node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Return the alloca pointer of a va_list variable without loading it."""
|
||||
if arg_node is None: return None
|
||||
# Name node: look up the alloca pointer from the symbol table (no load)
|
||||
if arg_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(arg_node)
|
||||
if nm.id is not None:
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is not None: return alloca
|
||||
# Fallback: translate_value (may return a loaded value; only valid when already a pointer)
|
||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
|
||||
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*, type_node: ast.AST*, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||
"""Resolve the va_arg/arg type argument to an LLVMType, defaulting to i32."""
|
||||
if type_node is None: return mlirlite.Int32(pool)
|
||||
# Resolve the type expression via HandlesType.resolve_annotation_type
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None: return resolved
|
||||
# Type node present but unresolvable: fail-fast, never fall back to i32 (would read the vararg slot at the wrong width)
|
||||
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg type resolution failed", "TypeError")
|
||||
return mlirlite.Int32(pool)
|
||||
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Emit a va_arg instruction reading the next variadic argument from the va_list."""
|
||||
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# First arg: va_list (evaluate to an i8* address; aligned with TPC _HandleVaArgLlvm). In own *args
|
||||
# translate_value returns the buffer address; in forwarding (sprintf -> vsnprintf) it loads the param.
|
||||
ap_node: ast.AST* = cargs.get(0)
|
||||
ap_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
|
||||
if ap_val is None: return mlirlite.const_int32(pool, 0)
|
||||
ap_ptr: mlirlite.Value* = ap_val
|
||||
|
||||
# Second arg: target type (optional, defaults to i32)
|
||||
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if can >= 2:
|
||||
type_node: ast.AST* = cargs.get(1)
|
||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||
|
||||
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (TPC emit_va_arg L72-73)
|
||||
# then trunc to target width; otherwise high bits are garbage (%d/%i wrong, A6 root cause).
|
||||
read_ty: mlirlite.LLVMType* = arg_ty
|
||||
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||
if abits_va != 0 and abits_va < 64:
|
||||
read_ty = mlirlite.Int64(pool)
|
||||
va_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||
if va_raw is None: return mlirlite.const_int32(pool, 0)
|
||||
# 8-byte slot -> target width (low bits); caller int64(...) re-applies sext/zext
|
||||
return mlirlite.build_trunc(builder, va_raw, arg_ty)
|
||||
|
||||
# Emit the va_arg instruction
|
||||
return mlirlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Emit va_arg reading from the enclosing variadic's own va_list or a forwarded va_list."""
|
||||
# arg() has two forms:
|
||||
# (1) Own-variadic form: inside `def f(a, *args)`, trans._va_list_ptr is the i8* address of
|
||||
# the function's implicit va_list buffer; read the next vararg straight from it.
|
||||
# (2) Forwarding form: in a NON-variadic wrapper such as vsnprintf(buf, size, fmt, args),
|
||||
# the caller's va_list pointer is passed by value through the 4th param named "args"
|
||||
# (vprintf style, see includes/viperlib.vp); there is no implicit va_list here, so
|
||||
# trans._va_list_ptr is None and the pointer must be recovered from that param.
|
||||
va_ptr: mlirlite.Value* = trans._va_list_ptr
|
||||
if va_ptr is None:
|
||||
# The "args" param's alloca is an i8** slot holding the forwarded i8* va_list value, so it
|
||||
# MUST be loaded once: va_arg needs the pointer value itself (i8*), not the slot address.
|
||||
args_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "args")
|
||||
if args_alloca is None: return mlirlite.const_int32(pool, 0)
|
||||
if args_alloca.Ty is None: return mlirlite.const_int32(pool, 0)
|
||||
va_ptr = mlirlite.build_load(builder, args_alloca.Ty.Pointee, args_alloca)
|
||||
if va_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# First arg: target type (optional, defaults to i32)
|
||||
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if can >= 1 and cargs is not None:
|
||||
type_node: ast.AST* = cargs.get(0)
|
||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||
|
||||
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (same as _translate_va_arg_sugar),
|
||||
# then trunc to target width (%d/%i high-bit garbage fix, see _translate_va_arg_sugar).
|
||||
read_ty: mlirlite.LLVMType* = arg_ty
|
||||
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||
if abits_arg != 0 and abits_arg < 64:
|
||||
read_ty = mlirlite.Int64(pool)
|
||||
arg_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, va_ptr, read_ty)
|
||||
if arg_raw is None: return mlirlite.const_int32(pool, 0)
|
||||
return mlirlite.build_trunc(builder, arg_raw, arg_ty)
|
||||
|
||||
# Emit va_arg, using va_ptr as the va_list address (own buffer or forwarded pointer)
|
||||
return mlirlite.build_va_arg(builder, va_ptr, read_ty)
|
||||
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Emit llvm.va_start to initialize the va_list (the second C-ABI argument is unused)."""
|
||||
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# First arg: va_list pointer (get its alloca, no load)
|
||||
ap_node: ast.AST* = cargs.get(0)
|
||||
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# bitcast to i8* (@llvm.va_start takes i8*)
|
||||
i8_ty_vs: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_vs: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_vs)
|
||||
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
|
||||
if ap_i8 is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
void_ty_vs: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||
mlirlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Emit llvm.va_end to clean up the va_list (a no-op on most platforms)."""
|
||||
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# First arg: va_list pointer (get its alloca, no load)
|
||||
ap_node: ast.AST* = cargs.get(0)
|
||||
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# bitcast to i8* (@llvm.va_end takes i8*)
|
||||
i8_ty_ve: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_ve: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_ve)
|
||||
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
|
||||
if ap_i8 is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
void_ty_ve: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||
mlirlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
def _get_func_fixed_param_count(self, func: mlirlite.Function*) -> int:
|
||||
"""Return the count of fixed params by walking the Params list."""
|
||||
if func is None: return 0
|
||||
count: int = 0
|
||||
param: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||
while param is not None:
|
||||
count += 1
|
||||
param = mlirlite.param_get_next(param)
|
||||
return count
|
||||
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
|
||||
"""Return the fixed-param count for known variadic C functions (-1 if unknown)."""
|
||||
if name is None: return -1
|
||||
if string.strcmp(name, "snprintf") == 0: return 3
|
||||
if string.strcmp(name, "sprintf") == 0: return 2
|
||||
if string.strcmp(name, "vsprintf") == 0: return 2
|
||||
return -1
|
||||
|
||||
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*, name: str, idx: int) -> mlirlite.LLVMType*:
|
||||
"""Return the type of the idx-th fixed param of a known variadic C function."""
|
||||
if pool is None or name is None or idx < 0: return None
|
||||
i8_ptr_kv: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
i64_kv: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
if string.strcmp(name, "snprintf") == 0:
|
||||
if idx == 0: return i8_ptr_kv
|
||||
if idx == 1: return i64_kv
|
||||
if idx == 2: return i8_ptr_kv
|
||||
return None
|
||||
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
|
||||
if idx == 0: return i8_ptr_kv
|
||||
if idx == 1: return i8_ptr_kv
|
||||
return None
|
||||
return None
|
||||
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, can: sizet, arg_nodes: list[ast.AST*]* = None, self_offset: int = 0, trans: HT.Translator* = None) -> int:
|
||||
"""Promote variadic args of a callee to the C ABI; returns 1 if variadic else 0."""
|
||||
if mod is None or call_name is None or head is None: return 0
|
||||
# Look up the function object
|
||||
func: mlirlite.Function* = self.find_func_in_module(mod, call_name)
|
||||
fixed_count: int = -1
|
||||
if func is not None and func.IsVarArg == 1: fixed_count = self._get_func_fixed_param_count(func)
|
||||
else:
|
||||
# Fall back to the known variadic C-lib signature table (snprintf etc.) when func is None (stub not yet injected)
|
||||
# or IsVarArg=0 (ad hoc declare); otherwise i32 varargs aren't promoted and the Win64 8-byte slot keeps only 4 bytes.
|
||||
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
|
||||
if fixed_count < 0: return 0
|
||||
|
||||
# No varargs (arg count <= fixed count) but still report is_variadic=1
|
||||
if can <= fixed_count: return 1
|
||||
|
||||
# Walk to the fixed_count-th node (the last fixed param)
|
||||
cur: mlirlite.Value* = head
|
||||
prev: mlirlite.Value* = None
|
||||
idx: int = 0
|
||||
while cur is not None and idx < fixed_count:
|
||||
prev = cur
|
||||
cur = cur.Next
|
||||
idx += 1
|
||||
|
||||
# cur now points at the first vararg; apply C ABI promotion to all varargs
|
||||
i32_ty_promote: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
double_ty_promote: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||
v_idx: int = fixed_count
|
||||
while cur is not None:
|
||||
next_node: mlirlite.Value* = cur.Next
|
||||
promoted: mlirlite.Value* = cur
|
||||
# C ABI integer promotion: i8/i16 → i32
|
||||
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
|
||||
if arg_bits != 0 and arg_bits < 32:
|
||||
# Unsigned ints (uint8/CUInt16T etc.) -> zext; signed/unknown -> sext.
|
||||
# Fix: printf("%02x", buf[i]) with CUInt8T must zext to 0xF3, not sext to 0xFFFFFFF3.
|
||||
is_u_va: int = 0
|
||||
if arg_nodes is not None and trans is not None:
|
||||
ast_idx_va: int = v_idx - self_offset
|
||||
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
|
||||
an_va: ast.AST* = arg_nodes.get(ast_idx_va)
|
||||
if an_va is not None:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0: is_u_va = 1
|
||||
if is_u_va != 0: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
|
||||
else:
|
||||
# If is_u_va is unknown (missing arg_nodes/trans or no annotation), default i8 to zext
|
||||
# (aligned with should_zext_widen); sext would print uint8 bytes as ffffffXX garbage.
|
||||
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
|
||||
else: promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
|
||||
if promoted is not None:
|
||||
promoted.Next = next_node
|
||||
if prev is not None: prev.Next = promoted
|
||||
# C ABI float promotion: float → double
|
||||
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(promoted.Ty)
|
||||
if arg_fbits != 0 and arg_fbits < 64:
|
||||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, promoted, double_ty_promote, None)
|
||||
if promoted is not None:
|
||||
promoted.Next = next_node
|
||||
if prev is not None: prev.Next = promoted
|
||||
# Win64 vararg slots are 8 bytes: i8/i16/i32 -> i64. The va_arg read side always reads i64
|
||||
# (_translate_va_arg_sugar), so callers must fill all 8 bytes or stale stack data remains.
|
||||
i64_ty_promote: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
|
||||
if pb_va != 0 and pb_va < 64:
|
||||
promoted_va2: mlirlite.Value* = None
|
||||
is_u_va2: int = 0
|
||||
if arg_nodes is not None and trans is not None:
|
||||
ast_idx_va2: int = v_idx - self_offset
|
||||
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
|
||||
an_va2: ast.AST* = arg_nodes.get(ast_idx_va2)
|
||||
if an_va2 is not None:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0: is_u_va2 = 1
|
||||
if is_u_va2 != 0: promoted_va2 = mlirlite.build_zext(builder, promoted, i64_ty_promote)
|
||||
else: promoted_va2 = mlirlite.build_sext(builder, promoted, i64_ty_promote)
|
||||
if promoted_va2 is not None:
|
||||
promoted_va2.Next = next_node
|
||||
if prev is not None: prev.Next = promoted_va2
|
||||
promoted = promoted_va2
|
||||
prev = promoted
|
||||
cur = next_node
|
||||
v_idx += 1
|
||||
|
||||
return 1
|
||||
@@ -0,0 +1,143 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# CallGenVt - CallGenMixin split (phase B): vtable slot writing
|
||||
# ============================================================
|
||||
novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
"""vtable slot writing."""
|
||||
|
||||
# _write_vtable_slot / _write_vtable_attr: write a module-level function pointer into an object vtable slot via copy-on-write (vtable slots are not data fields, so get_attribute_ptr cannot find them).
|
||||
|
||||
# ============================================================
|
||||
def _write_vtable_slot(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, obj_ptr: mlirlite.Value*, class_name: str, method_index: int, func_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||
"""Emit a copy-on-write vtable slot write."""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if obj_ptr is None or class_name is None or func_val is None: return 0
|
||||
if method_index < 0 or trans._cur_func is None: return 0
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if i8_ty is None: return 0
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
if i8_ptr_ty is None: return 0
|
||||
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||
if i8_ptr_ptr_ty is None: return 0
|
||||
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
|
||||
if method_count <= 0: return 0
|
||||
# Build the vtable type { i8*, i8*, ... }; its field list uses mlirlite.ParamNode, not Function*.
|
||||
vt_first: mlirlite.ParamNode* = None
|
||||
vt_prev: mlirlite.ParamNode* = None
|
||||
for vi in range(int(method_count)):
|
||||
pn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
if pn is None: continue
|
||||
if vt_first is None: vt_first = pn
|
||||
if vt_prev is not None: mlirlite.paramnode_set_next(vt_prev, pn)
|
||||
vt_prev = pn
|
||||
vtable_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, vt_first, method_count, None)
|
||||
if vtable_ty is None: return 0
|
||||
# Object vtable slot (object header -> i8**; the vtable pointer is always the first field).
|
||||
vtable_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
|
||||
if vtable_slot is None: return 0
|
||||
vtable_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||
if vtable_ptr is None: return 0
|
||||
# Address of the class default vtable (for comparison).
|
||||
class_vt: mlirlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
|
||||
if class_vt is None: return 0
|
||||
cmp_vt: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, vtable_ptr, class_vt)
|
||||
if cmp_vt is None: return 0
|
||||
# Unique block names: create_block does not auto-number, so repeated vtable writes in one function would collide.
|
||||
vtc3: int = trans._label_counter
|
||||
trans._label_counter = vtc3 + 1
|
||||
vt_bb_buf3: str = pool.alloc(64)
|
||||
if vt_bb_buf3 is None: return 0
|
||||
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
|
||||
vt_copy_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
||||
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
|
||||
vt_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
||||
if vt_copy_bb is None or vt_skip_bb is None: return 0
|
||||
mlirlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
|
||||
# copy block: create internal copy global (zeroinit) + memcpy + repoint object at it.
|
||||
mlirlite.position_at_end(builder, vt_copy_bb)
|
||||
vtc: int = trans._label_counter
|
||||
trans._label_counter = vtc + 1
|
||||
vcopy_buf: str = pool.alloc(256)
|
||||
if vcopy_buf is not None:
|
||||
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
|
||||
vcopy_gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, vcopy_buf, vtable_ty)
|
||||
if vcopy_gv is not None:
|
||||
mlirlite.module_add_global(mod, vcopy_gv)
|
||||
vcopy_gv.Linkage = None
|
||||
vcopy_gv.Initializer = "zeroinitializer"
|
||||
# Reference names need the @ prefix: SSAValue emits the name verbatim (unlike new_global_variable, it adds no @).
|
||||
vcopy_ref_nm: str = pool.alloc(272)
|
||||
if vcopy_ref_nm is None: vcopy_ref_nm = vcopy_buf
|
||||
else: viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
|
||||
vcopy_ref: mlirlite.Value* = mlirlite.SSAValue(pool, mlirlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
|
||||
if vcopy_ref is not None:
|
||||
vcopy_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
|
||||
if vcopy_i8 is not None:
|
||||
# ConstInt's name must be numeric text (e.g. "56"); an identifier would emit verbatim without % and break llc.
|
||||
vtsz_buf: str = pool.alloc(32)
|
||||
if vtsz_buf is not None:
|
||||
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
|
||||
vt_size: mlirlite.Value* = mlirlite.ConstInt(pool, mlirlite.Int64(pool), method_count * 8, vtsz_buf)
|
||||
if vt_size is not None: self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
|
||||
mlirlite.build_store(builder, vcopy_i8, vtable_slot)
|
||||
mlirlite.build_br(builder, vt_skip_bb)
|
||||
# skip block: reload vtable -> GEP the slot -> store the function pointer (i8*).
|
||||
mlirlite.position_at_end(builder, vt_skip_bb)
|
||||
vt_ptr2: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||
if vt_ptr2 is None: return 0
|
||||
vt_typed: mlirlite.Value* = mlirlite.build_bitcast(builder, vt_ptr2, mlirlite.Ptr(pool, vtable_ty))
|
||||
if vt_typed is None: return 0
|
||||
vt_slotp: mlirlite.Value* = mlirlite.build_gep_struct(builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
|
||||
if vt_slotp is None: return 0
|
||||
fptr_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, func_val, i8_ptr_ty)
|
||||
if fptr_i8 is not None: mlirlite.build_store(builder, fptr_i8, vt_slotp)
|
||||
return 1
|
||||
def _write_vtable_attr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, rhs_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||
"""Handle obj.method = func writes that target a vtable slot."""
|
||||
if node is None or builder is None or pool is None or mod is None: return 0
|
||||
if rhs_val is None or trans is None: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at is None or at.value is None or at.attr is None: return 0
|
||||
# Resolve the class name (AnnotClassName; strip the module prefix).
|
||||
class_nm_v: str = None
|
||||
if at.value.kind() == ast.ASTKind.Name:
|
||||
nm_v: ast.Name* = ast.Name*(at.value)
|
||||
if nm_v.id is not None:
|
||||
ve_v: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_v.id)
|
||||
if ve_v is not None: class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
|
||||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||||
# Cross-mixin: _resolve_attr_field_class lives in ExprGenAttr, called via self.Gen.
|
||||
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
|
||||
if class_nm_v is None: return 0
|
||||
fd_dot_v: str = string.strrchr(class_nm_v, '.')
|
||||
if fd_dot_v is not None: class_nm_v = fd_dot_v + 1
|
||||
if self.Gen.StructGen.get_struct_type(class_nm_v) is None: return 0
|
||||
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
|
||||
if vm_idx_v < 0: return 0
|
||||
# Resolve obj_ptr (same as the first half of get_attribute_ptr: Name looks up the alloca directly).
|
||||
obj_ptr_v: mlirlite.Value* = None
|
||||
if at.value.kind() == ast.ASTKind.Name: obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
|
||||
else: obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||||
if obj_ptr_v is None or obj_ptr_v.Ty is None: return 0
|
||||
# X* variable (Ptr(Ptr)): load-dereference to get the object pointer.
|
||||
match obj_ptr_v.Ty:
|
||||
case mlirlite.LLVMType.Ptr(inner_v):
|
||||
match inner_v:
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
obj_ptr_v = mlirlite.build_load(builder, inner_v, obj_ptr_v)
|
||||
case _:
|
||||
pass
|
||||
case _:
|
||||
pass
|
||||
if obj_ptr_v is None: return 0
|
||||
return self._write_vtable_slot(pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,404 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈
|
||||
#
|
||||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class CallGenFd(CallGenBase.CallGenBase):
|
||||
"""FakeDuck / requires 字段注入 / provider 栈"""
|
||||
|
||||
# ============================================================
|
||||
# _translate_fakeduck_call - FakeDuck: str 变量方法调用 → 扩展类 _Type/_str
|
||||
#
|
||||
# 对齐 TPC HandlesExprCall.py L2841-2880:
|
||||
# ClassName 常规解析失败且对象是 char*(i8*)时,遍历扩展类查找
|
||||
# {CN}.{method} 函数。对象是 Name 变量且有影子({var}__meta__,fd2
|
||||
# 创建)时 self=影子;否则(字符串字面量/匿名 str)在入口创建临时
|
||||
# 影子,写 __data__=obj 值、__mbuddy__=NULL 后作 self,避免
|
||||
# bitcast i8* → _str* 读取垃圾指针导致 ACCESS_VIOLATION。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _translate_fakeduck_call(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cls_name: str,
|
||||
method_name: str,
|
||||
cl: ast.Call | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None"""
|
||||
if pool is None or builder is None or cl is None or trans is None:
|
||||
return None
|
||||
if method_name is None:
|
||||
return None
|
||||
# 仅对 str 类型触发(AnnotClassName == "str",对象是 char*);
|
||||
# cls_name 为 None(表达式接收者解析失败)时也尝试
|
||||
if cls_name is not None and string.strcmp(cls_name, "str") != 0:
|
||||
return None
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute:
|
||||
return None
|
||||
fd_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||||
if fd_at is None or fd_at.value is None or fd_at.attr is None:
|
||||
return None
|
||||
if string.strcmp(fd_at.attr, method_name) != 0:
|
||||
return None
|
||||
# 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等)
|
||||
fd_func: llvmlite.Function | t.CPtr = None
|
||||
fd_cls: str = None
|
||||
fd_key: str = pool.alloc(160)
|
||||
if fd_key is not None:
|
||||
for fd_ci in range(2):
|
||||
fd_cn: str = "_Type"
|
||||
if fd_ci == 1:
|
||||
fd_cn = "_str"
|
||||
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
|
||||
fd_func = self.find_func_in_module(mod, fd_key)
|
||||
if fd_func is not None:
|
||||
fd_cls = fd_cn
|
||||
break
|
||||
if fd_func is None or fd_cls is None:
|
||||
return None
|
||||
# 扩展类结构体必须已注册(GEP/临时影子需要类型)
|
||||
fd_sty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(fd_cls)
|
||||
if fd_sty is None:
|
||||
return None
|
||||
# 扩展类字段数:有字段(_str)时 self 用影子结构体指针;
|
||||
# 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876)
|
||||
fd_entry_fd: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_type(fd_sty)
|
||||
fd_has_fields: int = 0
|
||||
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0:
|
||||
fd_has_fields = 1
|
||||
# self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时)
|
||||
fd_self: llvmlite.Value | t.CPtr = None
|
||||
if fd_at.value.kind() == ast.ASTKind.Name:
|
||||
fd_onm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(fd_at.value)
|
||||
if fd_onm.id is not None:
|
||||
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
|
||||
fd_mn: str = pool.alloc(256)
|
||||
if fd_mn is not None:
|
||||
viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
|
||||
fd_meta: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
|
||||
trans.SymTab, fd_mn)
|
||||
if fd_meta is not None and fd_has_fields != 0:
|
||||
fd_self = fd_meta
|
||||
if fd_self is None:
|
||||
if fd_has_fields != 0:
|
||||
# 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化
|
||||
# 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized)
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
|
||||
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
|
||||
if fd_self is None:
|
||||
return None
|
||||
fd_obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, fd_at.value, None, 0, trans)
|
||||
if fd_obj_val is not None:
|
||||
fd_dfe: GenTypes.FieldEntry | t.CPtr = \
|
||||
self.Gen.StructGen.lookup_field(fd_sty, "__data__")
|
||||
if fd_dfe is not None and fd_dfe.Ty is not None:
|
||||
fd_dp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||||
builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
|
||||
if fd_dp is not None:
|
||||
llvmlite.build_store(builder, fd_obj_val, fd_dp)
|
||||
fd_bfe: GenTypes.FieldEntry | t.CPtr = \
|
||||
self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
|
||||
if fd_bfe is not None and fd_bfe.Ty is not None:
|
||||
fd_bp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||||
builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
|
||||
fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull(
|
||||
pool, fd_bfe.Ty, "null")
|
||||
if fd_bp is not None and fd_null is not None:
|
||||
llvmlite.build_store(builder, fd_null, fd_bp)
|
||||
# FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)
|
||||
self._inject_requires_fields(
|
||||
trans, pool, builder, mod, fd_self, fd_cls)
|
||||
else:
|
||||
# 无字段扩展类(_Type):对象值 bitcast 到扩展类指针
|
||||
fd_obj_v: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, fd_at.value, None, 0, trans)
|
||||
if fd_obj_v is None:
|
||||
return None
|
||||
fd_self = llvmlite.build_bitcast(
|
||||
builder, fd_obj_v, llvmlite.Ptr(pool, fd_sty))
|
||||
if fd_self is None:
|
||||
return None
|
||||
# 构建参数链表: self → 其余参数
|
||||
llvmlite.value_set_next(fd_self, None)
|
||||
fd_head: llvmlite.Value | t.CPtr = fd_self
|
||||
fd_tail: llvmlite.Value | t.CPtr = fd_self
|
||||
fd_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args
|
||||
fd_can: int = 0
|
||||
if fd_args is not None:
|
||||
fd_can = len(fd_args)
|
||||
for fd_ai in range(fd_can):
|
||||
fd_arg: ast.AST | t.CPtr = fd_args.get(fd_ai)
|
||||
if fd_arg is not None:
|
||||
fd_av: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, fd_arg, None, 0, trans)
|
||||
# char 常量参数 → 单字符字符串(char 隐式转 str,
|
||||
# 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化,
|
||||
# 否则运行时 strlen/strncmp 解引用 44 崩溃)
|
||||
if fd_arg.kind() == ast.ASTKind.Constant:
|
||||
fd_ac: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(fd_arg)
|
||||
if fd_ac.const_kind == ast.CONST_CHAR:
|
||||
ch_buf: str = pool.alloc(2)
|
||||
if ch_buf is not None:
|
||||
ch_buf[0] = t.CChar(fd_ac.int_val)
|
||||
ch_buf[1] = '\0'
|
||||
fd_av = self.Gen.ExprGenConst.create_global_string(
|
||||
builder, pool, mod, ch_buf, trans)
|
||||
if fd_av is not None:
|
||||
llvmlite.value_set_next(fd_av, None)
|
||||
llvmlite.value_set_next(fd_tail, fd_av)
|
||||
fd_tail = fd_av
|
||||
# 返回类型与调用名(来自 find_func_in_module 命中的函数定义)
|
||||
fd_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(fd_func)
|
||||
if fd_ret_ty is None:
|
||||
fd_ret_ty = llvmlite.Int32(pool)
|
||||
fd_fname: str = llvmlite.function_get_name(fd_func)
|
||||
if fd_fname is None:
|
||||
return None
|
||||
# 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化
|
||||
# 为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标
|
||||
# 路径而非 list 内联 __getitem__(printf %s 解引用字符值崩溃)。
|
||||
# 从定义模块 text.ll 读取精确返回类型(对齐普通方法调用 _translate_method_call)。
|
||||
fd_sha1: str = pool.alloc(17)
|
||||
if fd_sha1 is not None:
|
||||
string.strncpy(fd_sha1, fd_fname, 16)
|
||||
fd_sha1[16] = '\0'
|
||||
fd_exact: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
|
||||
pool, fd_sha1, fd_cls, method_name)
|
||||
if fd_exact is not None:
|
||||
fd_ret_ty = fd_exact
|
||||
fd_total: int = 1 + fd_can
|
||||
return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
|
||||
# ============================================================
|
||||
# _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段
|
||||
#
|
||||
# 对齐 TPC HandlesExprCall.py L4021-4079:
|
||||
# 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用
|
||||
# _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时
|
||||
# bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖
|
||||
# 用户已手动设置的值)。_find_provider 未注册(未 import
|
||||
# _withcontent)时跳过注入,不影响非 with 场景。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _inject_requires_fields(self, trans: HT.Translator | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
meta_ptr: llvmlite.Value | t.CPtr,
|
||||
class_name: str) -> int:
|
||||
"""在 with 上下文注入 requires 字段,返回注入字段数"""
|
||||
if trans is None or pool is None or builder is None or mod is None:
|
||||
return 0
|
||||
if meta_ptr is None or class_name is None:
|
||||
return 0
|
||||
# _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent
|
||||
# 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050
|
||||
# (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with
|
||||
# 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...)
|
||||
# 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。
|
||||
fd_fn_probe: llvmlite.Function | t.CPtr = self.find_func_in_module(
|
||||
mod, "_find_provider")
|
||||
if fd_fn_probe is None:
|
||||
return 0
|
||||
fd_fname: str = llvmlite.function_get_name(fd_fn_probe)
|
||||
if fd_fname is None:
|
||||
return 0
|
||||
cnt: int = self.Gen.ClassGenBase.class_meta_count(
|
||||
GenTypes.CLASS_META_REQUIRES, class_name)
|
||||
if cnt <= 0:
|
||||
return 0
|
||||
sty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name)
|
||||
if sty is None:
|
||||
return 0
|
||||
i8_ty_j: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
if i8_ty_j is None:
|
||||
return 0
|
||||
i8_ptr_ty_j: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_j)
|
||||
if i8_ptr_ty_j is None:
|
||||
return 0
|
||||
cur_func: llvmlite.Function | t.CPtr = trans._cur_func
|
||||
if cur_func is None:
|
||||
return 0
|
||||
injected: int = 0
|
||||
for ji in range(cnt):
|
||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(
|
||||
GenTypes.CLASS_META_REQUIRES, class_name, ji)
|
||||
if fname is None:
|
||||
continue
|
||||
fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field(
|
||||
sty, fname)
|
||||
if fe is None or fe.Ty is None:
|
||||
continue
|
||||
# 参数:字段名字符串(i8*)
|
||||
fd_fstr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
|
||||
builder, pool, mod, fname, trans)
|
||||
if fd_fstr is None:
|
||||
continue
|
||||
llvmlite.value_set_next(fd_fstr, None)
|
||||
self._ensure_method_declare(
|
||||
pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
|
||||
fd_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call(
|
||||
builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
|
||||
if fd_ptr is None:
|
||||
continue
|
||||
# 条件注入:ptr != NULL 才写入(保留用户手动设置的值)
|
||||
fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull(
|
||||
pool, i8_ptr_ty_j, "null")
|
||||
if fd_null is None:
|
||||
continue
|
||||
fd_cmp: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_NE, fd_ptr, fd_null)
|
||||
if fd_cmp is None:
|
||||
continue
|
||||
# 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中
|
||||
# 冲突 → llc "Terminator found in the middle of a basic block")
|
||||
vtc2: int = trans._label_counter
|
||||
trans._label_counter = vtc2 + 1
|
||||
fd_bb_buf2: str = pool.alloc(64)
|
||||
if fd_bb_buf2 is None:
|
||||
continue
|
||||
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
|
||||
fd_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
|
||||
pool, cur_func, fd_bb_buf2)
|
||||
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
|
||||
fd_inject_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
|
||||
pool, cur_func, fd_bb_buf2)
|
||||
if fd_skip_bb is None or fd_inject_bb is None:
|
||||
continue
|
||||
llvmlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
|
||||
# inject 块:bitcast + GEP + store,跳回 skip
|
||||
llvmlite.position_at_end(builder, fd_inject_bb)
|
||||
fd_cast: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, fd_ptr, fe.Ty)
|
||||
if fd_cast is not None:
|
||||
fd_fp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||||
builder, sty, fe.Ty, meta_ptr, fe.Index)
|
||||
if fd_fp is not None:
|
||||
llvmlite.build_store(builder, fd_cast, fd_fp)
|
||||
llvmlite.build_br(builder, fd_skip_bb)
|
||||
# skip 块:恢复 CurBlock,继续后续代码生成
|
||||
llvmlite.position_at_end(builder, fd_skip_bb)
|
||||
injected += 1
|
||||
return injected
|
||||
# ============================================================
|
||||
# _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文
|
||||
# provider push/pop(__provides__ 元数据,fd1 注册)
|
||||
#
|
||||
# 对齐 TPC HandlesWith.py L138-174:
|
||||
# with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8);
|
||||
# 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责
|
||||
# 维护 TLS provider 栈,供 _inject_requires_fields 查找。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _emit_provider_push(self, trans: HT.Translator | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cls_name: str,
|
||||
ctx_val: llvmlite.Value | t.CPtr) -> int:
|
||||
"""生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数"""
|
||||
if trans is None or pool is None or builder is None or mod is None:
|
||||
return 0
|
||||
if cls_name is None or ctx_val is None:
|
||||
return 0
|
||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(
|
||||
GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||
if fd_pc <= 0:
|
||||
return 0
|
||||
# _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫:
|
||||
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
|
||||
fd_pfn_probe: llvmlite.Function | t.CPtr = self.find_func_in_module(
|
||||
mod, "_push_provider")
|
||||
if fd_pfn_probe is None:
|
||||
return 0
|
||||
fd_pfn: str = llvmlite.function_get_name(fd_pfn_probe)
|
||||
if fd_pfn is None:
|
||||
return 0
|
||||
i8_ty_p: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
if i8_ty_p is None:
|
||||
return 0
|
||||
i8_ptr_ty_p: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_p)
|
||||
if i8_ptr_ty_p is None:
|
||||
return 0
|
||||
ret_i32_p: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if ret_i32_p is None:
|
||||
return 0
|
||||
# ctx 值 bitcast 到 i8*(provider 栈存 void*)
|
||||
fd_ctx_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, ctx_val, i8_ptr_ty_p)
|
||||
if fd_ctx_i8 is None:
|
||||
return 0
|
||||
for pi in range(fd_pc):
|
||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(
|
||||
GenTypes.CLASS_META_PROVIDES, cls_name, pi)
|
||||
if fname is None:
|
||||
continue
|
||||
fd_fstr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
|
||||
builder, pool, mod, fname, trans)
|
||||
if fd_fstr is None:
|
||||
continue
|
||||
llvmlite.value_set_next(fd_fstr, fd_ctx_i8)
|
||||
llvmlite.value_set_next(fd_ctx_i8, None)
|
||||
self._ensure_method_declare(
|
||||
pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
|
||||
llvmlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
|
||||
return fd_pc
|
||||
def _emit_provider_pop(self, trans: HT.Translator | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cls_name: str) -> int:
|
||||
"""生成 _pop_provider() 调用(每提供字段一次),返回字段数"""
|
||||
if trans is None or pool is None or builder is None or mod is None:
|
||||
return 0
|
||||
if cls_name is None:
|
||||
return 0
|
||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(
|
||||
GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||
if fd_pc <= 0:
|
||||
return 0
|
||||
# _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫:
|
||||
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
|
||||
fd_pon_probe: llvmlite.Function | t.CPtr = self.find_func_in_module(
|
||||
mod, "_pop_provider")
|
||||
if fd_pon_probe is None:
|
||||
return 0
|
||||
fd_pon: str = llvmlite.function_get_name(fd_pon_probe)
|
||||
if fd_pon is None:
|
||||
return 0
|
||||
ret_i32_q: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if ret_i32_q is None:
|
||||
return 0
|
||||
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
|
||||
for _pi in range(fd_pc):
|
||||
llvmlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
|
||||
return fd_pc
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,584 +0,0 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenLib - CallGenMixin 拆分(阶段 C):C 库声明 / 返回类型推断 /
|
||||
# 变长参数 / memcpy / kwargs 调用辅助方法
|
||||
#
|
||||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class CallGenLib(CallGenBase.CallGenBase):
|
||||
"""C 库声明 / 返回类型推断 / kwargs 调用辅助(继承 Base)"""
|
||||
|
||||
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
name: str) -> llvmlite.Function | t.CPtr:
|
||||
"""为已知的 C 库函数创建 declare 声明"""
|
||||
if pool is None or mod is None or name is None:
|
||||
return None
|
||||
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
params: int = 0 # 参数数量
|
||||
|
||||
if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0:
|
||||
ret_ty = i64_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "memset") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "memcpy") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "memmove") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "memcmp") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "strcmp") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 2
|
||||
elif string.strcmp(name, "strncmp") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "strcpy") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 2
|
||||
elif string.strcmp(name, "strncpy") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 3
|
||||
elif string.strcmp(name, "strcat") == 0:
|
||||
ret_ty = i8_ptr_ty
|
||||
params = 2
|
||||
elif string.strcmp(name, "puts") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "atoi") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "atol") == 0:
|
||||
ret_ty = i64_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "abs") == 0:
|
||||
ret_ty = i32_ty
|
||||
params = 1
|
||||
elif string.strcmp(name, "labs") == 0:
|
||||
ret_ty = i64_ty
|
||||
params = 1
|
||||
else:
|
||||
return None
|
||||
|
||||
# 创建 declare
|
||||
fn: llvmlite.Function | t.CPtr = llvmlite.create_declare(pool, mod, name, ret_ty)
|
||||
if fn is None:
|
||||
return None
|
||||
# 添加参数
|
||||
if params == 1:
|
||||
if string.strcmp(name, "abs") == 0:
|
||||
llvmlite.add_param(pool, fn, i32_ty, "n")
|
||||
elif string.strcmp(name, "labs") == 0:
|
||||
llvmlite.add_param(pool, fn, i64_ty, "n")
|
||||
else:
|
||||
llvmlite.add_param(pool, fn, i8_ptr_ty, "s")
|
||||
elif params == 2:
|
||||
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
|
||||
llvmlite.add_param(pool, fn, i8_ptr_ty, "src")
|
||||
elif params == 3:
|
||||
if string.strcmp(name, "memset") == 0:
|
||||
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
|
||||
llvmlite.add_param(pool, fn, i32_ty, "c")
|
||||
llvmlite.add_param(pool, fn, i64_ty, "n")
|
||||
else:
|
||||
llvmlite.add_param(pool, fn, i8_ptr_ty, "dst")
|
||||
llvmlite.add_param(pool, fn, i8_ptr_ty, "src")
|
||||
llvmlite.add_param(pool, fn, i64_ty, "n")
|
||||
return fn
|
||||
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
func_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""根据函数名推断外部 includes 函数的返回类型
|
||||
|
||||
支持三种函数名形式:
|
||||
1. 裸名: "parse", "strlen"
|
||||
2. 别名: "json_parse"(from X import parse as json_parse)
|
||||
3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name)
|
||||
"""
|
||||
if pool is None or func_name is None:
|
||||
return llvmlite.Int32(pool)
|
||||
|
||||
# 提取裸函数名:去掉 SHA1 前缀(如 "240a9a4157959a9f.parse" → "parse")
|
||||
# SHA1 前缀是 16 位十六进制 + '.',检查是否有 '.' 分隔
|
||||
bare_name: str = func_name
|
||||
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
|
||||
if dot_pos is not None:
|
||||
# '.' 后的部分是裸函数名
|
||||
bare_name = dot_pos + 1
|
||||
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
|
||||
# 返回 i8* 的函数(指针返回值,截断会导致错误)
|
||||
if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strcpy") == 0 or string.strcmp(func_name, "strcpy") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strncpy") == 0 or string.strcmp(func_name, "strncpy") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memset") == 0 or string.strcmp(func_name, "memset") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memmove") == 0 or string.strcmp(func_name, "memmove") == 0:
|
||||
return i8_ptr_ty
|
||||
# JSON 解析函数返回 JsonValue*(指针)
|
||||
# from json.__parser import parse as json_parse → 别名 json_parse 也需覆盖
|
||||
if string.strcmp(bare_name, "parse") == 0 or string.strcmp(func_name, "parse") == 0 or string.strcmp(func_name, "json_parse") == 0:
|
||||
return i8_ptr_ty
|
||||
# MemBuddy 方法(当类型信息丢失时可能走外部函数路径)
|
||||
# 存疑
|
||||
# malloc:裸名 CExport 跨模块调用(uhci/desktop 等 mm.malloc → @malloc)
|
||||
# 在模块归属解析失败(mod_ret_sha1=None)或 vpi/text.ll 未命中时回退到
|
||||
# 此白名单。缺失导致返回类型被推断为 i32 → call i32 @malloc 截断 64 位
|
||||
# 指针 → mm.malloc 实际返回非空但调用方判 None(uhci frame list /
|
||||
# desktop bg_row_buf 根因)。calloc/realloc/alloc 均已登记,malloc 遗漏。
|
||||
if string.strcmp(bare_name, "malloc") == 0 or string.strcmp(func_name, "malloc") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
if string.strcmp(func_name, "alloc") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
if string.strcmp(func_name, "calloc") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
if string.strcmp(func_name, "realloc") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
if string.strcmp(func_name, "alloc_buf") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
# VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃)
|
||||
if string.strcmp(func_name, "get_logger") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
if string.strcmp(func_name, "fmt_buf") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
# Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏)
|
||||
if string.strcmp(func_name, "get_includes_binary_dir") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
# HandlesType 模块函数(返回 ast.AST|t.CPtr 联合类型,跨模块推断为 i32 导致指针截断)
|
||||
if string.strcmp(bare_name, "_parse_type_string") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
|
||||
# 返回 i64 的函数
|
||||
if string.strcmp(func_name, "strlen") == 0:
|
||||
return llvmlite.Int64(pool)
|
||||
if string.strcmp(func_name, "atoll") == 0:
|
||||
return llvmlite.Int64(pool)
|
||||
|
||||
# 返回 double 的函数
|
||||
if string.strcmp(func_name, "atof") == 0:
|
||||
return llvmlite.Double(pool)
|
||||
|
||||
# 返回 i8 的函数
|
||||
if string.strcmp(func_name, "samestr") == 0:
|
||||
return llvmlite.Int8(pool)
|
||||
|
||||
# Win32 API 返回指针(VOIDPTR/HANDLE)的函数
|
||||
# stubs 翻译时未注入,需推断返回 i8* 避免指针被截断为 i32 导致解引用崩溃
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
if string.strcmp(func_name, "VirtualAlloc") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapAlloc") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapReAlloc") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapCreate") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetProcessHeap") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GlobalAlloc") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GlobalFree") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GlobalLock") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LocalAlloc") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LocalFree") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetProcAddress") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetModuleHandleA") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetModuleHandleW") == 0:
|
||||
return i8_ptr_ty
|
||||
# GetCommandLineA/W 返回 LPSTR(i8*):缺则 64 位指针被截成 i32 →
|
||||
# inttoptr 后 strlen 读到无效地址(SysLibTest argv 段 ASAN 崩溃,
|
||||
# 非 ASAN 环境随机表现:IDE 0xC0000005 / 部分环境侥幸可读)。
|
||||
if string.strcmp(func_name, "GetCommandLineA") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetCommandLineW") == 0:
|
||||
return i8_ptr_ty
|
||||
# gethostbyname 返回 hostent*(i8*)
|
||||
if string.strcmp(func_name, "gethostbyname") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LoadLibraryA") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LoadLibraryW") == 0:
|
||||
return i8_ptr_ty
|
||||
# Win32 Handle 返回函数(HANDLE = void* = i8*)
|
||||
if string.strcmp(func_name, "GetStdHandle") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "CreateFileA") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "CreateFileW") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "FindFirstFileA") == 0:
|
||||
return i8_ptr_ty
|
||||
if string.strcmp(func_name, "FindFirstFileW") == 0:
|
||||
return i8_ptr_ty
|
||||
|
||||
# Win32 API 返回 i64 (SIZE_T) 的函数
|
||||
if string.strcmp(func_name, "VirtualQuery") == 0:
|
||||
return llvmlite.Int64(pool)
|
||||
if string.strcmp(func_name, "HeapSize") == 0:
|
||||
return llvmlite.Int64(pool)
|
||||
if string.strcmp(func_name, "HeapCompact") == 0:
|
||||
return llvmlite.Int64(pool)
|
||||
if string.strcmp(func_name, "GlobalSize") == 0:
|
||||
return llvmlite.Int64(pool)
|
||||
|
||||
# llvmlite build_* 函数返回类型推断
|
||||
# stubs 翻译时未注入,需推断返回类型避免指针被截断为 i32
|
||||
# 大多数 build_* 函数返回 Value*(指针),少数终止指令返回 void
|
||||
if string.strncmp(func_name, "build_", 6) == 0:
|
||||
# 返回 void 的 build_* 函数(终止指令和无返回值指令)
|
||||
if string.strcmp(func_name, "build_ret") == 0 or string.strcmp(func_name, "build_ret_void") == 0 or \
|
||||
string.strcmp(func_name, "build_store") == 0 or string.strcmp(func_name, "build_br") == 0 or \
|
||||
string.strcmp(func_name, "build_cond_br") == 0 or string.strcmp(func_name, "build_unreachable") == 0 or \
|
||||
string.strcmp(func_name, "build_switch") == 0:
|
||||
return llvmlite.Void(pool)
|
||||
# 其他 build_* 函数返回 Value*(指针)
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
|
||||
# llvmlite 类型工厂函数返回 LLVMType*(指针)
|
||||
# Int1/Int8/Int16/Int32/Int64/Ptr/Func/Array/Struct/Void/Half/Float/Double/FP128/Label
|
||||
# new_param_node/new_phi_incoming 返回 ParamNode*/PhiIncoming*(指针)
|
||||
# const_int32/const_int64/ConstFloat/ConstZero/ConstNull/ConstInt 返回 Value*(指针)
|
||||
# function_get_name/param_get_ty 等访问器返回指针
|
||||
if string.strcmp(func_name, "Int1") == 0 or string.strcmp(func_name, "Int8") == 0 or string.strcmp(func_name, "Int16") == 0 or \
|
||||
string.strcmp(func_name, "Int32") == 0 or string.strcmp(func_name, "Int64") == 0 or string.strcmp(func_name, "Ptr") == 0 or \
|
||||
string.strcmp(func_name, "Func") == 0 or string.strcmp(func_name, "Array") == 0 or string.strcmp(func_name, "Struct") == 0 or \
|
||||
string.strcmp(func_name, "Void") == 0 or string.strcmp(func_name, "Half") == 0 or string.strcmp(func_name, "Float") == 0 or \
|
||||
string.strcmp(func_name, "Double") == 0 or string.strcmp(func_name, "FP128") == 0 or string.strcmp(func_name, "Label") == 0 or \
|
||||
string.strcmp(func_name, "new_param_node") == 0 or string.strcmp(func_name, "new_phi_incoming") == 0 or \
|
||||
string.strcmp(func_name, "const_int32") == 0 or string.strcmp(func_name, "const_int64") == 0 or \
|
||||
string.strcmp(func_name, "ConstFloat") == 0 or string.strcmp(func_name, "ConstZero") == 0 or \
|
||||
string.strcmp(func_name, "ConstNull") == 0 or string.strcmp(func_name, "ConstInt") == 0 or \
|
||||
string.strcmp(func_name, "function_get_name") == 0 or string.strcmp(func_name, "param_get_ty") == 0 or \
|
||||
string.strcmp(func_name, "value_get_ty") == 0 or string.strcmp(func_name, "value_get_next") == 0 or \
|
||||
string.strcmp(func_name, "function_get_ret_ty") == 0 or string.strcmp(func_name, "function_get_params") == 0 or \
|
||||
string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
|
||||
# 默认 i32(整数):与本模块函数定义的默认返回类型一致(HandlesFunctions.py line 324)
|
||||
# 已知返回指针的函数在上方显式列出(返回 i8*),已知返回 i64 的函数也显式列出
|
||||
# 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配
|
||||
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
|
||||
# WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断
|
||||
# --nowarn-register 可抑制此 WARN(自举编译中大量出现,刷屏影响日志收敛)
|
||||
if VLogger.nowarn_register() == 0:
|
||||
VLogger.warning(
|
||||
f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}",
|
||||
"HandlesExprCall")
|
||||
return llvmlite.Int32(pool)
|
||||
def _get_external_func_param_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
func_name: str,
|
||||
param_idx: int) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""获取外部函数第 param_idx 个参数的期望类型,None 表示用字面量原类型
|
||||
|
||||
对 Win32 API 函数的所有非指针参数返回 i64,匹配 declare 中
|
||||
ULONG/SIZE_T 被统一翻译为 i64 的情况。
|
||||
"""
|
||||
if pool is None or func_name is None:
|
||||
return None
|
||||
|
||||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
|
||||
# Win32 API 函数列表:所有非指针参数在 declare 中都是 i64
|
||||
# (因为 ULONG/DWORD/SIZE_T 在 LLVM stubs 翻译时都被映射为 i64)
|
||||
win32_apis: list[str] | t.CPtr = [
|
||||
"VirtualAlloc", "VirtualFree", "VirtualProtect", "VirtualQuery",
|
||||
"VirtualLock", "VirtualUnlock",
|
||||
"HeapCreate", "HeapDestroy", "HeapAlloc", "HeapReAlloc",
|
||||
"HeapFree", "HeapSize", "HeapValidate", "HeapCompact",
|
||||
"GlobalAlloc", "GlobalFree", "GlobalLock", "GlobalUnlock", "GlobalSize",
|
||||
"LocalAlloc", "LocalFree",
|
||||
"GetLastError", "SetLastError",
|
||||
"Sleep", "SleepEx",
|
||||
"GetTickCount", "GetProcessHeap"
|
||||
]
|
||||
|
||||
# list 迭代不支持,用 range + get 替代
|
||||
api_count: t.CSizeT = len(win32_apis)
|
||||
for ai in range(api_count):
|
||||
api_name: str = win32_apis.get(ai)
|
||||
if string.strcmp(api_name, func_name) == 0:
|
||||
return i64_ty
|
||||
|
||||
# 内存分配函数:size 参数在声明中为 i64(size_t),裸名回退时
|
||||
# 不提升则 call(i32 size) 与 define(i64 size) 不匹配(desktop start
|
||||
# 第 4 次 mm.malloc(w*4) 返回值被破坏收到 0x0 根因;HelloWorld
|
||||
# vpsdk 的 malloc 同理)。calloc/realloc 的 size 参数在 idx 1。
|
||||
if string.strcmp(func_name, "malloc") == 0 \
|
||||
or string.strcmp(func_name, "alloc") == 0 \
|
||||
or string.strcmp(func_name, "alloc_buf") == 0:
|
||||
if param_idx == 0:
|
||||
return i64_ty
|
||||
if string.strcmp(func_name, "calloc") == 0:
|
||||
if param_idx == 0 or param_idx == 1:
|
||||
return i64_ty
|
||||
if string.strcmp(func_name, "realloc") == 0:
|
||||
if param_idx == 1:
|
||||
return i64_ty
|
||||
|
||||
return None
|
||||
def _is_sha1_mangled_call(self, call_name: str) -> int:
|
||||
"""判断 call_name 是否为跨模块 Viper 函数("{16位hex}.func" 形式)
|
||||
|
||||
C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0,
|
||||
不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。
|
||||
"""
|
||||
if call_name is None:
|
||||
return 0
|
||||
first_dot: str = string.strchr(call_name, '.')
|
||||
if first_dot is None:
|
||||
return 0
|
||||
pre_len: t.CSizeT = t.CSizeT(t.CUInt64T(first_dot) - t.CUInt64T(call_name))
|
||||
if pre_len != 16:
|
||||
return 0
|
||||
for hx in range(16):
|
||||
ch: int = call_name[hx]
|
||||
if ch < 48 or ch > 57:
|
||||
if ch < 97 or ch > 102:
|
||||
if ch < 65 or ch > 70:
|
||||
return 0
|
||||
return 1
|
||||
def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
dst_val: llvmlite.Value | t.CPtr,
|
||||
src_val: llvmlite.Value | t.CPtr,
|
||||
num_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""将 memcpy 调用映射到 @llvm.memcpy 内联函数
|
||||
|
||||
直接接收三个 Value 参数,避免通过 Next 链表遍历(共享 alloca 对象的
|
||||
Next 字段可能被其他操作污染,导致链表断裂)。
|
||||
"""
|
||||
if pool is None or builder is None or mod is None:
|
||||
return None
|
||||
if dst_val is None or src_val is None or num_val is None:
|
||||
return None
|
||||
|
||||
# 在函数顶部声明 void_ty,避免后续遮蔽
|
||||
void_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
|
||||
|
||||
# 声明 @llvm.memcpy(如未声明)
|
||||
memcpy_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, "llvm.memcpy")
|
||||
if memcpy_func is None:
|
||||
memcpy_func = llvmlite.create_declare(pool, mod, "llvm.memcpy", void_ty)
|
||||
if memcpy_func is not None:
|
||||
i8_ptr_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
i64_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
i1_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
|
||||
llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "dst")
|
||||
llvmlite.add_param(pool, memcpy_func, i8_ptr_ty_d, "src")
|
||||
llvmlite.add_param(pool, memcpy_func, i64_ty_d, "len")
|
||||
llvmlite.add_param(pool, memcpy_func, i1_ty_d, "isvolatile")
|
||||
|
||||
# 类型转换: dst, src → i8*; num → i64
|
||||
# 注意: 使用 value_get_ty 访问器获取 Ty,避免本地 Value 类型字段索引偏移
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
|
||||
# dst → i8*
|
||||
dst_coerced: llvmlite.Value | t.CPtr = dst_val
|
||||
dst_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(dst_val)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(dst_ty) != 0:
|
||||
dst_coerced = llvmlite.build_bitcast(builder, dst_val, i8_ptr_ty)
|
||||
else:
|
||||
dst_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dst_ty)
|
||||
if dst_bits != 0:
|
||||
if dst_bits < 64:
|
||||
dst_coerced = llvmlite.build_zext(builder, dst_val, i64_ty)
|
||||
dst_coerced = llvmlite.build_inttoptr(builder, dst_coerced, i8_ptr_ty)
|
||||
|
||||
# src → i8*
|
||||
src_coerced: llvmlite.Value | t.CPtr = src_val
|
||||
src_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(src_val)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(src_ty) != 0:
|
||||
src_coerced = llvmlite.build_bitcast(builder, src_val, i8_ptr_ty)
|
||||
else:
|
||||
src_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(src_ty)
|
||||
if src_bits != 0:
|
||||
if src_bits < 64:
|
||||
src_coerced = llvmlite.build_zext(builder, src_val, i64_ty)
|
||||
src_coerced = llvmlite.build_inttoptr(builder, src_coerced, i8_ptr_ty)
|
||||
|
||||
# num → i64
|
||||
num_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(builder, num_val, i64_ty)
|
||||
|
||||
if dst_coerced is None or src_coerced is None or num_coerced is None:
|
||||
return None
|
||||
|
||||
# 添加 i1 false 参数(isvolatile)— llvm.memcpy 需要 4 个参数
|
||||
false_val: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, llvmlite.Int1(pool), 0, "false")
|
||||
|
||||
# 使用 includes 中的访问器函数构建参数链表,避免本地 Value 类型字段索引偏移
|
||||
# (f26cd4c78b4d554c.Value 缺少 GSListNode 的 Next 字段,直接访问 .Next 会修改 .Ty)
|
||||
llvmlite.value_set_next(dst_coerced, src_coerced)
|
||||
llvmlite.value_set_next(src_coerced, num_coerced)
|
||||
if false_val is not None:
|
||||
llvmlite.value_set_next(num_coerced, false_val)
|
||||
llvmlite.value_set_next(false_val, None)
|
||||
else:
|
||||
llvmlite.value_set_next(num_coerced, None)
|
||||
|
||||
# 使用 build_call 发射 call 指令(内部用 includes Value 类型访问字段,索引正确)
|
||||
# _ll_name_needs_quote 已对 llvm. 前缀豁免引号
|
||||
result: llvmlite.Value | t.CPtr = llvmlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0)
|
||||
|
||||
# 返回 dst(匹配 C memcpy 语义: 返回 dest 指针)
|
||||
return dst_coerced
|
||||
# ============================================================
|
||||
# _translate_call_with_kwargs — 带关键字参数的函数调用
|
||||
#
|
||||
# 支持两种语法:
|
||||
# add(b=4, a=3) — 全关键字乱序
|
||||
# greet("Hello", name="World") — 位置 + 关键字混合
|
||||
#
|
||||
# 策略: 遍历 found_func.Params 链表,对每个参数:
|
||||
# 1. 若位置参数仍有剩余(param_idx < can),取位置参数
|
||||
# 2. 否则按参数名匹配关键字参数
|
||||
# 3. 按参数顺序构建调用参数链表
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
func_name: str,
|
||||
found_func: llvmlite.Function | t.CPtr,
|
||||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
can: t.CSizeT,
|
||||
keywords: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
kw_count: t.CSizeT,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""带关键字参数的函数调用,按参数顺序构建调用链表
|
||||
|
||||
使用 llvmlite 访问器函数绕过 stub 类型限制:
|
||||
found_func.Params/RetTy/Name 和 cur_param.Name/Ty/Next 在 stub
|
||||
类型中字段不足,直接访问会被 TransPyC 静默跳过。
|
||||
"""
|
||||
head: llvmlite.Value | t.CPtr = None
|
||||
tail: llvmlite.Value | t.CPtr = None
|
||||
actual_count: t.CSizeT = 0
|
||||
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
|
||||
mc_dst1: llvmlite.Value | t.CPtr = None
|
||||
mc_src1: llvmlite.Value | t.CPtr = None
|
||||
mc_num1: llvmlite.Value | t.CPtr = None
|
||||
|
||||
# 遍历函数参数链表(使用访问器获取 Head)
|
||||
param_idx: t.CSizeT = 0
|
||||
cur_param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
|
||||
|
||||
while cur_param is not None:
|
||||
arg_val: llvmlite.Value | t.CPtr = None
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param)
|
||||
if param_idx < can:
|
||||
# 优先使用位置参数
|
||||
arg: ast.AST | t.CPtr = cargs.get(param_idx)
|
||||
if arg is not None:
|
||||
# 传形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant)
|
||||
arg_val = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, arg, None, 0, trans, param_ty)
|
||||
else:
|
||||
# 查找匹配的关键字参数
|
||||
param_name: str = llvmlite.param_get_name(cur_param)
|
||||
if param_name is not None:
|
||||
for ki in range(kw_count):
|
||||
kw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(keywords.get(ki))
|
||||
if kw is None or kw.arg is None or kw.value is None:
|
||||
continue
|
||||
if self.Gen.CallGenCall._kwarg_name_matches(kw.arg, param_name) == 1:
|
||||
arg_val = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, kw.value, None, 0, trans, param_ty)
|
||||
break
|
||||
|
||||
if arg_val is not None:
|
||||
# 类型转换(按参数类型,使用访问器)
|
||||
if param_ty is not None:
|
||||
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, param_ty)
|
||||
arg_val.Next = None
|
||||
if head is None:
|
||||
head = arg_val
|
||||
else:
|
||||
tail.Next = arg_val
|
||||
tail = arg_val
|
||||
# memcpy 专用: 保存前三个参数的独立引用
|
||||
if actual_count == 0:
|
||||
mc_dst1 = arg_val
|
||||
elif actual_count == 1:
|
||||
mc_src1 = arg_val
|
||||
elif actual_count == 2:
|
||||
mc_num1 = arg_val
|
||||
actual_count += 1
|
||||
|
||||
param_idx += 1
|
||||
cur_param = llvmlite.param_get_next(cur_param)
|
||||
|
||||
# 使用模块中的实际函数名(可能带 SHA1 前缀,使用访问器)
|
||||
# declare 和 define 统一用 found_func.Name:
|
||||
# - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name)
|
||||
# - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名
|
||||
# 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀,
|
||||
# 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference
|
||||
call_name: str = func_name
|
||||
found_name: str = llvmlite.function_get_name(found_func)
|
||||
if found_name is not None:
|
||||
call_name = found_name
|
||||
|
||||
# 返回类型(使用访问器)
|
||||
call_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func)
|
||||
if call_ret_ty is None:
|
||||
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name)
|
||||
|
||||
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
|
||||
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3:
|
||||
return self._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1)
|
||||
return llvmlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,418 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenProp - CallGenMixin 拆分(阶段 B):属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)
|
||||
#
|
||||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class CallGenProp(CallGenBase.CallGenBase):
|
||||
"""属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)"""
|
||||
|
||||
# ============================================================
|
||||
# _ensure_method_declare - 为跨模块方法调用创建 declare 声明
|
||||
#
|
||||
# 当 found_func 为 None(stub 未注入或跨模块调用)时,
|
||||
# 在当前模块中创建函数声明,避免 llc 报 undefined value。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
|
||||
#
|
||||
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
|
||||
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
|
||||
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
# _translate_property_call — 翻译属性方法调用
|
||||
#
|
||||
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
|
||||
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
|
||||
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
|
||||
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
|
||||
#
|
||||
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
|
||||
# ============================================================
|
||||
def _translate_property_call(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
class_name: str,
|
||||
method_name: str,
|
||||
call_head: llvmlite.Value | t.CPtr,
|
||||
call_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译属性/静态方法调用,返回 call 结果 Value"""
|
||||
if pool is None or builder is None or class_name is None or method_name is None:
|
||||
return None
|
||||
|
||||
# 构造查找名: ClassName.method_name
|
||||
lookup_name: str = pool.alloc(128)
|
||||
if lookup_name is None:
|
||||
return None
|
||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
|
||||
|
||||
# 查找函数(精确/后缀匹配)
|
||||
found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, lookup_name)
|
||||
|
||||
# 确定调用名
|
||||
call_name: str = lookup_name
|
||||
if found_func is not None:
|
||||
found_name: str = llvmlite.function_get_name(found_func)
|
||||
if found_name is not None:
|
||||
call_name = found_name
|
||||
else:
|
||||
# 未找到:用类所属模块的 SHA1 构造调用名
|
||||
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||
if cls_sha1_pc is not None:
|
||||
mlen_pc: t.CSizeT = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
|
||||
mangled_pc: str = pool.alloc(mlen_pc)
|
||||
if mangled_pc is not None:
|
||||
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
|
||||
call_name = mangled_pc
|
||||
|
||||
# 确定返回类型
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
call_ret_ty: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if found_func is not None:
|
||||
frt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func)
|
||||
if frt is not None:
|
||||
call_ret_ty = frt
|
||||
else:
|
||||
sp_ty1: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty(pool, class_name, method_name)
|
||||
if sp_ty1 is not None:
|
||||
call_ret_ty = sp_ty1
|
||||
else:
|
||||
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||
looked_up_ty: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
|
||||
pool, cls_sha1_rt, class_name, method_name)
|
||||
if looked_up_ty is not None:
|
||||
call_ret_ty = looked_up_ty
|
||||
else:
|
||||
# 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单)
|
||||
vpi_ret_ty: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi(
|
||||
pool, cls_sha1_rt, class_name, method_name)
|
||||
if vpi_ret_ty is not None:
|
||||
call_ret_ty = vpi_ret_ty
|
||||
else:
|
||||
# Phase A 依赖未就绪(cls_sha1_rt None / 定义模块 text.ll 未生成):
|
||||
# 标记 pending 下遍重译(对齐 _translate_method_call L4783-4790),
|
||||
# 绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。
|
||||
if cls_sha1_rt is None \
|
||||
or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0:
|
||||
if trans is not None:
|
||||
trans._deps_pending = 1
|
||||
# [DEPDBG] 临时诊断:属性方法依赖未就绪即 pending 的根因(用后移除)
|
||||
dbg_ms8: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?"
|
||||
stdio.printf("[DEPDBG] pending(prop_ret): mod=%s cls=%s method=%s sha1=%s\n",
|
||||
dbg_ms8, class_name, method_name,
|
||||
cls_sha1_rt if cls_sha1_rt is not None else "none")
|
||||
# 依赖未就绪:保持默认 i32(build_call(Void) 返回 None
|
||||
# 会破坏表达式翻译;pending 文件本遍不写盘,下遍重译)
|
||||
call_ret_ty = llvmlite.Int32(pool)
|
||||
else:
|
||||
call_ret_ty = self._fail_method_ret_ty(method_name)
|
||||
|
||||
# 参数类型对齐:按目标函数声明的参数类型转换实参(与 _translate_method_call
|
||||
# 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong),
|
||||
# 不转换则 call(i32) 与 declare(i64) 不匹配,llc 报参数类型错误。
|
||||
# 目标函数优先用已找到的 found_func,否则按 mangled 调用名查模块 declare。
|
||||
tgt_func_pc: llvmlite.Function | t.CPtr = found_func
|
||||
if tgt_func_pc is None:
|
||||
tgt_func_pc = self.find_func_in_module(mod, call_name)
|
||||
if tgt_func_pc is not None:
|
||||
prev_pc: llvmlite.Value | t.CPtr = None
|
||||
cur_arg_pc: llvmlite.Value | t.CPtr = call_head
|
||||
cur_param_pc: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(tgt_func_pc)
|
||||
while cur_arg_pc is not None and cur_param_pc is not None:
|
||||
param_ty_pc: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_pc)
|
||||
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
|
||||
coerced_pc: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
|
||||
builder, cur_arg_pc, param_ty_pc)
|
||||
if coerced_pc is not None and coerced_pc != cur_arg_pc:
|
||||
# 保持链表完整性:新 Value 继承原节点的 Next,前驱指向新节点
|
||||
llvmlite.value_set_next(coerced_pc, cur_arg_pc.Next)
|
||||
if prev_pc is None:
|
||||
call_head = coerced_pc
|
||||
else:
|
||||
llvmlite.value_set_next(prev_pc, coerced_pc)
|
||||
cur_arg_pc = coerced_pc
|
||||
prev_pc = cur_arg_pc
|
||||
cur_arg_pc = cur_arg_pc.Next
|
||||
cur_param_pc = llvmlite.param_get_next(cur_param_pc)
|
||||
|
||||
# 确保 declare 存在,然后调用
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
|
||||
return llvmlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
|
||||
# ============================================================
|
||||
# _resolve_obj_class_name — 解析对象表达式的类名(短名)
|
||||
#
|
||||
# 优先从变量注解(AnnotClassName)解析(如 "memhub.MemBuddy" → "MemBuddy"),
|
||||
# 失败时从 LLVM 类型解析(Ptr(Struct) → 类名)。
|
||||
# 供属性访问/赋值/删除识别 property 时使用。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _resolve_obj_class_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
obj_node: ast.AST | t.CPtr) -> str:
|
||||
"""解析对象表达式的类名(短名),失败返回 None"""
|
||||
if pool is None or trans is None or obj_node is None:
|
||||
return None
|
||||
if obj_node.kind() == ast.ASTKind.Name:
|
||||
obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(obj_node)
|
||||
if obj_nm.id is not None:
|
||||
var_entry: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
|
||||
trans.SymTab, obj_nm.id)
|
||||
if var_entry is not None and var_entry.AnnotClassName is not None:
|
||||
annot_val: str = var_entry.AnnotClassName
|
||||
dot_ptr: str = string.strrchr(annot_val, '.')
|
||||
if dot_ptr is not None:
|
||||
return dot_ptr + 1
|
||||
return annot_val
|
||||
return None
|
||||
# ============================================================
|
||||
# _try_property_getter_attr — 尝试将 obj.attr 翻译为 property getter 调用
|
||||
#
|
||||
# 检查 obj 的类是否有 @property/@x.getter 的 attr 方法;
|
||||
# 若有则生成 call Class.attr(obj) 并返回调用结果 Value;否则返回 None。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _try_property_getter_attr(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
at_node: ast.Attribute | t.CPtr,
|
||||
obj_ptr: llvmlite.Value | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
fallback_class_name: str = None) -> llvmlite.Value | t.CPtr:
|
||||
"""尝试 property getter 属性访问,返回调用结果 Value;非 getter 返回 None
|
||||
|
||||
fallback_class_name: 对象表达式非 Name 时从 LLVM 类型解析出的类名兜底。
|
||||
"""
|
||||
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None:
|
||||
return None
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, at_node.value)
|
||||
if class_name is None:
|
||||
class_name = fallback_class_name
|
||||
if class_name is None:
|
||||
return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
|
||||
if meta & GenTypes.META_PROP_GETTER == 0:
|
||||
return None
|
||||
# 单参数调用: obj → getter
|
||||
llvmlite.value_set_next(obj_ptr, None)
|
||||
return self._translate_property_call(
|
||||
pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
|
||||
# ============================================================
|
||||
# _try_property_setter — 尝试将 obj.attr = v 翻译为 property setter 调用
|
||||
#
|
||||
# 检查 obj 的类是否有 @x.setter 的 attr 方法;
|
||||
# 若有则生成 call Class.attr$set(obj, v),返回 1;否则返回 0。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _try_property_setter(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
target_attr: ast.Attribute | t.CPtr,
|
||||
rhs_val: llvmlite.Value | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> int:
|
||||
"""尝试 property setter 赋值,返回 1=已处理(生成调用)"""
|
||||
if pool is None or builder is None or mod is None or trans is None:
|
||||
return 0
|
||||
if target_attr is None or target_attr.attr is None or rhs_val is None:
|
||||
return 0
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||
if class_name is None:
|
||||
return 0
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||
if meta & GenTypes.META_PROP_SETTER == 0:
|
||||
return 0
|
||||
# 翻译对象值(self 参数)
|
||||
obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, target_attr.value, None, 0, trans)
|
||||
if obj_val is None:
|
||||
return 0
|
||||
# 构建参数链表: obj → rhs
|
||||
llvmlite.value_set_next(obj_val, None)
|
||||
llvmlite.value_set_next(rhs_val, None)
|
||||
llvmlite.value_set_next(obj_val, rhs_val)
|
||||
# setter 函数名带 $set 后缀
|
||||
setter_name_buf: str = pool.alloc(128)
|
||||
if setter_name_buf is None:
|
||||
return 0
|
||||
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name,
|
||||
setter_name_buf, obj_val, 2, trans)
|
||||
return 1
|
||||
# ============================================================
|
||||
# _try_property_deleter — 尝试将 del obj.attr 翻译为 property deleter 调用
|
||||
#
|
||||
# 检查 obj 的类是否有 @x.deleter 的 attr 方法;
|
||||
# 若有则生成 call Class.attr$del(obj),返回 1;否则返回 0。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _try_property_deleter(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
target_attr: ast.Attribute | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> int:
|
||||
"""尝试 property deleter 删除,返回 1=已处理(生成调用)"""
|
||||
if pool is None or builder is None or mod is None or trans is None:
|
||||
return 0
|
||||
if target_attr is None or target_attr.attr is None:
|
||||
return 0
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||
if class_name is None:
|
||||
return 0
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||
if meta & GenTypes.META_PROP_DELETER == 0:
|
||||
return 0
|
||||
# 翻译对象值(self 参数)
|
||||
obj_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, target_attr.value, None, 0, trans)
|
||||
if obj_val is None:
|
||||
return 0
|
||||
llvmlite.value_set_next(obj_val, None)
|
||||
# deleter 函数名带 $del 后缀
|
||||
del_name_buf: str = pool.alloc(128)
|
||||
if del_name_buf is None:
|
||||
return 0
|
||||
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name,
|
||||
del_name_buf, obj_val, 1, trans)
|
||||
return 1
|
||||
# ============================================================
|
||||
# _translate_static_class_call — 翻译 ClassName.method(args) 静态/类方法调用
|
||||
#
|
||||
# @staticmethod: 直接 call Class.method(args...),无 self
|
||||
# @classmethod: 在参数前插入栈上分配的类实例指针(与 TPC 一致)
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _translate_static_class_call(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
class_name: str,
|
||||
method_name: str,
|
||||
cl: ast.Call | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译类名静态/类方法调用,返回 call 结果 Value;非静态/类方法返回 None"""
|
||||
if pool is None or builder is None or mod is None or trans is None:
|
||||
return None
|
||||
if class_name is None or method_name is None or cl is None:
|
||||
return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
|
||||
is_static: int = 0
|
||||
is_class: int = 0
|
||||
if meta & GenTypes.META_STATIC != 0:
|
||||
is_static = 1
|
||||
if meta & GenTypes.META_CLASS != 0:
|
||||
is_class = 1
|
||||
if is_static == 0 and is_class == 0:
|
||||
return None
|
||||
|
||||
# 构建参数链表
|
||||
head: llvmlite.Value | t.CPtr = None
|
||||
total_count: int = 0
|
||||
|
||||
# @classmethod: 插入栈上分配的类实例指针作为 cls 参数
|
||||
if is_class != 0:
|
||||
struct_ty_cc: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name)
|
||||
if struct_ty_cc is None:
|
||||
return None
|
||||
cls_ptr: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty_cc)
|
||||
if cls_ptr is None:
|
||||
return None
|
||||
llvmlite.value_set_next(cls_ptr, None)
|
||||
head = cls_ptr
|
||||
total_count = 1
|
||||
|
||||
# 翻译调用参数
|
||||
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
|
||||
can: t.CSizeT = 0
|
||||
if cargs is not None:
|
||||
can = len(cargs)
|
||||
tail: llvmlite.Value | t.CPtr = head
|
||||
for ai in range(can):
|
||||
arg_node: ast.AST | t.CPtr = cargs.get(ai)
|
||||
if arg_node is None:
|
||||
continue
|
||||
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, arg_node, None, 0, trans)
|
||||
if arg_val is None:
|
||||
continue
|
||||
llvmlite.value_set_next(arg_val, None)
|
||||
if head is None:
|
||||
head = arg_val
|
||||
tail = arg_val
|
||||
else:
|
||||
llvmlite.value_set_next(tail, arg_val)
|
||||
tail = arg_val
|
||||
total_count += 1
|
||||
|
||||
# 零参 @staticmethod(如 PyListObj.new()):head 保持 None 是合法的
|
||||
# 无参调用,照常走 _translate_property_call(SHA1 前缀名 + 精确返回类型)。
|
||||
# 若此处 return None,调用方回退通用路径把方法名当裸函数解析,生成
|
||||
# call i32 @new() → 链接 undefined reference(CPythonTest 根因)。
|
||||
# @classmethod 恒有 cls_ptr(head 非 None),不受影响。
|
||||
return self._translate_property_call(pool, builder, mod, class_name,
|
||||
method_name, head, total_count, trans)
|
||||
# ============================================================
|
||||
# _is_list_builtin_method - 判断是否是 list 泛型内置容器方法
|
||||
#
|
||||
# list[T] 是内置泛型容器(_list.vp),其方法(append/get/set/pop/clear/
|
||||
# __iter__/__len__/__getitem__/__setitem__)由 translate_call 的 list
|
||||
# 内置分支内联 GEP 处理,不注册为方法函数。若 obj.method() 检测把
|
||||
# list 特化 struct(如 list[AST | CPtr])误当普通结构体走
|
||||
# _translate_method_call,会查不到 append 返回类型 → fail-fast
|
||||
# (NewAstTest body.append 根因)。此判断用于跳过 method_call 路径。
|
||||
#
|
||||
# 返回: 1=list 内置方法(应落到 list 内置分支),0=普通方法
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _is_list_builtin_method(self, class_name: str,
|
||||
method_name: str) -> int:
|
||||
"""判断 (class_name, method_name) 是否是 list 泛型内置方法"""
|
||||
if class_name is None or method_name is None:
|
||||
return 0
|
||||
# 类名必须以 "list" 开头(list / list[AST | CPtr] 特化名)
|
||||
if string.strncmp(class_name, "list", 4) != 0:
|
||||
return 0
|
||||
# 方法名必须是 list 内置方法集合
|
||||
if string.strcmp(method_name, "append") == 0: return 1
|
||||
if string.strcmp(method_name, "get") == 0: return 1
|
||||
if string.strcmp(method_name, "set") == 0: return 1
|
||||
if string.strcmp(method_name, "pop") == 0: return 1
|
||||
if string.strcmp(method_name, "clear") == 0: return 1
|
||||
if string.strcmp(method_name, "__iter__") == 0: return 1
|
||||
if string.strcmp(method_name, "__len__") == 0: return 1
|
||||
if string.strcmp(method_name, "__getitem__") == 0: return 1
|
||||
if string.strcmp(method_name, "__setitem__") == 0: return 1
|
||||
return 0
|
||||
@@ -1,403 +0,0 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
|
||||
#
|
||||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class CallGenVa(CallGenBase.CallGenBase):
|
||||
"""va_arg 变长参数(start/arg/end / 变长调用提升)"""
|
||||
|
||||
# ============================================================
|
||||
# _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数
|
||||
#
|
||||
# EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/
|
||||
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
|
||||
# ============================================================
|
||||
|
||||
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
arg_node: ast.AST | t.CPtr,
|
||||
funcs_ptr: t.CPtr,
|
||||
func_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""获取 va_list 变量的 alloca 指针(不 load)
|
||||
|
||||
va_arg/va_start/va_end 需要的是 va_list 对象的地址(alloca 指针),
|
||||
而非 va_list 的值。若 arg_node 是 Name,查符号表获取 alloca;
|
||||
否则用 translate_value 翻译(调用者负责确保传入的是指针)。
|
||||
"""
|
||||
if arg_node is None:
|
||||
return None
|
||||
# Name 节点:直接查符号表获取 alloca 指针(不 load)
|
||||
if arg_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arg_node)
|
||||
if nm.id is not None:
|
||||
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is not None:
|
||||
return alloca
|
||||
# 回退:translate_value(可能返回 load 后的值,仅适用于已是指针的情况)
|
||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
|
||||
def _resolve_va_arg_type(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
type_node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""解析 va_arg/arg 的类型参数为 LLVMType
|
||||
|
||||
支持的类型表达式:
|
||||
t.CInt / t.CInt32T → i32
|
||||
t.CInt64T → i64
|
||||
t.CDouble → double
|
||||
t.CPtr / str → i8*
|
||||
str / bytes → i8*
|
||||
无类型参数时默认 i32(vsnprintf 中 va_arg(ap) 常用于读取 i32)
|
||||
"""
|
||||
if type_node is None:
|
||||
return llvmlite.Int32(pool)
|
||||
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
|
||||
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||||
pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None:
|
||||
return resolved
|
||||
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
|
||||
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg 类型解析失败", "TypeError")
|
||||
return llvmlite.Int32(pool)
|
||||
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cl: ast.Call | t.CPtr,
|
||||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
can: t.CSizeT,
|
||||
funcs_ptr: t.CPtr,
|
||||
func_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""va_arg(ap, type) / va_arg(ap) → %N = va_arg <ap_ty> <ap>, <type>
|
||||
|
||||
生成 LLVM va_arg 指令,从 va_list 读取下一个变长参数。
|
||||
"""
|
||||
if can == 0 or cargs is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:va_list(求值为 i8* 地址值,对齐 TPC _HandleVaArgLlvm)
|
||||
# 当前函数 *args:translate_value 经 vararg 名映射直接返回 va_list 缓冲区地址;
|
||||
# 转发场景(sprintf → vsnprintf 的 ap 参数):translate_value load 参数得到地址值。
|
||||
ap_node: ast.AST | t.CPtr = cargs.get(0)
|
||||
ap_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, ap_node, None, 0, trans)
|
||||
if ap_val is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
ap_ptr: llvmlite.Value | t.CPtr = ap_val
|
||||
|
||||
# 第二个参数:目标类型(可选,默认 i32)
|
||||
arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if can >= 2:
|
||||
type_node: ast.AST | t.CPtr = cargs.get(1)
|
||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||
|
||||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73)
|
||||
read_ty: llvmlite.LLVMType | t.CPtr = arg_ty
|
||||
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||
if abits_va != 0 and abits_va < 64:
|
||||
read_ty = llvmlite.Int64(pool)
|
||||
|
||||
# 生成 va_arg 指令
|
||||
return llvmlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||
def _translate_arg_sugar(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cl: ast.Call | t.CPtr,
|
||||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
can: t.CSizeT,
|
||||
funcs_ptr: t.CPtr,
|
||||
func_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""arg(type) → %N = va_arg <va_list_ty> <trans._va_list_ptr>, <type>
|
||||
|
||||
使用当前函数的隐式 va_list(trans._va_list_ptr),
|
||||
简化 *args 函数体内的 va_arg 调用。
|
||||
"""
|
||||
if trans._va_list_ptr is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:目标类型(可选,默认 i32)
|
||||
arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if can >= 1 and cargs is not None:
|
||||
type_node: ast.AST | t.CPtr = cargs.get(0)
|
||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||
|
||||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致)
|
||||
read_ty: llvmlite.LLVMType | t.CPtr = arg_ty
|
||||
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||
if abits_arg != 0 and abits_arg < 64:
|
||||
read_ty = llvmlite.Int64(pool)
|
||||
|
||||
# 生成 va_arg 指令(使用 trans._va_list_ptr 作为 va_list 地址)
|
||||
return llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
|
||||
def _translate_va_start_sugar(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
can: t.CSizeT,
|
||||
funcs_ptr: t.CPtr,
|
||||
func_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""va_start(ap, last_arg) → call void @llvm.va_start(i8* %ap)
|
||||
|
||||
初始化 va_list。第二个参数 last_arg 在 LLVM intrinsic 中不使用(C 宏兼容)。
|
||||
"""
|
||||
if can == 0 or cargs is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
||||
ap_node: ast.AST | t.CPtr = cargs.get(0)
|
||||
ap_ptr: llvmlite.Value | t.CPtr = self._va_arg_get_alloca_ptr(
|
||||
pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||
if ap_ptr is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
# bitcast 到 i8*(@llvm.va_start 接受 i8*)
|
||||
i8_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_vs)
|
||||
ap_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
|
||||
if ap_i8 is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
void_ty_vs: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
|
||||
llvmlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
def _translate_va_end_sugar(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
cargs: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
can: t.CSizeT,
|
||||
funcs_ptr: t.CPtr,
|
||||
func_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""va_end(ap) → call void @llvm.va_end(i8* %ap)
|
||||
|
||||
清理 va_list。在大多数平台上是 no-op,但调用它是良好的 C ABI 习惯。
|
||||
"""
|
||||
if can == 0 or cargs is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
||||
ap_node: ast.AST | t.CPtr = cargs.get(0)
|
||||
ap_ptr: llvmlite.Value | t.CPtr = self._va_arg_get_alloca_ptr(
|
||||
pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||
if ap_ptr is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
# bitcast 到 i8*(@llvm.va_end 接受 i8*)
|
||||
i8_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_ve)
|
||||
ap_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
|
||||
if ap_i8 is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
|
||||
void_ty_ve: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
|
||||
llvmlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
def _get_func_fixed_param_count(self, func: llvmlite.Function | t.CPtr) -> int:
|
||||
"""获取函数的固定参数数量(遍历 Params 链表)"""
|
||||
if func is None:
|
||||
return 0
|
||||
count: int = 0
|
||||
param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
|
||||
while param is not None:
|
||||
count += 1
|
||||
param = llvmlite.param_get_next(param)
|
||||
return count
|
||||
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
|
||||
"""已知变参 C 库函数固定参数个数(-1=未知)
|
||||
|
||||
跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin
|
||||
注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None),
|
||||
无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数
|
||||
(viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。
|
||||
"""
|
||||
if name is None:
|
||||
return -1
|
||||
if string.strcmp(name, "snprintf") == 0:
|
||||
return 3
|
||||
if string.strcmp(name, "sprintf") == 0:
|
||||
return 2
|
||||
if string.strcmp(name, "vsprintf") == 0:
|
||||
return 2
|
||||
return -1
|
||||
|
||||
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
name: str, idx: int) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
|
||||
|
||||
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
|
||||
时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。
|
||||
"""
|
||||
if pool is None or name is None or idx < 0:
|
||||
return None
|
||||
i8_ptr_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
i64_kv: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
if string.strcmp(name, "snprintf") == 0:
|
||||
if idx == 0:
|
||||
return i8_ptr_kv
|
||||
if idx == 1:
|
||||
return i64_kv
|
||||
if idx == 2:
|
||||
return i8_ptr_kv
|
||||
return None
|
||||
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
|
||||
if idx == 0:
|
||||
return i8_ptr_kv
|
||||
if idx == 1:
|
||||
return i8_ptr_kv
|
||||
return None
|
||||
return None
|
||||
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
call_name: str,
|
||||
head: llvmlite.Value | t.CPtr,
|
||||
can: t.CSizeT,
|
||||
arg_nodes: list[ast.AST | t.CPtr] | t.CPtr = None,
|
||||
self_offset: int = 0,
|
||||
trans: HT.Translator | t.CPtr = None) -> int:
|
||||
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
|
||||
|
||||
返回: is_variadic (1=变长调用, 0=普通调用)
|
||||
副作用: 修改 head 链表中超过固定参数数量的节点(C ABI 提升)
|
||||
|
||||
arg_nodes: 实参 AST 链表(不含 self),用于 i8/i16 提升的无符号性判断
|
||||
self_offset: Value 链中 self 占用的偏移(方法调用=1,普通函数=0)
|
||||
trans: 供 _node_type_unsigned 查询变量注解
|
||||
"""
|
||||
if mod is None or call_name is None or head is None:
|
||||
return 0
|
||||
# 查找函数对象
|
||||
func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name)
|
||||
fixed_count: int = -1
|
||||
if func is not None and func.IsVarArg == 1:
|
||||
fixed_count = self._get_func_fixed_param_count(func)
|
||||
else:
|
||||
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):
|
||||
# 1. func 为 None——跨模块 stub 未注入(CallGenMixin 注释:stubs
|
||||
# 在翻译后才注入,翻译时跨模块函数 found_func 恒 None);
|
||||
# 2. func 存在但 IsVarArg=0——该 declare 由 _ensure_method_declare
|
||||
# 按实参类型临时创建(无 ... 标记),不能代表真实签名。
|
||||
# 否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 →
|
||||
# va_arg(i64) 高 4 字节读残留栈垃圾(clean 日志 0x7ff700000052 根因)。
|
||||
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
|
||||
if fixed_count < 0:
|
||||
return 0
|
||||
|
||||
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
|
||||
if can <= fixed_count:
|
||||
return 1
|
||||
|
||||
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
|
||||
cur: llvmlite.Value | t.CPtr = head
|
||||
prev: llvmlite.Value | t.CPtr = None
|
||||
idx: int = 0
|
||||
while cur is not None and idx < fixed_count:
|
||||
prev = cur
|
||||
cur = cur.Next
|
||||
idx += 1
|
||||
|
||||
# cur 指向第 fixed_count+1 个节点(第一个变长参数)
|
||||
# 对变长参数进行 C ABI 提升
|
||||
i32_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
double_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
|
||||
v_idx: int = fixed_count
|
||||
while cur is not None:
|
||||
next_node: llvmlite.Value | t.CPtr = cur.Next
|
||||
promoted: llvmlite.Value | t.CPtr = cur
|
||||
# C ABI integer promotion: i8/i16 → i32
|
||||
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
|
||||
if arg_bits != 0 and arg_bits < 32:
|
||||
# 无符号整型(t.CUInt8T/CUInt16T 等)→ zext;有符号/未知 → sext。
|
||||
# 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得
|
||||
# 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。
|
||||
is_u_va: int = 0
|
||||
if arg_nodes is not None and trans is not None:
|
||||
ast_idx_va: int = v_idx - self_offset
|
||||
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
|
||||
an_va: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va)
|
||||
if an_va is not None:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0:
|
||||
is_u_va = 1
|
||||
if is_u_va != 0:
|
||||
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
|
||||
else:
|
||||
# is_u_va 判断失败(arg_nodes/trans 缺失或变量注解查不到)时,
|
||||
# i8 按系统编程惯例默认 zext(对齐 should_zext_widen 的
|
||||
# "i8 → zext(系统编程中 i8 几乎总是无符号字节)"策略),
|
||||
# 否则 printf("%02x", buf[i]) 中 uint8 字节被 sext 成
|
||||
# 0xFFFFFFF3,哈希摘要输出 ffffffXX 垃圾。
|
||||
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8:
|
||||
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
|
||||
else:
|
||||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
|
||||
if promoted is not None:
|
||||
promoted.Next = next_node
|
||||
if prev is not None:
|
||||
prev.Next = promoted
|
||||
# C ABI float promotion: float → double
|
||||
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(promoted.Ty)
|
||||
if arg_fbits != 0 and arg_fbits < 64:
|
||||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, promoted, double_ty_promote, None)
|
||||
if promoted is not None:
|
||||
promoted.Next = next_node
|
||||
if prev is not None:
|
||||
prev.Next = promoted
|
||||
# Win64 变参槽位 8 字节:i8/i16/i32 → i64
|
||||
# TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar),
|
||||
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
|
||||
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
|
||||
# L798-811 的 found_func.IsVarArg 提升)。
|
||||
i64_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
|
||||
if pb_va != 0 and pb_va < 64:
|
||||
promoted_va2: llvmlite.Value | t.CPtr = None
|
||||
is_u_va2: int = 0
|
||||
if arg_nodes is not None and trans is not None:
|
||||
ast_idx_va2: int = v_idx - self_offset
|
||||
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
|
||||
an_va2: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va2)
|
||||
if an_va2 is not None:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0:
|
||||
is_u_va2 = 1
|
||||
if is_u_va2 != 0:
|
||||
promoted_va2 = llvmlite.build_zext(builder, promoted, i64_ty_promote)
|
||||
else:
|
||||
promoted_va2 = llvmlite.build_sext(builder, promoted, i64_ty_promote)
|
||||
if promoted_va2 is not None:
|
||||
promoted_va2.Next = next_node
|
||||
if prev is not None:
|
||||
prev.Next = promoted_va2
|
||||
promoted = promoted_va2
|
||||
prev = promoted
|
||||
cur = next_node
|
||||
v_idx += 1
|
||||
|
||||
return 1
|
||||
@@ -1,246 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.ExprGenAttr as ExprGenAttr
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenVt - CallGenMixin 拆分(阶段 B):vtable 槽位写入
|
||||
#
|
||||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class CallGenVt(CallGenBase.CallGenBase):
|
||||
"""vtable 槽位写入"""
|
||||
|
||||
# ============================================================
|
||||
# _write_vtable_slot / _write_vtable_attr — vtable 方法槽位直接写入
|
||||
#
|
||||
# serial0_file.open = serial0_open(devfs 驱动注册模式):把模块级函数
|
||||
# 指针写入对象 vtable 的方法槽位。vtable 方法不是数据字段(FieldEntry),
|
||||
# get_attribute_ptr 查不到 → 此前 fatal_error "属性写入失败"。
|
||||
# 对齐 TPC HandlesAssign.py L905-949 的写时复制:
|
||||
# 对象 vtable 仍指向类默认 vtable 时,先复制一份(internal 全局 +
|
||||
# llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store
|
||||
# 函数指针(i8*)。多次写入互不覆盖(第二次起 vtable 已非默认,
|
||||
# 跳过复制直接写槽位)。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
def _write_vtable_slot(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
obj_ptr: llvmlite.Value | t.CPtr,
|
||||
class_name: str,
|
||||
method_index: int,
|
||||
func_val: llvmlite.Value | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> int:
|
||||
"""生成 vtable 槽位写入(写时复制),成功返回 1"""
|
||||
if pool is None or builder is None or mod is None or trans is None:
|
||||
return 0
|
||||
if obj_ptr is None or class_name is None or func_val is None:
|
||||
return 0
|
||||
if method_index < 0 or trans._cur_func is None:
|
||||
return 0
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
if i8_ty is None:
|
||||
return 0
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
if i8_ptr_ty is None:
|
||||
return 0
|
||||
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
|
||||
if i8_ptr_ptr_ty is None:
|
||||
return 0
|
||||
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
|
||||
if method_count <= 0:
|
||||
return 0
|
||||
# 构造 vtable 类型 { i8*, i8*, ... }
|
||||
vt_first: llvmlite.ParamNode | t.CPtr = None
|
||||
vt_prev: llvmlite.ParamNode | t.CPtr = None
|
||||
for vi in range(method_count):
|
||||
pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty)
|
||||
if pn is None:
|
||||
continue
|
||||
if vt_first is None:
|
||||
vt_first = pn
|
||||
if vt_prev is not None:
|
||||
llvmlite.paramnode_set_next(vt_prev, pn)
|
||||
vt_prev = pn
|
||||
vtable_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, vt_first, method_count, None)
|
||||
if vtable_ty is None:
|
||||
return 0
|
||||
# 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段)
|
||||
vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, obj_ptr, i8_ptr_ptr_ty)
|
||||
if vtable_slot is None:
|
||||
return 0
|
||||
vtable_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, i8_ptr_ty, vtable_slot)
|
||||
if vtable_ptr is None:
|
||||
return 0
|
||||
# 类默认 vtable 地址(比较用)
|
||||
class_vt: llvmlite.Value | t.CPtr = self.Gen.CallGenLeafKit._make_vtable_ref_i8(
|
||||
pool, builder, mod, class_name, trans)
|
||||
if class_vt is None:
|
||||
return 0
|
||||
cmp_vt: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_EQ, vtable_ptr, class_vt)
|
||||
if cmp_vt is None:
|
||||
return 0
|
||||
# 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如
|
||||
# devfs open/close/read/write 连续赋值)同名 label 冲突)
|
||||
vtc3: int = trans._label_counter
|
||||
trans._label_counter = vtc3 + 1
|
||||
vt_bb_buf3: str = pool.alloc(64)
|
||||
if vt_bb_buf3 is None:
|
||||
return 0
|
||||
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
|
||||
vt_copy_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
|
||||
pool, trans._cur_func, vt_bb_buf3)
|
||||
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
|
||||
vt_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
|
||||
pool, trans._cur_func, vt_bb_buf3)
|
||||
if vt_copy_bb is None or vt_skip_bb is None:
|
||||
return 0
|
||||
llvmlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
|
||||
# copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本
|
||||
llvmlite.position_at_end(builder, vt_copy_bb)
|
||||
vtc: int = trans._label_counter
|
||||
trans._label_counter = vtc + 1
|
||||
vcopy_buf: str = pool.alloc(256)
|
||||
if vcopy_buf is not None:
|
||||
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
|
||||
vcopy_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(
|
||||
pool, vcopy_buf, vtable_ty)
|
||||
if vcopy_gv is not None:
|
||||
llvmlite.module_add_global(mod, vcopy_gv)
|
||||
vcopy_gv.Linkage = None
|
||||
vcopy_gv.Initializer = "zeroinitializer"
|
||||
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
|
||||
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
|
||||
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
|
||||
# 输出时加的,引用侧不自动加)
|
||||
vcopy_ref_nm: str = pool.alloc(272)
|
||||
if vcopy_ref_nm is None:
|
||||
vcopy_ref_nm = vcopy_buf
|
||||
else:
|
||||
viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
|
||||
vcopy_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(
|
||||
pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
|
||||
if vcopy_ref is not None:
|
||||
vcopy_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, vcopy_ref, i8_ptr_ty)
|
||||
if vcopy_i8 is not None:
|
||||
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
|
||||
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
|
||||
# → llc "expected value token"(devfs memcpy 根因)
|
||||
vtsz_buf: str = pool.alloc(32)
|
||||
if vtsz_buf is not None:
|
||||
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
|
||||
vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
|
||||
pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf)
|
||||
if vt_size is not None:
|
||||
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(
|
||||
pool, builder, mod, vcopy_i8, class_vt, vt_size)
|
||||
llvmlite.build_store(builder, vcopy_i8, vtable_slot)
|
||||
llvmlite.build_br(builder, vt_skip_bb)
|
||||
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
|
||||
llvmlite.position_at_end(builder, vt_skip_bb)
|
||||
vt_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, i8_ptr_ty, vtable_slot)
|
||||
if vt_ptr2 is None:
|
||||
return 0
|
||||
vt_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, vt_ptr2, llvmlite.Ptr(pool, vtable_ty))
|
||||
if vt_typed is None:
|
||||
return 0
|
||||
vt_slotp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||||
builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
|
||||
if vt_slotp is None:
|
||||
return 0
|
||||
fptr_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, func_val, i8_ptr_ty)
|
||||
if fptr_i8 is not None:
|
||||
llvmlite.build_store(builder, fptr_i8, vt_slotp)
|
||||
return 1
|
||||
def _write_vtable_attr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
rhs_val: llvmlite.Value | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> int:
|
||||
"""属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)"""
|
||||
if node is None or builder is None or pool is None or mod is None:
|
||||
return 0
|
||||
if rhs_val is None or trans is None:
|
||||
return 0
|
||||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||||
if at is None or at.value is None or at.attr is None:
|
||||
return 0
|
||||
# 解析类名(AnnotClassName;去模块前缀)
|
||||
class_nm_v: str = None
|
||||
if at.value.kind() == ast.ASTKind.Name:
|
||||
nm_v: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||||
if nm_v.id is not None:
|
||||
ve_v: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
|
||||
trans.SymTab, nm_v.id)
|
||||
if ve_v is not None:
|
||||
class_nm_v = ve_v.AnnotClassName
|
||||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||||
# 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenAttr,经 self.Gen 调用
|
||||
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
|
||||
if class_nm_v is None:
|
||||
return 0
|
||||
fd_dot_v: str = string.strrchr(class_nm_v, '.')
|
||||
if fd_dot_v is not None:
|
||||
class_nm_v = fd_dot_v + 1
|
||||
if self.Gen.StructGen.get_struct_type(class_nm_v) is None:
|
||||
return 0
|
||||
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
|
||||
if vm_idx_v < 0:
|
||||
return 0
|
||||
# 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca)
|
||||
obj_ptr_v: llvmlite.Value | t.CPtr = None
|
||||
if at.value.kind() == ast.ASTKind.Name:
|
||||
obj_ptr_v = self.Gen.VarGen.lookup_var(
|
||||
trans.SymTab, ((ast.Name | t.CPtr)(at.value)).id)
|
||||
else:
|
||||
obj_ptr_v = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, at.value, None, 0, trans)
|
||||
if obj_ptr_v is None or obj_ptr_v.Ty is None:
|
||||
return 0
|
||||
# X|t.CPtr 变量(Ptr(Ptr))load 解引用取对象指针
|
||||
match obj_ptr_v.Ty:
|
||||
case llvmlite.LLVMType.Ptr(inner_v):
|
||||
match inner_v:
|
||||
case llvmlite.LLVMType.Ptr(_):
|
||||
obj_ptr_v = llvmlite.build_load(builder, inner_v, obj_ptr_v)
|
||||
case _:
|
||||
pass
|
||||
case _:
|
||||
pass
|
||||
if obj_ptr_v is None:
|
||||
return 0
|
||||
return self._write_vtable_slot(
|
||||
pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
|
||||
@@ -0,0 +1,943 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Local constants: metadata/decorator/exception table sizes and method metadata flag bits.
|
||||
define META_TLS = 32
|
||||
# Class compile-time metadata table capacity (__provides__/__requires__/__require_must__).
|
||||
define CLASS_META_MAX = 256
|
||||
# Custom-decorator wrapper table capacity (number of @decor functions).
|
||||
define DECOR_FUNC_MAX = 64
|
||||
# Decorator table slot length for function/wrapper names (incl. NUL).
|
||||
define DECOR_NAME_LEN = 128
|
||||
# Exception class registry capacity (user classes subclassing Exception).
|
||||
define EXCEPTION_MAX = 64
|
||||
# Exception table slot length for class/parent names (incl. NUL).
|
||||
define EXCEPTION_NAME_LEN = 128
|
||||
# Builtin exception codes (mirrors TPC EXCEPTION_CODE_MAP); user classes start at 100.
|
||||
# KeyError=6 IOError=7 OSError=8 AssertionError=9 Exception=99.
|
||||
define BUILTIN_EH_VALUE_ERROR = 1
|
||||
define BUILTIN_EH_RUNTIME_ERROR = 3
|
||||
define BUILTIN_EH_EXCEPTION = 99
|
||||
define EH_USER_CODE_START = 100
|
||||
# Method metadata table sizes (formerly HandlesClassDef.META_MAX/META_KEY_LEN).
|
||||
define META_MAX = 512
|
||||
define META_KEY_LEN = 128
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ClassGenBase - fields plus metadata/exception/inheritance/decorator helpers.
|
||||
novirtual class ClassGenBase:
|
||||
"""Fields plus method-metadata, exception, inheritance and decorator helpers."""
|
||||
|
||||
# Method metadata table (formerly _g_method_meta_keys/_flags/_count).
|
||||
Gen: Generator.Generator*
|
||||
MethodMetaKeys: bytes
|
||||
MethodMetaFlags: bytes
|
||||
MethodMetaCount: int
|
||||
|
||||
# REnum variant table (formerly _renum_class_names/_variant_counts/_variant_names/_variant_tags/_renum_class_count).
|
||||
RenumClassNames: sizet*
|
||||
RenumClassVariantCounts: sizet*
|
||||
RenumVariantNames: sizet*
|
||||
RenumVariantTags: sizet*
|
||||
RenumClassCount: int
|
||||
|
||||
# REnum field alias table (formerly _renum_alias_class/_alias_field/_alias_index/_alias_count).
|
||||
RenumAliasClass: sizet*
|
||||
RenumAliasField: sizet*
|
||||
RenumAliasIndex: sizet*
|
||||
# REnum variant member LLVM types (mirrors TPC class_members): deep-copied into perm_pool.
|
||||
# match binds slots by the real member type, not the raw i64 slot width.
|
||||
RenumAliasTy: sizet*
|
||||
# Alias variant name (perm_pool copy): the same payload offset has a different type
|
||||
# per variant, so match looks up the member type by (class, variant, offset).
|
||||
RenumAliasVariant: sizet*
|
||||
RenumAliasCount: int
|
||||
|
||||
# --- Generic specialization cache (formerly _spec_keys/_spec_names/_spec_count). ---
|
||||
SpecKeys: sizet*
|
||||
SpecNames: sizet*
|
||||
SpecCount: int
|
||||
|
||||
# --- Class compile-time metadata table (__provides__/__requires__/__require_must__).
|
||||
# perm_pool-allocated: kind(4B) + cls(64B) + field(64B) per slot, e.g. FakeDuck __requires__.
|
||||
ClassMetaKinds: int*
|
||||
ClassMetaClss: bytes
|
||||
ClassMetaFields: bytes
|
||||
ClassMetaCount: int
|
||||
|
||||
# --- Custom decorator wrapper table (@log/@skip/@repeat -> __decor_wrap_{name}).
|
||||
# translate_call rewrites the original name to the wrapper via lookup_decor_wrapper.
|
||||
DecoratorFuncCount: int
|
||||
DecoratorFuncNames: bytes
|
||||
DecoratorWrapNames: bytes
|
||||
# First constant decorator arg (the 3 in @repeat(3)); 0 when none (8B i64 per slot).
|
||||
DecoratorArgs: int64*
|
||||
|
||||
# --- Exception class registry for try/except (mirrors TPC HandlesClassDef).
|
||||
# perm_pool-allocated slots: class name(128B) + parent(128B) + code(4B); user codes from 100.
|
||||
ExceptionCount: int
|
||||
ExceptionNames: bytes
|
||||
ExceptionParents: bytes
|
||||
ExceptionCodes: int*
|
||||
# Next exception code to assign (user classes, starting at 100).
|
||||
NextExceptionCode: int
|
||||
# Temp counter for collect_descendant_codes (invalid after it returns).
|
||||
# Must be declared at class top; mid-method field annotations break Mixin layout.
|
||||
_exception_codes_tmp_count: int
|
||||
|
||||
# --- Generic specialization recursion depth (guards against cyclic specialization).
|
||||
# Incremented on entry, decremented on exit; exceeding the limit aborts.
|
||||
GenericSpecDepth: int
|
||||
|
||||
# --- Generic template tables (formerly HandlesClassDef._generic_class_names/
|
||||
# _generic_class_nodes/_generic_class_sha1s). ---
|
||||
GenericClassNames: list[str]*
|
||||
GenericClassNodes: list[ast.ClassDef*]*
|
||||
GenericClassSha1s: list[str]*
|
||||
# Source file path per template (same order as above): specialization temporarily
|
||||
# switches CurrentFile to it so template line numbers in errors stay correct.
|
||||
GenericClassFiles: list[str]*
|
||||
# Template type-param names (same order as GenericClassNames, deep-copied to stdlib):
|
||||
# Phase 1 type_params may expire across phases, so they are flattened here.
|
||||
GenericClassTpParams: sizet*
|
||||
|
||||
# --- Generic function template tables, keyed "{sha1}.{fname}" to template
|
||||
# node/SHA1/file/type-params; must stay at class top for Mixin layout. ---
|
||||
GenericFuncKeys: list[str]*
|
||||
GenericFuncNodes: list[ast.FunctionDef*]*
|
||||
GenericFuncSha1s: list[str]*
|
||||
GenericFuncFiles: list[str]*
|
||||
GenericFuncTpParams: sizet*
|
||||
|
||||
# --- vtable method collection tables (formerly _g_vt_full_names/
|
||||
# _g_vt_method_names/_g_vt_parent_sha1s/_g_vt_method_count). ---
|
||||
VtFullNames: sizet*
|
||||
VtMethodNames: sizet*
|
||||
VtParentSha1s: sizet*
|
||||
VtMethodCount: int
|
||||
# ============================================================
|
||||
# Methods below migrated in P5-2 from HandlesClassDef.vp; intra-module calls use self.
|
||||
|
||||
def _is_cenum_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the base node is t.CEnum, else 0."""
|
||||
if base_node is None: return 0
|
||||
k: int = base_node.kind()
|
||||
# Name('CEnum') form (from t import CEnum).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "CEnum") == 0: return 1
|
||||
# Alias form: from t import CEnum as en; class Color(en).
|
||||
# Resolve the original name (en -> CEnum) before comparing.
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
|
||||
if alias_orig is not None:
|
||||
og_len_ce: sizet = 0
|
||||
while alias_orig[og_len_ce] != ' ' and alias_orig[og_len_ce] != '\0': og_len_ce += 1
|
||||
if og_len_ce == 5 and string.strncmp(alias_orig, "CEnum", 5) == 0: return 1
|
||||
return 0
|
||||
# Attribute form was retired together with the t module prefix.
|
||||
return 0
|
||||
def _is_enum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the ClassDef is a CEnum (subclasses t.CEnum or uses @t.CEnum)."""
|
||||
if cd is None: return 0
|
||||
# Decorator form: @t.CEnum class Color: (equivalent to subclassing t.CEnum).
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CEnum") == 1: return 1
|
||||
if cd.bases is None: return 0
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return 0
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
if self._is_cenum_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _is_cunion_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the base node is t.CUnion, else 0."""
|
||||
if base_node is None: return 0
|
||||
k: int = base_node.kind()
|
||||
# Name('CUnion') form (from t import CUnion).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "CUnion") == 0: return 1
|
||||
# Alias form: from t import CUnion as un; class Data(un).
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
|
||||
if alias_orig is not None:
|
||||
og_len_cu: sizet = 0
|
||||
while alias_orig[og_len_cu] != ' ' and alias_orig[og_len_cu] != '\0': og_len_cu += 1
|
||||
if og_len_cu == 6 and string.strncmp(alias_orig, "CUnion", 6) == 0: return 1
|
||||
return 0
|
||||
# Attribute form was retired together with the t module prefix.
|
||||
return 0
|
||||
def _is_union_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the ClassDef is a CUnion (subclasses t.CUnion or uses @t.CUnion)."""
|
||||
if cd is None: return 0
|
||||
# Decorator form: @t.CUnion class Data:
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CUnion") == 1: return 1
|
||||
if cd.bases is None: return 0
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return 0
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
if self._is_cunion_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _is_crenum_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the base node is t.REnum, else 0."""
|
||||
if base_node is None: return 0
|
||||
k: int = base_node.kind()
|
||||
# Name('REnum') form (from t import REnum).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "REnum") == 0: return 1
|
||||
# Alias form: from t import REnum as ren; class Opt(ren).
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
|
||||
if alias_orig is not None:
|
||||
og_len_cr: sizet = 0
|
||||
while alias_orig[og_len_cr] != ' ' and alias_orig[og_len_cr] != '\0': og_len_cr += 1
|
||||
if og_len_cr == 5 and string.strncmp(alias_orig, "REnum", 5) == 0: return 1
|
||||
return 0
|
||||
# Attribute form was retired together with the t module prefix.
|
||||
return 0
|
||||
def _is_renum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the ClassDef is a REnum (subclasses t.REnum or uses @t.REnum)."""
|
||||
if cd is None: return 0
|
||||
# Decorator form: @t.REnum class X:
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "REnum") == 1: return 1
|
||||
if cd.bases is None: return 0
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return 0
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
if self._is_crenum_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _has_decorator(self, decorator_list: list[ast.AST*]*, deco_name: str) -> int:
|
||||
"""Return 1 if decorator_list contains the given bare builtin decorator name."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
# @NoVTable / @CVTable / @TLS / @CStatic form (bare builtin Name).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(deco)
|
||||
if nm.id is not None and string.strcmp(nm.id, deco_name) == 0: return 1
|
||||
return 0
|
||||
def _tattr_name(self, node: ast.AST*, attr_name: str) -> int:
|
||||
"""Extract the outermost attribute name from t.attr.<name> / t.attr.<name>(args) and compare it."""
|
||||
if node is None: return 0
|
||||
if node.kind() == ast.ASTKind.Call:
|
||||
cl2: ast.Call* = ast.Call*(node)
|
||||
if cl2.func is None: return 0
|
||||
return self._tattr_name(cl2.func, attr_name)
|
||||
if node.kind() == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(node)
|
||||
if at2.attr is None: return 0
|
||||
return 1 if string.strcmp(at2.attr, attr_name) == 0 else 0
|
||||
return 0
|
||||
def _class_has_tattr(self, decorator_list: list[ast.AST*]*, attr_name: str) -> int:
|
||||
"""Return 1 if decorator_list contains t.attr.<attr_name> (@c.Attribute(t.attr.xxx) or @t.attr.xxx)."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(deco)
|
||||
# @Attribute(attr.xxx) form: func is the bare builtin Name("Attribute").
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
|
||||
at0: ast.Name* = ast.Name*(cl.func)
|
||||
if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(int(an)):
|
||||
ag: ast.AST* = cl.args.get(ai)
|
||||
if ag is not None and self._tattr_name(ag, attr_name) != 0: return 1
|
||||
else:
|
||||
# Direct @t.attr.xxx form.
|
||||
if self._tattr_name(deco, attr_name) != 0: return 1
|
||||
return 0
|
||||
def _tattr_aligned_int(self, node: ast.AST*) -> int:
|
||||
"""Return N for t.attr.aligned(N); 0 when not aligned or the argument is missing."""
|
||||
if node is None: return 0
|
||||
if node.kind() == ast.ASTKind.Call:
|
||||
cl2: ast.Call* = ast.Call*(node)
|
||||
if cl2.func is None or cl2.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at2: ast.Attribute* = ast.Attribute*(cl2.func)
|
||||
if at2.attr is None or string.strcmp(at2.attr, "aligned") != 0: return 0
|
||||
if cl2.args is None or len(cl2.args) == 0: return 0
|
||||
a0: ast.AST* = cl2.args.get(0)
|
||||
if a0 is None or a0.kind() != ast.ASTKind.Constant: return 0
|
||||
cn: ast.Constant* = ast.Constant*(a0)
|
||||
if cn.const_kind != ast.CONST_INT: return 0
|
||||
return int(cn.int_val)
|
||||
return 0
|
||||
def _class_aligned_value(self, decorator_list: list[ast.AST*]*) -> int:
|
||||
"""Return the alignment bytes from @Attribute(attr.aligned(N)); 0 when absent."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
if deco.kind() == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(deco)
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
|
||||
at0: ast.Name* = ast.Name*(cl.func)
|
||||
if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(int(an)):
|
||||
ag: ast.AST* = cl.args.get(ai)
|
||||
if ag is not None:
|
||||
av: int = self._tattr_aligned_int(ag)
|
||||
if av > 0: return av
|
||||
else:
|
||||
av2: int = self._tattr_aligned_int(deco)
|
||||
if av2 > 0: return av2
|
||||
return 0
|
||||
def _extract_func_meta(self, decorator_list: list[ast.AST*]*) -> int:
|
||||
"""Build the method-metadata bitmask from decorators; 0=no decorator metadata."""
|
||||
meta: int = 0
|
||||
if decorator_list is None: return meta
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return meta
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(deco)
|
||||
if nm.id is not None:
|
||||
if string.strcmp(nm.id, "staticmethod") == 0: meta |= GenTypes.META_STATIC
|
||||
elif string.strcmp(nm.id, "property") == 0: meta |= GenTypes.META_PROP_GETTER
|
||||
elif string.strcmp(nm.id, "classmethod") == 0: meta |= GenTypes.META_CLASS
|
||||
elif string.strcmp(nm.id, "TLS") == 0: meta |= META_TLS
|
||||
elif k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(deco)
|
||||
if at.attr is not None and at.value is not None:
|
||||
if at.value.kind() == ast.ASTKind.Name:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
is_prop_name: int = 0
|
||||
if vn.id is not None and string.strcmp(vn.id, "property") == 0: is_prop_name = 1
|
||||
# Recognize @property.setter and @x.setter/getter/deleter.
|
||||
if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0:
|
||||
if string.strcmp(at.attr, "setter") == 0: meta |= GenTypes.META_PROP_SETTER
|
||||
elif string.strcmp(at.attr, "getter") == 0: meta |= GenTypes.META_PROP_GETTER
|
||||
elif string.strcmp(at.attr, "deleter") == 0: meta |= GenTypes.META_PROP_DELETER
|
||||
return meta
|
||||
def register_class_meta(self, pool: memhub.MemBuddy*, kind: int, cls: str, field: str) -> int:
|
||||
"""Register a class compile-time metadata field (__provides__/__requires__/__require_must__), idempotent."""
|
||||
if pool is None or cls is None or field is None: return -1
|
||||
if self.ClassMetaKinds is None:
|
||||
self.ClassMetaKinds = CompilerFactory.factory.PermPool.alloc(CLASS_META_MAX * 4)
|
||||
self.ClassMetaClss = CompilerFactory.factory.PermPool.alloc(CLASS_META_MAX * 64)
|
||||
self.ClassMetaFields = CompilerFactory.factory.PermPool.alloc(CLASS_META_MAX * 64)
|
||||
if self.ClassMetaKinds is None or self.ClassMetaClss is None or self.ClassMetaFields is None: return -1
|
||||
string.memset(self.ClassMetaKinds, 0, CLASS_META_MAX * 4)
|
||||
string.memset(self.ClassMetaClss, 0, CLASS_META_MAX * 64)
|
||||
string.memset(self.ClassMetaFields, 0, CLASS_META_MAX * 64)
|
||||
self.ClassMetaCount = 0
|
||||
if self.ClassMetaCount >= CLASS_META_MAX: return -1
|
||||
# Idempotent: skip duplicate (kind, cls, field).
|
||||
for i in range(int(self.ClassMetaCount)):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0:
|
||||
if string.strcmp(self.ClassMetaFields + i * 64, field) == 0: return 0
|
||||
idx_cm: sizet = sizet(self.ClassMetaCount) * 64
|
||||
self.ClassMetaKinds[self.ClassMetaCount] = kind
|
||||
string.strncpy(self.ClassMetaClss + idx_cm, cls, 63)
|
||||
self.ClassMetaClss[idx_cm + 63] = '\0'
|
||||
string.strncpy(self.ClassMetaFields + idx_cm, field, 63)
|
||||
self.ClassMetaFields[idx_cm + 63] = '\0'
|
||||
self.ClassMetaCount += 1
|
||||
return 0
|
||||
def class_meta_count(self, kind: int, cls: str) -> int:
|
||||
"""Return the field count of the given metadata kind for a class."""
|
||||
if self.ClassMetaKinds is None or cls is None: return 0
|
||||
n_cm: int = 0
|
||||
for i in range(int(self.ClassMetaCount)):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0: n_cm += 1
|
||||
return n_cm
|
||||
def class_meta_field_at(self, kind: int, cls: str, idx: int) -> str:
|
||||
"""Return the idx-th field name of the given metadata kind for a class."""
|
||||
if self.ClassMetaKinds is None or cls is None: return None
|
||||
n_cm2: int = 0
|
||||
for i in range(int(self.ClassMetaCount)):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0:
|
||||
if n_cm2 == idx: return self.ClassMetaFields + i * 64
|
||||
n_cm2 += 1
|
||||
return None
|
||||
def register_decorator(self, pool: memhub.MemBuddy*, func_name: str, wrapper_name: str, arg0: int64) -> int:
|
||||
"""Register a custom decorator function (@log/@skip/@repeat(3) -> __decor_wrap_{name}); arg0 is its first constant arg (0 when none)."""
|
||||
if pool is None or func_name is None or wrapper_name is None: return -1
|
||||
if self.DecoratorFuncNames is None:
|
||||
self.DecoratorFuncNames = CompilerFactory.factory.PermPool.alloc(DECOR_FUNC_MAX * DECOR_NAME_LEN)
|
||||
self.DecoratorWrapNames = CompilerFactory.factory.PermPool.alloc(DECOR_FUNC_MAX * DECOR_NAME_LEN)
|
||||
self.DecoratorArgs = CompilerFactory.factory.PermPool.alloc(DECOR_FUNC_MAX * 8)
|
||||
if self.DecoratorFuncNames is None or self.DecoratorWrapNames is None or self.DecoratorArgs is None: return -1
|
||||
string.memset(self.DecoratorFuncNames, 0, DECOR_FUNC_MAX * DECOR_NAME_LEN)
|
||||
string.memset(self.DecoratorWrapNames, 0, DECOR_FUNC_MAX * DECOR_NAME_LEN)
|
||||
string.memset(self.DecoratorArgs, 0, DECOR_FUNC_MAX * 8)
|
||||
self.DecoratorFuncCount = 0
|
||||
if self.DecoratorFuncCount >= DECOR_FUNC_MAX: return -1
|
||||
# Idempotent: skip an already-registered function name (multi-pass scan).
|
||||
for i in range(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return 0
|
||||
idx_dc: sizet = sizet(self.DecoratorFuncCount) * DECOR_NAME_LEN
|
||||
string.strncpy(self.DecoratorFuncNames + idx_dc, func_name, DECOR_NAME_LEN - 1)
|
||||
self.DecoratorFuncNames[idx_dc + DECOR_NAME_LEN - 1] = '\0'
|
||||
string.strncpy(self.DecoratorWrapNames + idx_dc, wrapper_name, DECOR_NAME_LEN - 1)
|
||||
self.DecoratorWrapNames[idx_dc + DECOR_NAME_LEN - 1] = '\0'
|
||||
self.DecoratorArgs[self.DecoratorFuncCount] = arg0
|
||||
self.DecoratorFuncCount += 1
|
||||
return 0
|
||||
def lookup_decor_wrapper(self, func_name: str) -> str:
|
||||
"""Look up the wrapper name by original function name; None when not registered."""
|
||||
if self.DecoratorFuncNames is None or func_name is None: return None
|
||||
for i in range(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorWrapNames + i * DECOR_NAME_LEN
|
||||
return None
|
||||
def lookup_decor_arg0(self, func_name: str) -> int64:
|
||||
"""Look up a decorator's first constant arg by original function name (0 when none)."""
|
||||
if self.DecoratorFuncNames is None or func_name is None: return 0
|
||||
for i in range(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorArgs[i]
|
||||
return 0
|
||||
# ============================================================
|
||||
# Exception class registry for try/except (mirrors TPC HandlesClassDef).
|
||||
def _ensure_exception_tables(self) -> int:
|
||||
"""Lazily initialize the exception tables (perm_pool); 0=success."""
|
||||
if self.ExceptionNames is not None: return 0
|
||||
pp_e: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if pp_e is None: return -1
|
||||
self.ExceptionNames = pp_e.alloc(EXCEPTION_MAX * EXCEPTION_NAME_LEN)
|
||||
self.ExceptionParents = pp_e.alloc(EXCEPTION_MAX * EXCEPTION_NAME_LEN)
|
||||
self.ExceptionCodes = pp_e.alloc(EXCEPTION_MAX * 4)
|
||||
if self.ExceptionNames is None or self.ExceptionParents is None or self.ExceptionCodes is None: return -1
|
||||
string.memset(self.ExceptionNames, 0, EXCEPTION_MAX * EXCEPTION_NAME_LEN)
|
||||
string.memset(self.ExceptionParents, 0, EXCEPTION_MAX * EXCEPTION_NAME_LEN)
|
||||
string.memset(self.ExceptionCodes, 0, EXCEPTION_MAX * 4)
|
||||
self.ExceptionCount = 0
|
||||
self.NextExceptionCode = EH_USER_CODE_START
|
||||
return 0
|
||||
def register_exception(self, class_name: str, parent_name: str) -> int:
|
||||
"""Register a user exception class and assign its code (idempotent by name); returns the code or -1."""
|
||||
if class_name is None: return -1
|
||||
if self._ensure_exception_tables() != 0: return -1
|
||||
# Idempotent: return the existing code if already registered (multi-pass).
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, class_name) == 0: return self.ExceptionCodes[i]
|
||||
if self.ExceptionCount >= EXCEPTION_MAX: return -1
|
||||
idx_e: sizet = sizet(self.ExceptionCount) * EXCEPTION_NAME_LEN
|
||||
string.strncpy(self.ExceptionNames + idx_e, class_name, EXCEPTION_NAME_LEN - 1)
|
||||
self.ExceptionNames[idx_e + EXCEPTION_NAME_LEN - 1] = '\0'
|
||||
if parent_name is not None:
|
||||
string.strncpy(self.ExceptionParents + idx_e, parent_name, EXCEPTION_NAME_LEN - 1)
|
||||
self.ExceptionParents[idx_e + EXCEPTION_NAME_LEN - 1] = '\0'
|
||||
code_e: int = self.NextExceptionCode
|
||||
self.NextExceptionCode += 1
|
||||
self.ExceptionCodes[self.ExceptionCount] = code_e
|
||||
self.ExceptionCount += 1
|
||||
return code_e
|
||||
def _builtin_exception_code(self, name: str) -> int:
|
||||
"""Return the builtin exception code (per TPC EXCEPTION_CODE_MAP); 0 when not builtin."""
|
||||
if name is None: return 0
|
||||
if string.strcmp(name, "ValueError") == 0: return 1
|
||||
if string.strcmp(name, "TypeError") == 0: return 2
|
||||
if string.strcmp(name, "RuntimeError") == 0: return 3
|
||||
if string.strcmp(name, "ZeroDivisionError") == 0: return 4
|
||||
if string.strcmp(name, "IndexError") == 0: return 5
|
||||
if string.strcmp(name, "KeyError") == 0: return 6
|
||||
if string.strcmp(name, "IOError") == 0: return 7
|
||||
if string.strcmp(name, "OSError") == 0: return 8
|
||||
if string.strcmp(name, "AssertionError") == 0: return 9
|
||||
if string.strcmp(name, "Exception") == 0: return 99
|
||||
return 0
|
||||
def lookup_exception_code(self, name: str) -> int:
|
||||
"""Look up an exception code: builtin table first, then user table; 0 when neither matches."""
|
||||
if name is None: return 0
|
||||
bi: int = self._builtin_exception_code(name)
|
||||
if bi != 0: return bi
|
||||
if self.ExceptionNames is None: return 0
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: return self.ExceptionCodes[i]
|
||||
return 0
|
||||
def lookup_exception_parent(self, name: str) -> str:
|
||||
"""Look up a user exception's parent name; None when it is not a user exception."""
|
||||
if name is None or self.ExceptionNames is None: return None
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0:
|
||||
p_e: str = self.ExceptionParents + i * EXCEPTION_NAME_LEN
|
||||
if p_e is not None and p_e[0] != '\0': return p_e
|
||||
return None
|
||||
return None
|
||||
def is_exception_name(self, name: str) -> int:
|
||||
"""Return 1 if name is a builtin or user exception class name."""
|
||||
if name is None: return 0
|
||||
if self._builtin_exception_code(name) != 0: return 1
|
||||
if self.ExceptionNames is None: return 0
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: return 1
|
||||
return 0
|
||||
def collect_descendant_codes(self, name: str, out_codes: int*, max_codes: int) -> int:
|
||||
"""Collect the codes of name and all its descendant exceptions into out_codes; returns the count."""
|
||||
if name is None or out_codes is None: return 0
|
||||
self._exception_codes_tmp_count = 0
|
||||
self._collect_desc_codes_rec(name, out_codes, max_codes, 0)
|
||||
return self._exception_codes_tmp_count
|
||||
def _collect_desc_codes_rec(self, name: str, out_codes: int*, max_codes: int, depth: int) -> None:
|
||||
"""Recursively collect name's code plus all descendant codes."""
|
||||
if depth > 32: return
|
||||
code_e: int = self.lookup_exception_code(name)
|
||||
if code_e != 0 and self._exception_codes_tmp_count < max_codes:
|
||||
# Deduplicate (guards against inheritance cycles).
|
||||
dup: int = 0
|
||||
for i in range(int(self._exception_codes_tmp_count)):
|
||||
if out_codes[i] == code_e:
|
||||
dup = 1
|
||||
break
|
||||
if dup == 0:
|
||||
out_codes[self._exception_codes_tmp_count] = code_e
|
||||
self._exception_codes_tmp_count += 1
|
||||
# Recurse over user exceptions whose parent is name.
|
||||
if self.ExceptionNames is None: return
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
cname_e: str = self.ExceptionNames + i * EXCEPTION_NAME_LEN
|
||||
pname_e: str = self.ExceptionParents + i * EXCEPTION_NAME_LEN
|
||||
if pname_e is not None and pname_e[0] != '\0':
|
||||
if string.strcmp(pname_e, name) == 0: self._collect_desc_codes_rec(cname_e, out_codes, max_codes, depth + 1)
|
||||
def _is_exception_class_def(self, cd: ast.ClassDef*) -> int:
|
||||
"""Return 1 if the class def is an exception class (base is Exception or a registered exception)."""
|
||||
if cd is None or cd.bases is None: return 0
|
||||
bases_e: list[ast.AST*]* = cd.bases
|
||||
bn_e: sizet = len(bases_e)
|
||||
for bi_e in range(int(bn_e)):
|
||||
base_e: ast.AST* = bases_e.get(bi_e)
|
||||
if base_e is None: continue
|
||||
base_name_e: str = None
|
||||
if base_e.kind() == ast.ASTKind.Name: base_name_e = (ast.Name*(base_e)).id
|
||||
elif base_e.kind() == ast.ASTKind.Attribute:
|
||||
base_at_e: ast.Attribute* = ast.Attribute*(base_e)
|
||||
if base_at_e.attr is not None: base_name_e = base_at_e.attr
|
||||
if base_name_e is None: continue
|
||||
if string.strcmp(base_name_e, "Exception") == 0: return 1
|
||||
if self.is_exception_name(base_name_e) != 0: return 1
|
||||
return 0
|
||||
def _get_exception_parent_name(self, cd: ast.ClassDef*) -> str:
|
||||
"""Return the real parent name of an exception class (first registered non-Exception base); None otherwise."""
|
||||
if cd is None or cd.bases is None: return None
|
||||
bases_e2: list[ast.AST*]* = cd.bases
|
||||
bn_e2: sizet = len(bases_e2)
|
||||
for bi_e2 in range(int(bn_e2)):
|
||||
base_e2: ast.AST* = bases_e2.get(bi_e2)
|
||||
if base_e2 is None: continue
|
||||
base_name_e2: str = None
|
||||
if base_e2.kind() == ast.ASTKind.Name: base_name_e2 = (ast.Name*(base_e2)).id
|
||||
elif base_e2.kind() == ast.ASTKind.Attribute:
|
||||
base_at_e2: ast.Attribute* = ast.Attribute*(base_e2)
|
||||
if base_at_e2.attr is not None: base_name_e2 = base_at_e2.attr
|
||||
if base_name_e2 is None: continue
|
||||
if string.strcmp(base_name_e2, "Exception") != 0 and self.is_exception_name(base_name_e2) != 0: return base_name_e2
|
||||
return None
|
||||
def _register_exception_class_def(self, trans: HT.Translator*, cd: ast.ClassDef*) -> int:
|
||||
"""Register an exception class (assign code + record parent) without emitting a struct; returns 0."""
|
||||
if cd is None or cd.name is None: return 0
|
||||
parent_e: str = self._get_exception_parent_name(cd)
|
||||
self.register_exception(cd.name, parent_e)
|
||||
return 0
|
||||
def _register_class_meta_list(self, pool: memhub.MemBuddy*, kind: int, cls: str, value_node: ast.AST*) -> None:
|
||||
"""Parse a class-body __provides__/__requires__ list[str] literal and register its entries."""
|
||||
if value_node is None or cls is None: return
|
||||
if value_node.kind() != ast.ASTKind.List: return
|
||||
lst_rcm: ast.List* = ast.List*(value_node)
|
||||
if lst_rcm.elts is None: return
|
||||
elts_rcm: list[ast.AST*]* = lst_rcm.elts
|
||||
en_rcm: sizet = len(elts_rcm)
|
||||
for ei_rcm in range(int(en_rcm)):
|
||||
el_rcm: ast.AST* = elts_rcm.get(ei_rcm)
|
||||
if el_rcm is None or el_rcm.kind() != ast.ASTKind.Constant: continue
|
||||
ec_rcm: ast.Constant* = ast.Constant*(el_rcm)
|
||||
if ec_rcm.str_val is not None: self.register_class_meta(pool, kind, cls, ec_rcm.str_val)
|
||||
def register_method_meta(self, class_name: str, method_name: str, meta: int) -> None:
|
||||
"""Register method metadata (idempotent)."""
|
||||
if class_name is None or method_name is None or meta == 0: return
|
||||
# Lazy initialization.
|
||||
if self.MethodMetaKeys is None:
|
||||
self.MethodMetaKeys = CompilerFactory.factory.PermPool.alloc(META_MAX * META_KEY_LEN)
|
||||
self.MethodMetaFlags = CompilerFactory.factory.PermPool.alloc(META_MAX * 4)
|
||||
if self.MethodMetaKeys is None or self.MethodMetaFlags is None: return
|
||||
string.memset(self.MethodMetaKeys, 0, META_MAX * META_KEY_LEN)
|
||||
string.memset(self.MethodMetaFlags, 0, META_MAX * 4)
|
||||
# Build key "ClassName.method".
|
||||
cl_len: sizet = string.strlen(class_name)
|
||||
mn_len: sizet = string.strlen(method_name)
|
||||
key_buf: bytes = stdlib.malloc(cl_len + mn_len + 2)
|
||||
if key_buf is None: return
|
||||
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
|
||||
# Update duplicate entry.
|
||||
i: int
|
||||
for i in range(int(self.MethodMetaCount)):
|
||||
if string.strcmp(self.MethodMetaKeys + i * META_KEY_LEN, key_buf) == 0:
|
||||
flags_ptr: int* = (int*)(uint64(self.MethodMetaFlags) + i * 4)
|
||||
# OR-combine flags: a getter then setter/deleter registers repeatedly,
|
||||
# so whole-value replacement would drop GETTER and crash downstream mlirlite.
|
||||
flags_ptr[0] = flags_ptr[0] | meta
|
||||
stdlib.free(key_buf)
|
||||
return
|
||||
if self.MethodMetaCount < META_MAX:
|
||||
# Slot is META_KEY_LEN=128 bytes; clamp strncpy to avoid overflowing the next slot.
|
||||
string.strncpy(self.MethodMetaKeys + self.MethodMetaCount * META_KEY_LEN, key_buf, META_KEY_LEN - 1)
|
||||
flags_ptr2: int* = (int*)(uint64(self.MethodMetaFlags) + self.MethodMetaCount * 4)
|
||||
flags_ptr2[0] = meta
|
||||
self.MethodMetaCount += 1
|
||||
stdlib.free(key_buf)
|
||||
def lookup_method_meta(self, class_name: str, method_name: str) -> int:
|
||||
"""Look up the method-metadata bitmask; 0=not registered or not a decorator method."""
|
||||
if class_name is None or method_name is None: return 0
|
||||
if self.MethodMetaKeys is None or self.MethodMetaCount <= 0: return 0
|
||||
cl_len: sizet = string.strlen(class_name)
|
||||
mn_len: sizet = string.strlen(method_name)
|
||||
key_buf: bytes = stdlib.malloc(cl_len + mn_len + 2)
|
||||
if key_buf is None: return 0
|
||||
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
|
||||
found: int = 0
|
||||
i: int
|
||||
for i in range(int(self.MethodMetaCount)):
|
||||
if string.strcmp(self.MethodMetaKeys + i * META_KEY_LEN, key_buf) == 0:
|
||||
flags_ptr: int* = (int*)(uint64(self.MethodMetaFlags) + i * 4)
|
||||
found = flags_ptr[0]
|
||||
break
|
||||
stdlib.free(key_buf)
|
||||
return found
|
||||
def _is_marker_base(self, base_name: str) -> int:
|
||||
"""Return 1 if the base name is a marker base class (Object/CVTable/Exception/CEnum/Enum/CStruct/Struct/CUnion/REnum)."""
|
||||
if base_name is None: return 0
|
||||
if string.strcmp(base_name, "Object") == 0: return 1
|
||||
if string.strcmp(base_name, "CVTable") == 0: return 1
|
||||
if string.strcmp(base_name, "Exception") == 0: return 1
|
||||
if string.strcmp(base_name, "CEnum") == 0: return 1
|
||||
if string.strcmp(base_name, "Enum") == 0: return 1
|
||||
if string.strcmp(base_name, "CStruct") == 0: return 1
|
||||
if string.strcmp(base_name, "Struct") == 0: return 1
|
||||
if string.strcmp(base_name, "CUnion") == 0: return 1
|
||||
if string.strcmp(base_name, "REnum") == 0: return 1
|
||||
return 0
|
||||
def _get_base_name(self, base_node: ast.AST*, pool: memhub.MemBuddy* = None, from_imports: str = None) -> str:
|
||||
"""Extract the base class name from a base AST node; Subscript nodes also build the specialized name; None on failure."""
|
||||
if base_node is None: return None
|
||||
k: int = base_node.kind()
|
||||
# Name(id) form.
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
return nm.id
|
||||
# Attribute(attr) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
return at.attr
|
||||
# Subscript(value, slice) form, e.g. GSListNode[Value].
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(base_node)
|
||||
if sub is None or sub.value is None or sub.slice is None: return None
|
||||
# Extract the base class name (e.g. "GSListNode").
|
||||
base_nm: str = self._get_base_name(sub.value, pool, from_imports)
|
||||
if base_nm is None: return None
|
||||
# Extract the type arg name (e.g. "Value") and normalize it like the generator.
|
||||
slice_k: int = sub.slice.kind()
|
||||
arg_nm: str = None
|
||||
if slice_k == ast.ASTKind.Name:
|
||||
sl_nm: ast.Name* = ast.Name*(sub.slice)
|
||||
arg_nm = sl_nm.id
|
||||
# Fall back to the base name when the type arg cannot be extracted.
|
||||
if arg_nm is None: return base_nm
|
||||
# Without a pool, fall back to the base name (cannot build the specialized name).
|
||||
if pool is None: return base_nm
|
||||
arg_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, arg_nm, from_imports)
|
||||
if arg_norm is not None: arg_nm = arg_norm
|
||||
# Build the specialized name "GSListNode[Value]".
|
||||
base_len: sizet = string.strlen(base_nm)
|
||||
arg_len: sizet = string.strlen(arg_nm)
|
||||
total_len: sizet = base_len + arg_len + 3 # "[" + arg + "]" + NUL
|
||||
mangled: str = pool.alloc(total_len)
|
||||
if mangled is None:
|
||||
return base_nm # fallback
|
||||
mangled[0] = '\0'
|
||||
string.strcat(mangled, base_nm)
|
||||
string.strcat(mangled, "[")
|
||||
string.strcat(mangled, arg_nm)
|
||||
string.strcat(mangled, "]")
|
||||
return mangled
|
||||
return None
|
||||
def _get_parent_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> str:
|
||||
"""Return the ClassDef's real parent name; None when there is no real parent."""
|
||||
if cd is None or cd.bases is None: return None
|
||||
pool: memhub.MemBuddy* = None
|
||||
if trans is not None: pool = trans.Pool
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return None
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
_fi_pc: str = None
|
||||
if trans is not None: _fi_pc = trans._from_imports
|
||||
bname: str = self._get_base_name(base_node, pool, _fi_pc)
|
||||
if bname is not None:
|
||||
# Resolve base aliases (from t import CStruct as cs -> cs is CStruct).
|
||||
# Otherwise find_struct("cs") fails and the class is deferred forever.
|
||||
bname = self._resolve_base_alias(bname, trans)
|
||||
if self._is_marker_base(bname) == 0: return bname
|
||||
return None
|
||||
def _resolve_base_alias(self, base_name: str, trans: HT.Translator* = None) -> str:
|
||||
"""Resolve a base alias (from t import CStruct as cs -> base cs is actually CStruct)."""
|
||||
if base_name is None or trans is None or trans._from_imports is None: return base_name
|
||||
orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, base_name)
|
||||
if orig is not None:
|
||||
# lookup_from_import_original returns a non-NUL-terminated interior pointer;
|
||||
# copy and cut at the space so strcmp gets a clean original name.
|
||||
og_len: sizet = 0
|
||||
while orig[og_len] != ' ' and orig[og_len] != '\0': og_len += 1
|
||||
if og_len > 0:
|
||||
og_buf: str = CompilerFactory.factory.PermPool.alloc(og_len + 1)
|
||||
if og_buf is not None:
|
||||
string.strncpy(og_buf, orig, og_len)
|
||||
og_buf[og_len] = '\0'
|
||||
return og_buf
|
||||
return base_name
|
||||
def _collect_parents(self, pool: memhub.MemBuddy*, cd: ast.ClassDef*, parents_buf: sizet*, max_parents: int, trans: HT.Translator* = None) -> int:
|
||||
"""Collect all non-marker parent names into parents_buf and return the count."""
|
||||
if pool is None or cd is None or cd.bases is None or parents_buf is None: return 0
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
count: int = 0
|
||||
for bi in range(int(bn)):
|
||||
if count >= max_parents: break
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
_fi_cp: str = None
|
||||
if trans is not None: _fi_cp = trans._from_imports
|
||||
bname: str = self._get_base_name(base_node, pool, _fi_cp)
|
||||
if bname is not None:
|
||||
# Resolve base alias (same as _get_parent_class, see _resolve_base_alias).
|
||||
bname = self._resolve_base_alias(bname, trans)
|
||||
if self._is_marker_base(bname) == 0:
|
||||
parents_buf[count] = sizet(bname)
|
||||
count += 1
|
||||
return count
|
||||
def _field_name_exists(self, names_buf: sizet*, count: int, name: str) -> int:
|
||||
"""Return 1 if the field name already exists in names_buf (first-declared parent wins; duplicates skipped)."""
|
||||
if names_buf is None or name is None: return 0
|
||||
for fi in range(int(count)):
|
||||
addr: sizet = names_buf[fi]
|
||||
if addr != 0:
|
||||
existing: str = rawptr(addr)
|
||||
if existing is not None and string.strcmp(existing, name) == 0: return 1
|
||||
return 0
|
||||
def _class_body_has_field(self, cd: ast.ClassDef*) -> int:
|
||||
"""Return 1 if the class body declares fields (AnnAssign); 0=methods-only/no-field class."""
|
||||
if cd is None or cd.children is None: return 0
|
||||
cn: sizet = len(cd.children)
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = cd.children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() == ast.ASTKind.AnnAssign: return 1
|
||||
return 0
|
||||
def _trigger_base_specialization(self, trans: HT.Translator*, cd: ast.ClassDef*):
|
||||
"""Trigger generic specialization for Subscript bases (e.g. GSListNode[Value])."""
|
||||
if trans is None or cd is None or cd.bases is None: return
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
bk: int = base_node.kind()
|
||||
if bk != ast.ASTKind.Subscript: continue
|
||||
sub: ast.Subscript* = ast.Subscript*(base_node)
|
||||
if sub is None or sub.value is None or sub.slice is None: continue
|
||||
# Extract the generic class name (e.g. "GSListNode").
|
||||
gen_class_name: str = self._get_base_name(sub.value, pool)
|
||||
if gen_class_name is None: continue
|
||||
# Skip marker base classes.
|
||||
if self._is_marker_base(gen_class_name) == 1: continue
|
||||
# Extract the type arg name (e.g. "Value").
|
||||
slice_k: int = sub.slice.kind()
|
||||
arg_nm: str = None
|
||||
if slice_k == ast.ASTKind.Name:
|
||||
sl_nm: ast.Name* = ast.Name*(sub.slice)
|
||||
arg_nm = sl_nm.id
|
||||
if arg_nm is None: continue
|
||||
# Build type_args (elem_size=8: str is a pointer; 1 would overlap elements).
|
||||
type_args: list[str]* = list[str](pool, 8)
|
||||
if type_args is None: continue
|
||||
type_args.append(arg_nm)
|
||||
# Trigger specialization (cached, so repeated calls are safe).
|
||||
self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_class_name, type_args)
|
||||
def _has_any_func_cvtable(self, cd: ast.ClassDef*) -> int:
|
||||
"""Return 1 if any non-constructor function in the class is marked @t.CVTable."""
|
||||
if cd is None or cd.children is None: return 0
|
||||
children: list[ast.AST*]* = cd.children
|
||||
cn: sizet = len(children)
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() != ast.ASTKind.FunctionDef: continue
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(stmt)
|
||||
if fd is None or fd.name is None: continue
|
||||
# Skip constructors.
|
||||
if string.strcmp(fd.name, "__init__") == 0: continue
|
||||
if string.strcmp(fd.name, "__before_init__") == 0: continue
|
||||
if string.strcmp(fd.name, "__new__") == 0: continue
|
||||
if fd.decorator_list is not None:
|
||||
if self._has_decorator(fd.decorator_list, "CVTable") == 1: return 1
|
||||
return 0
|
||||
def _should_method_be_virtual(self, cd: ast.ClassDef*, fd: ast.FunctionDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Decide whether a method belongs in the vtable; 1=include, 0=exclude."""
|
||||
if fd is None: return 0
|
||||
# 1. Function-level @t.NoVTable has top priority (exclude).
|
||||
if fd.decorator_list is not None:
|
||||
if self._has_decorator(fd.decorator_list, "NoVTable") == 1: return 0
|
||||
# 2. Function-level @t.CVTable (explicitly include).
|
||||
if self._has_decorator(fd.decorator_list, "CVTable") == 1: return 1
|
||||
|
||||
# 3. Class-level @t.CVTable.
|
||||
if cd is not None and cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CVTable") == 1: return 1
|
||||
|
||||
# 4. Check inheritance.
|
||||
parent_name: str = self._get_parent_class(cd, trans)
|
||||
if parent_name is not None:
|
||||
# SHA1-aware parent lookup to avoid cross-module same-name collisions.
|
||||
is_nvt: int = 0
|
||||
if trans is not None:
|
||||
p_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
|
||||
if p_entry is None:
|
||||
# Fall back to a global lookup for cross-module parents; consistent with
|
||||
# field flattening, otherwise a NoVTable parent is misread as having a vptr.
|
||||
p_entry = self.Gen.StructGen.find_struct(parent_name)
|
||||
if p_entry is not None: is_nvt = p_entry.IsNoVTable
|
||||
else: is_nvt = self.Gen.StructGen.is_novtable_by_name(parent_name)
|
||||
if is_nvt != 1:
|
||||
# vtable follows the parent: parent virtual -> method is virtual, else static.
|
||||
# Matches _detect_vtable_status to keep parent/child layouts aligned.
|
||||
if p_entry is None:
|
||||
return 1 # unknown parent: conservatively make it virtual
|
||||
if p_entry.HasVTable == 1: return 1
|
||||
return 0
|
||||
|
||||
# 5. Default: exclude.
|
||||
return 0
|
||||
def _detect_vtable_status(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Detect whether the class should enable a vtable."""
|
||||
if cd is None: return 0
|
||||
|
||||
# Check class decorators.
|
||||
if cd.decorator_list is not None:
|
||||
# @t.CVTable has top priority: enable directly.
|
||||
if self._has_decorator(cd.decorator_list, "CVTable") == 1: return 1
|
||||
# @t.NoVTable disables at class level, but still check function-level @t.CVTable.
|
||||
if self._has_decorator(cd.decorator_list, "NoVTable") == 1: return self._has_any_func_cvtable(cd)
|
||||
|
||||
# Check for a real parent (inheritance enables CVTable automatically).
|
||||
parent_name: str = self._get_parent_class(cd, trans)
|
||||
if parent_name is not None:
|
||||
# SHA1-aware parent lookup to avoid cross-module same-name collisions.
|
||||
is_nvt: int = 0
|
||||
if trans is not None:
|
||||
p_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
|
||||
if p_entry is None:
|
||||
# Fall back to a global lookup for cross-module parents.
|
||||
p_entry = self.Gen.StructGen.find_struct(parent_name)
|
||||
if p_entry is not None: is_nvt = p_entry.IsNoVTable
|
||||
else: is_nvt = self.Gen.StructGen.is_novtable_by_name(parent_name)
|
||||
if is_nvt == 1:
|
||||
# Parent is NoVTable; check function-level @t.CVTable.
|
||||
return self._has_any_func_cvtable(cd)
|
||||
# vtable follows the parent: parent virtual -> child virtual, else static.
|
||||
# A vptr mismatch would shift child fields and corrupt runtime access.
|
||||
if p_entry is None: return 1 # unknown/missing parent: conservatively enable vtable
|
||||
if p_entry.HasVTable == 1: return 1
|
||||
return 0
|
||||
|
||||
# Default (no decorator, no parent): check function-level @t.CVTable.
|
||||
return self._has_any_func_cvtable(cd)
|
||||
def InitClassGen(self) -> int:
|
||||
"""Initialize the ClassGen family fields (called by Generator during construction)."""
|
||||
if self is None: return 1
|
||||
self.MethodMetaKeys = None
|
||||
self.MethodMetaFlags = None
|
||||
self.MethodMetaCount = 0
|
||||
# REnum variant registry lives on the compile-time factory (perm_pool), so it is
|
||||
# restored here, not cleared: clearing loses variants registered by earlier work.
|
||||
self.RenumClassNames = CompilerFactory.factory.RenumClassNames
|
||||
self.RenumClassVariantCounts = CompilerFactory.factory.RenumClassVariantCounts
|
||||
self.RenumVariantNames = CompilerFactory.factory.RenumVariantNames
|
||||
self.RenumVariantTags = CompilerFactory.factory.RenumVariantTags
|
||||
self.RenumClassCount = CompilerFactory.factory.RenumClassCount
|
||||
self.RenumAliasClass = CompilerFactory.factory.RenumAliasClass
|
||||
self.RenumAliasField = CompilerFactory.factory.RenumAliasField
|
||||
self.RenumAliasIndex = CompilerFactory.factory.RenumAliasIndex
|
||||
self.RenumAliasCount = CompilerFactory.factory.RenumAliasCount
|
||||
self.SpecKeys = None
|
||||
self.SpecNames = None
|
||||
self.SpecCount = 0
|
||||
self.GenericClassNames = None
|
||||
self.GenericClassNodes = None
|
||||
self.GenericClassSha1s = None
|
||||
self.GenericClassFiles = None
|
||||
self.GenericClassTpParams = None
|
||||
self.GenericFuncKeys = None
|
||||
self.GenericFuncNodes = None
|
||||
self.GenericFuncSha1s = None
|
||||
self.GenericFuncFiles = None
|
||||
self.GenericFuncTpParams = None
|
||||
self.VtFullNames = None
|
||||
self.VtMethodNames = None
|
||||
self.VtParentSha1s = None
|
||||
self.VtMethodCount = 0
|
||||
self.RenumAliasTy = CompilerFactory.factory.RenumAliasTy
|
||||
self.RenumAliasVariant = CompilerFactory.factory.RenumAliasVariant
|
||||
self.ClassMetaKinds = None
|
||||
self.ClassMetaClss = None
|
||||
self.ClassMetaFields = None
|
||||
self.ClassMetaCount = 0
|
||||
self.DecoratorFuncCount = 0
|
||||
self.DecoratorFuncNames = None
|
||||
self.DecoratorWrapNames = None
|
||||
self.DecoratorArgs = None
|
||||
self.ExceptionCount = 0
|
||||
self.ExceptionNames = None
|
||||
self.ExceptionParents = None
|
||||
self.ExceptionCodes = None
|
||||
self.NextExceptionCode = 0
|
||||
self._exception_codes_tmp_count = 0
|
||||
self.GenericSpecDepth = 0
|
||||
return 0
|
||||
@@ -0,0 +1,587 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
from . import ClassGenBase
|
||||
|
||||
|
||||
# ClassGenDef - ClassGenMixin split (stage D): translate_class_def entry point
|
||||
novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
"""Entry point for translate_class_def."""
|
||||
|
||||
# translate_class_def - translate a class definition (cluster D)
|
||||
def translate_class_def(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a class definition; returns 0."""
|
||||
if node is None or trans is None: return 0
|
||||
|
||||
cd: ast.ClassDef* = ast.ClassDef*(node)
|
||||
if cd is None or cd.name is None: return 0
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
class_name: str = cd.name
|
||||
|
||||
if trans.ModuleSha1 is None:
|
||||
# fail-fast: ModuleSha1 missing while translating a class (namespace isolation depends on it)
|
||||
VLogger.error("ModuleSha1 missing while translating a class", "CLASS")
|
||||
self.Gen.TypeGenErr.fatal_error(cd, "ModuleSha1 missing while translating a class", "TypeError")
|
||||
|
||||
# Namespace isolation: mark as visible in the current file. Must precede
|
||||
# the existing check, or Phase B's early return would never mark it.
|
||||
self.Gen.StructGen.add_visible_struct(pool, class_name)
|
||||
# Record that this module declares the class (key "{SHA1}.{name}", independent
|
||||
# of struct registration) so bare self-references resolve to the right module.
|
||||
self.Gen.StructGen.mark_module_class(pool, trans.ModuleSha1, class_name)
|
||||
|
||||
# Enum class: handled by HandlesEnum. Fail-fast on dispatch ambiguity: REnum
|
||||
# takes priority over CEnum/CUnion, whose semantics are mutually exclusive.
|
||||
if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1:
|
||||
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"class '{class_name}' is classified as both CEnum and REnum (dispatch ambiguity), " f"the REnum variant table cannot be registered", "TypeError")
|
||||
return self.Gen.ClassGenEnum._translate_enum_def(trans, cd)
|
||||
|
||||
# Union class: handled by the union path
|
||||
if self.Gen.ClassGenBase._is_union_class(cd, trans) == 1: return self.Gen.ClassGenEnum._translate_union_def(trans, cd)
|
||||
|
||||
# REnum class: handled by REnum ({i32 tag, max_payload} layout)
|
||||
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1: return self.Gen.ClassGenEnum._translate_renum_def(trans, cd)
|
||||
|
||||
# Generic class: stored as a template, no IR emitted until instantiation.
|
||||
# Bind cd.type_params to a local to drive type inference.
|
||||
tp_val: list[str]* = cd.type_params
|
||||
if self.Gen.ClassGenGeneric._is_generic_class(tp_val) == 1:
|
||||
self.Gen.ClassGenGeneric._register_generic_template(pool, cd, trans.ModuleSha1)
|
||||
return 0
|
||||
|
||||
# Exception class (try-except): register encoding and inheritance, no struct
|
||||
# (aligned with TPC HandlesClassDef.py L509-511).
|
||||
if self.Gen.ClassGenBase._is_exception_class_def(cd) == 1: return self.Gen.ClassGenBase._register_exception_class_def(trans, cd)
|
||||
|
||||
# Nested class/struct: emit as a separate struct `{Outer}_{Inner}`, before all
|
||||
# early returns so both declare_only paths recurse and REnum/union don't repeat.
|
||||
self._translate_nested_classdefs(trans, cd)
|
||||
|
||||
# Check if already registered (SHA1 distinguishes same-named classes across modules)
|
||||
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
|
||||
# Incomplete empty-class registration: existing FieldCount==0 but the body has
|
||||
# fields, so neutralize the stale entry and rebuild via the full path below.
|
||||
needs_rereg: int = 0
|
||||
# Forward-declaration placeholder hit: do not early-return, fall into the
|
||||
# in-place fill branch below so Ptr(placeholder.Ty) refs stay valid.
|
||||
fill_fwd: int = 0
|
||||
if existing is not None:
|
||||
if existing.IsForwardDecl != 0: fill_fwd = 1
|
||||
elif existing.FieldCount == 0 and trans._declare_only == 0:
|
||||
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
|
||||
if existing.Name is not None: existing.Name[0] = '\0'
|
||||
existing.Ty = None
|
||||
existing.FieldCount = 0
|
||||
needs_rereg = 1
|
||||
if existing is not None and needs_rereg == 0 and fill_fwd == 0:
|
||||
# Struct already registered (Phase A-pre or Phase B retranslation), but methods still need translating here
|
||||
if existing.Ty is not None:
|
||||
# Phase 1b (declare_only=0): existing.Ty's Name points into Phase 1a pool
|
||||
# memory, so deep-copy the Name to stdlib to keep it alive cross-file.
|
||||
if trans._declare_only == 0:
|
||||
# Deep-copy existing.Ty to stdlib: the 1a pool Ty dangles across the
|
||||
# 1b reset, hanging when the dangling field chain is walked.
|
||||
ty_deep: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, existing.Ty)
|
||||
if ty_deep is not None: existing.Ty = ty_deep
|
||||
old_name: str = mlirlite.get_struct_name(existing.Ty)
|
||||
if old_name is not None:
|
||||
on_len: sizet = string.strlen(old_name)
|
||||
on_copy: str = CompilerFactory.factory.PermPool.alloc(on_len + 1)
|
||||
if on_copy is not None:
|
||||
string.strcpy(on_copy, old_name)
|
||||
# In-place overwrite keeps existing.Ty's pointer identity (Phase A
|
||||
# registered it); *= a boxed REnum variant is a load+store materialization.
|
||||
*existing.Ty = mlirlite.LLVMType.Struct(mlirlite.get_struct_fields(existing.Ty), mlirlite.get_struct_fcount(existing.Ty), on_copy)
|
||||
# Phase B: register the existing named struct into the current module
|
||||
mlirlite.module_add_named_type(trans.Module, pool, existing.Ty)
|
||||
# B5: the existing path also needs packed/aligned set, since Phase B's
|
||||
# re-add_named_type resets IsPacked=0 and prints `{...}` (idt_entry root cause).
|
||||
if self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed") != 0:
|
||||
mlirlite.module_set_named_type_packed(trans.Module, pool, existing.Ty, 1)
|
||||
# StructTable may be rebuilt per phase, so IsPacked must be re-set or
|
||||
# cross-module stub injection reads 0 and emits a non-packed stub.
|
||||
existing.IsPacked = 1
|
||||
# Complete field types like the fresh-definition path: value-type fields
|
||||
# need full definitions here or this class becomes unsized for GEP.
|
||||
self.Gen.StructGen._ensure_field_types_in_module(pool, trans.Module, existing.Ty)
|
||||
align_ex: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
|
||||
if align_ex > 0 and existing.Align == 0: existing.Align = align_ex
|
||||
# Phase A-pre (declare_only=1): class registered, skip method bodies.
|
||||
# Phase B (declare_only=0): translate method bodies in full.
|
||||
if trans._declare_only == 1: self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name, 1)
|
||||
else: self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name)
|
||||
# SHA1 is already set during register_struct, no need to re-set
|
||||
return 0
|
||||
|
||||
# Trigger generic specialization of Subscripts in base (e.g. GSListNode[Value])
|
||||
# before _detect_vtable_status and field inheritance, or the lookup returns None.
|
||||
self.Gen.ClassGenBase._trigger_base_specialization(trans, cd)
|
||||
|
||||
# VTable detection: decide whether a vtable is enabled
|
||||
has_vtable: int = self.Gen.ClassGenBase._detect_vtable_status(cd, trans)
|
||||
parent_name: str = self.Gen.ClassGenBase._get_parent_class(cd, trans)
|
||||
# Multiple inheritance: collect real parent names (skip marker bases) for flatten/vtable/MRO
|
||||
parents_buf: sizet* = pool.alloc(8 * GenTypes.PARENT_MAX)
|
||||
parents_count: int = 0
|
||||
if parents_buf is not None: parents_count = self.Gen.ClassGenBase._collect_parents(pool, cd, parents_buf, GenTypes.PARENT_MAX, trans)
|
||||
is_novtable_deco: int = 0
|
||||
if cd.decorator_list is not None:
|
||||
if self.Gen.ClassGenBase._has_decorator(cd.decorator_list, "NoVTable") == 1: is_novtable_deco = 1
|
||||
|
||||
# PhaseA pre-registration deferral: when the parent is unregistered, defer the
|
||||
# current class to a later pass. Phase B (declare_only==0) never defers.
|
||||
if trans._declare_only == 1 and parent_name is not None:
|
||||
parent_check: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
|
||||
if parent_check is None: parent_check = self.Gen.StructGen.find_struct(parent_name)
|
||||
if parent_check is None: return 0
|
||||
|
||||
# 1. Walk the class body and collect field info
|
||||
# Use CSizeT arrays to store pointer values (8 bytes on 64-bit systems)
|
||||
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_defaults_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_annot_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field volatile flag buffer (1 = annotation union contains t.CVolatile), parallel
|
||||
# to field_annot_buf with values 0/1.
|
||||
field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field pointee-volatile flag buffer (1 = rawptr[t.CVolatile | T], i.e. volatile T*),
|
||||
# parallel to field_volatile_buf with values 0/1.
|
||||
field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field const-object flag buffer (1 = annotation has t.CConst and the type is not
|
||||
# a pointer), parallel to field_volatile_buf with values 0/1.
|
||||
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field byte-order buffer (0 = native, 1 = t.BigEndian, 2 = t.LittleEndian), parallel
|
||||
# to field_volatile_buf; from the `X | t.BigEndian` annotation union.
|
||||
field_byteorder_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0
|
||||
if field_annot_buf is None or field_volatile_buf is None: return 0
|
||||
if field_pointee_volatile_buf is None or field_const_buf is None: return 0
|
||||
if field_byteorder_buf is None: return 0
|
||||
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_annot_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_const_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_byteorder_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
|
||||
field_count: int = 0
|
||||
|
||||
# 1.5 Inherit parent fields (field flattening): parent fields precede child
|
||||
# fields, layout [vtable_ptr?, parent..., child...]; dedup by name (first wins).
|
||||
if parents_count > 0 and parents_buf is not None:
|
||||
for pi in range(int(parents_count)):
|
||||
pname_addr: sizet = parents_buf[pi]
|
||||
if pname_addr == 0: continue
|
||||
pname: str = rawptr(pname_addr)
|
||||
if pname is None: continue
|
||||
# SHA1-aware parent lookup to avoid picking a same-named class in another module
|
||||
parent_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname, trans.ModuleSha1)
|
||||
if parent_entry is None:
|
||||
# The parent may live in another module (different SHA1), and
|
||||
# find_struct_by_module does not fall back, so query find_struct manually.
|
||||
parent_entry = self.Gen.StructGen.find_struct(pname)
|
||||
if parent_entry is None:
|
||||
# Parent not yet registered: do not register a partial entry (perm_pool
|
||||
# entries survive passes and the existing early-return never repairs it).
|
||||
return 0
|
||||
pfc: int = parent_entry.FieldCount
|
||||
for pfi in range(int(pfc)):
|
||||
pfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(parent_entry, pfi)
|
||||
if pfe is None: continue
|
||||
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
|
||||
if pfn is None: continue
|
||||
# Skip the parent's vtable pointer field
|
||||
if string.strcmp(pfn, "__vtable__") == 0: continue
|
||||
# Multi-inheritance dedup: skip existing field names (earlier parent wins)
|
||||
if self.Gen.ClassGenBase._field_name_exists(field_names_buf, field_count, pfn) != 0: continue
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
pft: mlirlite.LLVMType* = self.Gen.StructGen.get_field_type_ptr(pfe)
|
||||
pfd: ast.AST* = self.Gen.StructGen.get_field_default_ptr(pfe)
|
||||
pfa: str = self.Gen.StructGen.get_field_annot_class_name(pfe)
|
||||
field_names_buf[field_count] = sizet(pfn)
|
||||
field_types_buf[field_count] = sizet(pft)
|
||||
if pfd is not None: field_defaults_buf[field_count] = sizet(pfd)
|
||||
else: field_defaults_buf[field_count] = 0
|
||||
if pfa is not None:
|
||||
stored_val: sizet = sizet(pfa)
|
||||
field_annot_buf[field_count] = stored_val
|
||||
else: field_annot_buf[field_count] = 0
|
||||
# Inherit the volatile flag (semantics unchanged after flattening)
|
||||
field_volatile_buf[field_count] = sizet(pfe.IsVolatile)
|
||||
# Inherit the pointee-volatile flag (unchanged after flattening)
|
||||
field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile)
|
||||
# Inherit the const-object flag (unchanged after flattening)
|
||||
field_const_buf[field_count] = sizet(pfe.IsConst)
|
||||
# Inherit the byte-order flag (unchanged after flattening)
|
||||
field_byteorder_buf[field_count] = sizet(pfe.ByteOrder)
|
||||
field_count += 1
|
||||
|
||||
children: list[ast.AST*]* = cd.children
|
||||
if children is None: return 0
|
||||
|
||||
cn: sizet = len(children)
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
|
||||
if sk != ast.ASTKind.AnnAssign: continue
|
||||
|
||||
# AnnAssign(target=Name(id), annotation=type, value=...)
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||||
if aa is None or aa.target is None: continue
|
||||
|
||||
# Get the field name
|
||||
if aa.target.kind() != ast.ASTKind.Name: continue
|
||||
tgt: ast.Name* = ast.Name*(aa.target)
|
||||
if tgt.id is None: continue
|
||||
|
||||
# Class-body CDefine constant (any position): register into the global CDefine
|
||||
# table so bare names inside methods resolve; catches post-method AnnAssigns.
|
||||
if self.Gen.AssignGen.is_cdefine_annotation(aa.annotation) != 0:
|
||||
if aa.value is not None: self.Gen.AssignGen._register_cdefine_from_value(pool, tgt.id, aa.value, aa.annotation)
|
||||
# CDefine field type is fixed i32, collected directly to skip generic annotation
|
||||
# resolution (which does not know "CDefine") and avoid a Phase B NameError.
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
cdef_ty_l: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if cdef_ty_l is not None:
|
||||
field_names_buf[field_count] = sizet(tgt.id)
|
||||
field_types_buf[field_count] = sizet(cdef_ty_l)
|
||||
if aa.value is not None: field_defaults_buf[field_count] = sizet(aa.value)
|
||||
else: field_defaults_buf[field_count] = 0
|
||||
field_count += 1
|
||||
continue
|
||||
|
||||
# Skip compile-time metadata fields (__provides__/__requires__/__require_must__):
|
||||
# they only feed static with-context reachability checks, no runtime code.
|
||||
if string.strcmp(tgt.id, "__provides__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_PROVIDES, class_name, aa.value)
|
||||
continue
|
||||
if string.strcmp(tgt.id, "__requires__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRES, class_name, aa.value)
|
||||
continue
|
||||
if string.strcmp(tgt.id, "__require_must__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRE_MUST, class_name, aa.value)
|
||||
continue
|
||||
|
||||
# Resolve the field type
|
||||
# Value-reference markers are parameter-only: a struct field `x: &T` fails fast.
|
||||
self.Gen.TypeGenResolve.reject_ref_marker_position(aa.annotation, trans._imported_modules, "a struct field")
|
||||
field_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
# void is not a valid field type (only for `void*` and opaque typedefs); letting
|
||||
# it through builds an illegal layout, so fail-fast at compile time here.
|
||||
if field_ty is not None and self.Gen.StmtGenDecl._is_void_type(field_ty) != 0: self.Gen.TypeGenErr.fatal_error(aa, f"field '{tgt.id}' cannot be void type (void is only allowed for void* and typedef aliases)", "TypeError")
|
||||
# Single-arg t.CArray[elem] with a list literal -> static array [N x elem] (like
|
||||
# local AnnAssign); otherwise it stays Ptr(elem) and for-in iterates pointers wrongly.
|
||||
if field_ty is not None: field_ty = self.Gen.AssignGen._infer_static_array_ty_from_literal(pool, aa.annotation, aa.value, field_ty)
|
||||
if field_ty is None:
|
||||
# BUG 5 fix: remove the silent i32 fallback. Phase A-pre defers to the next
|
||||
# pass; Phase B fast-fails. Generic Subscripts fall back to Ptr(i8) instead.
|
||||
is_generic_sub: int = 0
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
|
||||
# t.CArray[elem, N] is a fixed-size value array, not a generic heap object;
|
||||
# falling back to Ptr(i8) would register a degraded layout Phase B reuses.
|
||||
sub_g: ast.Subscript* = ast.Subscript*(aa.annotation)
|
||||
is_carray_sub_g: int = 0
|
||||
if sub_g is not None and sub_g.value is not None:
|
||||
if sub_g.value.kind() == ast.ASTKind.Attribute:
|
||||
svg_at: ast.Attribute* = ast.Attribute*(sub_g.value)
|
||||
if svg_at.attr is not None and string.strcmp(svg_at.attr, "CArray") == 0: is_carray_sub_g = 1
|
||||
elif sub_g.value.kind() == ast.ASTKind.Name:
|
||||
svg_nm: ast.Name* = ast.Name*(sub_g.value)
|
||||
if svg_nm.id is not None and (string.strcmp(svg_nm.id, "CArray") == 0 or string.strcmp(svg_nm.id, "rawarray") == 0 or string.strcmp(svg_nm.id, "array") == 0):
|
||||
is_carray_sub_g = 1
|
||||
if is_carray_sub_g == 0: is_generic_sub = 1
|
||||
if is_generic_sub != 0:
|
||||
# Generic not specialized: fall back to Ptr(i8); AnnotClassName restores it later
|
||||
field_ty = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
else:
|
||||
# Phase A-pre (declare_only=1): a dependency may not be registered yet, so
|
||||
# silently return 0 and let the multi-pass convergence retry.
|
||||
if trans._declare_only == 1: return 0
|
||||
# Phase A (declare_only=0, multi-pass): a dependency module may translate
|
||||
# after this one, so do not register the struct with incomplete fields.
|
||||
return 0
|
||||
# Old behavior fast-failed with NameError (removed: it misreported while
|
||||
# dependencies were pending; the convergence check handles real errors).
|
||||
|
||||
# Store into the array (pointer cast to CSizeT)
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
diag_name_val: sizet = sizet(tgt.id)
|
||||
field_names_buf[field_count] = diag_name_val
|
||||
field_types_buf[field_count] = sizet(field_ty)
|
||||
# Store the default-value AST pointer (None = no default)
|
||||
if aa.value is not None: field_defaults_buf[field_count] = sizet(aa.value)
|
||||
else: field_defaults_buf[field_count] = 0
|
||||
# Extract the class name from the raw annotation (used to find the struct when a
|
||||
# union degrades to Ptr(i8)); pass pool so generic Subscripts return a mangled name.
|
||||
if aa.annotation is not None:
|
||||
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(aa.annotation, trans._imported_modules, pool, trans._from_imports)
|
||||
if annot_cn is not None:
|
||||
diag_cn_val: sizet = sizet(annot_cn)
|
||||
field_annot_buf[field_count] = diag_cn_val
|
||||
else: field_annot_buf[field_count] = 0
|
||||
# t.CVolatile storage-class flag: loads/stores through this field are
|
||||
# volatile (e.g. Spinlock.locked: t.CVolatile | t.CInt).
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "CVolatile", 0) != 0: field_volatile_buf[field_count] = 1
|
||||
else: field_volatile_buf[field_count] = 0
|
||||
# rawptr[t.CVolatile | T] (volatile pointee, i.e. C volatile T*): derefs
|
||||
# through this field (self.ptr[i]/self.ptr.field) are volatile.
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0: field_pointee_volatile_buf[field_count] = 1
|
||||
else: field_pointee_volatile_buf[field_count] = 0
|
||||
# const object field (annotation has t.CConst, non-pointer type): assigning
|
||||
# through the object fail-fasts, matching TPC's Gen.field_const_flags.
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0: field_const_buf[field_count] = 1
|
||||
else: field_const_buf[field_count] = 0
|
||||
# Byte-order marker (t.BigEndian / t.LittleEndian) -> 1 / 2; annotation `|`
|
||||
# means "and", so `X | t.BigEndian` is a BigEndian-encoded X field.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2
|
||||
else: field_byteorder_buf[field_count] = 0
|
||||
else:
|
||||
field_annot_buf[field_count] = 0
|
||||
field_volatile_buf[field_count] = 0
|
||||
field_pointee_volatile_buf[field_count] = 0
|
||||
field_const_buf[field_count] = 0
|
||||
field_byteorder_buf[field_count] = 0
|
||||
field_count += 1
|
||||
|
||||
if field_count == 0:
|
||||
# During PhaseA pre-registration the parent may be unregistered (single alphabetic
|
||||
# pass), so field_count==0; return silently and retry in a later pass.
|
||||
if trans._declare_only == 1 and parent_name is not None: return 0
|
||||
# Fieldless class (interface/marker): do not skip it, as it may hold only methods
|
||||
# (e.g. ShapeM/Vec2Extra); an empty struct is legal in LLVM.
|
||||
|
||||
# 2. Create the LLVM StructType
|
||||
# Build the ParamNode chain
|
||||
first_node: mlirlite.ParamNode* = None
|
||||
prev_node: mlirlite.ParamNode* = None
|
||||
total_field_count: int = field_count
|
||||
|
||||
# With a vtable, add an i8* vtable pointer as the first field
|
||||
if has_vtable == 1:
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
vtable_node: mlirlite.ParamNode* = pool.alloc(mlirlite.ParamNode.__sizeof__())
|
||||
if vtable_node is None:
|
||||
VLogger.error(f"failed to alloc vtable node for {class_name}", "CLASS")
|
||||
return 0
|
||||
string.memset(vtable_node, 0, mlirlite.ParamNode.__sizeof__())
|
||||
vtable_node.Ty = i8_ptr_ty
|
||||
first_node = vtable_node
|
||||
prev_node = vtable_node
|
||||
total_field_count = field_count + 1
|
||||
|
||||
for fi in range(int(field_count)):
|
||||
fty_addr: sizet = field_types_buf[fi]
|
||||
if fty_addr == 0: continue
|
||||
fty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr)
|
||||
|
||||
# Create the ParamNode
|
||||
pnode: mlirlite.ParamNode* = pool.alloc(mlirlite.ParamNode.__sizeof__())
|
||||
if pnode is None: continue
|
||||
string.memset(pnode, 0, mlirlite.ParamNode.__sizeof__())
|
||||
pnode.Ty = fty
|
||||
|
||||
if first_node is None: first_node = pnode
|
||||
if prev_node is not None: prev_node.Next = pnode
|
||||
prev_node = pnode
|
||||
|
||||
# Build the named struct type name "sha1.ClassName" or "ClassName". In Phase 1b the
|
||||
# Name must survive per-file reset_to(mark1), so deep-copy to stdlib; 1a uses pool.
|
||||
type_name: str = class_name
|
||||
if trans.ModuleSha1 is not None:
|
||||
name_buf: str = pool.alloc(64)
|
||||
if name_buf is not None:
|
||||
viperlib.snprintf(name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
|
||||
type_name = name_buf
|
||||
if trans._declare_only == 0 and type_name is not None:
|
||||
tn_len: sizet = string.strlen(type_name)
|
||||
tn_copy: str = CompilerFactory.factory.PermPool.alloc(tn_len + 1)
|
||||
if tn_copy is not None:
|
||||
string.strcpy(tn_copy, type_name)
|
||||
type_name = tn_copy
|
||||
|
||||
# Create the StructType (named struct)
|
||||
struct_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, first_node, total_field_count, type_name)
|
||||
if struct_ty is None:
|
||||
VLogger.error(f"failed to create StructType for {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
# Deep-copy the whole struct_ty tree to stdlib and replace the pointer so entry.Ty
|
||||
# and callers share one surviving copy; required too under StdlibAlloc=1.
|
||||
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
st_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, struct_ty)
|
||||
if st_copy is not None:
|
||||
struct_ty = st_copy
|
||||
type_name = mlirlite.get_struct_name(struct_ty)
|
||||
|
||||
# Forward-declaration hit: in-place fill keeps existing.Ty's identity, writing the
|
||||
# field body into the same object before module_add_named_type to avoid re-registration.
|
||||
if fill_fwd != 0 and existing is not None and existing.Ty is not None:
|
||||
_fwd_name: str = mlirlite.get_struct_name(existing.Ty)
|
||||
_fwd_fields: mlirlite.ParamNode* = mlirlite.get_struct_fields(struct_ty)
|
||||
_fwd_fc: int = mlirlite.get_struct_fcount(struct_ty)
|
||||
*existing.Ty = mlirlite.LLVMType.Struct(_fwd_fields, _fwd_fc, _fwd_name)
|
||||
struct_ty = existing.Ty
|
||||
type_name = _fwd_name
|
||||
existing.IsForwardDecl = 0
|
||||
existing.FieldCount = 0
|
||||
|
||||
# Register the named struct into the module (emit the %"name" = type { ... } line)
|
||||
mlirlite.module_add_named_type(trans.Module, pool, struct_ty)
|
||||
|
||||
# B5: @c.Attribute(t.attr.packed) emits <{...}> (LLVM packed struct, no padding) as
|
||||
# needed by binary-format structs like idt/gdt/elf/fat32.
|
||||
is_packed_cd: int = self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed")
|
||||
if is_packed_cd != 0: mlirlite.module_set_named_type_packed(trans.Module, pool, struct_ty, 1)
|
||||
|
||||
# Scan field types and declare cross-module struct refs in the current module, e.g.
|
||||
# list[int].__pool__ referencing memhub.MemManager needs a %"sha1.MemManager" opaque.
|
||||
for fi in range(int(field_count)):
|
||||
fty_addr_op: sizet = field_types_buf[fi]
|
||||
if fty_addr_op == 0: continue
|
||||
fty_op: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr_op)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(fty_op) != 0:
|
||||
# Pointer field: the pointee only needs an opaque declaration (GEP does not index into it)
|
||||
mlirlite.module_ensure_opaque_for_type(trans.Module, pool, fty_op)
|
||||
else:
|
||||
# Value-type field (struct body / struct array): copy the full definition, or
|
||||
# this class becomes unsized and llc rejects field-indexed GEPs.
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, trans.Module, fty_op)
|
||||
self.Gen.StructGen._ensure_field_types_in_module(pool, trans.Module, fty_op)
|
||||
|
||||
# 3. Register into HandlesStruct
|
||||
entry: GenTypes.StructEntry* = None
|
||||
if fill_fwd != 0 and existing is not None:
|
||||
# Forward-declaration placeholder: the field body is already in existing.Ty, so
|
||||
# reuse existing (a new entry/Ty would orphan issued Ptr(placeholder.Ty) refs).
|
||||
entry = existing
|
||||
else: entry = self.Gen.StructGen.register_struct(pool, class_name, struct_ty, trans.ModuleSha1)
|
||||
if entry is None:
|
||||
VLogger.error(f"failed to register {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
# B5: record the packed flag into StructEntry for __sizeof__/layout queries
|
||||
if is_packed_cd != 0: entry.IsPacked = 1
|
||||
# B5: record the aligned value (@c.Attribute(t.attr.aligned(N)), e.g. fpu_context 16)
|
||||
# for global/local alloca alignment.
|
||||
align_cd: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
|
||||
if align_cd > 0: entry.Align = align_cd
|
||||
|
||||
# Mark vtable status (use entry directly to avoid same-named cross-module lookup errors)
|
||||
if has_vtable == 1: entry.HasVTable = 1
|
||||
if is_novtable_deco == 1: entry.IsNoVTable = 1
|
||||
if parent_name is not None:
|
||||
# In 1b the parent name is a pool string that dangles after reset, and later files
|
||||
# strcmp it via find_struct, so copy it to stdlib for cross-file survival.
|
||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
pn_len: sizet = string.strlen(parent_name)
|
||||
pn_buf: str = CompilerFactory.factory.PermPool.alloc(pn_len + 1)
|
||||
if pn_buf is not None:
|
||||
string.strcpy(pn_buf, parent_name)
|
||||
entry.ParentName = pn_buf
|
||||
else: entry.ParentName = parent_name
|
||||
# Multiple inheritance: set the parent array for MRO/field/vtable queries (stdlib-deep-copied)
|
||||
if parents_count > 0 and parents_buf is not None: self.Gen.StructGen.set_parent_names(pool, class_name, parents_buf, parents_count)
|
||||
|
||||
# Add field info. With a vtable, add the __vtable__ placeholder first (index 0),
|
||||
# so user fields start at index 1.
|
||||
if has_vtable == 1:
|
||||
i8_ptr_ty2: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
|
||||
|
||||
for fi in range(int(field_count)):
|
||||
fname_addr: sizet = field_names_buf[fi]
|
||||
if fname_addr == 0: continue
|
||||
fname: str = rawptr(fname_addr)
|
||||
fty_addr2: sizet = field_types_buf[fi]
|
||||
if fty_addr2 == 0: continue
|
||||
fty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr2)
|
||||
# Restore the default-value AST pointer
|
||||
fdef_addr: sizet = field_defaults_buf[fi]
|
||||
fdef: ast.AST* = None
|
||||
if fdef_addr != 0: fdef = (ast.AST*)(fdef_addr)
|
||||
# Restore the annotation class-name pointer
|
||||
fannot_addr: sizet = field_annot_buf[fi]
|
||||
fannot: str = None
|
||||
if fannot_addr != 0: fannot = str(fannot_addr)
|
||||
if fname is not None and fty2 is not None:
|
||||
fidx_add: int = self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
|
||||
# t.CVolatile field flag (field_volatile_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_volatile_buf[fi] != 0:
|
||||
fe_vol: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_vol is not None: fe_vol.IsVolatile = 1
|
||||
# rawptr[t.CVolatile | T] field flag (field_pointee_volatile_buf[fi] corresponds
|
||||
# 1:1 with add_field): derefs through it load/store with volatile.
|
||||
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
|
||||
fe_pv: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_pv is not None: fe_pv.PointeeVolatile = 1
|
||||
# byte-order field flag (field_byteorder_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_byteorder_buf[fi] != 0:
|
||||
fe_bo: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_bo is not None: fe_bo.ByteOrder = int(field_byteorder_buf[fi])
|
||||
# const object field flag (field_const_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_const_buf[fi] != 0:
|
||||
fe_c: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_c is not None: fe_c.IsConst = 1
|
||||
|
||||
# Phase 1a declaration mode: only register the struct and OOP flags, no method bodies
|
||||
if trans._declare_only == 1:
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1)
|
||||
return 0
|
||||
|
||||
# 4. OOP methods: scan FunctionDefs, mark OOP, translate methods, emit __before_init__
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name)
|
||||
|
||||
return 0
|
||||
|
||||
# _translate_nested_classdefs - recursively translate nested classes/structs. Rewrite
|
||||
# child names to flat `{Outer}_{Inner}` (PermPool) then recurse, restoring the def window.
|
||||
def _translate_nested_classdefs(self, trans: HT.Translator*, cd: ast.ClassDef*) -> None:
|
||||
"""Recursively translate nested ClassDefs under the flat name {Outer}_{Inner}."""
|
||||
if trans is None or cd is None or cd.name is None: return
|
||||
children: list[ast.AST*]* = cd.children
|
||||
if children is None: return
|
||||
cn: sizet = len(children)
|
||||
ci: sizet = 0
|
||||
while ci < cn:
|
||||
child_ast: ast.AST* = children.get(ci)
|
||||
ci += 1
|
||||
if child_ast is None: continue
|
||||
if child_ast.kind() != ast.ASTKind.ClassDef: continue
|
||||
child: ast.ClassDef* = ast.ClassDef*(child_ast)
|
||||
if child is None or child.name is None: continue
|
||||
# Flat name `{Outer}_{Inner}` (prefer PermPool so it survives phases; fall back to
|
||||
# the pass pool when missing, matching the old path).
|
||||
flat_pool: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if flat_pool is None: flat_pool = trans.Pool
|
||||
if flat_pool is None: continue
|
||||
outer_len: sizet = string.strlen(cd.name)
|
||||
inner_len: sizet = string.strlen(child.name)
|
||||
flat_len: sizet = outer_len + 1 + inner_len
|
||||
flat_buf: str = flat_pool.alloc(flat_len + 1)
|
||||
if flat_buf is None: continue
|
||||
viperlib.snprintf(flat_buf, flat_len + 1, "%s_%s", cd.name, child.name)
|
||||
# Rewrite the child name then recurse (everything registers/emits with the flat name)
|
||||
old_name: str = child.name
|
||||
child.name = flat_buf
|
||||
saved_def_sha1: str = self.Gen.StructGen.CurDefModuleSha1
|
||||
saved_def_cls: str = self.Gen.StructGen.CurDefClassName
|
||||
self.translate_class_def(trans, child_ast)
|
||||
self.Gen.StructGen.CurDefModuleSha1 = saved_def_sha1
|
||||
self.Gen.StructGen.CurDefClassName = saved_def_cls
|
||||
child.name = old_name
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,6 @@
|
||||
from stdint import *
|
||||
from . import ClassGenBase
|
||||
|
||||
# ClassGenMixin - class definition / inheritance / generics / vtable / enum / union / renum.
|
||||
novirtual class ClassGenMixin(ClassGenBase.ClassGenBase):
|
||||
"""Class-definition mixin (top level after the stage-D split; methods live in sub-mixins)."""
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,579 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.ClassGenBase as ClassGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ClassGenDef - ClassGenMixin 拆分(阶段 D):translate_class_def 主入口
|
||||
#
|
||||
# 继承 ClassGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesClassDef 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
"""translate_class_def 主入口"""
|
||||
|
||||
# ============================================================
|
||||
# translate_class_def — 翻译 class 定义(簇 D)
|
||||
# ============================================================
|
||||
def translate_class_def(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 class 定义,返回 0"""
|
||||
if node is None or trans is None:
|
||||
return 0
|
||||
|
||||
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(node)
|
||||
if cd is None or cd.name is None:
|
||||
return 0
|
||||
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
class_name: str = cd.name
|
||||
|
||||
if trans.ModuleSha1 is None:
|
||||
# fail-fast:翻译类时模块 SHA1 缺失(命名空间隔离依赖它)——
|
||||
VLogger.error("翻译类时 ModuleSha1 缺失", "CLASS")
|
||||
self.Gen.TypeGenErr.fatal_error(cd, "翻译类时 ModuleSha1 缺失", "TypeError")
|
||||
|
||||
# 命名空间隔离:标记为当前文件可见(必须在 existing 检查之前,
|
||||
# 因为 Phase B 时结构体已注册会提前 return,否则永远无法标记本地类)
|
||||
self.Gen.StructGen.add_visible_struct(pool, class_name)
|
||||
|
||||
# 枚举类:交给 HandlesEnum 处理
|
||||
if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1:
|
||||
return self.Gen.ClassGenEnum._translate_enum_def(trans, cd)
|
||||
|
||||
# 联合体类:交给联合体处理
|
||||
if self.Gen.ClassGenBase._is_union_class(cd, trans) == 1:
|
||||
return self.Gen.ClassGenEnum._translate_union_def(trans, cd)
|
||||
|
||||
# REnum 类:交给 REnum 处理({i32 tag, max_payload} 布局)
|
||||
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
|
||||
return self.Gen.ClassGenEnum._translate_renum_def(trans, cd)
|
||||
|
||||
# 泛型类:存储为模板,不发射 IR(等实例化时特化)
|
||||
# 用局部变量接收 cd.type_params,触发类型推断
|
||||
tp_val: list[str] | t.CPtr = cd.type_params
|
||||
if self.Gen.ClassGenGeneric._is_generic_class(tp_val) == 1:
|
||||
self.Gen.ClassGenGeneric._register_generic_template(pool, cd, trans.ModuleSha1)
|
||||
return 0
|
||||
|
||||
# 异常类(try-except):注册编码+继承树,不生成 struct
|
||||
# (对齐 TPC HandlesClassDef.py L509-511:_is_exception_class →
|
||||
# _RegisterExceptionClass → return,异常类不编译为结构体)
|
||||
if self.Gen.ClassGenBase._is_exception_class_def(cd) == 1:
|
||||
return self.Gen.ClassGenBase._register_exception_class_def(trans, cd)
|
||||
|
||||
# 检查是否已注册(用 SHA1 区分跨模块同名类)
|
||||
existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
|
||||
class_name, trans.ModuleSha1)
|
||||
if existing is not None:
|
||||
# BUG 5 方案1修改点2:existing 时字段完整性检查
|
||||
# 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整)
|
||||
# 注意:无字段类(接口/标记类,类体无 AnnAssign)合法(空 struct + 方法),
|
||||
# 仅当"类体声明了字段但 FieldCount==0"才算注册不完整(Phase A-pre 漏注册)。
|
||||
if existing.FieldCount == 0 and trans._declare_only == 0:
|
||||
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
|
||||
# Phase B 时字段数为 0 说明注册不完整(Phase A-pre 可能漏注册)
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
node,
|
||||
f"结构体已注册但字段数为0,类: {class_name},可能注册不完整",
|
||||
"InternalCompilerError")
|
||||
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
|
||||
if existing.Ty is not None:
|
||||
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
|
||||
# 其 Name(如 "sha1.ClassName")指向 Phase 1a 的 pool 内存。虽然 mark1
|
||||
# 之前分配不被 reset 回收,但 struct_ty 会被后续文件翻译引用(vtable/
|
||||
# self 参数类型打印),部分实现下 pool 块被复用导致 Name 悬空崩溃。
|
||||
# 保险起见:深拷贝 Name 到 stdlib,确保跨文件永久存活。
|
||||
if trans._declare_only == 0:
|
||||
# 深拷贝整个 existing.Ty 到 stdlib:1a pool Ty 跨 1b reset 悬空,
|
||||
# _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表
|
||||
# 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType
|
||||
# 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。
|
||||
ty_deep: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
|
||||
CompilerFactory.get_perm_pool(), existing.Ty)
|
||||
if ty_deep is not None:
|
||||
existing.Ty = ty_deep
|
||||
old_name: str = llvmlite.get_struct_name(existing.Ty)
|
||||
if old_name is not None:
|
||||
on_len: t.CSizeT = string.strlen(old_name)
|
||||
on_copy: str = CompilerFactory.get_perm_pool().alloc(on_len + 1)
|
||||
if on_copy is not None:
|
||||
string.strcpy(on_copy, old_name)
|
||||
c.DerefAs(existing.Ty, llvmlite.LLVMType.Struct(
|
||||
llvmlite.get_struct_fields(existing.Ty),
|
||||
llvmlite.get_struct_fcount(existing.Ty),
|
||||
on_copy))
|
||||
# Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行)
|
||||
llvmlite.module_add_named_type(trans.Module, pool, existing.Ty)
|
||||
# Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体
|
||||
# Phase B(declare_only=0)时全量翻译方法体
|
||||
if trans._declare_only == 1:
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name, 1)
|
||||
else:
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name)
|
||||
# SHA1 已在 register_struct 时设置,无需再补
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])
|
||||
# 必须在 _detect_vtable_status 和父类字段继承之前调用,
|
||||
# 否则 find_struct_by_module("GSListNode[Value]") 返回 None
|
||||
# ============================================================
|
||||
self.Gen.ClassGenBase._trigger_base_specialization(trans, cd)
|
||||
|
||||
# ============================================================
|
||||
# VTable 检测:判断是否启用虚表
|
||||
# ============================================================
|
||||
has_vtable: int = self.Gen.ClassGenBase._detect_vtable_status(cd, trans)
|
||||
parent_name: str = self.Gen.ClassGenBase._get_parent_class(cd, trans)
|
||||
# 多继承:收集所有真实父类名(跳过 marker base),供字段展平/vtable/MRO 使用
|
||||
parents_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.PARENT_MAX)
|
||||
parents_count: int = 0
|
||||
if parents_buf is not None:
|
||||
parents_count = self.Gen.ClassGenBase._collect_parents(pool, cd, parents_buf, GenTypes.PARENT_MAX, trans)
|
||||
is_novtable_deco: int = 0
|
||||
if cd.decorator_list is not None:
|
||||
if self.Gen.ClassGenBase._has_decorator(cd.decorator_list, "NoVTable") == 1:
|
||||
is_novtable_deco = 1
|
||||
|
||||
# ============================================================
|
||||
# PhaseA 预注册延迟检查:父类未注册时延迟注册当前类
|
||||
#
|
||||
# 根因:AssignHandle(HandlesBase.Mixin) 有自己的字段 _CurrentClass,
|
||||
# 当 Mixin 未注册时(字母序:HandlesAssign.py 在 HandlesBase.py 之前),
|
||||
# field_count=1(不是0),field_count==0 静默返回不触发,
|
||||
# AssignHandle 被注册为只有 _CurrentClass 字段(缺 Trans)。
|
||||
# 后续遍 existing is not None,不补字段 → attribute ptr is None: self.Trans
|
||||
#
|
||||
# 修复:declare_only==1 且父类未注册时,延迟注册到下一遍。
|
||||
# 多遍 PhaseA-pre 会在后续遍中重试(父类已注册后完整注册)。
|
||||
# Phase B(declare_only==0)不延迟,避免死锁。
|
||||
# ============================================================
|
||||
if trans._declare_only == 1 and parent_name is not None:
|
||||
parent_check: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
|
||||
parent_name, trans.ModuleSha1)
|
||||
if parent_check is None:
|
||||
parent_check = self.Gen.StructGen.find_struct(parent_name)
|
||||
if parent_check is None:
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 1. 遍历 class body,收集字段信息
|
||||
# ============================================================
|
||||
# 用 CSizeT 数组存储指针值(64 位系统上 8 字节)
|
||||
field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_defaults_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_annot_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None:
|
||||
return 0
|
||||
if field_annot_buf is None:
|
||||
return 0
|
||||
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_annot_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
|
||||
field_count: int = 0
|
||||
|
||||
# ============================================================
|
||||
# 1.5 继承父类字段(字段展平)
|
||||
# ============================================================
|
||||
#
|
||||
# 父类字段排在子类字段之前,结构体布局:
|
||||
# [vtable_ptr?, parent_field1, ..., child_field1, ...]
|
||||
# 跳过父类的 __vtable__ 字段(子类会有自己的 vtable 指针)
|
||||
# ============================================================
|
||||
# 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明优先)
|
||||
if parents_count > 0 and parents_buf is not None:
|
||||
for pi in range(parents_count):
|
||||
pname_addr: t.CSizeT = parents_buf[pi]
|
||||
if pname_addr == 0:
|
||||
continue
|
||||
pname: str = (str | t.CPtr)(t.CVoid(pname_addr, t.CPtr))
|
||||
if pname is None:
|
||||
continue
|
||||
# 用 SHA1 感知查找父类,规避跨模块同名找错
|
||||
parent_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
|
||||
pname, trans.ModuleSha1)
|
||||
if parent_entry is None:
|
||||
# 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin),
|
||||
# find_struct_by_module 不回退全局查找,此处手动回退到 find_struct,
|
||||
# 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败
|
||||
parent_entry = self.Gen.StructGen.find_struct(pname)
|
||||
if parent_entry is None:
|
||||
# 父类尚未注册(依赖模块未翻译:Phase A 按文件序,子类模块可能
|
||||
# 先于父类模块,如 Generator.vp G < TypeGenMixin.vp T)。此时
|
||||
# **不能**继续注册——StructTable 在 StdlibAlloc=1 时用 perm_pool
|
||||
# 分配(init_struct_table L136-140),残缺条目跨 pass 存活,
|
||||
# 后续 pass 的 existing 早退(L2995-3006)永远无法修复 → 子类
|
||||
# __init__ 写父类字段(self.TraceFrames)每遍永久失败。
|
||||
# 整类 defer(不注册)+ 标记 _deps_pending 下遍重译;模块级
|
||||
# 变量/函数参数解析失败静默降级 i32(handle_module_level_var
|
||||
# L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。
|
||||
if trans is not None:
|
||||
trans._deps_pending = 1
|
||||
# [PHASEADBG] 临时诊断:父类未注册即 pending 的根因(用后移除)
|
||||
stdio.printf("[PHASEADBG] 父类未注册: class=%s parent=%s\n", class_name, pname)
|
||||
return 0
|
||||
pfc: int = parent_entry.FieldCount
|
||||
for pfi in range(pfc):
|
||||
pfe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry(
|
||||
parent_entry, pfi)
|
||||
if pfe is None:
|
||||
continue
|
||||
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
|
||||
if pfn is None:
|
||||
continue
|
||||
# 跳过父类的 vtable 指针字段
|
||||
if string.strcmp(pfn, "__vtable__") == 0:
|
||||
continue
|
||||
# 多继承去重:字段名已存在跳过(先声明父类优先)
|
||||
if self.Gen.ClassGenBase._field_name_exists(field_names_buf, field_count, pfn) != 0:
|
||||
continue
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
pft: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_field_type_ptr(pfe)
|
||||
pfd: ast.AST | t.CPtr = self.Gen.StructGen.get_field_default_ptr(pfe)
|
||||
pfa: str = self.Gen.StructGen.get_field_annot_class_name(pfe)
|
||||
field_names_buf[field_count] = t.CSizeT(pfn)
|
||||
field_types_buf[field_count] = t.CSizeT(pft)
|
||||
if pfd is not None:
|
||||
field_defaults_buf[field_count] = t.CSizeT(pfd)
|
||||
else:
|
||||
field_defaults_buf[field_count] = 0
|
||||
if pfa is not None:
|
||||
stored_val: t.CSizeT = t.CSizeT(pfa)
|
||||
field_annot_buf[field_count] = stored_val
|
||||
else:
|
||||
field_annot_buf[field_count] = 0
|
||||
field_count += 1
|
||||
|
||||
children: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||||
if children is None:
|
||||
return 0
|
||||
|
||||
cn: t.CSizeT = len(children)
|
||||
for ci in range(cn):
|
||||
stmt: ast.AST | t.CPtr = children.get(ci)
|
||||
if stmt is None:
|
||||
continue
|
||||
sk: int = stmt.kind()
|
||||
|
||||
if sk != ast.ASTKind.AnnAssign: continue
|
||||
|
||||
# AnnAssign(target=Name(id), annotation=type, value=...)
|
||||
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt)
|
||||
if aa is None or aa.target is None:
|
||||
continue
|
||||
|
||||
# 获取字段名
|
||||
if aa.target.kind() != ast.ASTKind.Name:
|
||||
continue
|
||||
tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target)
|
||||
if tgt.id is None:
|
||||
continue
|
||||
|
||||
# 类体 CDefine 常量(任意位置,2026-08-10):注册到全局 CDefine 表。
|
||||
# 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查
|
||||
# CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册
|
||||
# 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。
|
||||
if self.Gen.AssignGen.is_cdefine_annotation(aa.annotation) != 0:
|
||||
if aa.value is not None:
|
||||
self.Gen.AssignGen._register_cdefine_from_value(pool, tgt.id, aa.value)
|
||||
# CDefine 字段类型固定 i32,直接收集后跳过通用注解解析:
|
||||
# resolve_annotation_type 不识别 "CDefine"(map_t_type/CTypedef
|
||||
# 表均无此名),类体尾部常量(ISINSTANCE_CLASS_MAX 是首个触发
|
||||
# 该路径的实例)Phase B 会 NameError: 't.CDefine' is not defined。
|
||||
# 与类头常量(MRO_MAX 等)的 i32 字段语义保持一致。
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
cdef_ty_l: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if cdef_ty_l is not None:
|
||||
field_names_buf[field_count] = t.CSizeT(tgt.id)
|
||||
field_types_buf[field_count] = t.CSizeT(cdef_ty_l)
|
||||
if aa.value is not None:
|
||||
field_defaults_buf[field_count] = t.CSizeT(aa.value)
|
||||
else:
|
||||
field_defaults_buf[field_count] = 0
|
||||
field_count += 1
|
||||
continue
|
||||
|
||||
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
|
||||
# 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码
|
||||
# (FakeDuck:_str.__requires__=['__mbuddy__'])。此处解析 list[str]
|
||||
# 字面量注册到持久元数据表,供 requires 注入 / with provider 查询。
|
||||
if string.strcmp(tgt.id, "__provides__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_PROVIDES, class_name, aa.value)
|
||||
continue
|
||||
if string.strcmp(tgt.id, "__requires__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRES, class_name, aa.value)
|
||||
continue
|
||||
if string.strcmp(tgt.id, "__require_must__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRE_MUST, class_name, aa.value)
|
||||
continue
|
||||
|
||||
# 解析字段类型
|
||||
field_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||||
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
|
||||
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
|
||||
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
|
||||
# for x in self.field 会误走指针迭代(元素非整数 → null 终止符
|
||||
# 检查失败报错)。推断为静态数组后走数组迭代(按长度 N)。
|
||||
if field_ty is not None:
|
||||
field_ty = self.Gen.AssignGen._infer_static_array_ty_from_literal(
|
||||
pool, aa.annotation, aa.value, field_ty)
|
||||
if field_ty is None:
|
||||
# BUG 5 方案1修改点1:消除 i32 静默回退
|
||||
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
|
||||
# Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译
|
||||
# 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱)
|
||||
# 例外:泛型 Subscript(如 list[AST|t.CPtr])未特化时回退为 Ptr(i8),
|
||||
# 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name
|
||||
# 供 translate_attribute 做后续 bitcast。
|
||||
is_generic_sub: int = 0
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
|
||||
is_generic_sub = 1
|
||||
if is_generic_sub != 0:
|
||||
# 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复
|
||||
field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
else:
|
||||
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,
|
||||
# 静默 return 0 让多遍收敛机制生效(下一遍依赖类已注册即可成功)
|
||||
if trans._declare_only == 1:
|
||||
return 0
|
||||
# Phase A(declare_only=0,Phase2.vp L573 多遍):依赖模块按
|
||||
# 文件序可能晚于本模块翻译(如 TypeGenMixin.vp G < HandlesType.vp
|
||||
# H,TraceFrames 注解的 HandlesType.TraceFrame 未注册)→ 标记
|
||||
# _deps_pending 下遍重译,不在本遍以残缺字段注册结构体。
|
||||
# 真正的类型错误由 Phase A 收敛检查暴露("Phase A 无法完成
|
||||
# 翻译: <file>,请检查依赖模块是否已编译/注册"),不静默掩盖。
|
||||
if trans is not None:
|
||||
trans._deps_pending = 1
|
||||
# [PHASEADBG] 临时诊断:字段注解类型未解析即 pending 的根因(用后移除)
|
||||
dbg_ak: t.CInt = -1
|
||||
if aa.annotation is not None:
|
||||
dbg_ak = aa.annotation.kind()
|
||||
stdio.printf("[PHASEADBG] 字段类型未解析: class=%s field=%s annot_kind=%d\n",
|
||||
class_name, tgt.id, dbg_ak)
|
||||
return 0
|
||||
# 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时
|
||||
# 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底)
|
||||
|
||||
# 存储到数组(指针转 CSizeT)
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
diag_name_val: t.CSizeT = t.CSizeT(tgt.id)
|
||||
field_names_buf[field_count] = diag_name_val
|
||||
field_types_buf[field_count] = t.CSizeT(field_ty)
|
||||
# 存储默认值 AST 指针(None=无默认值)
|
||||
if aa.value is not None:
|
||||
field_defaults_buf[field_count] = t.CSizeT(aa.value)
|
||||
else:
|
||||
field_defaults_buf[field_count] = 0
|
||||
# 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体)
|
||||
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]")
|
||||
if aa.annotation is not None:
|
||||
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
|
||||
aa.annotation, trans._imported_modules, pool, trans._from_imports)
|
||||
if annot_cn is not None:
|
||||
diag_cn_val: t.CSizeT = t.CSizeT(annot_cn)
|
||||
field_annot_buf[field_count] = diag_cn_val
|
||||
else:
|
||||
field_annot_buf[field_count] = 0
|
||||
else:
|
||||
field_annot_buf[field_count] = 0
|
||||
field_count += 1
|
||||
|
||||
if field_count == 0:
|
||||
# PhaseA 预注册时,父类可能尚未注册(字母序单遍扫描),
|
||||
# 导致子类无法继承父类字段 → field_count==0。
|
||||
# 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。
|
||||
if trans._declare_only == 1 and parent_name is not None:
|
||||
return 0
|
||||
# 无字段类(接口/标记类):不能整体跳过——类可能只有方法
|
||||
# (如多继承中的 ShapeM/Vec2Extra),需注册类并翻译方法。
|
||||
# 空 struct 在 LLVM 中合法({});@t.CVTable 类由下方补 vtable 指针字段。
|
||||
|
||||
# ============================================================
|
||||
# 2. 创建 LLVM StructType
|
||||
# ============================================================
|
||||
# 构建 ParamNode 链表
|
||||
first_node: llvmlite.ParamNode | t.CPtr = None
|
||||
prev_node: llvmlite.ParamNode | t.CPtr = None
|
||||
total_field_count: int = field_count
|
||||
|
||||
# 如果有虚表,首字段添加 i8* vtable 指针
|
||||
if has_vtable == 1:
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
vtable_node: llvmlite.ParamNode | t.CPtr = pool.alloc(llvmlite.ParamNode.__sizeof__())
|
||||
if vtable_node is None:
|
||||
fb_vn: str = VLogger.fmt_buf()
|
||||
if fb_vn is not None:
|
||||
viperlib.snprintf(fb_vn, 1024, "failed to alloc vtable node for %s", class_name)
|
||||
VLogger.error(fb_vn, "CLASS")
|
||||
return 0
|
||||
string.memset(vtable_node, 0, llvmlite.ParamNode.__sizeof__())
|
||||
vtable_node.Ty = i8_ptr_ty
|
||||
first_node = vtable_node
|
||||
prev_node = vtable_node
|
||||
total_field_count = field_count + 1
|
||||
|
||||
for fi in range(field_count):
|
||||
fty_addr: t.CSizeT = field_types_buf[fi]
|
||||
if fty_addr == 0:
|
||||
continue
|
||||
fty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(t.CVoid(fty_addr, t.CPtr))
|
||||
|
||||
# 创建 ParamNode
|
||||
pnode: llvmlite.ParamNode | t.CPtr = pool.alloc(llvmlite.ParamNode.__sizeof__())
|
||||
if pnode is None:
|
||||
continue
|
||||
string.memset(pnode, 0, llvmlite.ParamNode.__sizeof__())
|
||||
pnode.Ty = fty
|
||||
|
||||
if first_node is None:
|
||||
first_node = pnode
|
||||
if prev_node is not None:
|
||||
prev_node.Next = pnode
|
||||
prev_node = pnode
|
||||
|
||||
# 构造命名结构体类型名: "sha1.ClassName" 或 "ClassName"
|
||||
# 注意: Phase 1b(declare_only=0)时 struct_ty 会注册到全局表并被后续
|
||||
# 文件翻译引用(vtable/self 参数类型打印),其 Name 必须跨 1b 每文件
|
||||
# reset_to(mark1) 存活 → 用 stdlib 深拷贝;Phase 1a 仍用 pool(无需跨文件)。
|
||||
type_name: str = class_name
|
||||
if trans.ModuleSha1 is not None:
|
||||
name_buf: str = pool.alloc(64)
|
||||
if name_buf is not None:
|
||||
viperlib.snprintf(name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
|
||||
type_name = name_buf
|
||||
if trans._declare_only == 0 and type_name is not None:
|
||||
tn_len: t.CSizeT = string.strlen(type_name)
|
||||
tn_copy: str = CompilerFactory.get_perm_pool().alloc(tn_len + 1)
|
||||
if tn_copy is not None:
|
||||
string.strcpy(tn_copy, type_name)
|
||||
type_name = tn_copy
|
||||
|
||||
# 创建 StructType(命名结构体)
|
||||
struct_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, first_node, total_field_count, type_name)
|
||||
if struct_ty is None:
|
||||
fb_st: str = VLogger.fmt_buf()
|
||||
if fb_st is not None:
|
||||
viperlib.snprintf(fb_st, 1024, "failed to create StructType for %s", class_name)
|
||||
VLogger.error(fb_st, "CLASS")
|
||||
return 0
|
||||
|
||||
# Phase 1b(declare_only=0)新注册的 struct_ty 会被注册到全局表并被后续文件
|
||||
# 翻译引用(vtable/self 参数类型打印/find_struct_by_type 指针匹配)。LLVMType
|
||||
# 对象和 Fields 链是 pool 分配的,本文件 reset_to(mark1) 后悬空。深拷贝整棵树
|
||||
# 到 stdlib,并替换 struct_ty 指针,保证 entry.Ty 与调用方持有的 struct_ty
|
||||
# 指向同一个 stdlib 拷贝(指针匹配不失效)且跨文件永久存活。
|
||||
# StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc(declare_only=1)
|
||||
# 预注册 includes 时若仍用 pool struct_ty,reset_to(mark2) 后 entry.Ty 悬空 →
|
||||
# 后续文件字段类型引用(resolve → Ptr(entry.Ty))→ opaque 扫描遍历悬空 fields
|
||||
# 崩溃(LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。
|
||||
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
st_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
|
||||
CompilerFactory.get_perm_pool(), struct_ty)
|
||||
if st_copy is not None:
|
||||
struct_ty = st_copy
|
||||
type_name = llvmlite.get_struct_name(struct_ty)
|
||||
|
||||
# 注册命名结构体到模块(输出 %"name" = type { ... } 定义行)
|
||||
llvmlite.module_add_named_type(trans.Module, pool, struct_ty)
|
||||
|
||||
# 扫描字段类型,为跨模块结构体引用添加 opaque 声明
|
||||
# 例如 list[int].__pool__ 引用 memhub.MemManager,需要在当前模块添加 %"sha1.MemManager" = type opaque
|
||||
for fi in range(field_count):
|
||||
fty_addr_op: t.CSizeT = field_types_buf[fi]
|
||||
if fty_addr_op == 0:
|
||||
continue
|
||||
fty_op: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(t.CVoid(fty_addr_op, t.CPtr))
|
||||
llvmlite.module_ensure_opaque_for_type(trans.Module, pool, fty_op)
|
||||
|
||||
# ============================================================
|
||||
# 3. 注册到 HandlesStruct
|
||||
# ============================================================
|
||||
entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.register_struct(
|
||||
pool, class_name, struct_ty, trans.ModuleSha1)
|
||||
if entry is None:
|
||||
fb_re: str = VLogger.fmt_buf()
|
||||
if fb_re is not None:
|
||||
viperlib.snprintf(fb_re, 1024, "failed to register %s", class_name)
|
||||
VLogger.error(fb_re, "CLASS")
|
||||
return 0
|
||||
|
||||
# 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错)
|
||||
if has_vtable == 1:
|
||||
entry.HasVTable = 1
|
||||
if is_novtable_deco == 1:
|
||||
entry.IsNoVTable = 1
|
||||
if parent_name is not None:
|
||||
# 1b 阶段父类名是 pool 字符串(本文件 AST/1a 预存),reset 后悬空;
|
||||
# _translate_oop_methods/_detect_vtable_status 会在后续文件
|
||||
# find_struct(ParentName) strcmp 解引用 → 用 stdlib 拷贝保证跨文件存活
|
||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
pn_len: t.CSizeT = string.strlen(parent_name)
|
||||
pn_buf: str = CompilerFactory.get_perm_pool().alloc(pn_len + 1)
|
||||
if pn_buf is not None:
|
||||
string.strcpy(pn_buf, parent_name)
|
||||
entry.ParentName = pn_buf
|
||||
else:
|
||||
entry.ParentName = parent_name
|
||||
# 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活)
|
||||
if parents_count > 0 and parents_buf is not None:
|
||||
self.Gen.StructGen.set_parent_names(pool, class_name, parents_buf, parents_count)
|
||||
|
||||
# 添加字段信息
|
||||
# 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始
|
||||
if has_vtable == 1:
|
||||
i8_ptr_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
|
||||
|
||||
for fi in range(field_count):
|
||||
fname_addr: t.CSizeT = field_names_buf[fi]
|
||||
if fname_addr == 0:
|
||||
continue
|
||||
fname: str = (str | t.CPtr)(t.CVoid(fname_addr, t.CPtr))
|
||||
fty_addr2: t.CSizeT = field_types_buf[fi]
|
||||
if fty_addr2 == 0:
|
||||
continue
|
||||
fty2: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(t.CVoid(fty_addr2, t.CPtr))
|
||||
# 恢复默认值 AST 指针
|
||||
fdef_addr: t.CSizeT = field_defaults_buf[fi]
|
||||
fdef: ast.AST | t.CPtr = None
|
||||
if fdef_addr != 0:
|
||||
fdef = (ast.AST | t.CPtr)(t.CVoid(fdef_addr, t.CPtr))
|
||||
# 恢复注解类名指针
|
||||
fannot_addr: t.CSizeT = field_annot_buf[fi]
|
||||
fannot: str = None
|
||||
if fannot_addr != 0:
|
||||
fannot = (str)(t.CVoid(fannot_addr, t.CPtr))
|
||||
if fname is not None and fty2 is not None:
|
||||
self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
|
||||
|
||||
# Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体
|
||||
if trans._declare_only == 1:
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 4. OOP 方法处理:扫描 FunctionDef,标记 OOP,翻译方法,生成 __before_init__
|
||||
# ============================================================
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name)
|
||||
|
||||
return 0
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,710 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.ClassGenBase as ClassGenBase
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# 泛型特化递归深度上限(泛型循环套用防护)
|
||||
# 正常嵌套特化深度 ≤ 3(如 list[list[str]]);64 远超合理值,命中即循环。
|
||||
# (原 HandlesClassDef/ClassGenMixin 顶层常量,阶段 D 拆分时遗漏,补回)
|
||||
GENERIC_SPEC_MAX_DEPTH: t.CDefine = 64
|
||||
# 泛型模板表容量上限(与 GenericClassNames 等 pool 表可容纳的模板数对齐;
|
||||
# 原 ClassGenMixin 顶层常量,阶段 D 拆分时遗漏,补回)
|
||||
GENERIC_TEMPLATE_MAX: t.CDefine = 512
|
||||
# 泛型特化缓存表容量(原 HandlesClassDef.SPEC_CACHE_MAX,阶段 D 拆分遗漏)
|
||||
SPEC_CACHE_MAX: t.CDefine = 512
|
||||
|
||||
# ============================================================
|
||||
# ClassGenGeneric - ClassGenMixin 拆分(阶段 D):泛型模板注册/特化/克隆
|
||||
#
|
||||
# 继承 ClassGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesClassDef 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
"""泛型模板注册/特化/克隆"""
|
||||
|
||||
# ============================================================
|
||||
# 簇 C:泛型模板注册/特化(P5-2 第三阶段迁移,函数体来自
|
||||
# HandlesClassDef.vp,逻辑零改动)
|
||||
#
|
||||
# 说明:
|
||||
# - _generic_class_* 全局表已迁入 self.GenericClassNames/
|
||||
# GenericClassNodes/GenericClassSha1s(P5-2 收尾)
|
||||
# - _spec_* 全局缓存迁移为 self.SpecKeys/SpecNames/SpecCount
|
||||
# ============================================================
|
||||
|
||||
def _is_generic_class(self, tp: list[str] | t.CPtr) -> int:
|
||||
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否
|
||||
|
||||
接收 type_params 而非 cd,避免 cd.type_params 属性访问
|
||||
因跨模块类型推断失败返回 None。
|
||||
"""
|
||||
if tp is None:
|
||||
return 0
|
||||
return 1
|
||||
def _register_generic_template(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
cd: ast.ClassDef | t.CPtr,
|
||||
module_sha1: str) -> None:
|
||||
"""注册泛型类模板(类名 + ClassDef 节点 + 来源模块 SHA1)"""
|
||||
if self.GenericClassNames is None:
|
||||
self.GenericClassNames = list[str](pool, 8)
|
||||
self.GenericClassNodes = list[ast.ClassDef | t.CPtr](pool, 8)
|
||||
self.GenericClassSha1s = list[str](pool, 8)
|
||||
self.GenericClassFiles = list[str](pool, 8)
|
||||
if self.GenericClassTpParams is None:
|
||||
self.GenericClassTpParams = CompilerFactory.get_perm_pool().alloc(GENERIC_TEMPLATE_MAX * 8)
|
||||
if self.GenericClassTpParams is None:
|
||||
return
|
||||
string.memset(self.GenericClassTpParams, 0, GENERIC_TEMPLATE_MAX * 8)
|
||||
# 用局部变量接收 list,触发类型推断(避免字段 __len__ 失败)
|
||||
names_local: list[str] | t.CPtr = self.GenericClassNames
|
||||
cn: t.CSizeT = len(names_local)
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, cd.name) == 0:
|
||||
return
|
||||
# type_params stdlib 深拷贝:模板节点的 type_params 是 pool 分配的
|
||||
tp_names_buf: str = self._copy_type_params_stdlib(cd.type_params)
|
||||
self.GenericClassNames.append(cd.name)
|
||||
self.GenericClassNodes.append(cd)
|
||||
self.GenericClassSha1s.append(module_sha1)
|
||||
# 同时保存模板来源文件路径(错误定位用,见字段注释)
|
||||
self.GenericClassFiles.append(self.Gen.TypeGenResolve.get_current_file())
|
||||
if tp_names_buf is not None and cn < GENERIC_TEMPLATE_MAX:
|
||||
self.GenericClassTpParams[cn] = t.CSizeT(tp_names_buf)
|
||||
def _copy_type_params_stdlib(self, tp: list[str] | t.CPtr) -> str:
|
||||
"""将模板 type_params 深拷贝为空格分隔、NUL 结尾的 stdlib 缓冲区
|
||||
|
||||
TPC Python 参考实现:type_params = [tp.name for tp in Node.type_params]
|
||||
(Python 字符串不可变天然跨阶段存活);Viper 的 type_params 是 pool
|
||||
分配 list,跨 Phase 内容可能被复用污染,必须注册时拷贝到 stdlib。
|
||||
返回缓冲区指针;tp 为 None(非泛型)或分配失败返回 None。
|
||||
"""
|
||||
if tp is None:
|
||||
return None
|
||||
tp_n: t.CSizeT = len(tp)
|
||||
if tp_n == 0:
|
||||
return None
|
||||
# 计算总长:各名字长度 + (n-1) 个空格 + NUL
|
||||
total: t.CSizeT = 1
|
||||
ti: t.CSizeT
|
||||
for ti in range(tp_n):
|
||||
tp_name: str = tp.get(ti)
|
||||
if tp_name is not None:
|
||||
total += string.strlen(tp_name) + 1
|
||||
buf: str = CompilerFactory.get_perm_pool().alloc(total)
|
||||
if buf is None:
|
||||
return None
|
||||
buf[0] = '\0'
|
||||
for ti in range(tp_n):
|
||||
tp_name = tp.get(ti)
|
||||
if tp_name is None:
|
||||
continue
|
||||
if ti > 0 and buf[0] != '\0':
|
||||
string.strcat(buf, " ")
|
||||
string.strcat(buf, tp_name)
|
||||
return buf
|
||||
def _find_generic_template_tp_params(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
class_name: str) -> list[str] | t.CPtr:
|
||||
"""查找泛型类模板的类型参数名列表,未找到返回 None
|
||||
|
||||
从注册时深拷贝的 stdlib 缓冲区重建当前 pool 的 list[str]
|
||||
(特化期间 pool 有效,方法与体翻译共用)。与 GenericClassNames 同序。
|
||||
"""
|
||||
if self.GenericClassNames is None or class_name is None \
|
||||
or self.GenericClassTpParams is None:
|
||||
return None
|
||||
names_local: list[str] | t.CPtr = self.GenericClassNames
|
||||
cn: t.CSizeT = len(names_local)
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
if i >= GENERIC_TEMPLATE_MAX:
|
||||
return None
|
||||
buf_addr: t.CSizeT = self.GenericClassTpParams[i]
|
||||
if buf_addr == 0:
|
||||
return None
|
||||
buf_tp: str = (str | t.CPtr)(t.CVoid(buf_addr, t.CPtr))
|
||||
# 空格分隔 → list[str]
|
||||
# 注意:list[str](pool, 8) 第二个参数是 elem_size(指针 8 字节),
|
||||
# 不是容量!传 4 会导致 get(i) 偏移算错、append 相互覆盖
|
||||
# (Pair[T1,T2] 的 seg1 写 data+4 覆盖 seg0 高 4 字节 → strcmp
|
||||
# 收到 0x15B78D5015B78CB0 混合垃圾崩溃,GenericTest 回归根因)
|
||||
tp_list: list[str] | t.CPtr = list[str](pool, 8)
|
||||
if tp_list is None:
|
||||
return None
|
||||
seg_start: t.CSizeT = 0
|
||||
pos: t.CSizeT = 0
|
||||
blen: t.CSizeT = string.strlen(buf_tp)
|
||||
while pos <= blen:
|
||||
if buf_tp[pos] == ' ' or buf_tp[pos] == '\0':
|
||||
seg_len: t.CSizeT = pos - seg_start
|
||||
if seg_len > 0:
|
||||
seg: str = pool.alloc(seg_len + 1)
|
||||
if seg is not None:
|
||||
string.strncpy(seg, buf_tp + seg_start, seg_len)
|
||||
seg[seg_len] = '\0'
|
||||
tp_list.append(seg)
|
||||
seg_start = pos + 1
|
||||
pos += 1
|
||||
return tp_list
|
||||
return None
|
||||
def _find_generic_template(self, class_name: str) -> ast.ClassDef | t.CPtr:
|
||||
"""查找泛型类模板,返回 ClassDef 节点或 None"""
|
||||
if self.GenericClassNames is None or class_name is None:
|
||||
return None
|
||||
# 用局部变量接收 list,触发类型推断(避免字段 __len__ 失败)
|
||||
names_local: list[str] | t.CPtr = self.GenericClassNames
|
||||
nodes_local: list[ast.ClassDef | t.CPtr] | t.CPtr = self.GenericClassNodes
|
||||
cn: t.CSizeT = len(names_local)
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
node: ast.ClassDef | t.CPtr = nodes_local.get(i)
|
||||
return node
|
||||
return None
|
||||
def _find_generic_template_sha1(self, class_name: str) -> str:
|
||||
"""查找泛型类模板的来源模块 SHA1,未找到返回 None"""
|
||||
if self.GenericClassNames is None or class_name is None:
|
||||
return None
|
||||
# 用局部变量接收 list,触发类型推断
|
||||
names_local: list[str] | t.CPtr = self.GenericClassNames
|
||||
sha1s_local: list[str] | t.CPtr = self.GenericClassSha1s
|
||||
cn: t.CSizeT = len(names_local)
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
sha1_val: str = sha1s_local.get(i)
|
||||
return sha1_val
|
||||
return None
|
||||
def _find_generic_template_file(self, class_name: str) -> str:
|
||||
"""查找泛型类模板的来源文件路径,未找到返回 None
|
||||
|
||||
与 _find_generic_template_sha1 同表同序;供特化翻译模板方法体时
|
||||
临时切换 CurrentFile(错误定位:模板 node.lineno 需配模板文件路径)。
|
||||
"""
|
||||
if self.GenericClassNames is None or class_name is None:
|
||||
return None
|
||||
names_local: list[str] | t.CPtr = self.GenericClassNames
|
||||
files_local: list[str] | t.CPtr = self.GenericClassFiles
|
||||
cn: t.CSizeT = len(names_local)
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
f_val: str = files_local.get(i)
|
||||
return f_val
|
||||
return None
|
||||
def _mangle_generic_class_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
class_name: str,
|
||||
type_args: list[str] | t.CPtr) -> str:
|
||||
"""生成特化类名: list + [int] -> list[int]
|
||||
|
||||
分配新缓冲区拼接,避免原地 strcat 导致缓冲区溢出
|
||||
"""
|
||||
if class_name is None or type_args is None or pool is None:
|
||||
return class_name
|
||||
n: t.CSizeT = len(type_args)
|
||||
if n == 0:
|
||||
return class_name
|
||||
# 计算总长度: class_name + "[" + arg0 + "," + arg1 + ... + "]" + NUL
|
||||
total_len: t.CSizeT = string.strlen(class_name) + 2 # "[" 和 "]"
|
||||
i: t.CSizeT
|
||||
for i in range(n):
|
||||
if i > 0:
|
||||
total_len += 1 # ","
|
||||
ta: str = type_args.get(i)
|
||||
if ta is not None:
|
||||
total_len += string.strlen(ta)
|
||||
total_len += 1 # NUL
|
||||
# 分配新缓冲区
|
||||
mangled: str = pool.alloc(total_len)
|
||||
if mangled is None:
|
||||
return class_name
|
||||
mangled[0] = '\0'
|
||||
string.strcat(mangled, class_name)
|
||||
string.strcat(mangled, "[")
|
||||
for i in range(n):
|
||||
if i > 0:
|
||||
string.strcat(mangled, ",")
|
||||
ta: str = type_args.get(i)
|
||||
if ta is not None:
|
||||
string.strcat(mangled, ta)
|
||||
string.strcat(mangled, "]")
|
||||
return mangled
|
||||
def _clone_annotation(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
tp_names: list[str] | t.CPtr,
|
||||
type_args: list[str] | t.CPtr) -> ast.AST | t.CPtr:
|
||||
"""拷贝类型注解表达式,替换类型参数 T 为具体类型名
|
||||
|
||||
tp_names: 类型参数名列表 ['T']
|
||||
type_args: 类型实参名列表 ['int']
|
||||
"""
|
||||
if node is None:
|
||||
return None
|
||||
k: int = node.kind()
|
||||
|
||||
# Name: 可能是类型参数 T
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||||
if nm.id is not None:
|
||||
n: t.CSizeT = len(tp_names) if tp_names is not None else 0
|
||||
i: t.CSizeT
|
||||
for i in range(n):
|
||||
tpn: str = tp_names.get(i)
|
||||
if tpn is not None and string.strcmp(nm.id, tpn) == 0:
|
||||
ta: str = type_args.get(i)
|
||||
# 类型实参可能是复合类型(如 "AST|t.CPtr"),用 _parse_type_string 解析
|
||||
parsed_node: ast.AST | t.CPtr = self.Gen.TypeGenResolve._parse_type_string(pool, ta, nm.ctx)
|
||||
return parsed_node
|
||||
return ast.Name(pool, nm.id, nm.ctx)
|
||||
return None
|
||||
|
||||
# Attribute: t.CPtr 等
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||||
new_value: ast.AST | t.CPtr = self._clone_annotation(pool, at.value, tp_names, type_args)
|
||||
return ast.Attribute(pool, new_value, at.attr, at.ctx)
|
||||
|
||||
# BinOp: T | t.CPtr 等
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
|
||||
new_left: ast.AST | t.CPtr = self._clone_annotation(pool, bop.left, tp_names, type_args)
|
||||
new_right: ast.AST | t.CPtr = self._clone_annotation(pool, bop.right, tp_names, type_args)
|
||||
return ast.BinOp(pool, new_left, bop.op, new_right)
|
||||
|
||||
# Subscript: list[int] 等
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
|
||||
new_value2: ast.AST | t.CPtr = self._clone_annotation(pool, sub.value, tp_names, type_args)
|
||||
new_slice: ast.AST | t.CPtr = self._clone_annotation(pool, sub.slice, tp_names, type_args)
|
||||
return ast.Subscript(pool, new_value2, new_slice, sub.ctx)
|
||||
|
||||
# Tuple: (T, int) 等
|
||||
if k == ast.ASTKind.Tuple:
|
||||
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
|
||||
new_elts: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8)
|
||||
if tup.elts is not None:
|
||||
tup_elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts
|
||||
en: t.CSizeT = len(tup_elts)
|
||||
ei: t.CSizeT
|
||||
for ei in range(en):
|
||||
el: ast.AST | t.CPtr = tup_elts.get(ei)
|
||||
new_el: ast.AST | t.CPtr = self._clone_annotation(pool, el, tp_names, type_args)
|
||||
if new_el is not None:
|
||||
new_elts.append(new_el)
|
||||
return ast.Tuple(pool, new_elts, tup.ctx)
|
||||
|
||||
# Constant: 默认值等
|
||||
if k == ast.ASTKind.Constant:
|
||||
cnst: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||||
return ast.Constant(pool, cnst.const_kind, cnst.int_val, cnst.float_val,
|
||||
cnst.str_val, cnst.lineno, cnst.col_offset)
|
||||
|
||||
# 其他: 直接引用(不拷贝)
|
||||
return node
|
||||
def _clone_arguments(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
args: ast.AST | t.CPtr,
|
||||
tp_names: list[str] | t.CPtr,
|
||||
type_args: list[str] | t.CPtr) -> ast.AST | t.CPtr:
|
||||
"""拷贝 Arguments 节点,替换 arg annotation 中的 T"""
|
||||
if args is None:
|
||||
return None
|
||||
if args.kind() != ast.ASTKind.Arguments:
|
||||
return args
|
||||
old_args: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args)
|
||||
# 拷贝 args 列表,替换每个 Arg 的 annotation
|
||||
new_arg_list: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8)
|
||||
if old_args.args is not None:
|
||||
old_arg_list: list[ast.AST | t.CPtr] | t.CPtr = old_args.args
|
||||
an: t.CSizeT = len(old_arg_list)
|
||||
ai: t.CSizeT
|
||||
for ai in range(an):
|
||||
arg_node: ast.AST | t.CPtr = old_arg_list.get(ai)
|
||||
if arg_node is None:
|
||||
continue
|
||||
if arg_node.kind() == ast.ASTKind.Arg:
|
||||
ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(arg_node)
|
||||
new_anno: ast.AST | t.CPtr = self._clone_annotation(pool, ag.annotation, tp_names, type_args)
|
||||
new_arg: ast.Arg | t.CPtr = ast.Arg(pool, ag.arg, new_anno)
|
||||
new_arg_list.append(new_arg)
|
||||
else:
|
||||
new_arg_list.append(arg_node)
|
||||
return ast.Arguments(pool, new_arg_list, old_args.vararg, old_args.kwarg,
|
||||
old_args.defaults, old_args.kw_defaults)
|
||||
def _clone_classdef_for_spec(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
cd: ast.ClassDef | t.CPtr,
|
||||
spec_name: str,
|
||||
tp_names: list[str] | t.CPtr,
|
||||
type_args: list[str] | t.CPtr) -> ast.ClassDef | t.CPtr:
|
||||
"""拷贝 ClassDef 节点用于泛型特化
|
||||
|
||||
- 替换类名为 spec_name
|
||||
- 清除 type_params(特化后不是泛型类)
|
||||
- 拷贝 children 中的 AnnAssign 和 FunctionDef,替换类型注解中的 T
|
||||
- 方法体直接引用原始节点(通过 type_map 上下文处理 T)
|
||||
"""
|
||||
# 创建新 ClassDef(type_params=None 表示非泛型)
|
||||
new_cd: ast.ClassDef | t.CPtr = ast.ClassDef(pool, spec_name, cd.bases, cd.keywords,
|
||||
cd.decorator_list, None)
|
||||
# 拷贝 children
|
||||
if cd.children is not None:
|
||||
cd_children: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||||
cn: t.CSizeT = len(cd_children)
|
||||
ci: t.CSizeT
|
||||
for ci in range(cn):
|
||||
child: ast.AST | t.CPtr = cd_children.get(ci)
|
||||
if child is None:
|
||||
continue
|
||||
ck: int = child.kind()
|
||||
|
||||
if ck == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child)
|
||||
new_target: ast.AST | t.CPtr = aa.target
|
||||
new_anno2: ast.AST | t.CPtr = self._clone_annotation(pool, aa.annotation, tp_names, type_args)
|
||||
new_value3: ast.AST | t.CPtr = aa.value
|
||||
new_aa: ast.AnnAssign | t.CPtr = ast.AnnAssign(pool, new_target, new_anno2, new_value3, aa.simple)
|
||||
new_cd.append(new_aa)
|
||||
|
||||
elif ck == ast.ASTKind.FunctionDef:
|
||||
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
|
||||
new_args2: ast.AST | t.CPtr = self._clone_arguments(pool, fd.args, tp_names, type_args)
|
||||
new_returns2: ast.AST | t.CPtr = self._clone_annotation(pool, fd.returns, tp_names, type_args)
|
||||
new_fd: ast.FunctionDef | t.CPtr = ast.FunctionDef(pool, fd.name, new_args2,
|
||||
fd.decorator_list, new_returns2, 0)
|
||||
# 方法体直接引用原始节点
|
||||
if fd.children is not None:
|
||||
fd_children: list[ast.AST | t.CPtr] | t.CPtr = fd.children
|
||||
bcn: t.CSizeT = len(fd_children)
|
||||
bci: t.CSizeT
|
||||
for bci in range(bcn):
|
||||
body_stmt: ast.AST | t.CPtr = fd_children.get(bci)
|
||||
if body_stmt is not None:
|
||||
new_fd.append(body_stmt)
|
||||
new_cd.append(new_fd)
|
||||
|
||||
else:
|
||||
new_cd.append(child)
|
||||
|
||||
return new_cd
|
||||
def _find_cached_spec(self, spec_key: str) -> str:
|
||||
"""查找已缓存的特化名,未找到返回 None"""
|
||||
if self.SpecKeys is None or spec_key is None:
|
||||
return None
|
||||
i: int
|
||||
for i in range(self.SpecCount):
|
||||
key_addr: t.CSizeT = self.SpecKeys[i]
|
||||
if key_addr != 0:
|
||||
k: str = (str | t.CPtr)(t.CVoid(key_addr, t.CPtr))
|
||||
if k is not None and string.strcmp(k, spec_key) == 0:
|
||||
name_addr: t.CSizeT = self.SpecNames[i]
|
||||
if name_addr != 0:
|
||||
return (str | t.CPtr)(t.CVoid(name_addr, t.CPtr))
|
||||
return None
|
||||
def _cache_spec(self, pool: memhub.MemBuddy | t.CPtr, spec_key: str, spec_name: str):
|
||||
"""缓存特化结果(stdlib 深拷贝存储,跨文件安全)"""
|
||||
if spec_key is None or spec_name is None:
|
||||
return
|
||||
if self.SpecKeys is None:
|
||||
self.SpecKeys = CompilerFactory.get_perm_pool().alloc(SPEC_CACHE_MAX * 8)
|
||||
self.SpecNames = CompilerFactory.get_perm_pool().alloc(SPEC_CACHE_MAX * 8)
|
||||
if self.SpecKeys is None or self.SpecNames is None:
|
||||
return
|
||||
string.memset(self.SpecKeys, 0, SPEC_CACHE_MAX * 8)
|
||||
string.memset(self.SpecNames, 0, SPEC_CACHE_MAX * 8)
|
||||
self.SpecCount = 0
|
||||
if self.SpecCount >= SPEC_CACHE_MAX:
|
||||
return
|
||||
# 深拷贝字符串到 stdlib 区
|
||||
key_len: t.CSizeT = string.strlen(spec_key)
|
||||
name_len: t.CSizeT = string.strlen(spec_name)
|
||||
key_copy: str = CompilerFactory.get_perm_pool().alloc(key_len + 1)
|
||||
name_copy: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
|
||||
if key_copy is None or name_copy is None:
|
||||
return
|
||||
string.strcpy(key_copy, spec_key)
|
||||
string.strcpy(name_copy, spec_name)
|
||||
self.SpecKeys[self.SpecCount] = t.CSizeT(key_copy)
|
||||
self.SpecNames[self.SpecCount] = t.CSizeT(name_copy)
|
||||
self.SpecCount += 1
|
||||
def _merge_template_imports(self, trans: HT.Translator | t.CPtr,
|
||||
template_sha1: str) -> None:
|
||||
"""将模板模块的 imports 并入 trans._imported_modules(空格分隔)"""
|
||||
if trans is None or template_sha1 is None:
|
||||
return
|
||||
tdeps: str = StubMergerStore.LookupIncludesDeps(template_sha1)
|
||||
if tdeps is None or tdeps[0] == '\0':
|
||||
return
|
||||
cur: str = trans._imported_modules
|
||||
if cur is None or cur[0] == '\0':
|
||||
trans._imported_modules = tdeps
|
||||
return
|
||||
cur_len: t.CSizeT = string.strlen(cur)
|
||||
td_len: t.CSizeT = string.strlen(tdeps)
|
||||
merged: str = trans.Pool.alloc(cur_len + td_len + 2)
|
||||
if merged is None:
|
||||
return
|
||||
string.strcpy(merged, cur)
|
||||
merged[cur_len] = ' '
|
||||
string.strcpy(merged + cur_len + 1, tdeps)
|
||||
trans._imported_modules = merged
|
||||
def _record_spec_trigger(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
spec_name: str, trigger_sha1: str) -> int:
|
||||
"""记录泛型特化类的触发文件 sha1(StructEntry.SpecTriggerSha1)
|
||||
|
||||
特化类 ModuleSha1=模板 sha1(方法名带模板前缀),Phase A _deps_pending
|
||||
作废重译时 reset_module_methods_emitted 按当前文件 sha1 匹配不到 →
|
||||
特化方法体 MethodsEmitted 残留 1 → 下遍不补译 → 链接 undefined symbol。
|
||||
记录触发文件 sha1 后按此精确重置(见 StructGenMixin.reset_module_methods_emitted)。
|
||||
"""
|
||||
if spec_name is None or trigger_sha1 is None:
|
||||
return 0
|
||||
entry_rt: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
|
||||
if entry_rt is None:
|
||||
return 0
|
||||
if string.strcmp(entry_rt.Name, spec_name) != 0:
|
||||
return 0
|
||||
ts_len_r: t.CSizeT = string.strlen(trigger_sha1)
|
||||
ts_buf_r: str = CompilerFactory.get_perm_pool().alloc(ts_len_r + 1)
|
||||
if ts_buf_r is None:
|
||||
return 0
|
||||
string.strcpy(ts_buf_r, trigger_sha1)
|
||||
entry_rt.SpecTriggerSha1 = ts_buf_r
|
||||
return 1
|
||||
def _specialize_generic_class(self, trans: HT.Translator | t.CPtr,
|
||||
class_name: str,
|
||||
type_args: list[str] | t.CPtr) -> str:
|
||||
"""特化泛型类,返回特化类名
|
||||
|
||||
1. 查找泛型模板
|
||||
2. 生成特化名
|
||||
3. 检查缓存
|
||||
4. 拷贝 ClassDef 节点(替换类型注解中的 T)
|
||||
5. 设置 type_map 上下文(用于方法体中的 T 替换)
|
||||
6. 调用 translate_class_def 翻译特化类
|
||||
7. 返回特化名
|
||||
"""
|
||||
if trans is None or class_name is None or type_args is None:
|
||||
return None
|
||||
# 泛型循环套用防护:递归深度 +1,超过上限立即报错终止(防 F-bounded
|
||||
# 等循环特化把编译变死循环)。所有返回路径都必须 -1(见各 return 前)。
|
||||
self.GenericSpecDepth += 1
|
||||
# 特化触发文件 sha1:进入函数时 trans.ModuleSha1 是触发方文件,
|
||||
# 翻译特化类时会被切换为模板 sha1(见下方),作废重译时按此重置
|
||||
# 特化类 MethodsEmitted(见 _record_spec_trigger / reset_module_methods_emitted)
|
||||
spec_trigger_sha1: str = trans.ModuleSha1
|
||||
if self.GenericSpecDepth > GENERIC_SPEC_MAX_DEPTH:
|
||||
err_spec: str = trans.Pool.alloc(256)
|
||||
if err_spec is not None:
|
||||
viperlib.snprintf(err_spec, 256,
|
||||
"泛型特化递归过深(疑似泛型循环套用),类: %s 深度=%d",
|
||||
class_name, self.GenericSpecDepth)
|
||||
self.Gen.TypeGenErr.fatal_error(None, err_spec, "InternalCompilerError")
|
||||
self.Gen.TypeGenErr.fatal_error(None, "泛型特化递归过深(疑似泛型循环套用)", "InternalCompilerError")
|
||||
# 防御:泛型上下文内收集的 type_args 若残留未替换的类型参数名(如 "T"),
|
||||
# 统一替换为具体类型名,避免 ndarray[T] 二次递归特化崩溃。
|
||||
# 调用方传入的 type_args 均为临时新建列表,就地替换无共享副作用。
|
||||
ta_cnt_g: t.CSizeT = len(type_args)
|
||||
ta_i_g: t.CSizeT
|
||||
for ta_i_g in range(ta_cnt_g):
|
||||
ta_raw_g: str = type_args.get(ta_i_g)
|
||||
if ta_raw_g is not None:
|
||||
ta_rsv_g: str = self.Gen.TypeGenBase.resolve_generic_type_name(ta_raw_g)
|
||||
if ta_rsv_g is not None and string.strcmp(ta_raw_g, ta_rsv_g) != 0:
|
||||
type_args.set(ta_i_g, ta_rsv_g)
|
||||
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
|
||||
# 1. 查找泛型模板
|
||||
template_cd: ast.ClassDef | t.CPtr = self._find_generic_template(class_name)
|
||||
if template_cd is None:
|
||||
self.GenericSpecDepth -= 1
|
||||
return None
|
||||
|
||||
# 2. 生成特化名
|
||||
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args)
|
||||
if spec_name is None:
|
||||
self.GenericSpecDepth -= 1
|
||||
return None
|
||||
|
||||
# 治本修复:Phase B 补译泛型特化类方法体(统一入口,在 cache/existing 检查之前)
|
||||
# Phase 1a(declare_only==1)特化时只注册结构体不译方法体(MethodsEmitted==0),
|
||||
# Phase B(declare_only==0)重复进入时若方法体未译,补译方法体生成 define,
|
||||
# 避免 GSList[Line].append 等符号只有 declare 无 define → 链接失败。
|
||||
# 首次 Phase B 特化(Phase 1a 未特化)find_struct 返回 None,不补译,走下方正常全量翻译。
|
||||
if trans._declare_only == 0:
|
||||
pre_existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
|
||||
if pre_existing is not None and pre_existing.MethodsEmitted == 0:
|
||||
# 防重入:先标记 MethodsEmitted=1,避免补译过程中方法体/返回类型注解
|
||||
# 再次触发 _specialize_generic_class(如 __iter__ 返回 list[T]|t.CPtr)
|
||||
# 导致无限递归补译 → OOM。补译完成后已设置,无需恢复。
|
||||
# 注意:此处提前置位后调用 _translate_oop_methods 必须传 reemit=1
|
||||
# (补译模式),否则其 MethodsEmitted==1 防重复检查会拦截补译,
|
||||
# 方法体永不发射 → 特化方法只有 call 无 define → 链接 undefined symbol。
|
||||
pre_existing.MethodsEmitted = 1
|
||||
# 从 stdlib 深拷贝表取 type_params(模板节点 pool list 跨 Phase 可能失效)
|
||||
tp_names_pre: list[str] | t.CPtr = self._find_generic_template_tp_params(
|
||||
pool, class_name)
|
||||
if tp_names_pre is None:
|
||||
tp_names_pre = template_cd.type_params
|
||||
spec_cd_pre: ast.ClassDef | t.CPtr = self._clone_classdef_for_spec(
|
||||
pool, template_cd, spec_name, tp_names_pre, type_args)
|
||||
if spec_cd_pre is not None:
|
||||
trans.GenericTypeParamNames = tp_names_pre
|
||||
trans.GenericTypeArgs = type_args
|
||||
self.Gen.TypeGenBase.set_generic_context(tp_names_pre, type_args)
|
||||
saved_ms_pre: str = trans.ModuleSha1
|
||||
saved_imp_pre: str = trans._imported_modules
|
||||
template_sha1_pre: str = self._find_generic_template_sha1(class_name)
|
||||
if template_sha1_pre is not None:
|
||||
trans.ModuleSha1 = template_sha1_pre
|
||||
self._merge_template_imports(trans, template_sha1_pre)
|
||||
# 错误定位上下文:补译的是模板方法体(spec_cd_pre 克隆自模板,
|
||||
# node.lineno 是模板文件行号)。切换 CurrentFile 到模板文件并压入
|
||||
# trace 帧,使 fatal_error 正确显示模板文件位置(翻译完恢复)。
|
||||
saved_cur_pre: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
tpl_file_pre: str = self._find_generic_template_file(class_name)
|
||||
if tpl_file_pre is not None:
|
||||
self.Gen.TypeGenResolve.set_current_file(tpl_file_pre)
|
||||
self.Gen.TypeGenBase.push_trace(tpl_file_pre, template_cd.lineno)
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, spec_cd_pre, pre_existing.Ty, spec_name, 0, 1)
|
||||
if tpl_file_pre is not None:
|
||||
self.Gen.TypeGenBase.pop_trace()
|
||||
self.Gen.TypeGenResolve.set_current_file(saved_cur_pre)
|
||||
trans.ModuleSha1 = saved_ms_pre
|
||||
trans._imported_modules = saved_imp_pre
|
||||
trans.GenericTypeParamNames = None
|
||||
trans.GenericTypeArgs = None
|
||||
self.Gen.TypeGenBase.clear_generic_context()
|
||||
|
||||
# 3. 检查缓存(spec_key 包含所有类型实参,避免多参数泛型缓存冲突)
|
||||
# 分配新缓冲区避免原地 strcat 溢出
|
||||
sk_n: t.CSizeT = len(type_args)
|
||||
sk_total: t.CSizeT = string.strlen(class_name) + 2 # "<" 和 ">"
|
||||
sk_i: t.CSizeT
|
||||
for sk_i in range(sk_n):
|
||||
if sk_i > 0:
|
||||
sk_total += 1 # ","
|
||||
sk_ta: str = type_args.get(sk_i)
|
||||
if sk_ta is not None:
|
||||
sk_total += string.strlen(sk_ta)
|
||||
sk_total += 1 # NUL
|
||||
spec_key: str = pool.alloc(sk_total)
|
||||
if spec_key is None:
|
||||
self.GenericSpecDepth -= 1
|
||||
return None
|
||||
spec_key[0] = '\0'
|
||||
string.strcat(spec_key, class_name)
|
||||
string.strcat(spec_key, "<")
|
||||
for sk_i in range(sk_n):
|
||||
if sk_i > 0:
|
||||
string.strcat(spec_key, ",")
|
||||
sk_ta: str = type_args.get(sk_i)
|
||||
if sk_ta is not None:
|
||||
string.strcat(spec_key, sk_ta)
|
||||
string.strcat(spec_key, ">")
|
||||
cached: str = self._find_cached_spec(spec_key)
|
||||
if cached is not None:
|
||||
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
|
||||
self.GenericSpecDepth -= 1
|
||||
return cached
|
||||
|
||||
# 4. 检查是否已注册(Phase B 重复特化)
|
||||
existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
|
||||
if existing is not None:
|
||||
self._cache_spec(pool, spec_key, spec_name)
|
||||
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
|
||||
self.GenericSpecDepth -= 1
|
||||
return spec_name
|
||||
|
||||
# 预注册缓存(防止递归)
|
||||
self._cache_spec(pool, spec_key, spec_name)
|
||||
|
||||
# 5. 拷贝 ClassDef 节点
|
||||
# 从 stdlib 深拷贝表取 type_params(模板节点 pool list 跨 Phase 可能失效)
|
||||
tp_names: list[str] | t.CPtr = self._find_generic_template_tp_params(
|
||||
pool, class_name)
|
||||
if tp_names is None:
|
||||
tp_names = template_cd.type_params
|
||||
spec_cd: ast.ClassDef | t.CPtr = self._clone_classdef_for_spec(
|
||||
pool, template_cd, spec_name, tp_names, type_args)
|
||||
if spec_cd is None:
|
||||
self.GenericSpecDepth -= 1
|
||||
return None
|
||||
|
||||
# 6. 设置 type_map 上下文(用于方法体中的 T 替换)
|
||||
trans.GenericTypeParamNames = tp_names
|
||||
trans.GenericTypeArgs = type_args
|
||||
self.Gen.TypeGenBase.set_generic_context(tp_names, type_args)
|
||||
|
||||
# 7. 翻译特化类
|
||||
# 切换 trans.ModuleSha1 为泛型模板定义模块的 SHA1,
|
||||
# 使结构体类型名与特化方法函数符号名都用定义模块的 SHA1 前缀
|
||||
# (跨模块一致,单态化特化名 = {模板SHA1}.Class[param].method)。
|
||||
# 同时并入模板模块的 imports(见 _merge_template_imports 注释),
|
||||
# 避免模板方法体内的跨模块调用被误报"模块未导入"。
|
||||
saved_module_sha1: str = trans.ModuleSha1
|
||||
saved_imported: str = trans._imported_modules
|
||||
template_sha1: str = self._find_generic_template_sha1(class_name)
|
||||
if template_sha1 is not None:
|
||||
trans.ModuleSha1 = template_sha1
|
||||
self._merge_template_imports(trans, template_sha1)
|
||||
# 错误定位上下文:spec_cd 克隆自模板,方法体的 node.lineno 是模板文件
|
||||
# 行号,而 CurrentFile 仍是调用方文件 → 错误显示错位。切换 CurrentFile
|
||||
# 到模板文件并压入 trace 帧(翻译完恢复),使 fatal_error 正确溯源。
|
||||
saved_cur_full: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
tpl_file_full: str = self._find_generic_template_file(class_name)
|
||||
if tpl_file_full is not None:
|
||||
self.Gen.TypeGenResolve.set_current_file(tpl_file_full)
|
||||
self.Gen.TypeGenBase.push_trace(tpl_file_full, template_cd.lineno)
|
||||
self.Gen.ClassGenDef.translate_class_def(trans, spec_cd)
|
||||
if tpl_file_full is not None:
|
||||
self.Gen.TypeGenBase.pop_trace()
|
||||
self.Gen.TypeGenResolve.set_current_file(saved_cur_full)
|
||||
# 恢复原始 ModuleSha1 与 imports
|
||||
trans.ModuleSha1 = saved_module_sha1
|
||||
trans._imported_modules = saved_imported
|
||||
|
||||
# 8. 清除 type_map 上下文
|
||||
trans.GenericTypeParamNames = None
|
||||
trans.GenericTypeArgs = None
|
||||
self.Gen.TypeGenBase.clear_generic_context()
|
||||
|
||||
# 特化结果字段名完整性检查(fail-fast:字段名缺失 = 字段表损坏,
|
||||
# 静默继续会让后续 GEP 按错索引访问)
|
||||
if string.strcmp(class_name, "GSListNode") == 0:
|
||||
result_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
|
||||
if result_entry is not None:
|
||||
ri: int
|
||||
for ri in range(result_entry.FieldCount):
|
||||
rfe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry(
|
||||
result_entry, ri)
|
||||
if rfe is not None:
|
||||
rfn: str = self.Gen.StructGen.get_field_name_ptr(rfe)
|
||||
if rfn is None:
|
||||
VLogger.error("SPEC 字段名缺失(字段表损坏)", "SPEC")
|
||||
self.Gen.TypeGenErr.fatal_error(None, "SPEC 字段名缺失(字段表损坏)", "TypeError")
|
||||
|
||||
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
|
||||
self.GenericSpecDepth -= 1
|
||||
return spec_name
|
||||
@@ -1,25 +0,0 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.ClassGenBase as ClassGenBase
|
||||
|
||||
# ============================================================
|
||||
# ClassGenMixin - 类定义/继承/泛型/vtable/enum/union/renum
|
||||
#
|
||||
# 500~1500 行/文件架构重构阶段 D:全部方法已拆至
|
||||
# ClassGenBase/Enum/Generic/VTable/Def,本文件保留类声明。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ClassGenMixin(ClassGenBase.ClassGenBase):
|
||||
"""类定义处理 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,378 @@
|
||||
from stdint import *
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import win32.win32file as win32file
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
# DebugInfoGen - emits function-level DWARF metadata (mlirlite text IR), mirroring TPC LLVMCG/DebugInfo.py node-for-node.
|
||||
# Active only under --traceback (SetEnabled); hooks come from the Gen layer. When disabled every entry point returns before touching DI APIs, keeping IR byte-identical.
|
||||
|
||||
# ---- module-scope state (reset per module by BeginModule) ----
|
||||
_g_enabled: int = 0
|
||||
_g_finalized: int = 0
|
||||
_g_mod: mlirlite.LLVMModule* = None
|
||||
_g_file_path: str = None
|
||||
_g_abs_file_path: str = None
|
||||
_g_file_id: int = -1
|
||||
_g_null_id: int = -1
|
||||
_g_subtype_id: int = -1
|
||||
_g_cu_id: int = -1
|
||||
_g_cur_sp: int = -1
|
||||
_g_cur_entry_ref: str = None
|
||||
_g_cur_builder: mlirlite.IRBuilder* = None
|
||||
_g_depth: int = 0
|
||||
|
||||
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder) per level; +1 lets 0 mean "no enclosing function".
|
||||
# The saved builder makes SetStatement reject a builder not pushed by the current DISubprogram (e.g. an async state machine in a bracketed function); such instrs must carry no !dbg or LLVM's verifier fails.
|
||||
define DBG_STACK_MAX = 256
|
||||
_g_stk_sp: sizet* = None
|
||||
_g_stk_ref: str = None
|
||||
_g_stk_bld: str = None
|
||||
|
||||
# DILocation cache: direct-mapped, up to 8-way linear probe, key (sp, line, col).
|
||||
define DBG_LOC_SLOTS = 8192
|
||||
define DBG_LOC_PROBE = 8
|
||||
define DBG_LOC_KEY_STRIDE = 3
|
||||
_g_loc_key: sizet* = None
|
||||
_g_loc_ref: str = None
|
||||
_g_loc_used: bytes = None
|
||||
|
||||
|
||||
# SetEnabled / IsEnabled - mirror the --traceback switch (flag -> _g_enabled, forwarded to mlirlite's debug call-site recorder).
|
||||
def SetEnabled(flag: bool) -> int:
|
||||
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
|
||||
global _g_enabled
|
||||
if flag: _g_enabled = 1
|
||||
else: _g_enabled = 0
|
||||
# Forward the same switch to mlirlite's debug call-site recorder (under --traceback we emit a <sha1>.vpcall side file for WriteStackMap+objdump).
|
||||
mlirlite.DebugSetEnabled(flag)
|
||||
return 0
|
||||
|
||||
|
||||
def IsEnabled() -> int:
|
||||
return _g_enabled
|
||||
|
||||
|
||||
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers.
|
||||
def _EnsureStorage() -> int:
|
||||
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
|
||||
if _g_stk_sp is not None: return 0
|
||||
sbuf1: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
||||
sbuf2: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
||||
sbuf3: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
||||
kbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * DBG_LOC_KEY_STRIDE * 8)
|
||||
rbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * 8)
|
||||
ubuf: bytes = stdlib.malloc(DBG_LOC_SLOTS)
|
||||
if sbuf1 is None or sbuf2 is None or sbuf3 is None or kbuf is None or rbuf is None or ubuf is None:
|
||||
VLogger.error("--traceback: failed to allocate DWARF metadata buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
string.memset(sbuf1, 0, DBG_STACK_MAX * 8)
|
||||
string.memset(sbuf2, 0, DBG_STACK_MAX * 8)
|
||||
string.memset(sbuf3, 0, DBG_STACK_MAX * 8)
|
||||
string.memset(kbuf, 0, DBG_LOC_SLOTS * DBG_LOC_KEY_STRIDE * 8)
|
||||
string.memset(rbuf, 0, DBG_LOC_SLOTS * 8)
|
||||
string.memset(ubuf, 0, DBG_LOC_SLOTS)
|
||||
_g_stk_sp = (sizet*)(sbuf1)
|
||||
_g_stk_ref = sbuf2
|
||||
_g_stk_bld = sbuf3
|
||||
_g_loc_key = (sizet*)(kbuf)
|
||||
_g_loc_ref = rbuf
|
||||
_g_loc_used = ubuf
|
||||
return 0
|
||||
|
||||
|
||||
def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
|
||||
"""Allocate a persistent "!N" reference string for a DI node id."""
|
||||
buf: str = pool.alloc(24)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, 24, "!%d", node_id)
|
||||
return buf
|
||||
|
||||
|
||||
# BeginModule - start of one module translation.
|
||||
def BeginModule(mod: mlirlite.LLVMModule*, file_path: str) -> int:
|
||||
"""Reset per-module DI state. Call once at the start of each module."""
|
||||
global _g_mod, _g_file_path, _g_abs_file_path, _g_finalized, _g_file_id, _g_null_id
|
||||
global _g_subtype_id, _g_cu_id, _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||
if _g_enabled == 0: return 0
|
||||
if _EnsureStorage() != 0: return 1
|
||||
_g_mod = mod
|
||||
_g_file_path = file_path
|
||||
_g_abs_file_path = None
|
||||
# Module-boundary fallback: clear any un-Pop'd debug function context (mid-translation failure) so the previous module's caller is not attributed here.
|
||||
mlirlite.DebugResetFunc()
|
||||
_g_finalized = 0
|
||||
_g_file_id = -1
|
||||
_g_null_id = -1
|
||||
_g_subtype_id = -1
|
||||
_g_cu_id = -1
|
||||
_g_cur_sp = -1
|
||||
_g_cur_entry_ref = None
|
||||
_g_cur_builder = None
|
||||
_g_depth = 0
|
||||
if _g_loc_used is not None: string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
|
||||
return 0
|
||||
|
||||
|
||||
# _EnsureModuleNodes - lazily create DIFile / !{ null } / DISubroutineType / DICompileUnit at the first define (once per module).
|
||||
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id, _g_abs_file_path
|
||||
if _g_cu_id >= 0: return 0
|
||||
mod: mlirlite.LLVMModule* = _g_mod
|
||||
if mod is None: return 1
|
||||
# DIFile: absolute path (forward slashes), filename keeps the whole path.
|
||||
# Must be absolute: relative paths make LLVM's DWARF line records degrade to a bare "main.vp" that runtime cannot reload; absolutizing works whether LLVM keeps the full name or splits dir+basename.
|
||||
src: str = _g_file_path
|
||||
if src is None: src = ""
|
||||
slen: sizet = string.strlen(src)
|
||||
cap_n: sizet = slen + 4112
|
||||
nbuf: str = pool.alloc(cap_n)
|
||||
if nbuf is None:
|
||||
VLogger.error("--traceback: failed to allocate DIFile path buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
if slen > 0: string.strncpy(nbuf, src, slen)
|
||||
nbuf[slen] = '\0'
|
||||
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
|
||||
# Relative path -> prefix CWD via a win32file low-level call (as in Repl.vp), not os._mbuddy which the compiler process may not have initialized.
|
||||
CwdBuf: bytes = pool.alloc(4112)
|
||||
if CwdBuf is not None:
|
||||
CwdBuf[0] = '\0'
|
||||
nc: ULONG = win32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
|
||||
if nc > 0:
|
||||
# Defensive: when the buffer is too small GetCurrentDirectoryA writes undefined content; force a NUL.
|
||||
CwdBuf[4110] = '\0'
|
||||
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
|
||||
# Normalize all backslashes (the CWD segment may contain '\' too).
|
||||
flen: sizet = string.strlen(nbuf)
|
||||
ni: sizet = 0
|
||||
for ni in range(int(flen)):
|
||||
if nbuf[ni] == '\\': nbuf[ni] = '/'
|
||||
# Save the absolute path for mlirlite's debug call-site recorder (.vpcall file paths must match DIFile: absolute + forward slash).
|
||||
_g_abs_file_path = nbuf
|
||||
# filename = whole path; directory = its prefix (both NUL-terminated, non-overlapping).
|
||||
fname: str = nbuf
|
||||
directory: str = ""
|
||||
slash: str = string.strrchr(nbuf, '/')
|
||||
if slash is not None:
|
||||
dlen: sizet = string.strlen(nbuf) - string.strlen(slash + 1) - 1
|
||||
dirbuf: str = pool.alloc(dlen + 1)
|
||||
if dirbuf is not None:
|
||||
string.strncpy(dirbuf, nbuf, dlen)
|
||||
dirbuf[dlen] = '\0'
|
||||
directory = dirbuf
|
||||
dsize: sizet = string.strlen(fname) + string.strlen(directory) + 96
|
||||
dbuf: str = pool.alloc(dsize)
|
||||
if dbuf is None:
|
||||
VLogger.error("--traceback: failed to allocate DIFile node buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
viperlib.snprintf(dbuf, dsize, "!DIFile(filename: \"%s\", directory: \"%s\")", fname, directory)
|
||||
fid: int = mlirlite.module_add_di_node(mod, pool, dbuf)
|
||||
if fid < 0:
|
||||
VLogger.error("--traceback: failed to allocate DIFile metadata node, aborting compilation", "DWARF")
|
||||
return 1
|
||||
_g_file_id = fid
|
||||
# types: !{ null } placeholder list, then DISubroutineType referencing it
|
||||
nid: int = mlirlite.module_add_di_node(mod, pool, "!{ null }")
|
||||
if nid < 0:
|
||||
VLogger.error("--traceback: failed to allocate DISubroutineType type table, aborting compilation", "DWARF")
|
||||
return 1
|
||||
_g_null_id = nid
|
||||
tbuf: str = pool.alloc(64)
|
||||
if tbuf is None:
|
||||
VLogger.error("--traceback: failed to allocate DISubroutineType buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
viperlib.snprintf(tbuf, 64, "!DISubroutineType(types: !%d)", _g_null_id)
|
||||
tid: int = mlirlite.module_add_di_node(mod, pool, tbuf)
|
||||
if tid < 0:
|
||||
VLogger.error("--traceback: failed to allocate DISubroutineType metadata node, aborting compilation", "DWARF")
|
||||
return 1
|
||||
_g_subtype_id = tid
|
||||
cbuf: str = pool.alloc(256)
|
||||
if cbuf is None:
|
||||
VLogger.error("--traceback: failed to allocate DICompileUnit buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
viperlib.snprintf(cbuf, 256, "distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)", _g_file_id)
|
||||
cid: int = mlirlite.module_add_di_node(mod, pool, cbuf)
|
||||
if cid < 0:
|
||||
VLogger.error("--traceback: failed to allocate DICompileUnit metadata node, aborting compilation", "DWARF")
|
||||
return 1
|
||||
_g_cu_id = cid
|
||||
return 0
|
||||
|
||||
|
||||
# _LocRefAt - read a cached DILocation ref string pointer.
|
||||
def _LocRefAt(slot: int) -> str:
|
||||
slotp: str* = (str*)(uint64(_g_loc_ref) + sizet(slot) * 8)
|
||||
r: str = slotp[0]
|
||||
return r
|
||||
|
||||
|
||||
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once.
|
||||
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
|
||||
if line <= 0: return _g_cur_entry_ref
|
||||
c: int = col
|
||||
if c <= 0: c = 1
|
||||
if _g_loc_used is None or _g_mod is None: return None
|
||||
h: uint64 = (uint64(sp) * 1000003 + uint64(line) * 31 + uint64(c)) & uint64(DBG_LOC_SLOTS - 1)
|
||||
pi: int = 0
|
||||
while pi < DBG_LOC_PROBE:
|
||||
slot: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
|
||||
if _g_loc_used[slot] == 0: break
|
||||
kbase: int = slot * DBG_LOC_KEY_STRIDE
|
||||
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line and int(_g_loc_key[kbase + 2]) == c: return _LocRefAt(slot)
|
||||
pi += 1
|
||||
lbuf: str = pool.alloc(96)
|
||||
if lbuf is None: return None
|
||||
viperlib.snprintf(lbuf, 96, "!DILocation(line: %d, column: %d, scope: !%d)", line, c, sp)
|
||||
lid: int = mlirlite.module_add_di_node(_g_mod, pool, lbuf)
|
||||
if lid < 0:
|
||||
VLogger.error("--traceback: failed to allocate DILocation metadata node, aborting compilation", "DWARF")
|
||||
return None
|
||||
lref: str = _RefOf(pool, lid)
|
||||
if lref is None:
|
||||
VLogger.error("--traceback: failed to allocate DILocation reference buffer, aborting compilation", "DWARF")
|
||||
return None
|
||||
if pi < DBG_LOC_PROBE:
|
||||
slot2: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
|
||||
kbase2: int = slot2 * DBG_LOC_KEY_STRIDE
|
||||
_g_loc_key[kbase2] = sizet(sp)
|
||||
_g_loc_key[kbase2 + 1] = sizet(line)
|
||||
_g_loc_key[kbase2 + 2] = sizet(c)
|
||||
rslot: str* = (str*)(uint64(_g_loc_ref) + sizet(slot2) * 8)
|
||||
rslot[0] = lref
|
||||
_g_loc_used[slot2] = 1
|
||||
return lref
|
||||
|
||||
|
||||
# PushFunction - create the DISubprogram for one define.
|
||||
def PushFunction(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, func: mlirlite.Function*, line: int, col: int) -> int:
|
||||
"""Attach a DISubprogram to one define and set the builder's entry DILocation."""
|
||||
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||
if _g_enabled == 0: return 0
|
||||
if pool is None or builder is None or func is None or _g_mod is None: return 0
|
||||
if _g_depth >= DBG_STACK_MAX:
|
||||
VLogger.error("--traceback: DWARF function nesting depth exceeds the limit, aborting compilation", "DWARF")
|
||||
return 0
|
||||
if _EnsureModuleNodes(pool) != 0: return 0
|
||||
name: str = mlirlite.function_get_name(func)
|
||||
if name is None:
|
||||
VLogger.error("--traceback: function name is empty, cannot emit DISubprogram, aborting compilation", "DWARF")
|
||||
return 0
|
||||
dline: int = line
|
||||
if dline <= 0: dline = 1
|
||||
dcol: int = col
|
||||
if dcol <= 0: dcol = 1
|
||||
ssize: sizet = string.strlen(name) + 320
|
||||
spbuf: str = pool.alloc(ssize)
|
||||
if spbuf is None:
|
||||
VLogger.error("--traceback: failed to allocate DISubprogram buffer, aborting compilation", "DWARF")
|
||||
return 0
|
||||
viperlib.snprintf(spbuf, ssize, "distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)", name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
|
||||
sp_id: int = mlirlite.module_add_di_node(_g_mod, pool, spbuf)
|
||||
if sp_id < 0:
|
||||
VLogger.error("--traceback: failed to allocate DISubprogram metadata node, aborting compilation", "DWARF")
|
||||
return 0
|
||||
sp_ref: str = _RefOf(pool, sp_id)
|
||||
if sp_ref is None:
|
||||
VLogger.error("--traceback: failed to allocate DISubprogram reference buffer, aborting compilation", "DWARF")
|
||||
return 0
|
||||
mlirlite.function_set_dbg(func, sp_ref)
|
||||
# save the enclosing function's DI context (LIFO), then switch to this one
|
||||
_g_stk_sp[_g_depth] = sizet(_g_cur_sp + 1)
|
||||
rslot: str* = (str*)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
||||
rslot[0] = _g_cur_entry_ref
|
||||
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
||||
bslot[0] = _g_cur_builder
|
||||
_g_depth += 1
|
||||
_g_cur_sp = sp_id
|
||||
_g_cur_builder = builder
|
||||
entry_ref: str = _EnsureLocation(pool, sp_id, dline, dcol)
|
||||
if entry_ref is None:
|
||||
VLogger.error("--traceback: failed to generate entry DILocation, aborting compilation", "DWARF")
|
||||
return 0
|
||||
_g_cur_entry_ref = entry_ref
|
||||
mlirlite.builder_set_debug_location(builder, entry_ref)
|
||||
# Enter this define: tell mlirlite's call-site recorder the current symbol/file/def-line and write an F record (for the .vpcall file + WriteStackMap entry fallback line).
|
||||
mlirlite.DebugNoteFunc(name, _g_abs_file_path, dline)
|
||||
return 1
|
||||
|
||||
|
||||
# PopFunction - restore the enclosing function's DI context.
|
||||
def PopFunction(builder: mlirlite.IRBuilder*) -> int:
|
||||
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||
if _g_enabled == 0: return 0
|
||||
if _g_depth <= 0: return 0
|
||||
_g_depth -= 1
|
||||
_g_cur_sp = int(_g_stk_sp[_g_depth]) - 1
|
||||
rslot: str* = (str*)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
||||
_g_cur_entry_ref = rslot[0]
|
||||
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
||||
_g_cur_builder = (mlirlite.IRBuilder*)(bslot[0])
|
||||
if builder is not None: mlirlite.builder_clear_debug_location(builder)
|
||||
# Leave this define: restore mlirlite's outer function context (for nested defines).
|
||||
mlirlite.DebugClearFunc()
|
||||
return 0
|
||||
|
||||
|
||||
# SetStatement - point the current builder at a statement DILocation.
|
||||
def SetStatement(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, line: int, col: int) -> int:
|
||||
"""Update the builder's DILocation to the current statement (line precision)."""
|
||||
if _g_enabled == 0 or pool is None or builder is None: return 0
|
||||
# Record the current statement line: C records from build_call in this statement use it as the call-site line.
|
||||
mlirlite.DebugNoteLine(line)
|
||||
if _g_cur_sp < 0: return 0
|
||||
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
|
||||
if ref is None: return 0
|
||||
mlirlite.builder_set_debug_location(builder, ref)
|
||||
return 1
|
||||
|
||||
|
||||
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
|
||||
def _ModuleHasDefine(mod: mlirlite.LLVMModule*) -> int:
|
||||
if mod is None: return 0
|
||||
cur: mlirlite.Function* = mod.FuncHead
|
||||
steps: int = 0
|
||||
while cur is not None:
|
||||
steps += 1
|
||||
if steps > 100000: break
|
||||
if mlirlite.function_is_declared(cur) == 0: return 1
|
||||
cur = mlirlite.function_get_next(cur)
|
||||
return 0
|
||||
|
||||
|
||||
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir.
|
||||
def Finalize(pool: memhub.MemBuddy*) -> int:
|
||||
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
|
||||
global _g_finalized
|
||||
if _g_enabled == 0: return 0
|
||||
if _g_finalized != 0: return 0
|
||||
mod: mlirlite.LLVMModule* = _g_mod
|
||||
if mod is None or pool is None: return 0
|
||||
_g_finalized = 1
|
||||
# Self-check (mirrors TPC Phase2Translator.py:3444): a module that emits a
|
||||
# define but carries no DI at all loses line info for its frames.
|
||||
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0: VLogger.warning("--traceback: this module emits a define but its IR carries no DWARF (!llvm.dbg.cu); its frames will show only addresses in the Ctrl+C stack", "DWARF")
|
||||
if _g_cu_id < 0: return 0
|
||||
cu_ref: str = _RefOf(pool, _g_cu_id)
|
||||
if cu_ref is None:
|
||||
VLogger.error("--traceback: failed to allocate DICompileUnit reference buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
mlirlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
|
||||
fbuf: str = pool.alloc(64)
|
||||
if fbuf is None:
|
||||
VLogger.error("--traceback: failed to allocate module-level debug flags buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
viperlib.snprintf(fbuf, 64, "!{i32 2, !\"Debug Info Version\", i32 3}")
|
||||
flags_id: int = mlirlite.module_add_di_node(mod, pool, fbuf)
|
||||
if flags_id < 0:
|
||||
VLogger.error("--traceback: failed to allocate llvm.module.flags metadata node, aborting compilation", "DWARF")
|
||||
return 1
|
||||
fl_ref: str = _RefOf(pool, flags_id)
|
||||
if fl_ref is None:
|
||||
VLogger.error("--traceback: failed to allocate llvm.module.flags reference buffer, aborting compilation", "DWARF")
|
||||
return 1
|
||||
mlirlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
|
||||
return 0
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,774 @@
|
||||
import lib.core.Gen.Generator as Generator
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
|
||||
|
||||
# Pointer-to-pointer cast reason codes (coerce_to_type ptr -> ptr branch).
|
||||
# PC_OK allowed; PC_DEPTH depth mismatch; PC_NONBASIC non-basic pointee; PC_LOSSY lossy.
|
||||
define PC_OK = 0
|
||||
define PC_DEPTH = 1
|
||||
define PC_NONBASIC = 2
|
||||
define PC_LOSSY = 3
|
||||
|
||||
|
||||
# ExprGenBase - expression basics split out of ExprGenMixin (stage D):
|
||||
# type checks / pointer level / coerce.
|
||||
novirtual class ExprGenBase:
|
||||
"""Expression basics: type checks / pointer levels / coercion."""
|
||||
|
||||
# --- docstring registry fields (__doc__ magic attribute support) ---
|
||||
Gen: Generator.Generator*
|
||||
DocKeys: bytes
|
||||
DocValues: bytes
|
||||
DocCount: int
|
||||
|
||||
# --- OOM diag: cross-module CDefine lookup counter (suspected CONST_INT loop) ---
|
||||
DiagCDefineCount: int
|
||||
# --- OOM diag: translate_name_value call counter (source of 14593 lookups) ---
|
||||
DiagNameValueCount: int
|
||||
# --- OOM diag: translate_value call counter (locates CONST_INT call loop) ---
|
||||
DiagTranslateValueCount: int
|
||||
# P3 migrated methods (bodies from HandlesExpr.vp module functions;
|
||||
# self. for local calls, module prefix for external ones).
|
||||
|
||||
# Extract the function name from an AST node.
|
||||
def get_func_name(self, func_node: ast.AST*) -> str:
|
||||
"""Extract the function name from an AST node."""
|
||||
if func_node is None: return None
|
||||
k: int = func_node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(func_node)
|
||||
return nm.id
|
||||
elif k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(func_node)
|
||||
return at.attr
|
||||
return None
|
||||
# Escape an LLVM IR string literal.
|
||||
def escape_llvm_string(self, pool: memhub.MemBuddy*, src: str) -> str:
|
||||
"""Convert a string into an LLVM IR string literal."""
|
||||
if src is None: return None
|
||||
slen: sizet = string.strlen(src)
|
||||
alloc_size: sizet = 4 * slen + 8
|
||||
buf: str = pool.alloc(alloc_size)
|
||||
if buf is None: return None
|
||||
pos: sizet = 0
|
||||
buf[pos] = 'c'
|
||||
pos += 1
|
||||
buf[pos] = '"'
|
||||
pos += 1
|
||||
for i in range(int(slen)):
|
||||
ch: char = src[i]
|
||||
if ch == '\0': break
|
||||
if ch == '\"' or ch == '\\' or ch < 0x20:
|
||||
buf[pos] = '\\'
|
||||
pos += 1
|
||||
hi: int = (ch >> 4) & 0xF
|
||||
lo: int = ch & 0xF
|
||||
buf[pos] = Utils.nibble_to_hex_char(hi, 1)
|
||||
pos += 1
|
||||
buf[pos] = Utils.nibble_to_hex_char(lo, 1)
|
||||
pos += 1
|
||||
else:
|
||||
buf[pos] = ch
|
||||
pos += 1
|
||||
buf[pos] = '\\'
|
||||
pos += 1
|
||||
buf[pos] = '0'
|
||||
pos += 1
|
||||
buf[pos] = '0'
|
||||
pos += 1
|
||||
buf[pos] = '"'
|
||||
pos += 1
|
||||
buf[pos] = '\0'
|
||||
return buf
|
||||
# Get the bit width of an LLVM integer type.
|
||||
# Delegates to TSpec.SpecTypeBits (single source; avoids diverging duplicates).
|
||||
def get_llvm_type_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Bit width of an LLVM integer type; 0 for non-integers (delegates to SpecTypeBits)."""
|
||||
return TSpec.SpecTypeBits(ty)
|
||||
# Get the bit width of an LLVM floating-point type.
|
||||
def get_llvm_float_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Bit width of an LLVM float type; 0 for non-floats."""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Float(bits):
|
||||
return bits
|
||||
case _:
|
||||
return 0
|
||||
# is_ptr_type - check whether a type is Ptr (1=yes, 0=no);
|
||||
# a widely used type-check helper (50+ call sites).
|
||||
def is_ptr_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Check whether ty is a Ptr type; returns 1=yes / 0=no."""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee):
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
# get_ptr_level - compute pointer level (0=non-ptr, 1/2/3+=ptr depth);
|
||||
# used by comparison checks to allow matching levels and reject mixed ones.
|
||||
def get_ptr_level(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Compute pointer level (0=non-ptr, 1=ptr, 2=ptr-to-ptr); delegates to TSpec.SpecTypePtrLevel."""
|
||||
return TSpec.SpecTypePtrLevel(ty)
|
||||
# _const_fits_int_type - 编译期整型常量的值能否**无损**落进目标整型位宽。
|
||||
# 有符号或无符号任一解释放得下即算「放得下」(位型不变);用于二元运算按常量适配另一侧位宽。
|
||||
def _const_fits_int_type(self, val: mlirlite.Value*, target_ty: mlirlite.LLVMType*) -> int:
|
||||
"""1 = the integer constant's value fits the target integer width losslessly; else 0."""
|
||||
if val is None or target_ty is None or val.IsConst != 1: return 0
|
||||
bits: int = self.get_llvm_type_bits(target_ty)
|
||||
if bits <= 0 or bits > 63: return 0
|
||||
v: int64 = val.IntVal
|
||||
lo: int64 = -(int64(1) << (bits - 1))
|
||||
hi: int64 = (int64(1) << (bits - 1)) - 1
|
||||
if v >= lo and v <= hi: return 1
|
||||
if v >= 0:
|
||||
if bits == 63: return 1
|
||||
if v <= (int64(1) << bits) - 1: return 1
|
||||
return 0
|
||||
# _int_type_of_bits - 合成指定位宽的 LLVM 整型(1/8/16/32/64 快路径,其余按 LLVMType.Int 合成)。
|
||||
def _int_type_of_bits(self, pool: memhub.MemBuddy*, bits: int) -> mlirlite.LLVMType*:
|
||||
"""LLVM integer type of the given bit width."""
|
||||
if bits == 1: return mlirlite.Int1(pool)
|
||||
if bits == 8: return mlirlite.Int8(pool)
|
||||
if bits == 16: return mlirlite.Int16(pool)
|
||||
if bits == 32: return mlirlite.Int32(pool)
|
||||
if bits == 64: return mlirlite.Int64(pool)
|
||||
ty: mlirlite.LLVMType* = pool.alloc(mlirlite.LLVMType.__sizeof__())
|
||||
if ty is None: return None
|
||||
string.memset(ty, 0, mlirlite.LLVMType.__sizeof__())
|
||||
*ty = *mlirlite.LLVMType.Int(bits)
|
||||
return ty
|
||||
# apply_byte_order_bswap - byte-order bswap primitive (TPC parity: BaseGen._apply_bswap_if_big,
|
||||
# extended to judge BOTH markers against the target's native order).
|
||||
# Swapping is needed only when the declared encoding differs from the target's native order,
|
||||
# so: BigEndian field on a little-endian target -> swap; LittleEndian field on a big-endian
|
||||
# target -> swap; declared order == target order -> no swap. byte_order==0 (unspecified)
|
||||
# means native and is always zero-cost: no IR is emitted at all. 16/32/64-bit integers only,
|
||||
# matching TPC (i8/floats/pointers/aggregates are never swapped).
|
||||
# Call name: bare "llvm.bswap" (no width suffix). build_call emits `call i32 @llvm.bswap(i32 %x)`;
|
||||
# __function.vp:2607 detects a bare `@llvm.*` (non-`llvm.mlir.`) callee and renders it as
|
||||
# `llvm.call_intrinsic "llvm.bswap"(...)`, and mlir-translate mangles the name from the operand
|
||||
# type to `llvm.bswap.i32` (README.md:367-378, the same path used by CallGenVa.vp:123 `llvm.va_start`
|
||||
# -> `llvm.va_start.p0`). Passing `llvm.bswap.i32` explicitly is unnecessary and would break the
|
||||
# uniform mangle path, so the un-suffixed name is used.
|
||||
def apply_byte_order_bswap(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, byte_order: int) -> mlirlite.Value*:
|
||||
"""Return llvm.bswap(val) when val's declared byte_order differs from the target order; else val."""
|
||||
if byte_order == 0: return val
|
||||
# 1=BigEndian, 2=LittleEndian; compare against the target's native order.
|
||||
want_big: int = 0
|
||||
if byte_order == 1: want_big = 1
|
||||
if want_big == self.Gen.TypeGenResolve.target_is_big_endian(): return val
|
||||
if val is None or val.Ty is None: return val
|
||||
bits: int = self.get_llvm_type_bits(val.Ty)
|
||||
if bits != 16 and bits != 32 and bits != 64: return val
|
||||
# Same-type in-and-out (iN -> iN); ret_ty=val.Ty keeps the call well-typed before mangling.
|
||||
swapped: mlirlite.Value* = mlirlite.build_call(builder, "llvm.bswap", val, 1, val.Ty, 0)
|
||||
if swapped is None: return val
|
||||
return swapped
|
||||
# _is_zero_int_literal - is the AST node the integer literal 0?
|
||||
# Used for ptr != 0 / ptr == 0, where 0 means NULL and is allowed.
|
||||
def _is_zero_int_literal(self, node: ast.AST*) -> int:
|
||||
"""Is the node the integer literal 0; returns 1=yes / 0=no."""
|
||||
if node is None: return 0
|
||||
if node.kind() != ast.ASTKind.Constant: return 0
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn is None or cn.const_kind != ast.CONST_INT: return 0
|
||||
if cn.int_val == 0: return 1
|
||||
return 0
|
||||
# is_array_type - check whether a type is Array (1=yes, 0=no).
|
||||
def is_array_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Check whether ty is an Array type; returns 1=yes / 0=no."""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Array(_, _):
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
# _get_custom_struct_cls_nm - class name if ty (or Ptr(ty)) is a registered struct.
|
||||
# Used by subscript to decide __getitem__/__setitem__ forwarding vs raw GEP.
|
||||
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> str:
|
||||
"""Class name if ty or its pointee is a registered struct; else None."""
|
||||
if ty is None: return None
|
||||
# Check whether ty itself is a registered struct.
|
||||
cls_nm_direct: str = self.Gen.StructGen.get_class_name_by_type(pool, ty)
|
||||
if cls_nm_direct is not None: return cls_nm_direct
|
||||
# If ty is Ptr, check the pointee.
|
||||
if self.is_ptr_type(ty) != 0:
|
||||
pointee_cs: mlirlite.LLVMType* = ty.Pointee
|
||||
if pointee_cs is not None: return self.Gen.StructGen.get_class_name_by_type(pool, pointee_cs)
|
||||
return None
|
||||
# _is_list_subscript_base - is the subscript base a list[T] variable?
|
||||
# Wrong routing would treat list[T] as a struct array GEP and crash.
|
||||
def _is_list_subscript_base(self, pool: memhub.MemBuddy*, trans: HT.Translator*, sub_node: ast.AST*) -> int:
|
||||
"""Is the subscript base a list[T] variable; 1=yes / 0=no."""
|
||||
if sub_node is None or trans is None: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm.id is None: return 0
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is None or alloca.Ty is None: return 0
|
||||
if self.is_ptr_type(alloca.Ty) == 0: return 0
|
||||
pointee: mlirlite.LLVMType* = alloca.Ty.Pointee
|
||||
if pointee is None: return 0
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Ptr(inner_ty):
|
||||
lsn_ls: str = None
|
||||
match inner_ty:
|
||||
case mlirlite.LLVMType.Struct(_, _, lsn3):
|
||||
lsn_ls = lsn3
|
||||
case _:
|
||||
pass
|
||||
if lsn_ls is not None and string.strstr(lsn_ls, "list[") is not None: return 1
|
||||
case _:
|
||||
pass
|
||||
return 0
|
||||
# _sub_base_class_name - resolve the struct class name of a subscript base.
|
||||
def _sub_base_class_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, sub_node: ast.AST*) -> str:
|
||||
"""Resolve the struct class name of a subscript base; None if absent."""
|
||||
if sub_node is None or trans is None: return None
|
||||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return None
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm.id is None: return None
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is None or alloca.Ty is None: return None
|
||||
if self.is_ptr_type(alloca.Ty) == 0: return None
|
||||
pointee: mlirlite.LLVMType* = alloca.Ty.Pointee
|
||||
if pointee is None: return None
|
||||
if self.is_ptr_type(pointee) != 0:
|
||||
inner: mlirlite.LLVMType* = pointee.Pointee
|
||||
if inner is not None: return self.Gen.StructGen.get_class_name_by_type(pool, inner)
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||||
# _sub_base_uses_getitem - should the subscript base use __getitem__ semantics?
|
||||
# list[T] / classes defining __getitem__ yes; plain struct-ptr arrays use GEP.
|
||||
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, sub_node: ast.AST*) -> int:
|
||||
"""Returns 1 if the subscript base uses __getitem__ semantics, else 0 (GEP address)."""
|
||||
if self._is_list_subscript_base(pool, trans, sub_node) != 0: return 1
|
||||
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
|
||||
if cls_ugi is None: return 0
|
||||
lookup_ugi: str = pool.alloc(128)
|
||||
if lookup_ugi is None: return 0
|
||||
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_ugi) is not None: return 1
|
||||
return 0
|
||||
# _deref_if_ptr_ptr - if obj_ptr is Ptr(Ptr(...)), load one level to deref.
|
||||
# Supports attribute access on X* variables while leaving value vars alone.
|
||||
def _deref_if_ptr_ptr(self, builder: mlirlite.IRBuilder*, obj_ptr: mlirlite.Value*) -> mlirlite.Value*:
|
||||
"""If obj_ptr is Ptr(Ptr(...)), load once to obtain the inner pointer."""
|
||||
if obj_ptr is None or obj_ptr.Ty is None: return obj_ptr
|
||||
if self.is_ptr_type(obj_ptr.Ty) == 0: return obj_ptr
|
||||
pointee: mlirlite.LLVMType* = obj_ptr.Ty.Pointee
|
||||
if pointee is None: return obj_ptr
|
||||
if self.is_ptr_type(pointee) == 0: return obj_ptr
|
||||
# obj_ptr is Ptr(Ptr(...)); load to dereference.
|
||||
loaded: mlirlite.Value* = mlirlite.build_load(builder, pointee, obj_ptr)
|
||||
if loaded is None: return obj_ptr
|
||||
return loaded
|
||||
# _value_node_of - drill an outer statement node down to its value sub-expression.
|
||||
# Fixes error locations pointing at the Assign node instead of the real value.
|
||||
def _value_node_of(self, node: ast.AST*) -> ast.AST*:
|
||||
"""Drill a statement node down to its value sub-expression; returns node if none."""
|
||||
if node is None: return node
|
||||
k: int = node.kind()
|
||||
v: ast.AST* = None
|
||||
if k == ast.ASTKind.Assign: v = (ast.Assign*(node)).value
|
||||
elif k == ast.ASTKind.AnnAssign: v = (ast.AnnAssign*(node)).value
|
||||
elif k == ast.ASTKind.AugAssign: v = (ast.AugAssign*(node)).value
|
||||
elif k == ast.ASTKind.Expr: v = (ast.Expr*(node)).value
|
||||
elif k == ast.ASTKind.Return: v = (ast.Return*(node)).value
|
||||
if v is None: return node
|
||||
return v
|
||||
# IsNoneValue - is val/node the None (empty) constant? 1=yes / 0=no.
|
||||
# None carries no type info, so it may be implicitly converted to any target.
|
||||
def IsNoneValue(self, val: mlirlite.Value*, node: ast.AST* = None) -> int:
|
||||
"""Is val/node the None constant; returns 1=yes / 0=no."""
|
||||
# Criterion 1: the AST node is a None literal.
|
||||
if node is not None and node.kind() == ast.ASTKind.Constant:
|
||||
cn_nv: ast.Constant* = ast.Constant*(node)
|
||||
if cn_nv is not None and cn_nv.const_kind == ast.CONST_NONE: return 1
|
||||
# Criterion 2: an already-built null constant (ptr type + compile-time zero).
|
||||
if val is not None and val.IsConst == 1 and val.IntVal == 0 and self.is_ptr_type(val.Ty) != 0: return 1
|
||||
return 0
|
||||
# _NoneValueAsTarget - materialize None as the target type's zero/empty value.
|
||||
# Used only when coerce_to_type has detected the source is None.
|
||||
def _NoneValueAsTarget(self, builder: mlirlite.IRBuilder*, target_ty: mlirlite.LLVMType*) -> mlirlite.Value*:
|
||||
"""Materialize None as the target type's zero/empty value."""
|
||||
if target_ty is None: return None
|
||||
pool: memhub.MemBuddy* = builder.Pool
|
||||
t_bits: int = self.get_llvm_type_bits(target_ty)
|
||||
if t_bits != 0: return mlirlite.ConstInt(pool, target_ty, 0, "0")
|
||||
t_fbits: int = self.get_llvm_float_bits(target_ty)
|
||||
if t_fbits != 0: return mlirlite.ConstFloat(pool, target_ty, 0.0)
|
||||
return mlirlite.ConstZero(pool, target_ty)
|
||||
# _is_universal_ptr_ty - is ty a universal pointer (t.CPtr / i8* = void*)?
|
||||
# Rule 1: universal pointers are wildcards, freely interconvertible with any T*.
|
||||
def _is_universal_ptr_ty(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Is ty a universal pointer (i8* / t.CPtr); 1=yes / 0=no."""
|
||||
if ty is None or self.is_ptr_type(ty) == 0: return 0
|
||||
if self.get_ptr_level(ty) != 1: return 0
|
||||
pointee: mlirlite.LLVMType* = ty.Pointee
|
||||
if pointee is None: return 1
|
||||
if self.get_llvm_type_bits(pointee) == 8: return 1
|
||||
return 0
|
||||
# "str / bytes are not universal pointers" - rule and unified fail-fast.
|
||||
# They share i8* in IR but are not wildcards, so the annotation source decides.
|
||||
def _annot_name_is_bare_str(self, txt: str) -> int:
|
||||
"""Is the annotation text a bare str / bytes; 1=yes / 0=no."""
|
||||
if txt is None or txt[0] == 0: return 0
|
||||
if string.strcmp(txt, "str") == 0 or string.strcmp(txt, "bytes") == 0: return 1
|
||||
return 0
|
||||
def _annot_is_bare_str(self, annot: ast.AST*) -> int:
|
||||
"""Is the annotation AST a bare str / bytes Name; 1=yes / 0=no."""
|
||||
if annot is None or annot.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm is None or nm.id is None: return 0
|
||||
return self._annot_name_is_bare_str(nm.id)
|
||||
def _str_rejects_ptr_kind(self, val: mlirlite.Value*) -> int:
|
||||
"""Is val an aggregate pointer that str/bytes must reject (1/2/3=yes, 0=no); char arrays exempt."""
|
||||
if val is None or val.Ty is None: return 0
|
||||
if self.is_ptr_type(val.Ty) == 0: return 0
|
||||
pointee: mlirlite.LLVMType* = val.Ty.Pointee
|
||||
if pointee is None: return 0
|
||||
pk: int = pointee.kind()
|
||||
if pk == mlirlite.LLVMType.Struct: return 1
|
||||
if pk == mlirlite.LLVMType.Func: return 3
|
||||
if pk == mlirlite.LLVMType.Array:
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Array(elem, _):
|
||||
if self.get_llvm_type_bits(elem) == 8: return 0
|
||||
case _:
|
||||
pass
|
||||
return 2
|
||||
return 0
|
||||
def fail_if_str_annot_agg_ptr(self, val: mlirlite.Value*, annot_is_str: int, node: ast.AST* = None) -> int:
|
||||
"""Fail fast (return 1) when a bare str/bytes annotation holds an aggregate pointer."""
|
||||
if annot_is_str == 0 or val is None or val.Ty is None: return 0
|
||||
if self._str_rejects_ptr_kind(val) == 0: return 0
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "str/bytes does not accept a pointer to a non-basic type (struct/class/non-char array/function): both are" "i8* at the IR level, but with different semantics (str/bytes is not a general pointer wildcard). If a pointer must be passed, " "use a t.CPtr/rawptr parameter instead, or cast explicitly (e.g. t.CPtr(p))", "CastError")
|
||||
return 1
|
||||
def fail_if_str_annot_ast_agg_ptr(self, val: mlirlite.Value*, annot: ast.AST*, node: ast.AST* = None) -> int:
|
||||
"""AST-annotation entry: fail fast when a bare str/bytes annotation holds an aggregate pointer."""
|
||||
return self.fail_if_str_annot_agg_ptr(val, self._annot_is_bare_str(annot), node)
|
||||
# _is_basic_scalar_ty - is ty a basic scalar (int/float)? 1=yes / 0=no.
|
||||
# Non-basic types (struct/array/func) may not take part in implicit ptr casts.
|
||||
def _is_basic_scalar_ty(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Is ty a basic scalar (int/float); 1=yes / 0=no."""
|
||||
if ty is None: return 0
|
||||
if self.get_llvm_type_bits(ty) != 0: return 1
|
||||
if self.get_llvm_float_bits(ty) != 0: return 1
|
||||
return 0
|
||||
# _scalar_convert_reason - is the basic scalar conversion T -> U lossless?
|
||||
# Returns PC_OK (lossless/allowed) or PC_LOSSY (lossy/forbidden).
|
||||
def _scalar_convert_reason(self, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""Implicit basic-scalar conversion code src_pt -> dst_pt (PC_OK / PC_LOSSY)."""
|
||||
s_int: int = self.get_llvm_type_bits(src_pt)
|
||||
d_int: int = self.get_llvm_type_bits(dst_pt)
|
||||
s_flt: int = self.get_llvm_float_bits(src_pt)
|
||||
d_flt: int = self.get_llvm_float_bits(dst_pt)
|
||||
# int <-> int: widening is lossless; narrowing (high->low) is lossy -> forbidden.
|
||||
if s_int != 0 and d_int != 0:
|
||||
if d_int >= s_int: return PC_OK
|
||||
return PC_LOSSY
|
||||
# float <-> float: allowed.
|
||||
if s_flt != 0 and d_flt != 0: return PC_OK
|
||||
# int -> float: lossless only when float width >= int width.
|
||||
if s_int != 0 and d_flt != 0:
|
||||
if d_flt >= s_int: return PC_OK
|
||||
return PC_LOSSY
|
||||
# float -> int: lossy, forbidden.
|
||||
if s_flt != 0 and d_int != 0: return PC_LOSSY
|
||||
return PC_OK
|
||||
# _class_name_of_ptr_pointee - map a pointer's pointee type to a registered class name.
|
||||
# Reuses StructGen.get_class_name_by_type; returns None for non-struct/unregistered.
|
||||
def _class_name_of_ptr_pointee(self, pool: memhub.MemBuddy*, pt: mlirlite.LLVMType*) -> str:
|
||||
"""Map a pointer's pointee type to a registered class name; None on failure."""
|
||||
if pt is None: return None
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, pt)
|
||||
# _is_inheritance_upcast - Derived* -> Base* (incl. multi-level) exemption of rule 2.
|
||||
# Allowed when the target class appears after the source in its MRO chain.
|
||||
def _is_inheritance_upcast(self, pool: memhub.MemBuddy*, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""Derived* -> Base* upcast (incl. multi-level); 1=allowed / 0=no."""
|
||||
src_name: str = self._class_name_of_ptr_pointee(pool, src_pt)
|
||||
if src_name is None: return 0
|
||||
dst_name: str = self._class_name_of_ptr_pointee(pool, dst_pt)
|
||||
if dst_name is None: return 0
|
||||
if string.strcmp(src_name, dst_name) == 0: return 0
|
||||
chain: sizet* = self.Gen.StructGen.get_mro_chain(pool, src_name)
|
||||
if chain is None: return 0
|
||||
# chain[0] = self; ancestors follow, terminated by 0. Only look after self.
|
||||
i: int = 1
|
||||
while chain[i] != 0:
|
||||
anc: str = rawptr(chain[i])
|
||||
if anc is not None and string.strcmp(anc, dst_name) == 0: return 1
|
||||
i += 1
|
||||
return 0
|
||||
# _is_array_decay - [N]T* -> T* one-way array decay exemption of rule 2.
|
||||
# Matches when the source pointee is a fixed array and the target is its element.
|
||||
def _is_array_decay(self, pool: memhub.MemBuddy*, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""Source pointee is a fixed array and target is its element type; 1=yes / 0=no."""
|
||||
if src_pt is None or dst_pt is None: return 0
|
||||
if self.is_array_type(src_pt) == 0: return 0
|
||||
elem: mlirlite.LLVMType* = None
|
||||
match src_pt:
|
||||
case mlirlite.LLVMType.Array(et, _):
|
||||
elem = et
|
||||
case _:
|
||||
return 0
|
||||
if elem is None: return 0
|
||||
return self.Gen.AssignGen._stg_ty_equal(pool, elem, dst_pt)
|
||||
# _pointee_convert_reason - reason code for pointee T -> U (the T->U of T* -> U*).
|
||||
# Applies rule 2 with the inheritance-upcast and array-decay exemptions.
|
||||
def _pointee_convert_reason(self, pool: memhub.MemBuddy*, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""Conversion code for pointee src_pt -> dst_pt (PC_OK/PC_NONBASIC/PC_LOSSY/PC_DEPTH)."""
|
||||
if src_pt is None or dst_pt is None: return PC_OK
|
||||
s_is_ptr: int = self.is_ptr_type(src_pt)
|
||||
d_is_ptr: int = self.is_ptr_type(dst_pt)
|
||||
if s_is_ptr != 0 or d_is_ptr != 0:
|
||||
# One side ptr, other not (e.g. Foo vs Foo*): non-basic swap, forbidden.
|
||||
if s_is_ptr == 0 or d_is_ptr == 0: return PC_NONBASIC
|
||||
# Both sides are pointers: recurse.
|
||||
return self._ptr_cast_reason(pool, src_pt, dst_pt)
|
||||
# One side is non-basic (struct/class/union/array/func): generally forbidden.
|
||||
if self._is_basic_scalar_ty(src_pt) == 0 or self._is_basic_scalar_ty(dst_pt) == 0:
|
||||
# Exemption 1: Derived* -> Base* inheritance upcast (incl. multi-level).
|
||||
if self._is_inheritance_upcast(pool, src_pt, dst_pt) != 0: return PC_OK
|
||||
# Exemption 2: one-way array decay [N]T* -> T* (source pointee is a
|
||||
# fixed array, target pointee is its element type).
|
||||
if self._is_array_decay(pool, src_pt, dst_pt) != 0: return PC_OK
|
||||
return PC_NONBASIC
|
||||
return self._scalar_convert_reason(src_pt, dst_pt)
|
||||
# _ptr_cast_reason - reason code for implicit ptr -> ptr casts (single source of truth).
|
||||
# Rules 1-4: wildcard ptr, lossless pointee, non-basic forbidden, identity allowed.
|
||||
def _ptr_cast_reason(self, pool: memhub.MemBuddy*, src_ty: mlirlite.LLVMType*, dst_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Reason code for implicit ptr -> ptr casts (PC_OK means allowed)."""
|
||||
if src_ty is None or dst_ty is None: return PC_OK
|
||||
slev: int = self.get_ptr_level(src_ty)
|
||||
dlev: int = self.get_ptr_level(dst_ty)
|
||||
if slev == 0 or dlev == 0: return PC_OK
|
||||
# 豁免 0(必须放在级数检查**之前**):固定数组退让 `[N]T* -> T*`(C 语义:数组名退让成
|
||||
# 首元素指针)。元素是 i8 时级数不变(1→1,恰好由"全类型指针通配"放行),但元素本身
|
||||
# 是指针时级数会 +1(`[N]str` -> `str*` 即 `Ptr([N]i8*) -> Ptr(i8*)`:1→2),
|
||||
# 级数检查会先判成 PC_DEPTH,使下面 _pointee_convert_reason 里那条既有豁免永远够不到
|
||||
# (死代码)。现场:includes/tomllib `_parse_key_path(s, segs: [N]str, cap)` —— 形参按
|
||||
# C 语义退让成 `str*`(FuncGenDef 的 Array 形参退让),实参仍是 `[N]str` 的数组地址。
|
||||
if self._is_array_decay(pool, src_ty.Pointee, dst_ty.Pointee) != 0: return PC_OK
|
||||
# Different depth: forbidden regardless of pointee.
|
||||
if slev != dlev: return PC_DEPTH
|
||||
# Rule 4: identical types (same depth and pointee) are not a conversion.
|
||||
if self.Gen.AssignGen._stg_ty_equal(pool, src_ty, dst_ty) != 0: return PC_OK
|
||||
# Rule 1: universal pointers are wildcards (i8* <-> any T*).
|
||||
if self._is_universal_ptr_ty(src_ty) != 0 or self._is_universal_ptr_ty(dst_ty) != 0: return PC_OK
|
||||
# Rules 2/3: compare pointee level by level.
|
||||
return self._pointee_convert_reason(pool, src_ty.Pointee, dst_ty.Pointee)
|
||||
# _type_text - readable LLVM type text via TypePrint ("?" on failure).
|
||||
def _type_text(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> str:
|
||||
"""Readable LLVM type text via TypePrint; "?" on failure."""
|
||||
if ty is None: return "?"
|
||||
buf: str = pool.alloc_tagged(4096, memhub.DETSITE_EXPRGENBASE_415)
|
||||
if buf is None: return "?"
|
||||
mlirlite.TypePrint(buf, 4096, ty, pool)
|
||||
if buf[0] == 0: return "?"
|
||||
return buf
|
||||
# _ptr_cast_reason_text - human-readable text for a forbidden ptr -> ptr cast.
|
||||
# Names the level and why, plus source/target types; result is heap-copied.
|
||||
# 注意:LLVM 不透明指针(!llvm.ptr)打印出来两边一模一样,看不出问题所在;故额外
|
||||
# 打印**级数 + pointee 类型**,否则现场只能看到一个"2 -> 1"而无法定位是哪个表达式。
|
||||
def _ptr_cast_reason_text(self, code: int, pool: memhub.MemBuddy*, src_ty: mlirlite.LLVMType*, dst_ty: mlirlite.LLVMType*) -> str:
|
||||
"""Human-readable reason for a forbidden ptr -> ptr cast (includes source/target types)."""
|
||||
why: str = "unknown reason"
|
||||
slev: int = self.get_ptr_level(src_ty)
|
||||
dlev: int = self.get_ptr_level(dst_ty)
|
||||
if code == PC_DEPTH: why = f"pointer level differs (source level {slev} -> target level {dlev})"
|
||||
elif code == PC_NONBASIC: why = "the pointee type is a non-basic type (struct/class/union/array etc.): implicit conversion is never allowed between a non-basic type and its pointer, or between pointers of different non-basic types"
|
||||
elif code == PC_LOSSY: why = "conversion between the pointee types is lossy (int -> float with insufficient width / float -> int)"
|
||||
s_txt: str = self._type_text(pool, src_ty)
|
||||
d_txt: str = self._type_text(pool, dst_ty)
|
||||
s_pt: str = "?"
|
||||
d_pt: str = "?"
|
||||
if src_ty is not None and slev > 0: s_pt = self._type_text(pool, src_ty.Pointee)
|
||||
if dst_ty is not None and dlev > 0: d_pt = self._type_text(pool, dst_ty.Pointee)
|
||||
return GenApi.escape_fstring_copy(f"implicit pointer-to-pointer conversion is forbidden: {why}; source type {s_txt} -> target type {d_txt}; source: level {slev}, pointee {s_pt} -> target: level {dlev}, pointee {d_pt}")
|
||||
# _ctor_call_agg_ty - aggregate type if node is a class/struct constructor call.
|
||||
# The only exception to rule 3 ("construction materialization"); else None.
|
||||
def _ctor_call_agg_ty(self, node: ast.AST*) -> mlirlite.LLVMType*:
|
||||
"""Aggregate type if node (or its value child) is a constructor call; else None."""
|
||||
n: ast.AST* = self._value_node_of(node)
|
||||
if n is None or n.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(n)
|
||||
if cl.func is None: return None
|
||||
callee: str = None
|
||||
if cl.func.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(cl.func)
|
||||
callee = nm.id
|
||||
elif cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
callee = at.attr
|
||||
if callee is None: return None
|
||||
ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(callee)
|
||||
if ent is not None and ent.Ty is not None: return ent.Ty
|
||||
# REnum variant ctor `EnumType.Variant(...)`: the callee is not a struct
|
||||
# name, but materializes the base enum, so return that aggregate type.
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
# Take the segment left of the variant name as the enum class name
|
||||
# (the qualifier, not the chain root which caused earlier misses).
|
||||
qual: ast.AST* = (ast.Attribute*(cl.func)).value
|
||||
base_nm: str = None
|
||||
if qual is not None:
|
||||
if qual.kind() == ast.ASTKind.Attribute: base_nm = (ast.Attribute*(qual)).attr
|
||||
elif qual.kind() == ast.ASTKind.Name: base_nm = (ast.Name*(qual)).id
|
||||
if base_nm is not None and self.Gen.ClassGenEnum._is_renum_class_name(base_nm) != 0:
|
||||
rn_ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(base_nm)
|
||||
if rn_ent is not None and rn_ent.Ty is not None: return rn_ent.Ty
|
||||
return None
|
||||
# _same_agg_ty - are two aggregate types the same (type match for ctor exemption)?
|
||||
def _same_agg_ty(self, pool: memhub.MemBuddy*, a: mlirlite.LLVMType*, b: mlirlite.LLVMType*) -> int:
|
||||
"""Are a and b the same aggregate type; 1=yes / 0=no."""
|
||||
if a is None or b is None: return 0
|
||||
return self.Gen.AssignGen._stg_ty_equal(pool, a, b)
|
||||
# _agg_value_slot_ty - 值语义槽:源表达式的变量槽**自身**持有聚合物(`x: Foo`,Spec.PtrLevel == 0)。
|
||||
# 真·指针值(`p: Foo*`,以及**裸类形参** `node: T` —— 其 ABI 即 T*,见 ClassGenVTable "objects are
|
||||
# pointers")的 Spec.PtrLevel >= 1,返回 None,仍必须显式解引用(c.deref(p) / *p)。
|
||||
# 返回该聚合类型;不适用时 None。
|
||||
def _agg_value_slot_ty(self, node: ast.AST*, val: mlirlite.Value*, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||
"""Aggregate type when the source is a variable slot holding the aggregate by value; else None."""
|
||||
n: ast.AST* = self._value_node_of(node)
|
||||
if n is None or n.kind() != ast.ASTKind.Name: return None
|
||||
nm: str = (ast.Name*(n)).id
|
||||
if nm is None or trans is None: return None
|
||||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm)
|
||||
if ve is None or ve.Alloca is None or ve.Spec is None: return None
|
||||
# 值语义槽:按名解析到该变量的值类型;此处仅做类型级判定(不依赖槽身份,避免别名/重复登记漏判)。
|
||||
# PtrLevel>=1(含裸类形参 `node: T` 的 ABI 指针):真指针,必须显式解引用。
|
||||
if ve.Spec.PtrLevel != 0: return None
|
||||
if self.is_ptr_type(ve.Alloca.Ty) == 0: return None
|
||||
return ve.Alloca.Ty.Pointee
|
||||
# Type coercion: int sext/trunc, float fpext/fptrunc, int<->float si2fp/fp2si.
|
||||
def coerce_to_type(self, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, target_ty: mlirlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Coerce val to target_ty: int sext/trunc, float fpext/fptrunc, int/float conversions."""
|
||||
if val is None or target_ty is None: return val
|
||||
# [FIX 2026-09-28] None may convert to any type: it carries no type info,
|
||||
# so all "real value" implicit-cast bans short-circuit for it (IsNoneValue).
|
||||
if self.IsNoneValue(val, node) != 0:
|
||||
if self.is_ptr_type(target_ty) != 0: return mlirlite.build_bitcast(builder, val, target_ty)
|
||||
return self._NoneValueAsTarget(builder, target_ty)
|
||||
# A void-typed value may not be used as a value: coerce to a non-void target
|
||||
# fails fast instead of emitting illegal IR (load void / bitcast void).
|
||||
if self.Gen.StmtGenDecl._is_void_type(val.Ty) != 0:
|
||||
if self.Gen.StmtGenDecl._is_void_type(target_ty) == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "a void value cannot take part in any operation (void is only allowed for void* and typedef aliases)", "TypeError")
|
||||
return val
|
||||
# 聚合目标(结构体/数组)不接受标量(整数/浮点)初始化或赋值:标量→聚合在
|
||||
# VPC 无转换语义;旧行为把标量原样交给 build_store,而 build_store 不做类型
|
||||
# 校验,落成 `store i32 0, ptr %x`——只写聚合首元素字节并抬高对齐,其余部分
|
||||
# 保持未初始化(历史现场:`l: [10]char = 0` → 打印 "Hello"+乱码)。
|
||||
# 聚合零初始化必须写 `= None`(上方 IsNoneValue → _NoneValueAsTarget 分支)。
|
||||
tg_kind_agg: int = target_ty.kind()
|
||||
if (tg_kind_agg == mlirlite.LLVMType.Struct or tg_kind_agg == mlirlite.LLVMType.Array) and self.is_ptr_type(val.Ty) == 0 and (self.get_llvm_type_bits(val.Ty) != 0 or self.get_llvm_float_bits(val.Ty) != 0):
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "a scalar (integer/float) cannot initialize or assign to an aggregate (struct/array): only the first element's bytes would be written (and the alignment raised), leaving the rest of the aggregate uninitialized; for zero-initialization write `= None`, for contents use a list literal `[...]` or assign element by element", "TypeError")
|
||||
return val
|
||||
# func -> ptr: bitcast (function Values have Func type, handled before
|
||||
# the integer/float conversions).
|
||||
match val.Ty:
|
||||
case mlirlite.LLVMType.Func(_, _, _):
|
||||
if self.is_ptr_type(target_ty) != 0: return mlirlite.build_bitcast(builder, val, target_ty)
|
||||
return val
|
||||
case _:
|
||||
pass
|
||||
# Integer conversion.
|
||||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||||
target_bits: int = self.get_llvm_type_bits(target_ty)
|
||||
# int -> ptr: implicit conversion forbidden (FIX 2026-08-30); only the C
|
||||
# null pointer constant 0 is exempt. Non-constant ints need an explicit cast.
|
||||
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
# [FIX 2026-08-31] C null pointer constant exemption: compile-time 0
|
||||
# assigned to a pointer becomes a null pointer (widen to i64, then inttoptr).
|
||||
if val.IsConst == 1 and val.IntVal == 0: return mlirlite.build_inttoptr(builder, mlirlite.const_int64(builder.Pool, 0), target_ty)
|
||||
m_i2p: str = "implicit integer-to-pointer conversion is forbidden: an explicit cast to pointer (e.g. t.CPtr(x)) is required to assign an integer to a pointer type"
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), m_i2p, "CastError")
|
||||
return val
|
||||
# Pointer -> integer: rejected (symmetric with the int -> ptr rule above).
|
||||
# An explicit cast is required; the old code silently did ptrtoint.
|
||||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
m_p2i: str = "implicit pointer-to-integer conversion is forbidden: an explicit cast (e.g. uint64(p) / int64(p) / int(p)) is required to assign a pointer to an integer type"
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), m_p2i, "CastError")
|
||||
return val
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
if val_bits == target_bits: return val
|
||||
if val_bits < target_bits:
|
||||
# Unified zext/sext: i1/i8 default zext, unsigned AST nodes zext,
|
||||
# otherwise sext. trans is required so unsigned annotations resolve.
|
||||
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
|
||||
return mlirlite.build_sext(builder, val, target_ty)
|
||||
# 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"):
|
||||
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。豁免只剩一类:
|
||||
# 源是**编译期常量且值能落进目标位宽**(如字面量 5 → i32、True/False → i1),无信息损失。
|
||||
# (注意 `True`/`False` 常量按 i32 1/0 物化,见 ExprGenConst.translate_constant,
|
||||
# 正是靠此豁免才能赋给 i1 目标。)
|
||||
if val.IsConst == 1:
|
||||
iv: int64 = val.IntVal
|
||||
lo_c: int64 = -(int64(1) << (target_bits - 1))
|
||||
hi_c: int64 = (int64(1) << target_bits) - 1
|
||||
if iv < lo_c or iv > hi_c:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"narrowing truncation overflow: constant {iv} is outside the {target_bits}-bit representable range [{lo_c}, {hi_c}]; use an explicit cast or widen the target type", "OverflowError")
|
||||
return mlirlite.build_trunc(builder, val, target_ty)
|
||||
# 规则 2026-10-13:目标 i1(bool / t.CBool)**不再**对宽整型做隐式真值归一化
|
||||
# (旧的 `x != 0 -> 1/0`)。真值归一化会掩盖真实的位宽不匹配(如 `b: bool = some_i32`),
|
||||
# 属静默转换,必须 fail-fast;需要显式 `bool(x)` / `t.CBool(x)`。
|
||||
# 合法写法不受影响:比较(!=/==/…)、`and` / `or`、`not` 本身即产出 i1
|
||||
# (translate_compare / translate_boolop / translate_unaryop),i1 源走上面的
|
||||
# `val_bits == target_bits` 直接放行;True/False 常量走上面的常量豁免。
|
||||
if target_bits == 1:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to bool (i1) (it would be silently reduced to its truthiness); use an explicit cast (e.g. bool(x) / t.CBool(x))", "CastError")
|
||||
return val
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to a {target_bits}-bit type (the high bits would be lost); use an explicit cast (e.g. int32(x) / t.CInt32T(x)) or widen the target type", "CastError")
|
||||
return val
|
||||
# Float conversion.
|
||||
val_fbits: int = self.get_llvm_float_bits(val.Ty)
|
||||
target_fbits: int = self.get_llvm_float_bits(target_ty)
|
||||
# float <-> ptr: forbidden (fast-fail to avoid the bogus build_load path).
|
||||
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or (self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "unsupported type conversion: float <-> ptr", "CastError")
|
||||
return val
|
||||
if val_fbits != 0 and target_fbits != 0:
|
||||
if val_fbits == target_fbits: return val
|
||||
if val_fbits < target_fbits: return mlirlite.build_fpext(builder, val, target_ty)
|
||||
return mlirlite.build_fptrunc(builder, val, target_ty)
|
||||
# int -> float
|
||||
if val_bits != 0 and target_fbits != 0: return mlirlite.build_si2fp(builder, val, target_ty)
|
||||
# float -> int
|
||||
if val_fbits != 0 and target_bits != 0: return mlirlite.build_fp2si(builder, val, target_ty)
|
||||
# Aggregate value -> pointer: spill a temp to the entry block and pass its
|
||||
# address (VPC never passes aggregates by value).
|
||||
if self.is_ptr_type(val.Ty) == 0 and self.is_ptr_type(target_ty) != 0:
|
||||
vk_agg: int = val.Ty.kind()
|
||||
if vk_agg == mlirlite.LLVMType.Struct or vk_agg == mlirlite.LLVMType.Array:
|
||||
# Rule 3: implicit value -> ptr for non-basic types (Foo -> Foo*) is
|
||||
# forbidden, except two exemptions:
|
||||
# (a) "construction materialization" (see _ctor_call_agg_ty);
|
||||
# (b) 同型聚合值 → 同型聚合指针:值拷贝语义(等价于 C 按值传结构体)。
|
||||
# 实参聚合值被拷进**本帧(调用者帧)**的临时槽,再把该临时槽地址按 ABI
|
||||
# 交给被调者:被调者得到一份独立拷贝、就地修改不影响调用者,且该地址在
|
||||
# 整个调用期间有效(生命周期覆盖调用),故合法用例不会悬垂。
|
||||
# 唯有被调者**保存**此地址(如容器 append 挂链)才会悬垂 —— 这类写法必须由
|
||||
# 源码显式声明为指针形参(`node: T*`)并传入既有对象的地址(此时实参本就是
|
||||
# 指针,不会走到此处隐式拷贝)。显式 T* / &v / c.addr(...) 的正常用法不受影响。
|
||||
same_v2p: int = 0
|
||||
match target_ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee_v2p):
|
||||
if self._same_agg_ty(builder.Pool, pointee_v2p, val.Ty) != 0: same_v2p = 1
|
||||
case _:
|
||||
pass
|
||||
if same_v2p == 0:
|
||||
ctor_v2p: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
if self._same_agg_ty(builder.Pool, ctor_v2p, val.Ty) == 0:
|
||||
m_v2p: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a non-basic value cannot be implicitly converted to a pointer (Foo -> Foo*); to take its address use c.addr(...) / &v explicitly, or cast explicitly"
|
||||
v2p_txt: str = GenApi.escape_fstring_copy(f"; source value type {self._type_text(builder.Pool, val.Ty)} -> target type {self._type_text(builder.Pool, target_ty)}")
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"{m_v2p}{v2p_txt}", "CastError")
|
||||
return val
|
||||
tmp_agg: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty)
|
||||
if tmp_agg is not None:
|
||||
mlirlite.build_store(builder, val, tmp_agg)
|
||||
match target_ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee_agg):
|
||||
if self.Gen.AssignGen._stg_ty_equal(builder.Pool, pointee_agg, val.Ty) != 0: return tmp_agg
|
||||
case _:
|
||||
pass
|
||||
bc_agg: mlirlite.Value* = mlirlite.build_bitcast(builder, tmp_agg, target_ty)
|
||||
if bc_agg is not None: return bc_agg
|
||||
return tmp_agg
|
||||
# Pointer -> non-pointer value(聚合目标)。两类隐式 load 被保留:
|
||||
# ① **指向同型聚合的指针 → 同型聚合值**:值拷贝语义。裸类形参(`elem: T`)的 ABI 就是
|
||||
# `T*`(对象即指针),把它赋给同型值字段/变量时按值拷贝;`x: T` 这类值语义槽指针同理。
|
||||
# 拷贝后目标独立、不会悬垂 —— 与「传参用指针」的 ABI 选择互不冲突。
|
||||
# ② 构造物化(见 _ctor_call_agg_ty)。
|
||||
# 其余(不同型聚合 / 多级指针)仍 fail-fast,须显式 *p / c.deref(p) 或显式 cast。
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
target_kind: int = target_ty.kind()
|
||||
if target_kind == mlirlite.LLVMType.Struct or target_kind == mlirlite.LLVMType.Array:
|
||||
# ① 同型指针 → 值拷贝(覆盖裸类形参的 `T*` ABI)
|
||||
if val.Ty.Pointee is not None and self._same_agg_ty(builder.Pool, val.Ty.Pointee, target_ty) != 0:
|
||||
return mlirlite.build_load(builder, target_ty, val)
|
||||
ctor_p2v: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
# ② 值语义槽(`x: Foo`,槽自身持有聚合物):读槽 = 值拷贝
|
||||
slot_p2v: mlirlite.LLVMType* = self._agg_value_slot_ty(node, val, trans)
|
||||
if self._same_agg_ty(builder.Pool, slot_p2v, target_ty) != 0:
|
||||
return mlirlite.build_load(builder, target_ty, val)
|
||||
if self._same_agg_ty(builder.Pool, ctor_p2v, target_ty) == 0:
|
||||
m_p2v: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a pointer type cannot be implicitly converted to a non-basic value (Foo* -> Foo); to dereference use c.deref(p) / *p explicitly, or cast explicitly"
|
||||
p2v_txt: str = GenApi.escape_fstring_copy(f"; source type {self._type_text(builder.Pool, val.Ty)} -> target value type {self._type_text(builder.Pool, target_ty)}")
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"{m_p2v}{p2v_txt}", "CastError")
|
||||
return val
|
||||
return mlirlite.build_load(builder, target_ty, val)
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"implicit pointer-to-non-pointer-value conversion is forbidden: an explicit dereference (*p / *p) or explicit cast (uint64(p)) is required", "CastError")
|
||||
return val
|
||||
# ptr -> ptr: implicit conversion must satisfy the new rules (see
|
||||
# _ptr_cast_reason); violators fail fast with a CastError.
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
pc: int = self._ptr_cast_reason(builder.Pool, val.Ty, target_ty)
|
||||
if pc != PC_OK:
|
||||
reason: str = self._ptr_cast_reason_text(pc, builder.Pool, val.Ty, target_ty)
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), reason, "CastError")
|
||||
return val
|
||||
return mlirlite.build_bitcast(builder, val, target_ty)
|
||||
return val
|
||||
|
||||
def _zext_or_sext_val(self, pool: memhub.MemBuddy*, val: mlirlite.Value*, node: ast.AST* = None, trans: HT.Translator* = None) -> int:
|
||||
"""Unified integer widening direction: 1=zext (unsigned), 0=sext; delegates to ExprGenOps.should_zext_widen."""
|
||||
if val is None or val.Ty is None: return 0
|
||||
return self.Gen.ExprGenOps.should_zext_widen(pool, val, None, node, trans)
|
||||
|
||||
# translate_ptr_arith: ptr +/- int via getelementptr (index scaled by pointee
|
||||
# size); array pointees ([N x ty]*) decay to element pointers first.
|
||||
def translate_ptr_arith(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, ptr_val: mlirlite.Value*, int_val: mlirlite.Value*, op: int) -> mlirlite.Value*:
|
||||
"""Pointer arithmetic: ptr +/- int via GEP with the index scaled by element size."""
|
||||
if ptr_val is None or int_val is None or ptr_val.Ty is None: return None
|
||||
# Extract the pointee type (non-pointer returns None).
|
||||
elem_ty: mlirlite.LLVMType* = None
|
||||
match ptr_val.Ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee):
|
||||
elem_ty = pointee
|
||||
case _:
|
||||
return None
|
||||
if elem_ty is None: return None
|
||||
# Normalize the index to i64.
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
idx: mlirlite.Value* = self.coerce_to_type(builder, int_val, i64_ty, None)
|
||||
if idx is None: return None
|
||||
# ptr - int -> negative index (LLVM GEP indices are additive).
|
||||
if op == ast.OpKind.Sub:
|
||||
neg_idx: mlirlite.Value* = mlirlite.build_sub(builder, mlirlite.const_int64(pool, 0), idx)
|
||||
if neg_idx is None: return None
|
||||
idx = neg_idx
|
||||
# Array pointee: decay to an element pointer first (GEP 0,0).
|
||||
if self.is_array_type(elem_ty) != 0:
|
||||
inner_ty: mlirlite.LLVMType* = None
|
||||
match elem_ty:
|
||||
case mlirlite.LLVMType.Array(inner, _):
|
||||
inner_ty = inner
|
||||
case _:
|
||||
return None
|
||||
if inner_ty is None: return None
|
||||
decayed: mlirlite.Value* = mlirlite.build_gep_array(builder, elem_ty, inner_ty, ptr_val, mlirlite.const_int64(pool, 0))
|
||||
if decayed is None: return None
|
||||
return mlirlite.build_gep(builder, inner_ty, decayed, idx)
|
||||
return mlirlite.build_gep(builder, elem_ty, ptr_val, idx)
|
||||
def InitExprGen(self) -> int:
|
||||
"""Initialize ExprGen family fields (called by Generator at construction)."""
|
||||
if self is None: return 1
|
||||
self.DocKeys = None
|
||||
self.DocValues = None
|
||||
self.DocCount = 0
|
||||
self.DiagCDefineCount = 0
|
||||
self.DiagNameValueCount = 0
|
||||
self.DiagTranslateValueCount = 0
|
||||
return 0
|
||||
@@ -0,0 +1,919 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenCdef - ExprGenMixin split (stage D): cross-module CDefine lookup
|
||||
novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
"""Cross-module CDefine lookup"""
|
||||
|
||||
# _build_source_path_from_sha1: build a source .vp/.py path from SHA1 via the StubMerger
|
||||
# global store (.vpi missing -> source .py); "lib/..." -> SourceDir, else IncludesDir.
|
||||
def _build_source_path_from_sha1(self, sha1: str) -> str:
|
||||
"""Build a source .py path from a SHA1 via the global store"""
|
||||
if sha1 is None: return None
|
||||
if Config.IncludesDir is None: return None
|
||||
# get the rel-path array from the StubMerger global store
|
||||
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
|
||||
count: int = StubMergerStore.GetSha1StoreCount()
|
||||
if rel_arr_ptr is None or count <= 0: return None
|
||||
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
|
||||
if sha1_arr_ptr is None: return None
|
||||
# iterate to find the matching SHA1
|
||||
idx_s: int = -1
|
||||
for i in range(int(count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
cur_sha1: str = sha1_arr_ptr + sidx
|
||||
if cur_sha1[0] == '\0': continue
|
||||
if string.strcmp(cur_sha1, sha1) == 0:
|
||||
idx_s = i
|
||||
break
|
||||
if idx_s < 0: return None
|
||||
# get the rel path (MAX_REL_PATH_LEN=256, avoids cross-module CDefine issues)
|
||||
MAX_RPL: sizet = 256
|
||||
ridx: sizet = sizet(idx_s) * MAX_RPL
|
||||
rel_path: str = rel_arr_ptr + ridx
|
||||
if rel_path[0] == '\0': return None
|
||||
# choose base dir: 1) stored includes root IncDir (absolute, supports multiple dirs);
|
||||
# 2) prefix heuristic: "lib/" -> SourceDir, "includes/" -> IncludesDir, else SourceDir then IncludesDir.
|
||||
base_dir: str = None
|
||||
rel_used: str = rel_path
|
||||
inc_arr_ptr: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
|
||||
if inc_arr_ptr is not None:
|
||||
iidx: sizet = sizet(idx_s) * StubMerger.MAX_INC_DIR_LEN
|
||||
inc_dir: str = inc_arr_ptr + iidx
|
||||
if inc_dir[0] != '\0': base_dir = inc_dir
|
||||
if base_dir is None and string.strlen(rel_path) >= 4:
|
||||
if (rel_path[0] == 'l' and rel_path[1] == 'i' and rel_path[2] == 'b' and rel_path[3] == '/'):
|
||||
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
|
||||
if base_dir is None and string.strlen(rel_path) >= 9:
|
||||
if (rel_path[0] == 'i' and rel_path[1] == 'n' and rel_path[2] == 'c' and rel_path[3] == 'l' and rel_path[4] == 'u' and rel_path[5] == 'd' and rel_path[6] == 'e' and rel_path[7] == 's' and rel_path[8] == '/'):
|
||||
rel_used = rel_path + 9
|
||||
base_dir = Config.IncludesDir
|
||||
if base_dir is None: base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
|
||||
# build full path: {base_dir}/{rel_used}
|
||||
base_len: sizet = string.strlen(base_dir)
|
||||
rel_len: sizet = string.strlen(rel_used)
|
||||
path_len: sizet = base_len + rel_len + 2
|
||||
buf: str = stdlib.malloc(path_len)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used)
|
||||
# existence check: if missing under SourceDir and IncludesDir differs, retry IncludesDir
|
||||
pf_chk: fileio.File* = fileio.File(buf, fileio.MODE.R)
|
||||
if pf_chk.closed:
|
||||
pf_chk.close()
|
||||
if Config.IncludesDir is not None and base_dir != Config.IncludesDir:
|
||||
stdlib.free(buf)
|
||||
base_len2: sizet = string.strlen(Config.IncludesDir)
|
||||
path_len2: sizet = base_len2 + rel_len + 2
|
||||
buf = stdlib.malloc(path_len2)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, path_len2, "%s/%s", Config.IncludesDir, rel_used)
|
||||
pf_chk2: fileio.File* = fileio.File(buf, fileio.MODE.R)
|
||||
if pf_chk2.closed:
|
||||
pf_chk2.close()
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
pf_chk2.close()
|
||||
else:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
else: pf_chk.close()
|
||||
return buf
|
||||
# _pyi_annot_float_kind - 从 .vpi/.pyi 行的注解段(`NAME:` 到 `=` 之间)判定浮点宽度种类:
|
||||
# 独立单词 float → 1(f32);double / 未声明 / 非浮点 → 0。词边界判定避免命中 `double`/`CFloat32`。
|
||||
def _pyi_annot_float_kind(self, buf: bytes, start: sizet, end: sizet) -> int:
|
||||
"""Return 1 when the annotation text [start,end) declares a 32-bit `float`, else 0."""
|
||||
i: sizet = start
|
||||
while i + 5 <= end:
|
||||
if (buf[i] == 'f' and buf[i + 1] == 'l' and buf[i + 2] == 'o' and buf[i + 3] == 'a' and buf[i + 4] == 't'):
|
||||
ok_before: int = 1
|
||||
ok_after: int = 1
|
||||
if i > start:
|
||||
pb: char = buf[i - 1]
|
||||
if (pb >= 'A' and pb <= 'Z') or (pb >= 'a' and pb <= 'z') or (pb >= '0' and pb <= '9') or pb == '_': ok_before = 0
|
||||
if i + 5 < end:
|
||||
pa: char = buf[i + 5]
|
||||
if (pa >= 'A' and pa <= 'Z') or (pa >= 'a' and pa <= 'z') or (pa >= '0' and pa <= '9') or pa == '_': ok_after = 0
|
||||
if ok_before != 0 and ok_after != 0: return 1
|
||||
i += 1
|
||||
return 0
|
||||
# _lookup_cross_module_cdefine - cross-module CDefine lookup: resolve the source module
|
||||
# from from_imports, parse its pyi "NAME: t.CDefine = value", then cache in the current module.
|
||||
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*, name: str, from_imports: str, imported_modules: str = None, depth: int = 0, node: ast.AST* = None) -> int64:
|
||||
"""Look up a CDefine constant across modules, returning its 64-bit value or -1"""
|
||||
# recursion-depth guard: re-export chains + imported_modules walk can loop and OOM;
|
||||
# cap at 32 to fail fast on cyclic imports (error carries node line + module chain).
|
||||
if depth > 32:
|
||||
err_buf: str = pool.alloc(256)
|
||||
if err_buf is not None:
|
||||
viperlib.snprintf(err_buf, 256, "cross-module CDefine lookup recursion too deep (suspected module import/re-export cycle): %s (depth=%d fi=%s)", name, depth, from_imports if from_imports is not None else "(nil)")
|
||||
self.Gen.TypeGenErr.fatal_error(node, err_buf, "InternalCompilerError")
|
||||
self.Gen.TypeGenErr.fatal_error(node, "cross-module CDefine lookup recursion too deep (suspected module import/re-export cycle)", "InternalCompilerError")
|
||||
# clear the found flag at entry so every early return reports "not found",
|
||||
# avoiding a stale success flag left over from a previous recursive call.
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
if name is None: return -1
|
||||
if from_imports is None: from_imports = ""
|
||||
|
||||
# 1. resolve the module name for `name` from from_imports; try exact match first
|
||||
# (allow_star_fallback=0) so a star import cannot mislead CDefine constants.
|
||||
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 0)
|
||||
if mod_name_raw is None:
|
||||
# exact match failed: try the star import fallback
|
||||
mod_name_raw = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 1)
|
||||
|
||||
# truncate module name at NUL: lookup_from_import returns a pointer into from_imports
|
||||
# (not NUL-terminated); match _lookup_sha1_from_modname_raw (space/colon/NUL delimited).
|
||||
base_mod: str = None
|
||||
if mod_name_raw is not None:
|
||||
mbl: sizet = 0
|
||||
while mod_name_raw[mbl] != ' ' and mod_name_raw[mbl] != '\0' and mod_name_raw[mbl] != ':': mbl += 1
|
||||
mb_buf: str = pool.alloc(mbl + 1)
|
||||
if mb_buf is not None:
|
||||
mi2: sizet
|
||||
for mi2 in range(int(mbl)): mb_buf[mi2] = mod_name_raw[mi2]
|
||||
mb_buf[mbl] = '\0'
|
||||
base_mod = mb_buf
|
||||
while base_mod is not None and base_mod[0] == '.': base_mod = base_mod + 1
|
||||
|
||||
# cross-module CDefine cache (perm_pool, reused across files): key = "name@srcModule"
|
||||
# keeps modules isolated so same-name constants from different modules do not cross.
|
||||
xkey_buf: str = None
|
||||
if base_mod is not None:
|
||||
xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
xkey_buf = pool.alloc(xk_len)
|
||||
if xkey_buf is not None:
|
||||
viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod)
|
||||
xval: str = self.Gen.TypeGenCDef.lookup_xmod_cdefine(xkey_buf)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval)
|
||||
elif self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
xd_v: double = string.atof(xval)
|
||||
# 缓存里带宽度种类(f32 声明 → 1),否则使用方会把 f32 常量当 double 物化
|
||||
xd_f32: int = self.Gen.TypeGenCDef.is_cdefine_float32()
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32)
|
||||
else:
|
||||
xv_i: int64 = string.atoll(xval)
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
|
||||
return string.atoll(xval)
|
||||
|
||||
# 2. from_imports failed: fall back to the imported_modules walk (step 9.6);
|
||||
# a star fallback may name a wrong module, so keep scanning imported_modules.
|
||||
if mod_name_raw is None:
|
||||
# from_imports matched nothing (exact and star both failed):
|
||||
# go straight to the imported_modules walk (step 9.6).
|
||||
if imported_modules is None: return -1
|
||||
# skip reading the from_imports module; go directly to the step 9.6 walk.
|
||||
# result_found=0 skips steps 3-9 and jumps to 9.6.
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
# iterate imported_modules directly
|
||||
im_len: sizet = string.strlen(imported_modules)
|
||||
im_ci: sizet = 0
|
||||
while im_ci < im_len:
|
||||
while im_ci < im_len and imported_modules[im_ci] == ' ': im_ci += 1
|
||||
if im_ci >= im_len: break
|
||||
im_start: sizet = im_ci
|
||||
while im_ci < im_len and imported_modules[im_ci] != ' ': im_ci += 1
|
||||
im_end: sizet = im_ci
|
||||
im_nlen: sizet = im_end - im_start
|
||||
if im_nlen > 0 and im_nlen < 256:
|
||||
im_buf: str = pool.alloc(im_nlen + 1)
|
||||
if im_buf is not None:
|
||||
for mk in range(int(im_nlen)):
|
||||
im_buf[mk] = imported_modules[im_start + mk]
|
||||
im_buf[im_nlen] = '\0'
|
||||
sub_fi: str = pool.alloc(im_nlen + 3)
|
||||
if sub_fi is not None:
|
||||
sub_fi[0] = '*'
|
||||
sub_fi[1] = ':'
|
||||
for mk2 in range(int(im_nlen)):
|
||||
sub_fi[2 + mk2] = im_buf[mk2]
|
||||
sub_fi[2 + im_nlen] = '\0'
|
||||
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return sub_val
|
||||
return -1
|
||||
|
||||
# truncate module name, strip leading dots, find SHA1 via exact+suffix (shared logic)
|
||||
sha1: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
|
||||
if sha1 is None: return -1
|
||||
|
||||
# 5. get temp_dir
|
||||
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
|
||||
if temp_dir is None: return -1
|
||||
|
||||
# 6. build the vpi file path
|
||||
td_len: sizet = string.strlen(temp_dir)
|
||||
pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "vpi")
|
||||
if pyi_path is None: return -1
|
||||
|
||||
# 7. read vpi; if missing or too short (empty stub), fall back to the source .vp/.py
|
||||
PYI_READ_BUF_SIZE: sizet = 65536
|
||||
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
|
||||
if pyi_buf is None:
|
||||
stdlib.free(pyi_path)
|
||||
return -1
|
||||
pf: fileio.File* = fileio.File(pyi_path, fileio.MODE.R)
|
||||
need_py_fallback: int = 0
|
||||
bytes_read: LONG = 0
|
||||
if pf.closed:
|
||||
# .vpi missing: fall back to the source .vp/.py
|
||||
need_py_fallback = 1
|
||||
else:
|
||||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||||
pf.close()
|
||||
# vpi too short (< 10 bytes): treat as empty stub, fall back to .py
|
||||
if bytes_read < 10: need_py_fallback = 1
|
||||
else:
|
||||
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
|
||||
# vpi stubs may declare a CDefine name without a value; the real value lives
|
||||
# in the source .vp/.py, so trust vpi only when the target name's line has a value.
|
||||
cd_sig: int = 0
|
||||
name_has_val: int = 0
|
||||
cs_name_len: sizet = string.strlen(name)
|
||||
cs_total: sizet = string.strlen(pyi_buf)
|
||||
cs_i: sizet = 0
|
||||
while cs_i + 9 < cs_total:
|
||||
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and pyi_buf[cs_i + 8] == 'e'):
|
||||
# check whether the rest of the line has '=' (only valued decls match in vpi)
|
||||
cd_eq: int = 0
|
||||
cs_j: sizet = cs_i + 9
|
||||
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
|
||||
if pyi_buf[cs_j] == '=':
|
||||
cd_eq = 1
|
||||
break
|
||||
cs_j += 1
|
||||
if cd_eq != 0:
|
||||
cd_sig = 1
|
||||
# locate line start; check it begins with "name:" (target has a value)
|
||||
cs_line_start: sizet = cs_i
|
||||
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n': cs_line_start -= 1
|
||||
if cs_i - cs_line_start >= cs_name_len + 1:
|
||||
nm_ok: int = 1
|
||||
for ki2 in range(int(cs_name_len)):
|
||||
if pyi_buf[cs_line_start + ki2] != name[ki2]:
|
||||
nm_ok = 0
|
||||
break
|
||||
if nm_ok == 1 and pyi_buf[cs_line_start + cs_name_len] == ':': name_has_val = 1
|
||||
cs_i += 1
|
||||
if cd_sig == 0 or name_has_val == 0: need_py_fallback = 1
|
||||
stdlib.free(pyi_path)
|
||||
|
||||
if need_py_fallback != 0:
|
||||
# look up the rel path from the SHA1 store; build {base_dir}/{rel_path}
|
||||
src_path: str = self._build_source_path_from_sha1(sha1)
|
||||
if src_path is None:
|
||||
# SHA1 absent from the store = compiler bug (Phase1/Phase2 not populated):
|
||||
# report and abort instead of silently returning -1 (avoids bad @UNDEFINED refs).
|
||||
err_buf: str = pool.alloc_tagged(4096, memhub.DETSITE_EXPRGENCDEF_263)
|
||||
if err_buf is not None:
|
||||
viperlib.snprintf(err_buf, 512, "cross-module CDefine lookup failed: SHA1 %s is not in the store (base_mod=%s name=%s). " "Phase1/Phase2 did not correctly populate PopulateSha1MapStore/AppendToSha1MapStore", sha1, base_mod, name)
|
||||
VLogger.error(err_buf, "XMOD-CD")
|
||||
stdlib.free(pyi_buf)
|
||||
return -1
|
||||
pf = fileio.File(src_path, fileio.MODE.R)
|
||||
stdlib.free(src_path)
|
||||
if pf.closed:
|
||||
stdlib.free(pyi_buf)
|
||||
return -1
|
||||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||||
pf.close()
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(pyi_buf)
|
||||
return -1
|
||||
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
|
||||
|
||||
# 8. search pyi_buf for the "name: t.CDefine = value" line
|
||||
# format: NAME: t.CDefine = value
|
||||
name_len: sizet = string.strlen(name)
|
||||
total_len: sizet = string.strlen(pyi_buf)
|
||||
pos: sizet = 0
|
||||
result_val: int64 = 0
|
||||
result_found: int = 0
|
||||
res_is_double: int = 0
|
||||
# 浮点宽度种类(1 = 注解声明为 32 位 float):跨模块缓存与本地注册都要带上,
|
||||
# 否则使用方把 f32 常量当 double 物化(`define PI: float = ...` 现场)。
|
||||
res_is_f32: int = 0
|
||||
res_is_str: int = 0
|
||||
while pos < total_len:
|
||||
# find line start
|
||||
line_start: sizet = pos
|
||||
# find line end
|
||||
while pos < total_len and pyi_buf[pos] != '\n': pos += 1
|
||||
line_len: sizet = pos - line_start
|
||||
if pos < total_len:
|
||||
pos += 1 # skip '\n'
|
||||
|
||||
# check whether the line starts with "name:"
|
||||
if line_len < name_len + 1: continue
|
||||
match_ok: int = 1
|
||||
for ki in range(int(name_len)):
|
||||
if pyi_buf[line_start + ki] != name[ki]:
|
||||
match_ok = 0
|
||||
break
|
||||
if match_ok == 0: continue
|
||||
if pyi_buf[line_start + name_len] != ':': continue
|
||||
|
||||
# check the line contains the CDefine marker
|
||||
has_cdefine: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
while ci + 7 <= line_start + line_len:
|
||||
if (pyi_buf[ci] == 'C' and pyi_buf[ci + 1] == 'D' and pyi_buf[ci + 2] == 'e' and pyi_buf[ci + 3] == 'f' and pyi_buf[ci + 4] == 'i' and pyi_buf[ci + 5] == 'n' and pyi_buf[ci + 6] == 'e'):
|
||||
has_cdefine = 1
|
||||
break
|
||||
ci += 1
|
||||
if has_cdefine == 0: continue
|
||||
|
||||
# find the value after "="
|
||||
eq_pos: sizet = line_start + name_len + 1
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=': eq_pos += 1
|
||||
if eq_pos >= line_start + line_len: continue
|
||||
# 注解段 = [名字冒号后, '='),用于判定声明的浮点宽度(如 `PI: float | t.CDefine`)
|
||||
annot_start: sizet = line_start + name_len + 1
|
||||
annot_end: sizet = eq_pos
|
||||
eq_pos += 1 # skip '='
|
||||
|
||||
# skip spaces
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# check for type-ctor forms like t.CUnsignedLong(...) / t.CLong(...) / int(...);
|
||||
# if so, skip the "t.CXxx(" prefix, parse inside the parens, ignore trailing ')'.
|
||||
is_type_ctor: int = 0
|
||||
if eq_pos + 2 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
|
||||
# find the '(' position
|
||||
paren_pos: sizet = eq_pos + 2
|
||||
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(': paren_pos += 1
|
||||
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
|
||||
is_type_ctor = 1
|
||||
eq_pos = paren_pos + 1 # skip '('
|
||||
# skip possible leading spaces inside the parens
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# paren-wrapped flag: (1 << 23) / (512) / (A | B)
|
||||
is_paren_expr: int = 0
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(':
|
||||
is_paren_expr = 1
|
||||
eq_pos += 1
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# === parse left operand token (number / hex / sign / name) ===
|
||||
# sign flag
|
||||
is_neg: int = 0
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||||
is_neg = 1
|
||||
eq_pos += 1
|
||||
val_str_start: sizet = eq_pos
|
||||
val_str_len: sizet = 0
|
||||
is_hex: int = 0
|
||||
is_float_val: int = 0
|
||||
is_name_val: int = 0
|
||||
is_str_val: int = 0
|
||||
# string value (e.g. ZLIB_VERSION = "1.3.2"): read inside the quotes; without this,
|
||||
# cross-module read-back skipped it and reported "CDefine not found".
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||||
is_str_val = 1
|
||||
eq_pos += 1 # skip opening quote
|
||||
val_str_start = eq_pos
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '"':
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||||
eq_pos += 1 # skip closing quote
|
||||
if eq_pos + 1 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
|
||||
is_hex = 1
|
||||
val_str_len = 2
|
||||
eq_pos += 2
|
||||
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or (pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or pyi_buf[eq_pos] == '_':
|
||||
is_name_val = 1
|
||||
while eq_pos < line_start + line_len:
|
||||
c_tk: char = pyi_buf[eq_pos]
|
||||
if (c_tk >= 'A' and c_tk <= 'Z') or (c_tk >= 'a' and c_tk <= 'z') or (c_tk >= '0' and c_tk <= '9') or c_tk == '_':
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
# continue reading digit/hex chars (string values are read whole, skipped)
|
||||
while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0:
|
||||
ch: char = pyi_buf[eq_pos]
|
||||
if is_hex == 1:
|
||||
if ((ch >= '0' and ch <= '9') or (ch >= 'a' and ch <= 'f') or (ch >= 'A' and ch <= 'F')):
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
else:
|
||||
if ch >= '0' and ch <= '9':
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
elif ch == '.' and is_float_val == 0:
|
||||
# float value: '3.14159' ('.' allowed only once)
|
||||
is_float_val = 1
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
|
||||
if val_str_len == 0 and is_str_val == 0: continue
|
||||
|
||||
# copy the left token into a buffer
|
||||
val_buf: str = pool.alloc(val_str_len + 1)
|
||||
if val_buf is None: continue
|
||||
for vi in range(int(val_str_len)): val_buf[vi] = pyi_buf[val_str_start + vi]
|
||||
val_buf[val_str_len] = '\0'
|
||||
|
||||
# evaluate the left token
|
||||
lval: int64 = 0
|
||||
l_is_double: int = 0
|
||||
ldbl: double = 0.0
|
||||
if is_str_val != 0:
|
||||
# string CDefine (e.g. ZLIB_VERSION = "1.3.2"): register the string value
|
||||
# (including ""), mark result_found + res_is_str so the tail skips integers.
|
||||
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf)
|
||||
result_val = 0
|
||||
result_found = 1
|
||||
res_is_str = 1
|
||||
elif is_name_val != 0:
|
||||
# True/False literal -> 1/0 (TPC treats bool as integer in CDefine context)
|
||||
if string.strcmp(val_buf, "True") == 0: lval = 1
|
||||
elif string.strcmp(val_buf, "False") == 0: lval = 0
|
||||
else:
|
||||
# name reference: look up the current CDefine table (e.g. FOREGROUND_COMBO)
|
||||
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# name not registered: cannot evaluate, skip the line
|
||||
continue
|
||||
elif is_hex == 1:
|
||||
# parse hex manually (skip the 0x prefix) with 64-bit accumulation so
|
||||
# 0xFFFFFFFF=4294967295 keeps full precision (constant side truncates to i32 -1).
|
||||
hex_result: int64 = 0
|
||||
hex_buf: str = val_buf + 2 # skip "0x"
|
||||
for hi in range(int(val_str_len - 2)):
|
||||
hc: char = hex_buf[hi]
|
||||
hd: int = 0
|
||||
if hc >= '0' and hc <= '9': hd = hc - '0'
|
||||
elif hc >= 'a' and hc <= 'f': hd = hc - 'a' + 10
|
||||
elif hc >= 'A' and hc <= 'F': hd = hc - 'A' + 10
|
||||
else: break
|
||||
hex_result = hex_result * 16 + hd
|
||||
lval = hex_result
|
||||
elif is_float_val != 0:
|
||||
l_is_double = 1
|
||||
ldbl = string.atof(val_buf)
|
||||
lval = string.atoll(val_buf)
|
||||
else: lval = string.atoll(val_buf)
|
||||
if is_neg != 0:
|
||||
lval = -lval
|
||||
if l_is_double != 0: ldbl = -ldbl
|
||||
|
||||
# skip spaces; check for a binary operator
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# parse operator (<< >> | & ^ + - * /)
|
||||
cd_op: int = 0
|
||||
if eq_pos + 1 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == '<' and pyi_buf[eq_pos + 1] == '<':
|
||||
cd_op = 1 # LShift
|
||||
eq_pos += 2
|
||||
elif pyi_buf[eq_pos] == '>' and pyi_buf[eq_pos + 1] == '>':
|
||||
cd_op = 2 # RShift
|
||||
eq_pos += 2
|
||||
if cd_op == 0 and eq_pos < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == '|':
|
||||
cd_op = 3 # BitOr
|
||||
eq_pos += 1
|
||||
elif pyi_buf[eq_pos] == '&':
|
||||
cd_op = 4 # BitAnd
|
||||
eq_pos += 1
|
||||
elif pyi_buf[eq_pos] == '^':
|
||||
cd_op = 5 # BitXor
|
||||
eq_pos += 1
|
||||
elif pyi_buf[eq_pos] == '+':
|
||||
cd_op = 6 # Add
|
||||
eq_pos += 1
|
||||
elif pyi_buf[eq_pos] == '-':
|
||||
cd_op = 7 # Sub
|
||||
eq_pos += 1
|
||||
elif pyi_buf[eq_pos] == '*':
|
||||
cd_op = 8 # Mult
|
||||
eq_pos += 1
|
||||
elif pyi_buf[eq_pos] == '/':
|
||||
cd_op = 9 # Div
|
||||
eq_pos += 1
|
||||
|
||||
if cd_op != 0:
|
||||
# === parse right operand token ===
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
r_neg: int = 0
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||||
r_neg = 1
|
||||
eq_pos += 1
|
||||
r_start: sizet = eq_pos
|
||||
r_len: sizet = 0
|
||||
r_hex: int = 0
|
||||
r_name: int = 0
|
||||
if eq_pos + 1 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
|
||||
r_hex = 1
|
||||
r_len = 2
|
||||
eq_pos += 2
|
||||
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or (pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or pyi_buf[eq_pos] == '_':
|
||||
r_name = 1
|
||||
while eq_pos < line_start + line_len:
|
||||
c_r: char = pyi_buf[eq_pos]
|
||||
if (c_r >= 'A' and c_r <= 'Z') or (c_r >= 'a' and c_r <= 'z') or (c_r >= '0' and c_r <= '9') or c_r == '_':
|
||||
r_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
while eq_pos < line_start + line_len and r_name == 0:
|
||||
c_r2: char = pyi_buf[eq_pos]
|
||||
if r_hex == 1:
|
||||
if ((c_r2 >= '0' and c_r2 <= '9') or (c_r2 >= 'a' and c_r2 <= 'f') or (c_r2 >= 'A' and c_r2 <= 'F')):
|
||||
r_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
else:
|
||||
if c_r2 >= '0' and c_r2 <= '9':
|
||||
r_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
if r_len == 0:
|
||||
# no right operand after operator: cannot evaluate, skip the line
|
||||
continue
|
||||
r_buf: str = pool.alloc(r_len + 1)
|
||||
if r_buf is None: continue
|
||||
for r_i in range(int(r_len)): r_buf[r_i] = pyi_buf[r_start + r_i]
|
||||
r_buf[r_len] = '\0'
|
||||
rval: int64 = 0
|
||||
if r_name != 0:
|
||||
# True/False literal -> 1/0
|
||||
if string.strcmp(r_buf, "True") == 0: rval = 1
|
||||
elif string.strcmp(r_buf, "False") == 0: rval = 0
|
||||
else:
|
||||
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# name not registered: cannot evaluate, skip the line
|
||||
continue
|
||||
elif r_hex != 0:
|
||||
rhex_v: int64 = 0
|
||||
rh_buf: str = r_buf + 2
|
||||
for rh_i in range(int(r_len - 2)):
|
||||
rc: char = rh_buf[rh_i]
|
||||
rd: int = 0
|
||||
if rc >= '0' and rc <= '9': rd = rc - '0'
|
||||
elif rc >= 'a' and rc <= 'f': rd = rc - 'a' + 10
|
||||
elif rc >= 'A' and rc <= 'F': rd = rc - 'A' + 10
|
||||
else: break
|
||||
rhex_v = rhex_v * 16 + rd
|
||||
rval = rhex_v
|
||||
else: rval = string.atoll(r_buf)
|
||||
if r_neg != 0: rval = -rval
|
||||
# compute the binary expression result (aligned with TPC ConstEvaluator)
|
||||
if cd_op == 1: result_val = lval << rval
|
||||
elif cd_op == 2: result_val = lval >> rval
|
||||
elif cd_op == 3: result_val = lval | rval
|
||||
elif cd_op == 4: result_val = lval & rval
|
||||
elif cd_op == 5: result_val = lval ^ rval
|
||||
elif cd_op == 6: result_val = lval + rval
|
||||
elif cd_op == 7: result_val = lval - rval
|
||||
elif cd_op == 8: result_val = lval * rval
|
||||
else:
|
||||
if rval == 0: continue
|
||||
result_val = lval // rval
|
||||
result_found = 1
|
||||
break
|
||||
else:
|
||||
# single value (no operator): parenthesized values like (512) / (-11) also land here
|
||||
if is_paren_expr != 0:
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')': eq_pos += 1
|
||||
if is_float_val != 0:
|
||||
# float CDefine (e.g. U_M_PI = 3.14159...): cache as double for double
|
||||
# constant generation; int part serves the int path (is_cdefine_double flag).
|
||||
# 注解声明为 float 时带上 f32 种类,使用方才能直接出 f32 常量。
|
||||
res_is_f32 = self._pyi_annot_float_kind(pyi_buf, annot_start, annot_end)
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32)
|
||||
result_val = lval
|
||||
result_found = 1
|
||||
res_is_double = 1
|
||||
else:
|
||||
result_val = lval
|
||||
result_found = 1
|
||||
break
|
||||
|
||||
# 9.5 if not found in this module, recurse into submodules for `from .X import NAME` /
|
||||
# `from .X import *` (e.g. ast.__init__ re-exports CONST_INT from .base).
|
||||
if result_found == 0:
|
||||
# base_mod was computed at function entry (mod_name_raw with leading dots removed);
|
||||
# the submodule recursion uses it to build the full "base_mod.X" name.
|
||||
name_len_rc: sizet = string.strlen(name)
|
||||
rc_pos: sizet = 0
|
||||
while rc_pos < total_len and result_found == 0:
|
||||
# find line start
|
||||
rc_line_start: sizet = rc_pos
|
||||
# find line end
|
||||
while rc_pos < total_len and pyi_buf[rc_pos] != '\n': rc_pos += 1
|
||||
rc_line_len: sizet = rc_pos - rc_line_start
|
||||
if rc_pos < total_len: rc_pos += 1
|
||||
|
||||
# check whether the line starts with "from " (module import)
|
||||
if rc_line_len < 12: continue
|
||||
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or pyi_buf[rc_line_start+4] != ' ': continue
|
||||
|
||||
# extract the module token (skip leading dots); support both relative
|
||||
# `from .base import X` (join base_mod.base) and absolute `from vqt6._types import *`.
|
||||
rc_is_rel: int = 0
|
||||
rc_sub_start: sizet = rc_line_start + 5
|
||||
while rc_sub_start < rc_line_start + rc_line_len and pyi_buf[rc_sub_start] == '.':
|
||||
rc_is_rel = 1
|
||||
rc_sub_start += 1
|
||||
rc_sub_end: sizet = rc_sub_start
|
||||
while rc_sub_end < rc_line_start + rc_line_len and pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t': rc_sub_end += 1
|
||||
rc_sub_len: sizet = rc_sub_end - rc_sub_start
|
||||
if rc_sub_len == 0: continue
|
||||
|
||||
# check whether the line contains "import"
|
||||
rc_has_import: int = 0
|
||||
rc_ipos: sizet = rc_sub_end
|
||||
while rc_ipos + 6 <= rc_line_start + rc_line_len:
|
||||
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
|
||||
rc_has_import = 1
|
||||
break
|
||||
rc_ipos += 1
|
||||
if rc_has_import == 0: continue
|
||||
|
||||
# check NAME is in the import list, or the import is *; handle multi-line
|
||||
# imports: scan from "import" up to ")" when a "(" is present.
|
||||
rc_imp_start: sizet = rc_ipos + 6
|
||||
rc_star: int = 0
|
||||
rc_name_match: int = 0
|
||||
|
||||
# determine the scan range: single line or multi-line (inside parens)
|
||||
rc_scan_end: sizet = rc_line_start + rc_line_len
|
||||
rc_has_paren: int = 0
|
||||
rc_pp: sizet = rc_imp_start
|
||||
while rc_pp < rc_line_start + rc_line_len:
|
||||
if pyi_buf[rc_pp] == '(':
|
||||
rc_has_paren = 1
|
||||
break
|
||||
if pyi_buf[rc_pp] == '*': rc_star = 1
|
||||
rc_pp += 1
|
||||
if rc_has_paren != 0:
|
||||
# multi-line import: scan from after "(" to ")"
|
||||
rc_scan_end = rc_pp + 1
|
||||
rc_paren_closed: int = 0
|
||||
rc_sp: sizet = rc_pp + 1
|
||||
while rc_sp < total_len and rc_paren_closed == 0:
|
||||
if pyi_buf[rc_sp] == ')':
|
||||
rc_paren_closed = 1
|
||||
rc_scan_end = rc_sp
|
||||
break
|
||||
rc_sp += 1
|
||||
if rc_paren_closed == 0: rc_scan_end = total_len
|
||||
|
||||
# scan from rc_imp_start to rc_scan_end for NAME and *
|
||||
rc_cpos: sizet = rc_imp_start
|
||||
while rc_cpos < rc_scan_end:
|
||||
rc_ch: char = pyi_buf[rc_cpos]
|
||||
if rc_ch == '*': rc_star = 1
|
||||
# check NAME matches as a whole word
|
||||
if rc_cpos + name_len_rc <= rc_scan_end:
|
||||
rc_match: int = 1
|
||||
rc_ki: sizet
|
||||
for rc_ki in range(int(name_len_rc)):
|
||||
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
|
||||
rc_match = 0
|
||||
break
|
||||
if rc_match == 1:
|
||||
# check the preceding char is a non-identifier char
|
||||
rc_before_ok: int = 0
|
||||
if rc_cpos == rc_imp_start: rc_before_ok = 1
|
||||
else:
|
||||
rc_before_ch: char = pyi_buf[rc_cpos - 1]
|
||||
if rc_before_ch == ' ' or rc_before_ch == '\t' or rc_before_ch == '(' or rc_before_ch == ',' or rc_before_ch == '\n' or rc_before_ch == '\r': rc_before_ok = 1
|
||||
# check the following char is a non-identifier char
|
||||
rc_after_ok: int = 0
|
||||
rc_after_pos: sizet = rc_cpos + name_len_rc
|
||||
if rc_after_pos >= rc_scan_end: rc_after_ok = 1
|
||||
else:
|
||||
rc_after_ch: char = pyi_buf[rc_after_pos]
|
||||
if rc_after_ch == ' ' or rc_after_ch == '\t' or rc_after_ch == ')' or rc_after_ch == ',' or rc_after_ch == '\r' or rc_after_ch == '\n': rc_after_ok = 1
|
||||
if rc_before_ok == 1 and rc_after_ok == 1: rc_name_match = 1
|
||||
rc_cpos += 1
|
||||
|
||||
if rc_star == 0 and rc_name_match == 0: continue
|
||||
|
||||
# copy the submodule name into a new buffer (pyi_buf + rc_sub_start is not NUL-terminated)
|
||||
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
|
||||
if rc_sub_buf is None: continue
|
||||
rc_si: sizet
|
||||
for rc_si in range(int(rc_sub_len)): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
|
||||
rc_sub_buf[rc_sub_len] = '\0'
|
||||
|
||||
# build the full module name: relative import -> package + "." + token (the base
|
||||
# must be the package, not the module name); absolute import -> token as-is.
|
||||
rc_full_mod: str = None
|
||||
if rc_is_rel != 0:
|
||||
rc_base_mod: str = base_mod
|
||||
rc_pkg: str = self.Gen.CallGenBase._module_package_from_sha1(pool, sha1)
|
||||
if rc_pkg is not None: rc_base_mod = rc_pkg
|
||||
rc_full_len: sizet = string.strlen(rc_base_mod) + 1 + rc_sub_len + 1
|
||||
rc_full_buf: str = pool.alloc(rc_full_len)
|
||||
if rc_full_buf is None: continue
|
||||
viperlib.snprintf(rc_full_buf, rc_full_len, "%s.%s", rc_base_mod, rc_sub_buf)
|
||||
rc_full_mod = rc_full_buf
|
||||
else:
|
||||
if string.strcmp(rc_sub_buf, base_mod) == 0: continue
|
||||
rc_full_mod = rc_sub_buf
|
||||
|
||||
# loop guard: if base_mod already ends with "."+rc_sub_buf (ast.base + .base),
|
||||
# the recursion is re-appending the same segment -> skip (infinite-growth root cause).
|
||||
rc_blen: sizet = string.strlen(base_mod)
|
||||
if rc_blen > rc_sub_len + 1:
|
||||
rc_tail: str = base_mod + (rc_blen - rc_sub_len)
|
||||
if rc_tail[0] == '.' and string.strncmp(rc_tail + 1, rc_sub_buf, rc_sub_len) == 0: continue
|
||||
|
||||
# build a temporary from_imports "name:full_mod"
|
||||
rc_fi_len: sizet = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
|
||||
rc_fi: str = pool.alloc(rc_fi_len)
|
||||
if rc_fi is None: continue
|
||||
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
|
||||
|
||||
# recursive call to search the submodule
|
||||
rc_sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
result_val = rc_sub_val
|
||||
result_found = 1
|
||||
|
||||
stdlib.free(pyi_buf)
|
||||
|
||||
# 9.6 star-fallback module had no match: fall back to walking imported_modules,
|
||||
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import win32.fileio`.
|
||||
if result_found == 0 and imported_modules is not None:
|
||||
im2_len: sizet = string.strlen(imported_modules)
|
||||
im2_ci: sizet = 0
|
||||
while im2_ci < im2_len and result_found == 0:
|
||||
while im2_ci < im2_len and imported_modules[im2_ci] == ' ': im2_ci += 1
|
||||
if im2_ci >= im2_len: break
|
||||
im2_start: sizet = im2_ci
|
||||
while im2_ci < im2_len and imported_modules[im2_ci] != ' ': im2_ci += 1
|
||||
im2_end: sizet = im2_ci
|
||||
im2_nlen: sizet = im2_end - im2_start
|
||||
if im2_nlen > 0 and im2_nlen < 256:
|
||||
im2_buf: str = pool.alloc(im2_nlen + 1)
|
||||
if im2_buf is not None:
|
||||
for mk3 in range(int(im2_nlen)):
|
||||
im2_buf[mk3] = imported_modules[im2_start + mk3]
|
||||
im2_buf[im2_nlen] = '\0'
|
||||
sub_fi2: str = pool.alloc(im2_nlen + 3)
|
||||
if sub_fi2 is not None:
|
||||
sub_fi2[0] = '*'
|
||||
sub_fi2[1] = ':'
|
||||
for mk4 in range(int(im2_nlen)):
|
||||
sub_fi2[2 + mk4] = im2_buf[mk4]
|
||||
sub_fi2[2 + im2_nlen] = '\0'
|
||||
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
result_val = sub_val2
|
||||
result_found = 1
|
||||
|
||||
# 10. cache into the current module's CDefine table and the cross-module perm_pool;
|
||||
# float CDefines are already cached as double, so only integers are written here.
|
||||
if result_found != 0:
|
||||
if res_is_str != 0:
|
||||
# string CDefine: the value was already registered in the match loop (val_buf is
|
||||
# the hit line's content, valid across the function); only write the cross cache.
|
||||
if base_mod is not None:
|
||||
ck_len_s: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
ckey_s: str = pool.alloc(ck_len_s)
|
||||
if ckey_s is not None:
|
||||
viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod)
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_s, val_buf, 0, 1)
|
||||
elif res_is_double == 0:
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val)
|
||||
# cross-module cache (perm_pool, reused across files), integers: key matches the
|
||||
# entry query "name@srcModule" (truncated base_mod, leading dots removed).
|
||||
if base_mod is not None:
|
||||
ck_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
ckey_buf: str = pool.alloc(ck_len)
|
||||
if ckey_buf is not None:
|
||||
viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod)
|
||||
cbuf_i: str = pool.alloc(32)
|
||||
if cbuf_i is not None:
|
||||
viperlib.snprintf(cbuf_i, 32, "%lld", result_val)
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0)
|
||||
else:
|
||||
# float: fetch %.17g text from the local table and write the cross cache (lossless)
|
||||
if base_mod is not None:
|
||||
ck_len2: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
ckey2: str = pool.alloc(ck_len2)
|
||||
if ckey2 is not None:
|
||||
viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod)
|
||||
dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
cbuf_d: str = pool.alloc(64)
|
||||
if cbuf_d is not None:
|
||||
stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc)
|
||||
# 浮点种类随缓存一起带上(1=f64 / 2=f32),供后续使用方直接物化宽度
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, self.Gen.TypeGenCDef.xmod_float_kind(), 0)
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(1)
|
||||
else: self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
|
||||
return result_val
|
||||
# _is_cdefine_name_pattern - CDefine names follow ALL_CAPS (A-Z, 0-9, _ with >=1 uppercase);
|
||||
# lowercase keywords/variables won't match, avoiding fail-fast false positives. 1=match, 0=no.
|
||||
def _is_cdefine_name_pattern(self, name: str) -> int:
|
||||
"""Return 1 if name follows the CDefine convention (uppercase letters, digits, underscore)"""
|
||||
if name is None: return 0
|
||||
has_upper: int = 0
|
||||
i: sizet = 0
|
||||
while name[i] != '\0':
|
||||
c: char = name[i]
|
||||
if c >= 'a' and c <= 'z': return 0
|
||||
if c >= 'A' and c <= 'Z': has_upper = 1
|
||||
i += 1
|
||||
return has_upper
|
||||
# ============================================================
|
||||
# Translate a variable reference (Name node) -> load
|
||||
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64) -> mlirlite.Value*:
|
||||
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)
|
||||
|
||||
位宽优先用 define 的**已知位宽**(右侧显式强转或声明注解,见 register_cdefine_constant 的 bits);
|
||||
没有位宽记录时按**推断位宽**物化(值的最小可存宽度;0x80000000 → 32)。
|
||||
这样 `define X: WORD = WORD(...)` 的 X 保持 u16、`X | Y` 里两个同宽 u16 不会被提升成
|
||||
i32;无注解的 `define A = 0x80000000` 也不会退化成 i64 把 `A | B` 放大到 64 位,
|
||||
而小值 define 说成 8/16 也在算术提升下不会溢出。
|
||||
"""
|
||||
dbits: int = 0
|
||||
if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name)
|
||||
if dbits == 8 or dbits == 16 or dbits == 32 or dbits == 64:
|
||||
return mlirlite.const_int(pool, dbits, self._cdefine_bits_value(value, dbits))
|
||||
# 无位宽记录(例如只从 .vpi 跨模块引入、未登记 bits)→ 按**推断位宽**物化:
|
||||
# 值的最小可存宽度(2^31..2^32-1 取 32 位无符号位型,不退化成 i64;
|
||||
# 小值取 8/16,参与算术时由整数提升先升到 i32,不会溢出)。
|
||||
wmin: int = self.Gen.AssignGen.define_min_width(value)
|
||||
return mlirlite.const_int(pool, wmin, self._cdefine_bits_value(value, wmin))
|
||||
|
||||
# ============================================================
|
||||
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
|
||||
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
|
||||
"""把值按位宽处理:**只有在当前位宽的两种解释下都装不下时才截断**,否则原样保留。
|
||||
|
||||
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0)。
|
||||
但不该把"无符号可容、有符号装不下"的**正值**重解释成负数:旧实现无条件
|
||||
`(value << 56) >> 56`,8 位下的 160 被读成 -96,正值静默变负 —— 现场
|
||||
Gargantua `_start` 的 `string.memset(fb, 0, GW * GH * 4)`(GW=160, GH=90)
|
||||
被折成 -34560,作为长度写穿映射区 → #PF。位型不变(const_int 本就按位宽截断),
|
||||
只是不再翻转可容正值的语义。实现仍避开大掩码字面量(见下)。
|
||||
"""
|
||||
if bits == 8:
|
||||
if value >= -128 and value <= 127: return value
|
||||
if value > 127 and (value >> 8) == 0: return value
|
||||
return (value << 56) >> 56
|
||||
if bits == 16:
|
||||
if value >= -32768 and value <= 32767: return value
|
||||
if value > 32767 and (value >> 16) == 0: return value
|
||||
return (value << 48) >> 48
|
||||
if bits == 32:
|
||||
if value >= -2147483647 - 1 and value <= 2147483647: return value
|
||||
if value > 2147483647 and (value >> 32) == 0: return value
|
||||
return (value << 32) >> 32
|
||||
return value
|
||||
|
||||
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""Materialize a CDefine integer constant at a known use-site width, or None on fallback"""
|
||||
bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(target_ty)
|
||||
if bits != 8 and bits != 16 and bits != 32 and bits != 64: return None
|
||||
wmin: int = self.Gen.AssignGen.define_min_width(value)
|
||||
if bits < wmin:
|
||||
# "representable range at the use site" uses the same rule as the check = TPC
|
||||
# DefineWidthLoHi = [-(2^(bits-1)), 2^bits - 1]; literals built from safe 2147483647.
|
||||
m31: int64 = 2147483647
|
||||
lo: int64 = -m31 - 1
|
||||
hi: int64 = m31 * 2 + 1
|
||||
if bits == 8:
|
||||
lo = -128
|
||||
hi = 255
|
||||
elif bits == 16:
|
||||
lo = -32768
|
||||
hi = 65535
|
||||
ctx: str = ""
|
||||
if use_ctx is not None:
|
||||
# this language's str is i8* with no `str + str`: `+` degrades to pointer add.
|
||||
# Build strings with f-string interpolation / string.strcat / viperlib.snprintf.
|
||||
ctx = f"({use_ctx})"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[VPC] define constant use site width insufficient: constant '{name}' actual value {value} " f"needs at least {wmin} bits (minimum representable width W_min), " f"but the use site{ctx} provides only {bits} bits " f"(the acceptable range for this bit width under both signed/unsigned interpretation: {lo}..{hi}). " f"silent truncation/downgrade is forbidden, compilation aborted. " f"fix: widen the type of the use site{ctx} to at least {wmin} bits " f"(e.g. int{wmin}T / CUInt{wmin}T or wider types), or use a smaller constant value.", "TypeError")
|
||||
return None
|
||||
return mlirlite.const_int(pool, bits, self._cdefine_bits_value(value, bits))
|
||||
@@ -0,0 +1,383 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# docstring registry capacity constants (kept in sync with ExprGenMixin)
|
||||
define DOC_MAX = 512
|
||||
define DOC_KEY_SZ = 192
|
||||
define DOC_VAL_SZ = 4096
|
||||
|
||||
|
||||
# ExprGenCls - split of ExprGenMixin (stage D): class-name inference / docstring registry
|
||||
novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
"""Expression class inference and docstring registry."""
|
||||
|
||||
# docstring extract/register/lookup (__doc__ support): pre-registration scans
|
||||
# ASTs into a global "{module_sha1}.{symbol}" table; lookups inline the string.
|
||||
|
||||
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
|
||||
"""Extract a docstring from a body when body[0] is an Expr(Constant str)."""
|
||||
if body is None or len(body) == 0: return None
|
||||
first: ast.AST* = body.get(0)
|
||||
if first is None or first.kind() != ast.ASTKind.Expr: return None
|
||||
ex: ast.Expr* = ast.Expr*(first)
|
||||
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant: return None
|
||||
cn: ast.Constant* = ast.Constant*(ex.value)
|
||||
if cn is None or cn.const_kind != ast.CONST_STR: return None
|
||||
return cn.str_val
|
||||
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
|
||||
"""Register a docstring under key "{module_sha1}.{symbol}", deduplicated."""
|
||||
if module_sha1 is None or symbol is None or doc is None: return
|
||||
if string.strlen(doc) == 0: return
|
||||
if self.DocKeys is None:
|
||||
self.DocKeys = CompilerFactory.factory.PermPool.alloc(DOC_MAX * DOC_KEY_SZ)
|
||||
self.DocValues = CompilerFactory.factory.PermPool.alloc(DOC_MAX * DOC_VAL_SZ)
|
||||
if self.DocKeys is None or self.DocValues is None: return
|
||||
string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ)
|
||||
string.memset(self.DocValues, 0, DOC_MAX * DOC_VAL_SZ)
|
||||
if self.DocCount >= DOC_MAX: return
|
||||
ml: sizet = string.strlen(module_sha1)
|
||||
sl: sizet = string.strlen(symbol)
|
||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||
if key_buf is None: return
|
||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||
for i in range(int(self.DocCount)):
|
||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||
stdlib.free(key_buf)
|
||||
return
|
||||
string.strcpy(self.DocKeys + self.DocCount * DOC_KEY_SZ, key_buf)
|
||||
string.strcpy(self.DocValues + self.DocCount * DOC_VAL_SZ, doc)
|
||||
self.DocCount += 1
|
||||
stdlib.free(key_buf)
|
||||
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
|
||||
"""Look up a docstring by "{module_sha1}.{symbol}", or None on miss."""
|
||||
if self.DocKeys is None or module_sha1 is None or symbol is None: return None
|
||||
ml: sizet = string.strlen(module_sha1)
|
||||
sl: sizet = string.strlen(symbol)
|
||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||
if key_buf is None: return None
|
||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||
for i in range(int(self.DocCount)):
|
||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||
stdlib.free(key_buf)
|
||||
return self.DocValues + i * DOC_VAL_SZ
|
||||
stdlib.free(key_buf)
|
||||
return None
|
||||
def extract_module_docstrings(self, tree: ast.AST*, module_sha1: str) -> None:
|
||||
"""Scan a module AST and register docstrings of its functions, classes and methods."""
|
||||
if tree is None or module_sha1 is None: return
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None: return
|
||||
cn: sizet = len(ch)
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd: int = child.kind()
|
||||
if kd == ast.ASTKind.FunctionDef:
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(child)
|
||||
if fd is not None and fd.name is not None:
|
||||
fd_body: list[ast.AST*]* = fd.children
|
||||
fdoc: str = self._extract_docstring_from_body(fd_body)
|
||||
if fdoc is not None: self.register_docstring(module_sha1, fd.name, fdoc)
|
||||
elif kd == ast.ASTKind.ClassDef:
|
||||
cd: ast.ClassDef* = ast.ClassDef*(child)
|
||||
if cd is None or cd.name is None: continue
|
||||
cd_body0: list[ast.AST*]* = cd.children
|
||||
cdoc: str = self._extract_docstring_from_body(cd_body0)
|
||||
if cdoc is not None: self.register_docstring(module_sha1, cd.name, cdoc)
|
||||
# class method docstring (key="{ClassName}.{method}")
|
||||
cd_body: list[ast.AST*]* = cd.children
|
||||
if cd_body is not None:
|
||||
cbn: sizet = len(cd_body)
|
||||
for mi in range(int(cbn)):
|
||||
m: ast.AST* = cd_body.get(mi)
|
||||
if m is None or m.kind() != ast.ASTKind.FunctionDef: continue
|
||||
mfd: ast.FunctionDef* = ast.FunctionDef*(m)
|
||||
if mfd is None or mfd.name is None: continue
|
||||
mdoc: str = self._extract_docstring_from_body(mfd.children)
|
||||
if mdoc is not None:
|
||||
cl_nl: sizet = string.strlen(cd.name)
|
||||
mt_nl: sizet = string.strlen(mfd.name)
|
||||
sym_buf: bytes = stdlib.malloc(cl_nl + mt_nl + 2)
|
||||
if sym_buf is not None:
|
||||
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
|
||||
self.register_docstring(module_sha1, sym_buf, mdoc)
|
||||
stdlib.free(sym_buf)
|
||||
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str, out_mod: str, out_sym: str) -> int:
|
||||
"""Resolve a Name symbol's docstring owner into out_mod/out_sym; return 1 on success."""
|
||||
if trans is None or name_id is None or out_mod is None or out_sym is None: return 0
|
||||
if trans.ModuleSha1 is None: return 0
|
||||
# 1. local variable (function/global)
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 2. struct class (entry.ModuleSha1 distinguishes cross-module pre-registration)
|
||||
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(name_id)
|
||||
if se is not None:
|
||||
if se.ModuleSha1 is not None: string.strcpy(out_mod, se.ModuleSha1)
|
||||
else: string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 3. module name (rare); use StubMerger module-level functions to avoid a
|
||||
# cross-Mixin self call that would emit a nonexistent symbol.
|
||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id)
|
||||
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
|
||||
if mod_sha1 is not None:
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 4. fall back to the current module
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
def _resolve_doc_symbol_attr(self, trans: HT.Translator*, at: ast.Attribute*, out_mod: str, out_sym: str) -> int:
|
||||
"""Resolve a nested Attribute chain's docstring owner into out_mod/out_sym."""
|
||||
if trans is None or at is None or out_mod is None or out_sym is None: return 0
|
||||
if trans.ModuleSha1 is None: return 0
|
||||
# collect the Attribute chain (outermost attr first)
|
||||
DOC_CHAIN_MAX: int = 8
|
||||
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
|
||||
if items is None: return 0
|
||||
n_items: int = 0
|
||||
cur: ast.AST* = (ast.AST*)(at)
|
||||
while cur is not None and cur.kind() == ast.ASTKind.Attribute and n_items < DOC_CHAIN_MAX:
|
||||
cat: ast.Attribute* = ast.Attribute*(cur)
|
||||
if cat.attr is None: break
|
||||
string.strcpy(items + n_items * 128, cat.attr)
|
||||
n_items += 1
|
||||
cur = cat.value
|
||||
# leftmost must be a Name
|
||||
head_id: str = None
|
||||
if cur is not None and cur.kind() == ast.ASTKind.Name:
|
||||
hnm: ast.Name* = ast.Name*(cur)
|
||||
head_id = hnm.id
|
||||
if head_id is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
# head decision: module name or variable/class
|
||||
is_mod: int = 0
|
||||
mod_sha1: str = None
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, head_id) is None:
|
||||
mod_sha1 = StubMergerStore.LookupSha1ByModName(head_id)
|
||||
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
|
||||
if mod_sha1 is not None: is_mod = 1
|
||||
if is_mod != 0:
|
||||
# module-qualified: symbol = items reversed (excluding head_id)
|
||||
if n_items == 0:
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, head_id)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
total: sizet = 0
|
||||
for j in range(int(n_items)): total += string.strlen(items + j * 128) + 1
|
||||
sym_buf: bytes = stdlib.malloc(total + 1)
|
||||
if sym_buf is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
sym_buf[0] = '\0'
|
||||
for j in range(int(n_items)):
|
||||
if j > 0: string.strcat(sym_buf, ".")
|
||||
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, sym_buf)
|
||||
stdlib.free(sym_buf)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
# variable/class: symbol = head_id + '.' + items reversed
|
||||
total2: sizet = string.strlen(head_id)
|
||||
for j in range(int(n_items)): total2 += string.strlen(items + j * 128) + 1
|
||||
sym2: bytes = stdlib.malloc(total2 + 1)
|
||||
if sym2 is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
sym2[0] = '\0'
|
||||
string.strcat(sym2, head_id)
|
||||
for j in range(int(n_items)):
|
||||
string.strcat(sym2, ".")
|
||||
string.strcat(sym2, items + (n_items - 1 - j) * 128)
|
||||
se2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(head_id)
|
||||
if se2 is not None and se2.ModuleSha1 is not None: string.strcpy(out_mod, se2.ModuleSha1)
|
||||
else: string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, sym2)
|
||||
stdlib.free(sym2)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
# P3 migrated methods (from HandlesExprOps.vp: operator overload + binary ops)
|
||||
|
||||
# infer_expr_class - infer the struct class name from an expression (shared by
|
||||
# unannotated assignment and method dispatch); returns None if undecidable.
|
||||
def infer_expr_class(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator*) -> str:
|
||||
"""Infer the struct class name from an expression, or None if undecidable."""
|
||||
if node is None or trans is None: return None
|
||||
k: int = node.kind()
|
||||
# Call: class instantiation / method call
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl is None or cl.func is None: return None
|
||||
if cl.func.kind() == ast.ASTKind.Name:
|
||||
cand: str = (ast.Name*(cl.func)).id
|
||||
if cand is None: return None
|
||||
if self.Gen.StructGen.find_struct(cand) is not None: return cand
|
||||
return None
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
# Method call a.cross(b): resolve the return class from the vpi return
|
||||
# annotation cache (may differ from the receiver class).
|
||||
ca: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if ca is None or ca.attr is None or ca.value is None: return None
|
||||
# module-level call (Module.func()): receiver is an imported module name
|
||||
if ca.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm2: str = (ast.Name*(ca.value)).id
|
||||
if mod_nm2 is not None and trans._from_imports is not None:
|
||||
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, mod_nm2)
|
||||
if mod_sha1_2 is not None:
|
||||
temp_dir2: str = None
|
||||
if self.Gen.CallGenBase.TempDir is not None: temp_dir2 = self.Gen.CallGenBase.TempDir
|
||||
if temp_dir2 is not None: VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
|
||||
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(mod_sha1_2, None, ca.attr)
|
||||
if annot_ret2 is not None:
|
||||
ret_cls2: str = self._extract_annot_class_name(pool, annot_ret2)
|
||||
if ret_cls2 is not None: return ret_cls2
|
||||
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
|
||||
if obj_cls is None: return None
|
||||
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: return None
|
||||
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
|
||||
if cls_sha1 is None and trans.ModuleSha1 is not None: cls_sha1 = trans.ModuleSha1
|
||||
ret_cls: str = None
|
||||
if cls_sha1 is not None:
|
||||
temp_dir: str = None
|
||||
# cross sub-object access via self.Gen for CallGenBase.TempDir
|
||||
if self.Gen.CallGenBase.TempDir is not None: temp_dir = self.Gen.CallGenBase.TempDir
|
||||
if temp_dir is not None: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
|
||||
annot_ret: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, obj_cls, ca.attr)
|
||||
if annot_ret is not None: ret_cls = self._extract_annot_class_name(pool, annot_ret)
|
||||
return ret_cls
|
||||
return None
|
||||
# Name: source variable annotation
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm is None or nm.id is None: return None
|
||||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if ve is None: return None
|
||||
acn_n: str = self.Gen.VarGen.var_annot_class_name(ve)
|
||||
if acn_n is None: return None
|
||||
# Strip the module prefix (json.JsonValue -> JsonValue): the lookups use
|
||||
# bare names; generic names containing '[' are not truncated.
|
||||
acn_dot_n: str = string.strrchr(acn_n, 46)
|
||||
if acn_dot_n is not None and string.strchr(acn_n, 91) is None: acn_n = acn_dot_n + 1
|
||||
return acn_n
|
||||
# BinOp/UnaryOp: operator overload returns the same class object
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if bop is None or bop.left is None: return None
|
||||
dunder: str = self.Gen.ExprGenOps._binop_to_dunder(bop.op)
|
||||
if dunder is None: return None
|
||||
lhs_cls: str = self.infer_expr_class(pool, mod, bop.left, trans)
|
||||
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0: return None
|
||||
return lhs_cls
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uop: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uop is None or uop.operand is None: return None
|
||||
dunder: str = self.Gen.ExprGenOps._unaryop_to_dunder(uop.op)
|
||||
if dunder is None: return None
|
||||
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
|
||||
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: return None
|
||||
return opr_cls
|
||||
# Attribute: field annotation class name (recursive chain, e.g. p = r.origin)
|
||||
if k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
|
||||
return None
|
||||
# _cls_has_dunder - whether a class (or its MRO ancestors) has the dunder/method;
|
||||
# defined in this module or declared in a cross-module stub SHA1.
|
||||
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, class_name: str, dunder: str) -> int:
|
||||
"""Return 1 if the class or its MRO ancestors define the dunder method."""
|
||||
if class_name is None or dunder is None or mod is None: return 0
|
||||
lookup_name: str = pool.alloc(128)
|
||||
if lookup_name is None: return 0
|
||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None: return 1
|
||||
# walk MRO ancestors
|
||||
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||||
mi: int = 1
|
||||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||||
anc: str = rawptr(mro_ptr[mi])
|
||||
if anc is not None:
|
||||
anc_lookup: str = pool.alloc(128)
|
||||
if anc_lookup is not None:
|
||||
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None: return 1
|
||||
mi += 1
|
||||
# cross-module: a class with a SHA1 (method declared in a stub) counts as existing
|
||||
if self.Gen.StructGen.get_struct_sha1(class_name) is not None: return 1
|
||||
return 0
|
||||
# _extract_annot_class_name - extract a registered struct class name from a vpi
|
||||
# return annotation, splitting on '|' and taking the first hit.
|
||||
def _extract_annot_class_name(self, pool: memhub.MemBuddy*, annot: str) -> str:
|
||||
"""Extract a registered struct class name from return annotation text, or None."""
|
||||
if pool is None or annot is None: return None
|
||||
alen: sizet = string.strlen(annot)
|
||||
if alen == 0: return None
|
||||
seg: str = pool.alloc(256)
|
||||
if seg is None: return None
|
||||
start: sizet = 0
|
||||
i: sizet
|
||||
for i in range(int(alen)):
|
||||
if annot[i] == '|':
|
||||
s: sizet = start
|
||||
e: sizet = i
|
||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
|
||||
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
|
||||
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
|
||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
|
||||
seglen: sizet = e - s
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
seglen -= 6
|
||||
seg[seglen] = '\0'
|
||||
if self.Gen.StructGen.find_struct(seg) is not None: return seg
|
||||
# On a find_struct miss, retry with the last '.'-prefix (module
|
||||
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
|
||||
seg_dot2: str = string.strrchr(seg, 46)
|
||||
if seg_dot2 is not None and seg_dot2 != seg:
|
||||
seg_bare2: str = seg_dot2 + 1
|
||||
if self.Gen.StructGen.find_struct(seg_bare2) is not None: return seg_bare2
|
||||
start = i + 1
|
||||
# last segment (no '|' suffix)
|
||||
s: sizet = start
|
||||
e: sizet = alen
|
||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
|
||||
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
|
||||
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
|
||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
|
||||
seglen: sizet = e - s
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
seglen -= 6
|
||||
seg[seglen] = '\0'
|
||||
if self.Gen.StructGen.find_struct(seg) is not None: return seg
|
||||
# On a find_struct miss, retry with the last '.'-prefix (module
|
||||
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
|
||||
seg_dot3: str = string.strrchr(seg, 46)
|
||||
if seg_dot3 is not None and seg_dot3 != seg:
|
||||
seg_bare3: str = seg_dot3 + 1
|
||||
if self.Gen.StructGen.find_struct(seg_bare3) is not None: return seg_bare3
|
||||
return None
|
||||
@@ -0,0 +1,315 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
|
||||
|
||||
# ExprGenConst - split from ExprGenMixin (stage D): constants / global strings / list literals
|
||||
novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
"""Translation of constants, global strings and list literals."""
|
||||
|
||||
# Create global string constant
|
||||
def create_global_string(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, str_val: str, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Create a global string constant and return its i8* bitcast."""
|
||||
escaped: str = self.escape_llvm_string(pool, str_val)
|
||||
if escaped is None: return None
|
||||
|
||||
slen: sizet = string.strlen(str_val)
|
||||
count: int = int(slen + 1)
|
||||
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
arr_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, count)
|
||||
|
||||
# Use the module-level counter (trans._label_counter) to avoid cross-function naming conflicts
|
||||
str_idx: int = trans._label_counter
|
||||
trans._label_counter += 1
|
||||
|
||||
# Prefix string names with the SHA1 (same rule as function export) to avoid cross-module collisions
|
||||
gv_name: str = pool.alloc(48)
|
||||
if gv_name is None: return None
|
||||
if trans.ModuleSha1 is not None: viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
|
||||
else: viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
|
||||
|
||||
gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gv_name, arr_ty)
|
||||
if gv is None: return None
|
||||
mlirlite.module_add_global(mod, gv)
|
||||
|
||||
# Drop private linkage, same as funcs: declare in stub, define in text
|
||||
mlirlite.global_set_unnamed_addr(gv, 1)
|
||||
mlirlite.global_set_constant(gv, 1)
|
||||
# LLVM 22+ forbids initializers on external-linkage globals, so set internal
|
||||
gv.Linkage = "internal"
|
||||
gv.Initializer = escaped
|
||||
|
||||
arr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, arr_ty)
|
||||
gv_ref_name: str = pool.alloc(64)
|
||||
if gv_ref_name is None: return None
|
||||
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
|
||||
gv_ref: mlirlite.Value* = mlirlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
|
||||
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
|
||||
bc: mlirlite.Value* = mlirlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
||||
return bc
|
||||
# _explicit_width_int_const - 显式宽度字面量(`123_int32` / `0xFFFFFFFF_uint32`):
|
||||
# 按声明宽度出常量;值必须落在该宽度的可表示范围(有符号 [-2^(w-1), 2^(w-1)-1];无符号 [0, 2^w-1]),
|
||||
# 越界编译期 fail-fast(不允许隐式截断)。w==64 时 int64 全域都合法。
|
||||
def _explicit_width_int_const(self, pool: memhub.MemBuddy*, node: ast.AST*, w: int, is_unsigned: int, iv: int64) -> mlirlite.Value*:
|
||||
"""Explicit-width integer literal: strict range check, then a constant of exactly w bits."""
|
||||
ok: int = 1
|
||||
if is_unsigned != 0:
|
||||
if w < 64 and iv < 0: ok = 0
|
||||
elif w <= 62 and iv > (int64(1) << w) - 1: ok = 0
|
||||
else:
|
||||
if w < 64:
|
||||
lo: int64 = -(int64(1) << (w - 1))
|
||||
hi: int64 = (int64(1) << (w - 1)) - 1
|
||||
if iv < lo or iv > hi: ok = 0
|
||||
if ok == 0:
|
||||
wname: str = "int"
|
||||
if is_unsigned != 0: wname = "uint"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"integer literal {iv} does not fit the explicit width {wname}{w} (it would truncate); use a wider suffix or an explicit cast", "OverflowError")
|
||||
return mlirlite.const_int(pool, w, iv)
|
||||
# Translate constant expressions
|
||||
def translate_constant(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
|
||||
"""Translate a constant node (int/str/bool)."""
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn is None: return None
|
||||
ck: int = cn.const_kind
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
# Bit width: (1) if the target context is >32-bit int, use i64 directly - required
|
||||
# since atoll wraps 0xFFFF... to -1 as signed int64; (2) else i64 if out of i32 range, otherwise i32.
|
||||
iv: int64 = cn.int_val
|
||||
# 显式宽度后缀(_int<N>/_uint<N>/_i<N>/_u<N>):按声明宽度出常量并严格校验不截断
|
||||
if cn.int_width > 0: return self._explicit_width_int_const(pool, node, cn.int_width, cn.int_unsigned, iv)
|
||||
if target_ty is not None and self.is_ptr_type(target_ty) == 0 and self.get_llvm_type_bits(target_ty) > 32: return mlirlite.const_int64(pool, iv)
|
||||
# 位宽按值推导:i32 放不下、但 u32 放得下的字面量归为 32 位(u32)常量,而不是一律落 64 位
|
||||
# (否则 `crc ^ 0xFFFFFFFF` 的异或会变成 64 位,赋值回 32 位 uint 时报“隐式窄化禁止”)。
|
||||
# 文本用有符号等价(LLVM/MLIR 惯例,避免越界字面量);IntVal 保留无符号真值,
|
||||
# 使窄化检查按真实数值判定,不静默截断。
|
||||
if iv > 2147483647 and iv <= 4294967295:
|
||||
ty32: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
nm32: str = pool.alloc(32)
|
||||
if nm32 is None: return mlirlite.const_int32(pool, iv)
|
||||
viperlib.snprintf(nm32, 32, "%lld", iv - 4294967296)
|
||||
return mlirlite.ConstInt(pool, ty32, iv, nm32)
|
||||
if iv > 2147483647 or iv < -2147483648: return mlirlite.const_int64(pool, iv)
|
||||
return mlirlite.const_int32(pool, iv)
|
||||
elif cn.const_kind == ast.CONST_FLOAT:
|
||||
# 目标类型是 32 位浮点(float(1.5) / x: float = 1.5 / f32 形参)→ 直接出 f32 常量。
|
||||
# 旧实现无视 target_ty 一律先出 double 常量、再由 coerce_to_type fptrunc,
|
||||
# 现场:float(2.0) / float(1.0) 生成 f64 常量 + fptrunc,与双精度常量混算时又多出 fpext。
|
||||
# 其余情况(无目标类型 / double / 非浮点)保持 double 常量,由 coerce_to_type 负责转换。
|
||||
if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32:
|
||||
return mlirlite.ConstFloat(pool, target_ty, cn.float_val)
|
||||
double_ty: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||
return mlirlite.ConstFloat(pool, double_ty, cn.float_val)
|
||||
elif cn.const_kind == ast.CONST_CHAR:
|
||||
# Single-char literal (CONST_CHAR): default as char value (i32, coerce truncates to i8); if the target type is a pointer (str/bytes/t.CPtr) materialize a 1-char string, else inttoptr treats the char code as a pointer -> strlen crash. In 1a stage (declare_only=1) found_func=None so a misaligned target_ty is treated as garbage and skipped
|
||||
if target_ty is not None and (uint64(target_ty) & 7) == 0 and self.is_ptr_type(target_ty) != 0:
|
||||
cbuf: bytes = stdlib.malloc(2)
|
||||
if cbuf is None: return mlirlite.const_int32(pool, 0)
|
||||
cbuf[0] = char(cn.int_val)
|
||||
cbuf[1] = '\0'
|
||||
r_cs: mlirlite.Value* = self.create_global_string(builder, pool, mod, cbuf, trans)
|
||||
stdlib.free(cbuf)
|
||||
return r_cs
|
||||
cv: int64 = cn.int_val
|
||||
return mlirlite.const_int32(pool, cv)
|
||||
elif cn.const_kind == ast.CONST_STR:
|
||||
sv: str = cn.str_val
|
||||
if sv is None: return None
|
||||
r: mlirlite.Value* = self.create_global_string(builder, pool, mod, sv, trans)
|
||||
return r
|
||||
elif cn.const_kind == ast.CONST_BOOL:
|
||||
if cn.int_val != 0: return mlirlite.const_int32(pool, 1)
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
elif cn.const_kind == ast.CONST_NONE:
|
||||
# None -> i8* null (null pointer constant) for `p is None` / `p is not None` comparisons
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if i8_ty is None: return None
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
if i8_ptr_ty is None: return None
|
||||
rv_none: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||||
return rv_none
|
||||
return None
|
||||
# _infer_elem_type_from_value - infer the list element type name from an LLVM Value.
|
||||
# Used for element type inference of list literals [a, b, c].
|
||||
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*, value: mlirlite.Value*) -> str:
|
||||
"""Infer the list element type name from an LLVM Value; None if unmappable."""
|
||||
if value is None or value.Ty is None: return None
|
||||
ty: mlirlite.LLVMType* = value.Ty
|
||||
bits: int = self.get_llvm_type_bits(ty)
|
||||
if bits != 0:
|
||||
if bits == 32: return "int"
|
||||
if bits == 64: return "CSizeT"
|
||||
if bits == 8: return "CInt8T"
|
||||
if bits == 16: return "CInt16T"
|
||||
return None
|
||||
if self.is_ptr_type(ty) != 0: return "str"
|
||||
fbits: int = self.get_llvm_float_bits(ty)
|
||||
if fbits != 0:
|
||||
if fbits == 64: return "CDouble"
|
||||
if fbits == 32: return "CFloat"
|
||||
return None
|
||||
# _find_pool_var - find the pool variable in the context.
|
||||
# Tries "pool", "_mbuddy", "mbuddy", "mb" in order.
|
||||
def _find_pool_var(self, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Find the pool variable in context (pool/_mbuddy/mbuddy/mb), else fall back."""
|
||||
if trans is None: return None
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
|
||||
if alloca is not None: return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
|
||||
if alloca is not None: return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mbuddy")
|
||||
if alloca is not None: return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
|
||||
if alloca is not None: return alloca
|
||||
# Fallback: create a module-mangled @_mbuddy definition (linkonce_odr + zeroinitializer),
|
||||
# matching the SHA1-prefixed global naming; a real crash exposes an uninitialized pool (fail-fast).
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
if pool is None or trans.Module is None or trans.SymTab is None: return None
|
||||
# First check whether it is already registered in the module scope (avoid duplicate globals)
|
||||
existing: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
|
||||
if existing is not None: return existing
|
||||
# Create the i8* type (t.CPtr = i8*)
|
||||
i8_ty_fb: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_fb)
|
||||
# Module-mangled name (@<module-sha1>._mbuddy)
|
||||
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
|
||||
# Create a linkonce_odr definition (linker-mergeable, self-contained for linking)
|
||||
gv_ext: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
|
||||
if gv_ext is not None:
|
||||
gv_ext.Linkage = "linkonce_odr"
|
||||
gv_ext.Initializer = "zeroinitializer"
|
||||
mlirlite.module_add_global(trans.Module, gv_ext)
|
||||
# Create the Value reference (@<module-sha1>._mbuddy, type i8**)
|
||||
i8_ptr_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty_fb)
|
||||
ref_name_fb: str = pool.alloc(96)
|
||||
if ref_name_fb is not None:
|
||||
# Mangled names start with a digit (SHA1 prefix), so the reference must be quoted (see make_global_ref)
|
||||
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0: viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
|
||||
else: viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
|
||||
gv_ref_fb: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
||||
# Register in the module scope to avoid duplicate creation
|
||||
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
||||
return gv_ref_fb
|
||||
# translate_list_literal - lower [a, b, c] to list[T](pool) construction + append calls; elem_type_name None means infer from elements; returns Ptr(list[T] struct) or None
|
||||
def translate_list_literal(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, elem_type_name: str, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Lower a list literal to list[T](pool) construction plus append calls."""
|
||||
if node is None or trans is None: return None
|
||||
|
||||
list_node: ast.List* = ast.List*(node)
|
||||
if list_node is None: return None
|
||||
|
||||
elts: list[ast.AST*]* = list_node.elts
|
||||
elts_count: sizet = 0
|
||||
if elts is not None: elts_count = len(elts)
|
||||
|
||||
# Infer element type
|
||||
inferred_type_name: str = elem_type_name
|
||||
first_elem_val: mlirlite.Value* = None
|
||||
if inferred_type_name is None:
|
||||
if elts_count > 0:
|
||||
first_elem_node: ast.AST* = elts.get(0)
|
||||
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
|
||||
if first_elem_val is None or first_elem_val.Ty is None:
|
||||
# fail-fast: first-element translation failed/Value type missing -> abort now (surface root cause)
|
||||
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list literal element type inference failed (Value type missing)", "TypeError")
|
||||
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
|
||||
if inferred_type_name is None:
|
||||
# fail-fast: first-element Value type cannot map to a list element type -> abort now
|
||||
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list literal element type inference failed (Value type missing)", "TypeError")
|
||||
else:
|
||||
# Empty list has no element to infer, keep default str (list[str])
|
||||
inferred_type_name = "str"
|
||||
|
||||
# Find the pool variable
|
||||
pool_alloca: mlirlite.Value* = self._find_pool_var(trans)
|
||||
if pool_alloca is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
|
||||
return None
|
||||
|
||||
# Load the pool variable value (i8*)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
pool_val: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, pool_alloca)
|
||||
if pool_val is None: return None
|
||||
|
||||
# Specialize the list[T] class (elem_size=8: str is a pointer; 4 would overlap elements)
|
||||
type_args: list[str]* = list[str](pool, 8)
|
||||
type_args.append(inferred_type_name)
|
||||
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
|
||||
if spec_name is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
|
||||
return None
|
||||
|
||||
# Find the struct entry
|
||||
struct_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
|
||||
if struct_entry is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
|
||||
return None
|
||||
struct_ty: mlirlite.LLVMType* = struct_entry.Ty
|
||||
if struct_ty is None: return None
|
||||
|
||||
# alloca temporary list storage; ensure the cross-module struct definition is
|
||||
# fully available in the current module (so alloca can size it).
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||||
tmp: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
|
||||
if tmp is None: return None
|
||||
|
||||
# Storage pointer: alloca by default, or the pointer returned by __new__ if present.
|
||||
# __new__ is optional and only called when the generic template defines it (consistent with ctor lowering).
|
||||
storage_ptr: mlirlite.Value* = tmp
|
||||
has_new_flag: int = 0
|
||||
if struct_entry is not None: has_new_flag = struct_entry.HasNew
|
||||
if has_new_flag != 0:
|
||||
new_args: sizet* = pool.alloc(8 * 32)
|
||||
if new_args is None: return None
|
||||
string.memset(new_args, 0, 8 * 32)
|
||||
new_args[0] = sizet(pool_val)
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
|
||||
if new_ptr is not None: storage_ptr = new_ptr
|
||||
|
||||
# Call __before_init__(storage_ptr)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
|
||||
|
||||
# Call __init__(storage_ptr, pool_val, elem_size=0)
|
||||
init_args: sizet* = pool.alloc(8 * 32)
|
||||
if init_args is None: return None
|
||||
string.memset(init_args, 0, 8 * 32)
|
||||
init_args[0] = sizet(pool_val)
|
||||
elem_size_val: mlirlite.Value* = mlirlite.const_int64(pool, 0)
|
||||
init_args[1] = sizet(elem_size_val)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
|
||||
|
||||
# Call append(storage_ptr, elem_val) for each element
|
||||
if elts_count > 0:
|
||||
# The first element may already be translated
|
||||
if first_elem_val is not None:
|
||||
append_args: sizet* = pool.alloc(8 * 32)
|
||||
if append_args is not None:
|
||||
string.memset(append_args, 0, 8 * 32)
|
||||
append_args[0] = sizet(first_elem_val)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
||||
|
||||
# Translate the remaining elements
|
||||
for i in range(1, int(elts_count)):
|
||||
elem_node_i: ast.AST* = elts.get(i)
|
||||
elem_val_i: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
||||
if elem_val_i is not None:
|
||||
ap_args: sizet* = pool.alloc(8 * 32)
|
||||
if ap_args is not None:
|
||||
string.memset(ap_args, 0, 8 * 32)
|
||||
ap_args[0] = sizet(elem_val_i)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
|
||||
|
||||
return storage_ptr
|
||||
@@ -0,0 +1,407 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
|
||||
# f-string translation constants (local copy of ExprGenMixin's to avoid a cross-module
|
||||
# CDefine lookup bug: ExprGenFstr compiles before ExprGenMixin, so the table is unregistered).
|
||||
define FSTR_BUF_SIZE = 1024
|
||||
define FSTR_SPEC_MAX = 64
|
||||
define FSTR_FMT_MAX = 64
|
||||
|
||||
# ExprGenFstr - ExprGenMixin split (stage D): f-string/formatted-value translation
|
||||
novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
"""f-string and formatted-value translation."""
|
||||
|
||||
# translate_joined_str - f-string translation (aligned with TPC _HandleJoinedStrLlvm):
|
||||
# join literals and format specs, snprintf into a per-site i8* stack buffer (no escape).
|
||||
def translate_joined_str(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Lower an f-string to snprintf into a per-site i8* stack buffer."""
|
||||
if node is None or builder is None or trans is None: return None
|
||||
js: ast.JoinedStr* = ast.JoinedStr*(node)
|
||||
if js is None or js.values is None: return None
|
||||
|
||||
parts: list[ast.AST*]* = js.values
|
||||
pn: sizet = len(parts)
|
||||
|
||||
# Size the format buffer from the actual parts: each literal may double ('%' -> '%%') and
|
||||
# each conversion contributes a short spec. A fixed 1024 silently dropped trailing
|
||||
# literals/converters while their arguments were still appended -> wrong runtime output.
|
||||
fmt_cap: sizet = 16
|
||||
for pi0 in range(int(pn)):
|
||||
part0: ast.AST* = parts.get(pi0)
|
||||
if part0 is None: continue
|
||||
if part0.kind() == ast.ASTKind.Constant:
|
||||
pc0: ast.Constant* = ast.Constant*(part0)
|
||||
if pc0.const_kind == ast.CONST_STR and pc0.str_val is not None:
|
||||
fmt_cap = fmt_cap + string.strlen(pc0.str_val) * 2
|
||||
elif part0.kind() == ast.ASTKind.FormattedValue:
|
||||
fmt_cap = fmt_cap + FSTR_SPEC_MAX + 16
|
||||
|
||||
# Compile-time format string buffer (pool-allocated)
|
||||
fmt_buf: str = pool.alloc(fmt_cap)
|
||||
if fmt_buf is None: return None
|
||||
fmt_buf[0] = '\0'
|
||||
|
||||
# Runtime argument list (linked by Value.Next, matching the printf call)
|
||||
arg_head: mlirlite.Value* = None
|
||||
arg_tail: mlirlite.Value* = None
|
||||
arg_count: int = 0
|
||||
|
||||
for pi in range(int(pn)):
|
||||
part: ast.AST* = parts.get(pi)
|
||||
if part is None: continue
|
||||
if part.kind() == ast.ASTKind.Constant:
|
||||
pcn: ast.Constant* = ast.Constant*(part)
|
||||
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
|
||||
self._fstr_append_literal(fmt_buf, fmt_cap, pcn.str_val)
|
||||
continue
|
||||
if part.kind() != ast.ASTKind.FormattedValue: continue
|
||||
fv: ast.FormattedValue* = ast.FormattedValue*(part)
|
||||
if fv.value is None: continue
|
||||
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
||||
if val is None:
|
||||
# fail-fast: a failed formatted-value translation must abort compilation
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string formatted value translation failed (expression returned None)", "TypeError")
|
||||
continue
|
||||
# Object defines __str__ and no format is given -> use its returned string (like TPC)
|
||||
if fv.format_spec is None:
|
||||
str_ov_fs: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
|
||||
if str_ov_fs is not None: val = str_ov_fs
|
||||
# Determine the C format specifier (format_spec first, else infer from the value type)
|
||||
c_fmt: str = None
|
||||
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||
if c_fmt is None:
|
||||
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||
# Append the format specifier
|
||||
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
|
||||
# Convert the argument type (i8/i16->i32, bool->i32, f32->f64)
|
||||
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None: cast_val = val
|
||||
cast_val.Next = None
|
||||
if arg_head is None: arg_head = cast_val
|
||||
else: arg_tail.Next = cast_val
|
||||
arg_tail = cast_val
|
||||
arg_count += 1
|
||||
|
||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||
def translate_formatted_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Translate a bare FormattedValue without any literal parts."""
|
||||
if node is None or builder is None or trans is None: return None
|
||||
fv: ast.FormattedValue* = ast.FormattedValue*(node)
|
||||
if fv is None or fv.value is None: return None
|
||||
# A bare FormattedValue contributes exactly one conversion spec (bounded by FSTR_SPEC_MAX
|
||||
# plus the C prefix/width); size the buffer from that instead of the fixed 1024.
|
||||
fmt_cap: sizet = FSTR_SPEC_MAX + 64
|
||||
fmt_buf: str = pool.alloc(fmt_cap)
|
||||
if fmt_buf is None: return None
|
||||
fmt_buf[0] = '\0'
|
||||
arg_head: mlirlite.Value* = None
|
||||
arg_tail: mlirlite.Value* = None
|
||||
arg_count: int = 0
|
||||
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
||||
if val is None:
|
||||
# fail-fast: a failed bare FormattedValue translation must abort compilation
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string formatted value translation failed (expression returned None)", "TypeError")
|
||||
else:
|
||||
# Object defines __str__ and no format is given -> use its returned string (like TPC)
|
||||
if fv.format_spec is None:
|
||||
str_ov_sf: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
|
||||
if str_ov_sf is not None: val = str_ov_sf
|
||||
c_fmt: str = None
|
||||
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||
if c_fmt is None:
|
||||
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
|
||||
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None: cast_val = val
|
||||
cast_val.Next = None
|
||||
arg_head = cast_val
|
||||
arg_tail = cast_val
|
||||
arg_count = 1
|
||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||
def _fstr_finalize(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, fmt_buf: str, arg_head: mlirlite.Value*, arg_tail: mlirlite.Value*, arg_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Emit the final snprintf over the global format and per-site buffer."""
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if fmt_buf is None or fmt_buf[0] == '\0':
|
||||
# Empty f-string (f"") -> empty string constant
|
||||
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
|
||||
# Format string -> global string constant (i8*)
|
||||
fmt_ptr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
|
||||
if fmt_ptr is None: return None
|
||||
# Result buffer: a per-site entry alloca (thread-safe, no runtime dependency), so the
|
||||
# f-string result is a function-local temporary that must not escape; copy to escape.
|
||||
buf_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
|
||||
buf_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
|
||||
if buf_alloca is None: return None
|
||||
zero_i32: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
buf_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
|
||||
if buf_ptr is None: return None
|
||||
# f-string lowers to snprintf, declared in includes/stdio.vp. If the source imports no
|
||||
# module declaring it, add the declare here (implicit-provide); i8*, i64, i8* + varargs.
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, "snprintf") is None:
|
||||
snprintf_decl: mlirlite.Function* = mlirlite.create_declare(pool, mod, "snprintf", i32_ty)
|
||||
if snprintf_decl is not None:
|
||||
snprintf_decl.IsVarArg = 1
|
||||
# buf/fmt are pointer params (i8*), not the element type i8: a bare i8 declare
|
||||
# would cause an MLIR 'llvm.call' operand type mismatch ('!llvm.ptr' != 'i8').
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "buf")
|
||||
mlirlite.add_param(pool, snprintf_decl, mlirlite.Int64(pool), "size")
|
||||
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "fmt")
|
||||
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
|
||||
size_val: mlirlite.Value* = mlirlite.const_int64(pool, FSTR_BUF_SIZE)
|
||||
buf_ptr.Next = size_val
|
||||
size_val.Next = fmt_ptr
|
||||
fmt_ptr.Next = arg_head
|
||||
if arg_tail is not None: arg_tail.Next = None
|
||||
mlirlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
||||
return buf_ptr
|
||||
def _fstr_append_literal(self, fmt_buf: str, cap: sizet, lit: str) -> None:
|
||||
"""Append a literal to the format string, escaping percent signs; fail-fast on overflow."""
|
||||
if fmt_buf is None or lit is None: return
|
||||
cur_len: sizet = string.strlen(fmt_buf)
|
||||
src_len: sizet = string.strlen(lit)
|
||||
# Worst case: every character expands to "%%".
|
||||
if cur_len + src_len * 2 + 1 > cap:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + up to {src_len * 2} escaped + 1 NUL); shorten the literal", "OverflowError")
|
||||
si: sizet = 0
|
||||
while si < src_len:
|
||||
ch: char = lit[si]
|
||||
if ch == '%':
|
||||
fmt_buf[cur_len] = '%'
|
||||
fmt_buf[cur_len + 1] = '%'
|
||||
cur_len += 2
|
||||
else:
|
||||
fmt_buf[cur_len] = ch
|
||||
cur_len += 1
|
||||
si += 1
|
||||
fmt_buf[cur_len] = '\0'
|
||||
def _fstr_append_raw(self, fmt_buf: str, cap: sizet, s: str) -> None:
|
||||
"""Append a raw string without percent escaping; fail-fast on overflow."""
|
||||
if fmt_buf is None or s is None: return
|
||||
cur_len: sizet = string.strlen(fmt_buf)
|
||||
src_len: sizet = string.strlen(s)
|
||||
if cur_len + src_len + 1 > cap:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + {src_len} to append + 1 NUL); shorten the format spec", "OverflowError")
|
||||
i: sizet = 0
|
||||
while i < src_len:
|
||||
fmt_buf[cur_len + i] = s[i]
|
||||
i += 1
|
||||
fmt_buf[cur_len + src_len] = '\0'
|
||||
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*, spec_node: ast.AST*, val: mlirlite.Value*, arg_node: ast.AST*) -> str:
|
||||
"""Convert a Python format_spec into a C printf format string."""
|
||||
if spec_node is None or pool is None or val is None: return None
|
||||
# 1. Extract the spec string (JoinedStr with constants only)
|
||||
spec: str = pool.alloc(FSTR_SPEC_MAX)
|
||||
if spec is None: return None
|
||||
spec[0] = '\0'
|
||||
sk: int = spec_node.kind()
|
||||
if sk == ast.ASTKind.JoinedStr:
|
||||
jss: ast.JoinedStr* = ast.JoinedStr*(spec_node)
|
||||
if jss.values is None: return None
|
||||
vn: sizet = len(jss.values)
|
||||
for vi in range(int(vn)):
|
||||
vpart: ast.AST* = jss.values.get(vi)
|
||||
if vpart is None: continue
|
||||
if vpart.kind() != ast.ASTKind.Constant: return None
|
||||
vcn: ast.Constant* = ast.Constant*(vpart)
|
||||
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None: return None
|
||||
# fail-fast: a truncated spec can still look valid (e.g. ">10.5f" cut to ">10."),
|
||||
# which silently changes the output format.
|
||||
if string.strlen(spec) + string.strlen(vcn.str_val) >= FSTR_SPEC_MAX - 1:
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
|
||||
string.strcat(spec, vcn.str_val)
|
||||
elif sk == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(spec_node)
|
||||
if cn.const_kind != ast.CONST_STR or cn.str_val is None: return None
|
||||
if string.strlen(cn.str_val) >= FSTR_SPEC_MAX - 1:
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
|
||||
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
|
||||
else: return None
|
||||
slen: sizet = string.strlen(spec)
|
||||
if slen == 0: return None
|
||||
|
||||
# 2. Parse segment by segment: [fill]align / sign / # / 0 / width / , / .precision / type
|
||||
pos: sizet = 0
|
||||
align_char: char = 0
|
||||
if pos < slen:
|
||||
ch0: char = spec[pos]
|
||||
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
|
||||
align_char = ch0
|
||||
pos += 1
|
||||
elif pos + 1 < slen:
|
||||
ch1: char = spec[pos + 1]
|
||||
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
|
||||
align_char = ch1
|
||||
pos += 2
|
||||
c_flags: str = pool.alloc(FSTR_FMT_MAX)
|
||||
c_width: str = pool.alloc(32)
|
||||
c_prec: str = pool.alloc(32)
|
||||
if c_flags is None or c_width is None or c_prec is None: return None
|
||||
c_flags[0] = '\0'
|
||||
c_width[0] = '\0'
|
||||
c_prec[0] = '\0'
|
||||
# sign
|
||||
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
|
||||
if spec[pos] == '+': string.strcat(c_flags, "+")
|
||||
elif spec[pos] == ' ': string.strcat(c_flags, " ")
|
||||
pos += 1
|
||||
# alt '#'
|
||||
if pos < slen and spec[pos] == '#':
|
||||
string.strcat(c_flags, "#")
|
||||
pos += 1
|
||||
# zero '0'
|
||||
if pos < slen and spec[pos] == '0':
|
||||
string.strcat(c_flags, "0")
|
||||
pos += 1
|
||||
# Left align '<'/'^' -> '-' (C semantics give '-' priority over '0')
|
||||
if align_char == '<' or align_char == '^': string.strcat(c_flags, "-")
|
||||
# width digits
|
||||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||||
wl: sizet = string.strlen(c_width)
|
||||
if wl < 30:
|
||||
c_width[wl] = spec[pos]
|
||||
c_width[wl + 1] = '\0'
|
||||
pos += 1
|
||||
# comma ',' -> C printf has no thousands separator, ignore
|
||||
if pos < slen and spec[pos] == ',': pos += 1
|
||||
# .precision
|
||||
if pos < slen and spec[pos] == '.':
|
||||
string.strcat(c_prec, ".")
|
||||
pos += 1
|
||||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||||
pl: sizet = string.strlen(c_prec)
|
||||
if pl < 30:
|
||||
c_prec[pl] = spec[pos]
|
||||
c_prec[pl + 1] = '\0'
|
||||
pos += 1
|
||||
if string.strlen(c_prec) == 1:
|
||||
return None # a lone '.' is invalid
|
||||
# type char
|
||||
fmt_type: char = 0
|
||||
if pos < slen:
|
||||
fmt_type = spec[pos]
|
||||
pos += 1
|
||||
if pos != slen: return None # extra characters cannot be parsed
|
||||
|
||||
# 3. Length modifier (by value type bit width)
|
||||
len_mod: str = pool.alloc(4)
|
||||
if len_mod is None: return None
|
||||
len_mod[0] = '\0'
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
# Do not write a length modifier for 32-bit (N14): `{x:08X}` passes an i32, but `l`
|
||||
# would make %08lX (long) mismatch on LP64 and misread the varargs.
|
||||
if ibits == 64: string.strcpy(len_mod, "ll")
|
||||
elif ibits == 16: string.strcpy(len_mod, "h")
|
||||
elif ibits == 8: string.strcpy(len_mod, "hh")
|
||||
|
||||
# 4. Assemble the C format string
|
||||
out: str = pool.alloc(FSTR_FMT_MAX)
|
||||
if out is None: return None
|
||||
out[0] = '\0'
|
||||
string.strcat(out, "%")
|
||||
string.strcat(out, c_flags)
|
||||
string.strcat(out, c_width)
|
||||
string.strcat(out, c_prec)
|
||||
if fmt_type == 0:
|
||||
# No type: infer from the value type
|
||||
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if ibits != 0:
|
||||
if ibits == 8: string.strcat(out, "c")
|
||||
elif ibits == 1: string.strcat(out, "d")
|
||||
else:
|
||||
string.strcat(out, len_mod)
|
||||
string.strcat(out, "d")
|
||||
elif fbits2 != 0: string.strcat(out, "f")
|
||||
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: string.strcat(out, "s")
|
||||
else: string.strcat(out, "d")
|
||||
elif fmt_type == 'c': string.strcat(out, "c")
|
||||
elif fmt_type == 'f' or fmt_type == 'F': string.strcat(out, "f")
|
||||
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
|
||||
tmp_ch: str = pool.alloc(2)
|
||||
if tmp_ch is None: return None
|
||||
tmp_ch[0] = fmt_type
|
||||
tmp_ch[1] = '\0'
|
||||
string.strcat(out, tmp_ch)
|
||||
elif fmt_type == 's': string.strcat(out, "s")
|
||||
elif fmt_type == '%': string.strcat(out, "%")
|
||||
elif fmt_type == 'b':
|
||||
return None # binary is unsupported
|
||||
elif fmt_type == 'd' or fmt_type == 'i':
|
||||
string.strcat(out, len_mod)
|
||||
string.strcat(out, "d")
|
||||
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
|
||||
string.strcat(out, len_mod)
|
||||
tmp_ch2: str = pool.alloc(2)
|
||||
if tmp_ch2 is None: return None
|
||||
tmp_ch2[0] = fmt_type
|
||||
tmp_ch2[1] = '\0'
|
||||
string.strcat(out, tmp_ch2)
|
||||
else: return None
|
||||
return out
|
||||
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*, val: mlirlite.Value*, arg_node: ast.AST*, trans: HT.Translator*) -> str:
|
||||
"""Infer the C printf format from the value type when no format_spec is given."""
|
||||
if val is None or pool is None: return None
|
||||
out: str = pool.alloc(16)
|
||||
if out is None: return None
|
||||
out[0] = '\0'
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if ibits == 1: return "%d" # bool
|
||||
if ibits == 8: return "%c" # character
|
||||
if ibits != 0:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
||||
if ibits == 64: string.strcpy(out, "%llu")
|
||||
else: string.strcpy(out, "%u")
|
||||
else:
|
||||
if ibits == 64: string.strcpy(out, "%lld")
|
||||
else: string.strcpy(out, "%d")
|
||||
return out
|
||||
if fbits != 0: return "%f"
|
||||
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: return "%s"
|
||||
# fail-fast: missing value type (not int/float/pointer); cannot infer the C format
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||
return None
|
||||
def _fstr_cast_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, val: mlirlite.Value*, c_fmt: str, arg_node: ast.AST*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Cast the value to the type required by the C format specifier."""
|
||||
if val is None or c_fmt is None: return None
|
||||
# Find the format type char (last letter, ignoring l/h length modifiers)
|
||||
fmt_char: char = 0
|
||||
flen: sizet = string.strlen(c_fmt)
|
||||
fi: sizet = flen
|
||||
while fi > 0:
|
||||
fi -= 1
|
||||
ch: char = c_fmt[fi]
|
||||
if ch == '%': break
|
||||
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
|
||||
if ch != 'l' and ch != 'h':
|
||||
fmt_char = ch
|
||||
break
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if fmt_char == 's': return val
|
||||
if fmt_char == 'c':
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
if ibits != 0 and ibits < 32: return mlirlite.build_zext(builder, val, i32_ty)
|
||||
return val
|
||||
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
|
||||
ibits = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
if ibits == 1: return mlirlite.build_zext(builder, val, i32_ty)
|
||||
if ibits == 8 or ibits == 16:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0: return mlirlite.build_zext(builder, val, i32_ty)
|
||||
return mlirlite.build_sext(builder, val, i32_ty)
|
||||
return val
|
||||
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' or fmt_char == 'g' or fmt_char == 'G':
|
||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if fbits == 32: return mlirlite.build_fpext(builder, val, mlirlite.Double(pool))
|
||||
return val
|
||||
return val
|
||||
@@ -0,0 +1,81 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import memhub
|
||||
import mlirlite
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
|
||||
|
||||
# docstring registry capacities (__doc__ magic attribute support).
|
||||
# DOC_MAX: max entries; DOC_KEY_SZ: key "{module_sha1}.{symbol}" width; DOC_VAL_SZ: content width.
|
||||
define DOC_MAX = 512
|
||||
define DOC_KEY_SZ = 192
|
||||
define DOC_VAL_SZ = 4096
|
||||
|
||||
# f-string translation constants (aligned with TPC HandlesExprFormat).
|
||||
# FSTR_BUF_SIZE: runtime stack buffer; FSTR_SPEC_MAX: format_spec parse width; FSTR_FMT_MAX: printf format width.
|
||||
define FSTR_BUF_SIZE = 1024
|
||||
define FSTR_SPEC_MAX = 64
|
||||
define FSTR_FMT_MAX = 64
|
||||
|
||||
|
||||
# ExprGenMixin - expression handling mixin (objectified P3).
|
||||
|
||||
|
||||
# ExprGenMixin - expressions / constants / subscripts / attributes / operators.
|
||||
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
"""Expression-handling mixin (pure methods, no state fields)."""
|
||||
|
||||
# Translate a value expression (RHS dispatch) - module-level version.
|
||||
def translate_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""Translate a value expression to an LLVM Value (depth-guard wrapper; see _translate_value_impl).
|
||||
target_ty = assignment/parameter type context; use_ctx = real call site for CDefine width checks."""
|
||||
if trans is not None:
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "expression translation nested too deep (suspected AST self-reference cycle or translation bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
rv: mlirlite.Value* = self._translate_value_impl(builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty, use_ctx)
|
||||
if trans is not None: trans.TranslateDepth -= 1
|
||||
return rv
|
||||
|
||||
def _translate_value_impl(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""Translate a value expression to an LLVM Value."""
|
||||
if node is None: return None
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
rv: mlirlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
|
||||
return rv
|
||||
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
|
||||
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans, target_ty)
|
||||
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
|
||||
# M2.4 `new stack|heap|memgr(x) [slife|hlife] T(args)`:只换"取存储",其余复用构造序列
|
||||
elif k == ast.ASTKind.New: return self.Gen.CallGenCtor._translate_new_expr(pool, builder, mod, ast.New*(node), trans)
|
||||
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans, target_ty)
|
||||
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
|
||||
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.List: return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
|
||||
elif k == ast.ASTKind.IfExp: return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Lambda: return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Await:
|
||||
# Coroutine await (VPC-M1): inline-translate when the await target is an async call.
|
||||
# Returns the child coroutine's i32 ret; top-level await is handled by AsyncGen.
|
||||
aw: ast.Await* = ast.Await*(node)
|
||||
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
|
||||
aw_cl: ast.Call* = ast.Call*(aw.value)
|
||||
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
|
||||
aw_nm: ast.Name* = ast.Name*(aw_cl.func)
|
||||
if aw_nm.id is not None and trans is not None:
|
||||
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0: return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
|
||||
return None
|
||||
elif k == ast.ASTKind.Starred:
|
||||
# *x in a value context: the parser tags '*' as Starred, but Viper has no unpacking.
|
||||
# Treat it as C unary deref (*ptr -> load pointee) by building a Deref UnaryOp.
|
||||
st: ast.Starred* = ast.Starred*(node)
|
||||
if st is None or st.value is None: return None
|
||||
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
|
||||
if deref_node is None: return None
|
||||
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans)
|
||||
return None
|
||||
@@ -0,0 +1,954 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
|
||||
# ExprGenName - split out of ExprGenMixin (stage D): Name/Subscript and subscript access
|
||||
novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
"""Name/Subscript subscript access helpers."""
|
||||
|
||||
def _cut_from_import_mod(self, pool: memhub.MemBuddy*, raw: str) -> str:
|
||||
"""Copy the module name in raw into its own buffer, cut at space/colon/NUL; None when invalid."""
|
||||
if raw is None: return None
|
||||
mbl_c: sizet = 0
|
||||
while raw[mbl_c] != ' ' and raw[mbl_c] != '\0' and raw[mbl_c] != ':': mbl_c += 1
|
||||
if mbl_c == 0: return None
|
||||
buf_c: str = pool.alloc(mbl_c + 1)
|
||||
if buf_c is None: return None
|
||||
string.strncpy(buf_c, raw, mbl_c)
|
||||
buf_c[mbl_c] = '\0'
|
||||
return buf_c
|
||||
|
||||
def translate_name_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""Translate a variable reference (Name node) to a load; target_ty/use_ctx guide width."""
|
||||
# mod must come from trans.Module: the signature has no mod param, and a bare `mod`
|
||||
# would resolve to None, silently dropping string CDefine globals (llc undefined value).
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm is None: return None
|
||||
nm_id: str = nm.id
|
||||
if nm_id is None: return None
|
||||
# P2 willFree:读取已被"消费"的变量(其所有权被 @life(willFree(...)) 标注的调用夺取)→ fail-fast。
|
||||
# 只在**读**路径检查;对同名变量重新赋值会清除失效态(见 AssignGenMixin)。
|
||||
if trans is not None:
|
||||
if self.Gen.VarGen._FreedCheck(self.Gen.VarGen.lookup_var(trans.SymTab, nm_id)) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"cannot use '{nm_id}': already released by a @life(willFree(...)) call", "LifetimeError")
|
||||
# CDefine compile-time constant: return the constant (float -> double, int -> i32).
|
||||
# Existing variables win over CDefine table entries injected from vpi stubs (lookup_var guard).
|
||||
cdef_val_d: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
if trans is None or self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
|
||||
# String CDefine (e.g. ZLIB_VERSION: t.CDefine = "1.3.2"): return a global
|
||||
# string (i8*) for `return ZLIB_VERSION`.
|
||||
cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
|
||||
if cdef_sv is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv, trans)
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"CDefine string value missing: {nm_id}", "InternalCompilerError")
|
||||
# 浮点常量:声明为 32 位 float(`define PI: float = ...`)时直接出 f32 常量,
|
||||
# 否则出 f64 —— 出 f64 会把后续 f32 字面量整体提升成 double(fpext/cvtss2sd)。
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), cdef_val_d)
|
||||
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), cdef_val_d)
|
||||
# Integer CDefine: re-look-up the full 64-bit value (a double round-trip loses
|
||||
# precision and would generate a wrong constant).
|
||||
cdef_val_c: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_id)
|
||||
# Use-site width (passed via target_ty; use_ctx carries the site kind for messages):
|
||||
# wider -> widen in place; narrower -> fail-fast; unknown -> the existing 32/64 default.
|
||||
uw_val: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val_c, target_ty, use_ctx)
|
||||
if uw_val is not None: return uw_val
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val_c)
|
||||
|
||||
# Not found locally: try cross-module .vpi lookup, only for ALL_CAPS names so ordinary
|
||||
# variable names never trigger the costly cross-module source read.
|
||||
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(nm_id) != 0:
|
||||
if trans is not None:
|
||||
# ALL_CAPS may also be a plain variable (e.g. CAM/FWD/RIGHT/UP: V3* module globals);
|
||||
# an existing variable in scope takes priority, skipping the cross-module CDefine lookup.
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None:
|
||||
cdef_val: int64 = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, nm_id, trans._from_imports, trans._imported_modules, 0, node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
|
||||
cdef_sv2: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
|
||||
if cdef_sv2 is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv2, trans)
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"CDefine string value missing: {nm_id}", "InternalCompilerError")
|
||||
# Cross-module float CDefine is cached as double; return a float constant
|
||||
# rather than a truncated integer.
|
||||
dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
|
||||
# 同本模块路径:f32 声明的常量直接出 f32 常量(跨模块缓存已带宽度种类)。
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nm)
|
||||
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nm)
|
||||
# Same use-site width rule (cross-module CDefine)
|
||||
uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx)
|
||||
if uw_x is not None: return uw_x
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val)
|
||||
|
||||
# CDefine lookup failed: from-import module-level globals (e.g. `from Phase2 import
|
||||
# PA_DEPS_MAX_STR`, an i64 global) are located by suffix after confirming the source module.
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
fi_mod_raw: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_id, 0)
|
||||
if fi_mod_raw is not None:
|
||||
# Look up by the source module's SHA1 prefix to avoid same-name suffix collisions;
|
||||
# fi_mod_raw is not NUL-terminated, so truncate before the SHA1 lookup.
|
||||
fi_mod_cut: str = self._cut_from_import_mod(pool, fi_mod_raw)
|
||||
if fi_mod_cut is None: fi_mod_cut = fi_mod_raw
|
||||
fi_gv_q: str = self.Gen.FuncGenBase._mangle_name_with_sha1(StubMergerStore.LookupSha1ByModName(fi_mod_cut), nm_id)
|
||||
fi_gv: mlirlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(trans.Module, fi_gv_q)
|
||||
if fi_gv is not None and fi_gv.Ty is not None:
|
||||
fi_ref: mlirlite.Value* = self.Gen.ExprGenAttr.make_global_ref(pool, fi_gv.Name, fi_gv.Ty)
|
||||
if fi_ref is not None and fi_ref.Ty is not None:
|
||||
fi_pty: mlirlite.LLVMType* = fi_ref.Ty.Pointee
|
||||
if fi_pty is not None:
|
||||
match fi_pty:
|
||||
case mlirlite.LLVMType.Array(_, _):
|
||||
# Array globals decay to the base address per C semantics
|
||||
return fi_ref
|
||||
case _:
|
||||
pass
|
||||
return mlirlite.build_load(builder, fi_pty, fi_ref)
|
||||
|
||||
# global variable: look up in the module scope (global declaration has highest priority)
|
||||
if trans is not None:
|
||||
if GenApi.is_global_name(trans, nm_id) != 0:
|
||||
mod_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm_id)
|
||||
if mod_alloca is not None:
|
||||
load_ty: mlirlite.LLVMType* = None
|
||||
if mod_alloca.Ty is not None: load_ty = mod_alloca.Ty.Pointee
|
||||
if load_ty is None:
|
||||
# fail-fast: a global with a missing storage type aborts compilation
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"variable '{nm_id}' storage type missing", "TypeError")
|
||||
# Global arrays decay to the base address per C semantics; otherwise a loaded
|
||||
# array value passed as i8* loses writes (test_pyzlib "error message is empty").
|
||||
match load_ty:
|
||||
case mlirlite.LLVMType.Array(_, _):
|
||||
return mod_alloca
|
||||
case _:
|
||||
pass
|
||||
# t.CVolatile global: load carries volatile (prevents optimizing away repeated reads)
|
||||
vol_g: int = 0
|
||||
if trans is not None:
|
||||
ve_g: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
|
||||
vol_g = self.Gen.VarGen.var_is_volatile(ve_g)
|
||||
return mlirlite.build_load(builder, load_ty, mod_alloca, vol_g)
|
||||
|
||||
# nonlocal variable: accessed through the closure env
|
||||
if GenApi.is_nonlocal_name(trans, nm_id) != 0: return self.Gen.StmtGenDecl.load_nonlocal_var(trans, nm_id)
|
||||
|
||||
# *args va_list: the name resolves directly to an i8* pointing at the 24-byte va_list
|
||||
# buffer (no load of the buffer contents; pass the address by value when forwarding).
|
||||
if trans is not None and trans._vararg_name is not None:
|
||||
if nm_id is not None and string.strcmp(nm_id, trans._vararg_name) == 0:
|
||||
if trans._va_list_ptr is not None: return trans._va_list_ptr
|
||||
|
||||
# Local variable lookup must precede the module-alias check, or a local with the same
|
||||
# name as a module (e.g. local `c` after `import t, c`) would be misread and not found.
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_id)
|
||||
if alloca is not None:
|
||||
load_ty: mlirlite.LLVMType* = None
|
||||
if alloca.Ty is not None: load_ty = alloca.Ty.Pointee
|
||||
if load_ty is None:
|
||||
# fail-fast: a local with a missing storage type aborts compilation
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"variable '{nm_id}' storage type missing", "TypeError")
|
||||
# Struct-value variable (alloca of Struct): rvalues return its address by default;
|
||||
# a value is loaded only when the use site demands a same-type struct value.
|
||||
match load_ty:
|
||||
case mlirlite.LLVMType.Struct(_, _, _):
|
||||
# Struct-value locals return the alloca address by default; when the use site
|
||||
# explicitly needs a same-type non-pointer struct value, load it (_same_agg_ty).
|
||||
if target_ty is not None:
|
||||
if self.Gen.ExprGenBase._same_agg_ty(pool, load_ty, target_ty) != 0:
|
||||
vol_s: int = 0
|
||||
if trans is not None:
|
||||
ve_s: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
|
||||
vol_s = self.Gen.VarGen.var_is_volatile(ve_s)
|
||||
return mlirlite.build_load(builder, load_ty, alloca, vol_s)
|
||||
return alloca
|
||||
case mlirlite.LLVMType.Array(_, _):
|
||||
# Array variables decay to a pointer (base address) per C semantics; loading the
|
||||
# whole array value into "%s" reads wrong data (ClassArrayTest crash root cause).
|
||||
return alloca
|
||||
# t.CVolatile local: load carries volatile
|
||||
vol_l: int = 0
|
||||
bo_l: int = 0
|
||||
if trans is not None:
|
||||
ve_l: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
|
||||
vol_l = self.Gen.VarGen.var_is_volatile(ve_l)
|
||||
bo_l = self.Gen.VarGen.var_byte_order(ve_l)
|
||||
local_val_l: mlirlite.Value* = mlirlite.build_load(builder, load_ty, alloca, vol_l)
|
||||
# BigEndian local: the slot is byte-swapped, so un-swap the load (pairs the store in AssignGenMixin).
|
||||
return self.apply_byte_order_bswap(pool, builder, local_val_l, bo_l)
|
||||
|
||||
# REPL session variables: a name missing from the local table is read back from its ctx slot,
|
||||
# checked after locals and before module aliases; non-REPL behavior is unchanged.
|
||||
if trans is not None and trans.ReplCtx is not None:
|
||||
sess_val: mlirlite.Value* = self.Gen.VarGen.repl_try_read(trans, nm_id, node)
|
||||
if sess_val is not None: return sess_val
|
||||
|
||||
# Module alias check (only after SymTab misses): if nm_id is an imported module name that is
|
||||
# not a declared local, return None so the caller can handle attribute access.
|
||||
if trans is not None and trans._imported_modules is not None:
|
||||
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, nm_id) != 0: return None
|
||||
|
||||
# Module-level function reference: build a real function type from the Function's RetTy/Params
|
||||
# and wrap it in a new Value (returning/bitcasting the Function itself yields illegal IR).
|
||||
if trans is not None and trans.Module is not None:
|
||||
fn_ref: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(trans.Module, nm_id)
|
||||
if fn_ref is not None:
|
||||
fn_name: str = mlirlite.function_get_name(fn_ref)
|
||||
if fn_name is None: return None
|
||||
# Build the function type: ret_ty + parameter ParamNode list
|
||||
fn_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(fn_ref)
|
||||
if fn_ret_ty is None:
|
||||
# fail-fast: a missing function return type prevents building the function pointer
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"function '{nm_id}' return type missing, cannot build the function pointer", "TypeError")
|
||||
fn_pp: mlirlite.Param* = mlirlite.function_get_param_head(fn_ref)
|
||||
fn_first_node: mlirlite.ParamNode* = None
|
||||
fn_prev_node: mlirlite.ParamNode* = None
|
||||
fn_pcount: int = 0
|
||||
while fn_pp is not None:
|
||||
fn_pty: mlirlite.LLVMType* = mlirlite.param_get_ty(fn_pp)
|
||||
fn_pn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, fn_pty)
|
||||
if fn_pn is not None:
|
||||
if fn_first_node is None: fn_first_node = fn_pn
|
||||
if fn_prev_node is not None: mlirlite.paramnode_set_next(fn_prev_node, fn_pn)
|
||||
fn_prev_node = fn_pn
|
||||
fn_pcount += 1
|
||||
fn_pp = mlirlite.param_get_next(fn_pp)
|
||||
fn_ty: mlirlite.LLVMType* = mlirlite.Func(pool, fn_ret_ty, fn_first_node, fn_pcount)
|
||||
if fn_ty is None: return None
|
||||
# Build the function pointer Value (wrapped in new_value to avoid touching Function.Next)
|
||||
fn_ref_v: mlirlite.Value* = mlirlite.new_value(pool)
|
||||
if fn_ref_v is None: return None
|
||||
mlirlite.value_set_ty(fn_ref_v, mlirlite.Ptr(pool, fn_ty))
|
||||
mlirlite.value_set_isconst(fn_ref_v, 1)
|
||||
fn_ref_name: str = pool.alloc(256)
|
||||
if fn_ref_name is not None:
|
||||
fn_ref_name[0] = '\0'
|
||||
if self.Gen.ClassGenVTable._vtable_name_needs_quote(fn_name) != 0: viperlib.snprintf(fn_ref_name, 256, "@\"%s\"", fn_name)
|
||||
else: viperlib.snprintf(fn_ref_name, 256, "@%s", fn_name)
|
||||
mlirlite.value_set_name(fn_ref_v, fn_ref_name)
|
||||
fn_i8ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
return mlirlite.build_bitcast(builder, fn_ref_v, fn_i8ptr_ty)
|
||||
|
||||
# Cross-module from-import global read (e.g. `from .tokens import _op_head`): no local SymTab
|
||||
# entry exists, so emit an external reference + load; the source module provides the definition.
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
if trans.Module is not None:
|
||||
# Is a from-import name (star fallback disabled to avoid misattributing to star modules)
|
||||
fi_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_id, 0)
|
||||
if fi_mod is not None:
|
||||
# Exclude registered class/enum names: these are types, not global values
|
||||
fi_is_class: int = 0
|
||||
if self.Gen.StructGen.get_struct_type(nm_id) is not None: fi_is_class = 1
|
||||
# Exclude ALL_CAPS CDefine (already handled by local + cross-module lookup)
|
||||
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(nm_id) != 0: fi_is_class = 1
|
||||
if fi_is_class == 0:
|
||||
fi_gname: str = nm_id
|
||||
fi_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm_id)
|
||||
if fi_orig is not None:
|
||||
og_len_fi: sizet = 0
|
||||
while fi_orig[og_len_fi] != ' ' and fi_orig[og_len_fi] != '\0': og_len_fi += 1
|
||||
if og_len_fi > 0:
|
||||
og_buf_fi: str = pool.alloc(og_len_fi + 1)
|
||||
if og_buf_fi is not None:
|
||||
string.strncpy(og_buf_fi, fi_orig, og_len_fi)
|
||||
og_buf_fi[og_len_fi] = '\0'
|
||||
fi_gname = og_buf_fi
|
||||
# Reference the stub-injected global directly, or fail-fast (declaring external
|
||||
# i8* masks the cause); look up by source module SHA1 after truncating fi_mod.
|
||||
fi_mod_cut2: str = self._cut_from_import_mod(pool, fi_mod)
|
||||
if fi_mod_cut2 is None: fi_mod_cut2 = fi_mod
|
||||
gv_fi_q: str = self.Gen.FuncGenBase._mangle_name_with_sha1(StubMergerStore.LookupSha1ByModName(fi_mod_cut2), fi_gname)
|
||||
gv_fi: mlirlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(trans.Module, gv_fi_q)
|
||||
ref_fi: mlirlite.Value* = None
|
||||
if gv_fi is not None and gv_fi.Ty is not None: ref_fi = self.Gen.ExprGenAttr.make_global_ref(pool, gv_fi.Name, gv_fi.Ty)
|
||||
else:
|
||||
# Dependency vpi/stub not injected yet (file-order Phase A may translate this
|
||||
# before its dependency), so return None without fail-fast to avoid false errors.
|
||||
return None
|
||||
if ref_fi is not None and ref_fi.Ty is not None:
|
||||
lty_fi: mlirlite.LLVMType* = ref_fi.Ty.Pointee
|
||||
if lty_fi is not None: return mlirlite.build_load(builder, lty_fi, ref_fi)
|
||||
return ref_fi
|
||||
|
||||
# All lookup paths failed: fail-fast (NameError) instead of returning None and crashing
|
||||
# downstream. Exempt keywords/builtins (self/pool/None/_) whose absence is normal.
|
||||
if nm_id is None: return None
|
||||
if string.strcmp(nm_id, "CIf") == 0 or string.strcmp(nm_id, "CIfdef") == 0 or string.strcmp(nm_id, "CIfndef") == 0 or string.strcmp(nm_id, "CElif") == 0 or string.strcmp(nm_id, "CElse") == 0 or string.strcmp(nm_id, "CEndif") == 0:
|
||||
# The c.CIf-family spellings were removed; report the replacement.
|
||||
self.Gen.TypeGenErr.fatal_error(node, "c.CIf/c.CIfdef/c.CIfndef/c.CElif/c.CElse/c.CEndif were removed; use the cif/celif/celse statements and the defined(NAME) predicate instead (e.g. `cif defined(NAME):` ... `celse:` ...)", "NameError")
|
||||
return None
|
||||
if string.strcmp(nm_id, "self") == 0 or string.strcmp(nm_id, "pool") == 0 or string.strcmp(nm_id, "true") == 0 or string.strcmp(nm_id, "false") == 0 or string.strcmp(nm_id, "None") == 0 or string.strcmp(nm_id, "match") == 0 or string.strcmp(nm_id, "_") == 0:
|
||||
return None
|
||||
# Diagnostics: report the generic context, since "undefined name 'T'" is often far from the
|
||||
# real cause (0 params = no context; params>0/args 0 = lost args; both>0 = name not a param).
|
||||
ctx_txt: str = ""
|
||||
if trans is not None and trans.GenericTypeParamNames is not None:
|
||||
ctp_n: sizet = len(trans.GenericTypeParamNames)
|
||||
cta_n: sizet = 0
|
||||
if trans.GenericTypeArgs is not None: cta_n = len(trans.GenericTypeArgs)
|
||||
ctx_txt = f"(current generic context: {ctp_n} type params / {cta_n} type args)"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"undefined name '{nm_id}'{ctx_txt}", "NameError")
|
||||
return None
|
||||
# list_getitem_inline - inline list[T] __getitem__; returns the element address (i8*).
|
||||
# Layout (6 fields x 8 bytes): __data__(0) __count__(8) __capacity__(16) __pool__(24) __elem_size__(32) __iter_index__(40).
|
||||
def list_getitem_inline(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, lm_obj: mlirlite.Value*, idx_val: mlirlite.Value*, node: ast.AST* = None) -> mlirlite.Value*:
|
||||
"""Inline list __getitem__; return the element address (i8*)."""
|
||||
if builder is None or pool is None or lm_obj is None or idx_val is None: return None
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
# Load __data__ (offset 0, index 0)
|
||||
g_idx0: mlirlite.Value* = mlirlite.const_int64(pool, 0)
|
||||
g_dpp: mlirlite.Value* = mlirlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
|
||||
if g_dpp is None: return None
|
||||
g_data: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, g_dpp)
|
||||
# Load __elem_size__ (offset 32, index 4)
|
||||
g_idx4: mlirlite.Value* = mlirlite.const_int64(pool, 4)
|
||||
g_epp: mlirlite.Value* = mlirlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
|
||||
if g_epp is None: return None
|
||||
g_esize: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, g_epp)
|
||||
if g_data is None or g_esize is None: return None
|
||||
g_idx_i64: mlirlite.Value* = self.coerce_to_type(builder, idx_val, i64_ty, node)
|
||||
if g_idx_i64 is None: return None
|
||||
g_offset: mlirlite.Value* = mlirlite.build_mul(builder, g_idx_i64, g_esize)
|
||||
if g_offset is None: return None
|
||||
# Return the element address (i8*) without loading
|
||||
return mlirlite.build_gep(builder, i8_ty, g_data, g_offset)
|
||||
# _list_elem_type_from_name - extract the element LLVM type from a list type name.
|
||||
# Name format: <sha1>.list[<elem_type>]; strstr checks the full pattern (no substring extraction).
|
||||
def _list_elem_type_from_name(self, pool: memhub.MemBuddy*, struct_name: str) -> mlirlite.LLVMType*:
|
||||
"""Extract the element LLVM type from a list type name."""
|
||||
if pool is None or struct_name is None: return None
|
||||
# Check the full pattern with strstr to avoid substring extraction
|
||||
if string.strstr(struct_name, "list[int]") is not None: return mlirlite.Int32(pool)
|
||||
if string.strstr(struct_name, "list[CInt]") is not None: return mlirlite.Int32(pool)
|
||||
if string.strstr(struct_name, "list[CInt8T]") is not None: return mlirlite.Int8(pool)
|
||||
if string.strstr(struct_name, "list[CInt16T]") is not None: return mlirlite.Int16(pool)
|
||||
if string.strstr(struct_name, "list[CInt32T]") is not None: return mlirlite.Int32(pool)
|
||||
if string.strstr(struct_name, "list[CInt64T]") is not None: return mlirlite.Int64(pool)
|
||||
if string.strstr(struct_name, "list[CSizeT]") is not None: return mlirlite.Int64(pool)
|
||||
if string.strstr(struct_name, "list[CDouble]") is not None: return mlirlite.Double(pool)
|
||||
if string.strstr(struct_name, "list[CFloat]") is not None: return mlirlite.Float(pool)
|
||||
if string.strstr(struct_name, "list[CChar]") is not None: return mlirlite.Int8(pool)
|
||||
# Known pointer element types (str/bytes/CPtr) -> i8* (elements hold 8-byte addresses)
|
||||
i8_ty_d: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if string.strstr(struct_name, "list[str]") is not None: return mlirlite.Ptr(pool, i8_ty_d)
|
||||
if string.strstr(struct_name, "list[bytes]") is not None: return mlirlite.Ptr(pool, i8_ty_d)
|
||||
if string.strstr(struct_name, "list[CPtr]") is not None: return mlirlite.Ptr(pool, i8_ty_d)
|
||||
# Unknown list[T] element type (custom struct element): the slot holds an 8-byte object
|
||||
# pointer, so restore Ptr(%Struct) to carry the element class for chained method dispatch.
|
||||
elem_obj_ty_l: mlirlite.LLVMType* = self._list_elem_obj_ty_from_struct_name(pool, struct_name)
|
||||
if elem_obj_ty_l is not None: return elem_obj_ty_l
|
||||
# Struct not registered (dependency not ready): fall back to Ptr(i8) (same 8-byte slot width),
|
||||
# letting AnnotClassName/method dispatch fallbacks preserve semantics; never fail-fast here.
|
||||
return mlirlite.Ptr(pool, i8_ty_d)
|
||||
# _list_elem_obj_ty_from_struct_name - restore the element object pointer type from a list
|
||||
# specialization name (strip rawptr[...], split unions by '|', resolve against registered structs).
|
||||
def _list_elem_obj_ty_from_struct_name(self, pool: memhub.MemBuddy*, struct_name: str) -> mlirlite.LLVMType*:
|
||||
"""Restore the element object pointer type from a list specialization name; None on failure."""
|
||||
if pool is None or struct_name is None: return None
|
||||
lb_e: str = string.strchr(struct_name, '[')
|
||||
if lb_e is None: return None
|
||||
rb_e: str = string.strrchr(struct_name, ']')
|
||||
if rb_e is None: return None
|
||||
elen_e: sizet = sizet(uint64(rb_e) - uint64(lb_e)) - 1
|
||||
if elen_e == 0 or elen_e > 512: return None
|
||||
elem_buf_e: str = pool.alloc(elen_e + 1)
|
||||
if elem_buf_e is None: return None
|
||||
string.strncpy(elem_buf_e, lb_e + 1, elen_e)
|
||||
elem_buf_e[elen_e] = '\0'
|
||||
# Strip the pointer wrapper (elements declared as object pointers): CPtr[...] / rawptr[...]
|
||||
cur_e: str = elem_buf_e
|
||||
while string.strncmp(cur_e, "CPtr[", 5) == 0 or string.strncmp(cur_e, "rawptr[", 7) == 0:
|
||||
if string.strncmp(cur_e, "CPtr[", 5) == 0: cur_e = cur_e + 5
|
||||
else: cur_e = cur_e + 7
|
||||
# Split unions by '|', trimming leading/trailing whitespace, trailing ']' and quotes; each
|
||||
# segment goes to find_struct (builtin names return None; "sha1.Class" prefix matches by module).
|
||||
total_e: sizet = string.strlen(cur_e)
|
||||
seg_s_e: sizet = 0
|
||||
i_e: sizet = 0
|
||||
while i_e <= total_e:
|
||||
hit_e: int = 0
|
||||
if i_e == total_e: hit_e = 1
|
||||
elif cur_e[i_e] == '|': hit_e = 1
|
||||
if hit_e == 0:
|
||||
i_e += 1
|
||||
continue
|
||||
b_e: sizet = i_e
|
||||
a_e: sizet = seg_s_e
|
||||
# Also strip a trailing '*' (normalized element text is `T*`), since find_struct only
|
||||
# accepts the class name; otherwise object elements fall back to Ptr(i8), losing the class.
|
||||
while b_e > a_e and (cur_e[b_e - 1] == ' ' or cur_e[b_e - 1] == ']' or cur_e[b_e - 1] == '\'' or cur_e[b_e - 1] == '"' or cur_e[b_e - 1] == '*'): b_e -= 1
|
||||
while a_e < b_e and (cur_e[a_e] == ' ' or cur_e[a_e] == '\'' or cur_e[a_e] == '"'): a_e += 1
|
||||
if b_e > a_e:
|
||||
seg_len_e: sizet = b_e - a_e
|
||||
if seg_len_e < 256:
|
||||
seg_buf_e: str = pool.alloc(seg_len_e + 1)
|
||||
if seg_buf_e is not None:
|
||||
string.strncpy(seg_buf_e, cur_e + a_e, seg_len_e)
|
||||
seg_buf_e[seg_len_e] = '\0'
|
||||
s_entry_e: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(seg_buf_e)
|
||||
if s_entry_e is not None and s_entry_e.Ty is not None: return mlirlite.Ptr(pool, s_entry_e.Ty)
|
||||
if i_e == total_e: break
|
||||
seg_s_e = i_e + 1
|
||||
i_e += 1
|
||||
return None
|
||||
# list_setitem_inline - inline list[T] __setitem__: coerce rhs_val to i8* and store to the element address.
|
||||
def list_setitem_inline(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, lm_obj: mlirlite.Value*, idx_val: mlirlite.Value*, rhs_val: mlirlite.Value*, node: ast.AST* = None) -> int:
|
||||
"""Inline list __setitem__; returns 0."""
|
||||
if builder is None or pool is None or lm_obj is None or idx_val is None or rhs_val is None: return 0
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
# Load __data__ (offset 0, index 0)
|
||||
s_idx0: mlirlite.Value* = mlirlite.const_int64(pool, 0)
|
||||
s_dpp: mlirlite.Value* = mlirlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
|
||||
if s_dpp is None: return 0
|
||||
s_data: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, s_dpp)
|
||||
# Load __elem_size__ (offset 32, index 4)
|
||||
s_idx4: mlirlite.Value* = mlirlite.const_int64(pool, 4)
|
||||
s_epp: mlirlite.Value* = mlirlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
|
||||
if s_epp is None: return 0
|
||||
s_esize: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, s_epp)
|
||||
if s_data is None or s_esize is None: return 0
|
||||
s_idx_i64: mlirlite.Value* = self.coerce_to_type(builder, idx_val, i64_ty, node)
|
||||
if s_idx_i64 is None: return 0
|
||||
s_offset: mlirlite.Value* = mlirlite.build_mul(builder, s_idx_i64, s_esize)
|
||||
if s_offset is None: return 0
|
||||
s_addr: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, s_data, s_offset)
|
||||
if s_addr is None: return 0
|
||||
# Element type T from the list specialization name (list[int]->i32, list[str]->i8*); a fixed
|
||||
# i8* store would overwrite adjacent elements (CallGenMixin list branch, ListTest pop FAILED).
|
||||
s_sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lm_obj)
|
||||
s_name: str = None
|
||||
if s_sty is not None: s_name = mlirlite.get_struct_name(s_sty)
|
||||
s_elem_ty: mlirlite.LLVMType* = None
|
||||
if s_name is not None: s_elem_ty = self._list_elem_type_from_name(pool, s_name)
|
||||
if s_elem_ty is None: s_elem_ty = i8_ptr_ty
|
||||
s_val_ptr: mlirlite.Value* = self.coerce_to_type(builder, rhs_val, s_elem_ty, node)
|
||||
if s_val_ptr is None: return 0
|
||||
s_ep: mlirlite.Value* = mlirlite.build_bitcast(builder, s_addr, mlirlite.Ptr(pool, s_elem_ty))
|
||||
if s_ep is not None: mlirlite.build_store(builder, s_val_ptr, s_ep)
|
||||
return 0
|
||||
# is_list_subscript - check whether a Subscript node is list[T]; returns the list object pointer (Ptr(list[T])) or None.
|
||||
def is_list_subscript(self, node: ast.AST*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Return the list object pointer when a Subscript is list[T]; None otherwise."""
|
||||
if node is None or trans is None: return None
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub is None or sub.value is None: return None
|
||||
if sub.value.kind() != ast.ASTKind.Name: return None
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm is None or nm.id is None: return None
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is None or alloca.Ty is None: return None
|
||||
if self.is_ptr_type(alloca.Ty) == 0: return None
|
||||
pointee: mlirlite.LLVMType* = alloca.Ty.Pointee
|
||||
if pointee is None: return None
|
||||
# Check whether pointee is Ptr(Struct)
|
||||
inner_ty: mlirlite.LLVMType* = None
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Ptr(it):
|
||||
inner_ty = it
|
||||
case _:
|
||||
return None
|
||||
if inner_ty is None: return None
|
||||
# Check whether inner_ty is a Struct whose name contains "list["
|
||||
struct_name: str = None
|
||||
match inner_ty:
|
||||
case mlirlite.LLVMType.Struct(_, _, sname):
|
||||
struct_name = sname
|
||||
case _:
|
||||
return None
|
||||
if struct_name is None: return None
|
||||
if string.strstr(struct_name, "list[") is None: return None
|
||||
# Is a list type: load out the list object pointer
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
return mlirlite.build_load(builder, pointee, alloca)
|
||||
# _nonlocal_subscript_ptr - element pointer for a nonlocal array/pointer subscript: read the
|
||||
# shared heap address from env (_load_raw_var_ptr), bitcast, then GEP (the outer alloca is unusable).
|
||||
def _nonlocal_subscript_ptr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, trans: HT.Translator*, nm_id: str, idx_val: mlirlite.Value*) -> mlirlite.Value*:
|
||||
"""Element pointer for a nonlocal array/pointer subscript (env heap addr -> bitcast -> GEP)."""
|
||||
if trans is None or nm_id is None or idx_val is None: return None
|
||||
nl_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, nm_id)
|
||||
if nl_ty is None: return None
|
||||
raw_nl: mlirlite.Value* = self.Gen.StmtGenDecl._load_raw_var_ptr(trans, nm_id)
|
||||
if raw_nl is None: return None
|
||||
match nl_ty:
|
||||
case mlirlite.LLVMType.Array(elem_ty_nl, _cnt_nl):
|
||||
arr_ptr_nl: mlirlite.Value* = mlirlite.build_bitcast(builder, raw_nl, mlirlite.Ptr(pool, nl_ty))
|
||||
if arr_ptr_nl is None: return None
|
||||
return mlirlite.build_gep_array(builder, nl_ty, elem_ty_nl, arr_ptr_nl, idx_val)
|
||||
case mlirlite.LLVMType.Ptr(inner_nl):
|
||||
arr_ptr_nl2: mlirlite.Value* = mlirlite.build_bitcast(builder, raw_nl, mlirlite.Ptr(pool, nl_ty))
|
||||
if arr_ptr_nl2 is None: return None
|
||||
return mlirlite.build_gep(builder, inner_nl, arr_ptr_nl2, idx_val)
|
||||
case _:
|
||||
arr_ptr_nl3: mlirlite.Value* = mlirlite.build_bitcast(builder, raw_nl, mlirlite.Ptr(pool, nl_ty))
|
||||
if arr_ptr_nl3 is None: return None
|
||||
return mlirlite.build_gep(builder, nl_ty, arr_ptr_nl3, idx_val)
|
||||
return None
|
||||
# translate_subscript - translate a subscript expression ptr[i] / arr[i]: pointer traversal
|
||||
# loads the pointer then GEP + load; arrays use a double-index GEP; list[T] goes through __getitem__.
|
||||
def translate_subscript(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Translate a subscript expression; return the loaded element value."""
|
||||
if node is None or builder is None: return None
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub is None: return None
|
||||
|
||||
# Slice form a[start:end] (e.g. path[n:]): return an i8* base pointer plus the start offset.
|
||||
# Slices must not be treated as single-index subscripts, or the argument is dropped entirely.
|
||||
if sub.slice is not None and sub.slice.kind() == ast.ASTKind.Slice:
|
||||
slc: ast.Slice* = ast.Slice*(sub.slice)
|
||||
base_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.value, None, 0, trans)
|
||||
if base_v is None: return None
|
||||
if slc.lower is None: return base_v
|
||||
start_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, slc.lower, None, 0, trans)
|
||||
if start_v is None: return None
|
||||
i64_sc: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
start_w: mlirlite.Value* = start_v
|
||||
if start_v.Ty is not None:
|
||||
bt_sc: int = self.Gen.ExprGenBase.get_llvm_type_bits(start_v.Ty)
|
||||
if bt_sc != 0 and bt_sc < 64: start_w = mlirlite.build_sext(builder, start_v, i64_sc)
|
||||
if start_w is None: return None
|
||||
i8_sc: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
return mlirlite.build_gep(builder, i8_sc, base_v, start_w)
|
||||
|
||||
# Translate the index
|
||||
idx_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.slice, None, 0, trans)
|
||||
if idx_val is None: return None
|
||||
# Narrow integer subscripts (i8/i16) are zero-extended inside mlirlite.build_gep /
|
||||
# build_gep_array (the single source of truth); doing it here would miss bare pointers.
|
||||
|
||||
# Nested subscript read a[i][j]: recursively take the inner pointer and load, matching
|
||||
# get_subscript_ptr's nested branch.
|
||||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Subscript:
|
||||
inner_ptr_r: mlirlite.Value* = self.get_subscript_ptr(builder, pool, mod, sub.value, trans)
|
||||
if inner_ptr_r is None or inner_ptr_r.Ty is None: return None
|
||||
inner_elem_r: mlirlite.LLVMType* = inner_ptr_r.Ty.Pointee
|
||||
if inner_elem_r is None: return None
|
||||
match inner_elem_r:
|
||||
case mlirlite.LLVMType.Array(arr_ie_r, _):
|
||||
elem_ptr_r: mlirlite.Value* = mlirlite.build_gep_array(builder, inner_elem_r, arr_ie_r, inner_ptr_r, idx_val)
|
||||
if elem_ptr_r is not None: return mlirlite.build_load(builder, arr_ie_r, elem_ptr_r)
|
||||
return None
|
||||
case _:
|
||||
elem_ptr_r2: mlirlite.Value* = mlirlite.build_gep(builder, inner_elem_r, inner_ptr_r, idx_val)
|
||||
if elem_ptr_r2 is not None: return mlirlite.build_load(builder, inner_elem_r, elem_ptr_r2)
|
||||
return None
|
||||
|
||||
# If value is a Name, try to infer from the alloca type
|
||||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm.id is not None and trans is not None:
|
||||
# rawptr[t.CVolatile | T] variable (volatile pointee): element loads (p[i] dereference)
|
||||
# carry volatile; loading the pointer value itself does not.
|
||||
vol_sub_rd: int = self.Gen.VarGen.var_pointee_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
# nonlocal array/pointer: read the shared heap address from env (the outer alloca
|
||||
# cannot be used); the read path loads the element value from the element pointer.
|
||||
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
|
||||
nl_ptr_r: mlirlite.Value* = self._nonlocal_subscript_ptr(builder, pool, trans, nm.id, idx_val)
|
||||
if nl_ptr_r is not None and nl_ptr_r.Ty is not None:
|
||||
nl_elem_ty: mlirlite.LLVMType* = nl_ptr_r.Ty.Pointee
|
||||
if nl_elem_ty is not None: return mlirlite.build_load(builder, nl_elem_ty, nl_ptr_r)
|
||||
return None
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is not None and alloca.Ty is not None:
|
||||
if self.is_ptr_type(alloca.Ty) != 0:
|
||||
pointee: mlirlite.LLVMType* = alloca.Ty.Pointee
|
||||
if pointee is not None:
|
||||
# Do not call pointee.kind() (avoids a cross-module LLVMType.kind reference);
|
||||
# use match to inspect the pointee type.
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Array(elem_ty, count):
|
||||
# Array traversal: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
|
||||
elem_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, pointee, elem_ty, alloca, idx_val)
|
||||
if elem_ptr is not None:
|
||||
arr_val_rd: mlirlite.Value* = mlirlite.build_load(builder, elem_ty, elem_ptr)
|
||||
# BigEndian array variable: the element slot is byte-swapped; un-swap
|
||||
# on load (pairs the element store in AssignGenMixin). Non-Name bases are
|
||||
# not covered (matches TPC, which also keys off the base Name).
|
||||
bo_arr_rd: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
return self.apply_byte_order_bswap(pool, builder, arr_val_rd, bo_arr_rd)
|
||||
return None
|
||||
case mlirlite.LLVMType.Struct(_fs_v, _fc_v, lsn_v):
|
||||
# list[T] value variable (an unannotated assignment rhs returns a list
|
||||
# value): the alloca holds the list object, so go straight to list __getitem__.
|
||||
if lsn_v is not None:
|
||||
if string.strstr(lsn_v, "list[") is not None:
|
||||
elem_addr_v: mlirlite.Value* = self.list_getitem_inline(builder, pool, alloca, idx_val, node)
|
||||
if elem_addr_v is not None:
|
||||
elem_ty_v: mlirlite.LLVMType* = self._list_elem_type_from_name(pool, lsn_v)
|
||||
if elem_ty_v is not None:
|
||||
elem_ptr_ty_v: mlirlite.LLVMType* = mlirlite.Ptr(pool, elem_ty_v)
|
||||
casted_v: mlirlite.Value* = mlirlite.build_bitcast(builder, elem_addr_v, elem_ptr_ty_v)
|
||||
if casted_v is not None: return mlirlite.build_load(builder, elem_ty_v, casted_v)
|
||||
# 值类型类实例(`d: dict = dict(bd)`):槽里就是对象本身,`d[k]` 必须按
|
||||
# 类的 `__getitem__` 调用编译,self 传槽地址(= &对象)。此前这里直接
|
||||
# return None,调用方(AnnAssign)又对 None 静默不写,整条语句凭空消失
|
||||
# ——现场:p1/p2/p3 全未赋值,随后解引用栈垃圾断言崩溃。
|
||||
cls_nm_val: str = self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||||
if not (cls_nm_val is None):
|
||||
gi_lookup_val: str = pool.alloc(128)
|
||||
if gi_lookup_val is not None:
|
||||
viperlib.snprintf(gi_lookup_val, 128, "%s.__getitem__", cls_nm_val)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_val) is not None:
|
||||
arg_vals_val: sizet* = pool.alloc(8)
|
||||
if not (arg_vals_val is None):
|
||||
arg_vals_val[0] = sizet(idx_val)
|
||||
ret_val_sub: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, cls_nm_val, "__getitem__", alloca, arg_vals_val, 1, trans, sub)
|
||||
if not (ret_val_sub is None): return ret_val_sub
|
||||
# 非 list、无 __getitem__ 的结构体值不可下标:按对象大小 GEP 是非法 IR,
|
||||
# 明确 fail-fast,绝不静默返回 None(调用方会把整条语句丢掉)。
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"cannot subscript the value-type object '{nm.id}' of type '{lsn_v}' (the class defines no __getitem__); use a pointer variable ('T*') or define __getitem__", "TypeError")
|
||||
return None
|
||||
case mlirlite.LLVMType.Ptr(inner_ty):
|
||||
# Check whether inner_ty is list[T]; list subscripts go through __getitem__ (inline), not pointer traversal
|
||||
list_struct_name: str = None
|
||||
match inner_ty:
|
||||
case mlirlite.LLVMType.Struct(_, _, lsn):
|
||||
list_struct_name = lsn
|
||||
case _:
|
||||
pass
|
||||
if list_struct_name is not None:
|
||||
if string.strstr(list_struct_name, "list[") is not None:
|
||||
# list[T] type: inline-generate the __getitem__ logic
|
||||
ptr_val_list: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_list is not None:
|
||||
elem_addr_list: mlirlite.Value* = self.list_getitem_inline(builder, pool, ptr_val_list, idx_val, node)
|
||||
if elem_addr_list is not None:
|
||||
# Extract the element type from the name, bitcast then load
|
||||
elem_ty_list: mlirlite.LLVMType* = self._list_elem_type_from_name(pool, list_struct_name)
|
||||
if elem_ty_list is not None:
|
||||
elem_ptr_ty_list: mlirlite.LLVMType* = mlirlite.Ptr(pool, elem_ty_list)
|
||||
casted_addr_list: mlirlite.Value* = mlirlite.build_bitcast(builder, elem_addr_list, elem_ptr_ty_list)
|
||||
if casted_addr_list is not None: return mlirlite.build_load(builder, elem_ty_list, casted_addr_list)
|
||||
return None
|
||||
# inner_ty being Ptr means alloca.Ty is a triple pointer (e.g. T=AST*, T*=AST**),
|
||||
# so [i] is a pointer dereference (stride 8), not a struct __getitem__ call.
|
||||
if self.is_ptr_type(inner_ty) != 0:
|
||||
ptr_val_pp: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_pp is not None:
|
||||
elem_ptr_pp: mlirlite.Value* = mlirlite.build_gep(builder, inner_ty, ptr_val_pp, idx_val)
|
||||
if elem_ptr_pp is not None:
|
||||
# Volatile pointee: dereference load carries volatile
|
||||
return mlirlite.build_load(builder, inner_ty, elem_ptr_pp, vol_sub_rd)
|
||||
return None
|
||||
# Custom struct (e.g. HashTable*, JsonValue*): forward the method call only if
|
||||
# the class defines __getitem__; otherwise do a plain GEP array access.
|
||||
cls_nm_rd_ptr: str = self._get_custom_struct_cls_nm(pool, inner_ty)
|
||||
if not (cls_nm_rd_ptr is None):
|
||||
gi_lookup_rd: str = pool.alloc(128)
|
||||
if gi_lookup_rd is not None:
|
||||
viperlib.snprintf(gi_lookup_rd, 128, "%s.__getitem__", cls_nm_rd_ptr)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_rd) is not None:
|
||||
obj_ptr_rd: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if not (obj_ptr_rd is None):
|
||||
arg_vals_rd_p: sizet* = pool.alloc(8)
|
||||
if not (arg_vals_rd_p is None):
|
||||
arg_vals_rd_p[0] = sizet(idx_val)
|
||||
ret_rd_p: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, cls_nm_rd_ptr, "__getitem__", obj_ptr_rd, arg_vals_rd_p, 1, trans, sub)
|
||||
if not (ret_rd_p is None): return ret_rd_p
|
||||
# No __getitem__: struct-pointer array access (GEP + load)
|
||||
ptr_val_rd_g: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_rd_g is not None:
|
||||
elem_rd_g: mlirlite.Value* = mlirlite.build_gep(builder, inner_ty, ptr_val_rd_g, idx_val)
|
||||
if elem_rd_g is not None:
|
||||
# Volatile pointee: dereference load carries volatile
|
||||
return mlirlite.build_load(builder, inner_ty, elem_rd_g, vol_sub_rd)
|
||||
return None
|
||||
# bytes*/str*: the alloca is i8**, so GEP with an i8* stride (8 bytes) and load i8*;
|
||||
# pointer traversal applies only when inner_ty is not a custom struct.
|
||||
if trans is not None:
|
||||
ve_pe_ts: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe_ts) != 0:
|
||||
# bytes*/str*/T*: the variable holds a pointer value, so elem[i] means
|
||||
# load [ptr + i*8]; load the alloca first then GEP with an i8* stride.
|
||||
ptr_val_pe_ts: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_pe_ts is not None:
|
||||
elem_ptr_ts: mlirlite.Value* = mlirlite.build_gep(builder, pointee, ptr_val_pe_ts, idx_val)
|
||||
if elem_ptr_ts is not None:
|
||||
# Volatile pointee: dereference load carries volatile
|
||||
return mlirlite.build_load(builder, pointee, elem_ptr_ts, vol_sub_rd)
|
||||
return None
|
||||
# Plain pointer traversal: load the pointer value, then GEP
|
||||
ptr_val: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val is not None:
|
||||
elem_ptr2: mlirlite.Value* = mlirlite.build_gep(builder, inner_ty, ptr_val, idx_val)
|
||||
if elem_ptr2 is not None:
|
||||
# Volatile pointee: dereference load carries volatile
|
||||
return mlirlite.build_load(builder, inner_ty, elem_ptr2, vol_sub_rd)
|
||||
return None
|
||||
case _:
|
||||
# Union-typed variable (IsPtrElement=1, e.g. elem_ptr: T* degraded to i8*): pointee
|
||||
# is i8 but elements are 8-byte pointers, so GEP/load must use an i8* stride.
|
||||
if trans is not None:
|
||||
ve_pe_rd: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe_rd) != 0:
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Int(bits_rd):
|
||||
if bits_rd == 8:
|
||||
i8_ptr_rd: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
elem_ptr_rd: mlirlite.Value* = mlirlite.build_gep(builder, i8_ptr_rd, alloca, idx_val)
|
||||
if elem_ptr_rd is not None: return mlirlite.build_load(builder, i8_ptr_rd, elem_ptr_rd)
|
||||
case _:
|
||||
pass
|
||||
# Custom class (struct type): forward the method call only if the class defines
|
||||
# __getitem__; otherwise do a plain GEP array access on the struct pointer.
|
||||
cls_nm_sub: str = self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||||
if not (cls_nm_sub is None):
|
||||
gi_lookup_sub: str = pool.alloc(128)
|
||||
if gi_lookup_sub is not None:
|
||||
viperlib.snprintf(gi_lookup_sub, 128, "%s.__getitem__", cls_nm_sub)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_sub) is not None:
|
||||
obj_val_sub: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if not (obj_val_sub is None):
|
||||
arg_vals_sub: sizet* = pool.alloc(8)
|
||||
if not (arg_vals_sub is None):
|
||||
arg_vals_sub[0] = sizet(idx_val)
|
||||
ret_sub: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, cls_nm_sub, "__getitem__", obj_val_sub, arg_vals_sub, 1, trans, sub)
|
||||
if not (ret_sub is None): return ret_sub
|
||||
# No __getitem__: struct-pointer array access (GEP + load)
|
||||
ptr_val_sub_g: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_sub_g is not None:
|
||||
elem_sub_g: mlirlite.Value* = mlirlite.build_gep(builder, pointee, ptr_val_sub_g, idx_val)
|
||||
if elem_sub_g is not None:
|
||||
# Volatile pointee: dereference load carries volatile
|
||||
return mlirlite.build_load(builder, pointee, elem_sub_g, vol_sub_rd)
|
||||
return None
|
||||
pass
|
||||
|
||||
# Generic path: translate value to obtain the pointer
|
||||
ptr_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.value, None, 0, trans)
|
||||
if ptr_val is None or ptr_val.Ty is None: return None
|
||||
|
||||
# Check whether it is a pointer type
|
||||
if self.is_ptr_type(ptr_val.Ty) != 0:
|
||||
# Generic-path base (e.g. self.ptr / a complex expression): element loads carry volatile only
|
||||
# when the base resolves to a VarEntry/FieldEntry marked rawptr[t.CVolatile|T]; otherwise 0.
|
||||
vol_sub_gen: int = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub.value)
|
||||
elem_ty2: mlirlite.LLVMType* = ptr_val.Ty.Pointee
|
||||
if elem_ty2 is not None:
|
||||
# Array type: double-index GEP + load element
|
||||
match elem_ty2:
|
||||
case mlirlite.LLVMType.Array(arr_elem_ty2, arr_count2):
|
||||
elem_ptr3: mlirlite.Value* = mlirlite.build_gep_array(builder, elem_ty2, arr_elem_ty2, ptr_val, idx_val)
|
||||
if elem_ptr3 is not None: return mlirlite.build_load(builder, arr_elem_ty2, elem_ptr3, vol_sub_gen)
|
||||
return None
|
||||
case _:
|
||||
pass
|
||||
# 64-bit scalar pointer fields keep the declared i64 element type (a pointer needs a cast);
|
||||
# custom structs forward __getitem__ only if defined, else do a plain GEP array access.
|
||||
cls_nm_rd_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
|
||||
if not (cls_nm_rd_gen is None):
|
||||
gi_lookup_gen: str = pool.alloc(128)
|
||||
if gi_lookup_gen is not None:
|
||||
viperlib.snprintf(gi_lookup_gen, 128, "%s.__getitem__", cls_nm_rd_gen)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_gen) is not None:
|
||||
arg_vals_rd_g: sizet* = pool.alloc(8)
|
||||
if not (arg_vals_rd_g is None):
|
||||
arg_vals_rd_g[0] = sizet(idx_val)
|
||||
ret_rd_g: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, cls_nm_rd_gen, "__getitem__", ptr_val, arg_vals_rd_g, 1, trans, sub)
|
||||
if not (ret_rd_g is None): return ret_rd_g
|
||||
# No __getitem__: struct-pointer array access (GEP + load)
|
||||
elem_ptr_gen: mlirlite.Value* = mlirlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
|
||||
if elem_ptr_gen is not None: return mlirlite.build_load(builder, elem_ty2, elem_ptr_gen, vol_sub_gen)
|
||||
return None
|
||||
# Pointer type: single-index GEP + load
|
||||
elem_ptr4: mlirlite.Value* = mlirlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
|
||||
if elem_ptr4 is not None: return mlirlite.build_load(builder, elem_ty2, elem_ptr4, vol_sub_gen)
|
||||
return None
|
||||
# get_subscript_ptr - get the subscript element pointer (without loading the value) for assignment lhs
|
||||
def _annot_is_64bit_scalar(self, annot: str) -> int:
|
||||
"""Return 1 when the annotation is a 64-bit scalar | t.CPtr union (element type Int64)."""
|
||||
if annot is None: return 0
|
||||
if string.strstr(annot, "CInt64T") is not None: return 1
|
||||
if string.strstr(annot, "CUInt64T") is not None: return 1
|
||||
if string.strstr(annot, "CSizeT") is not None: return 1
|
||||
return 0
|
||||
|
||||
# subscript_base_store_kind - read-only base storage classification: 1 = array slot (whole-object
|
||||
# assignment allowed), 0 = pointer pointee (caller fail-fast), -1 = undeterminable.
|
||||
def subscript_base_store_kind(self, pool: memhub.MemBuddy*, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Base storage kind of a subscript: 1=array, 0=pointer, -1=undeterminable."""
|
||||
if node is None or trans is None: return -1
|
||||
sub_bk: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_bk is None or sub_bk.value is None: return -1
|
||||
base_ty_bk: mlirlite.LLVMType* = None
|
||||
if sub_bk.value.kind() == ast.ASTKind.Name:
|
||||
nm_bk: ast.Name* = ast.Name*(sub_bk.value)
|
||||
if nm_bk.id is None: return -1
|
||||
alloca_bk: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_bk.id)
|
||||
if alloca_bk is None: alloca_bk = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm_bk.id)
|
||||
if alloca_bk is None or alloca_bk.Ty is None: return -1
|
||||
if self.is_ptr_type(alloca_bk.Ty) == 0: return -1
|
||||
base_ty_bk = alloca_bk.Ty.Pointee
|
||||
elif sub_bk.value.kind() == ast.ASTKind.Attribute:
|
||||
# Single-level obj.field (e.g. self.arr): read-only, non-fatal field type resolution
|
||||
fe_bk: GenTypes.FieldEntry* = self.Gen.ExprGenAttr._resolve_attr_field_entry(pool, trans, sub_bk.value)
|
||||
if fe_bk is None or fe_bk.Ty is None: return -1
|
||||
base_ty_bk = fe_bk.Ty
|
||||
else: return -1
|
||||
if base_ty_bk is None: return -1
|
||||
# The base itself is an array value -> array storage
|
||||
if self.is_array_type(base_ty_bk) != 0: return 1
|
||||
if self.is_ptr_type(base_ty_bk) != 0:
|
||||
inner_bk: mlirlite.LLVMType* = base_ty_bk.Pointee
|
||||
if inner_bk is None: return -1
|
||||
# Ptr(Array) -> t.CArray[T,N] local/field (array storage)
|
||||
if self.is_array_type(inner_bk) != 0: return 1
|
||||
# Ptr(Struct) -> rawptr[T] (pointer storage)
|
||||
if inner_bk.kind() == mlirlite.LLVMType.Struct: return 0
|
||||
return -1
|
||||
|
||||
def get_subscript_ptr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Return the subscript element pointer for an assignment lhs."""
|
||||
if node is None or builder is None: return None
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub is None: return None
|
||||
|
||||
# Translate the index
|
||||
idx_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.slice, None, 0, trans)
|
||||
if idx_val is None: return None
|
||||
# Narrow integer subscripts (i8/i16) are zero-extended inside mlirlite.build_gep /
|
||||
# build_gep_array (the single source of truth); doing it here would miss bare pointers.
|
||||
|
||||
# Nested subscript a[i][j] = v: recursively resolve the inner subscript pointer then GEP
|
||||
# (the generic translate_value path would load the inner element as a value and fail).
|
||||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Subscript:
|
||||
inner_ptr: mlirlite.Value* = self.get_subscript_ptr(builder, pool, mod, sub.value, trans)
|
||||
if inner_ptr is None or inner_ptr.Ty is None: return None
|
||||
inner_elem: mlirlite.LLVMType* = inner_ptr.Ty.Pointee
|
||||
if inner_elem is None: return None
|
||||
match inner_elem:
|
||||
case mlirlite.LLVMType.Array(arr_ie, _):
|
||||
return mlirlite.build_gep_array(builder, inner_elem, arr_ie, inner_ptr, idx_val)
|
||||
case _:
|
||||
return mlirlite.build_gep(builder, inner_elem, inner_ptr, idx_val)
|
||||
|
||||
# If value is a Name, try to infer from the alloca type
|
||||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm.id is not None and trans is not None:
|
||||
# nonlocal array/pointer: read the shared heap address from env (the outer alloca
|
||||
# cannot be used because its SSA number belongs to another function).
|
||||
if GenApi.is_nonlocal_name(trans, nm.id) != 0: return self._nonlocal_subscript_ptr(builder, pool, trans, nm.id, idx_val)
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is not None and alloca.Ty is not None:
|
||||
if self.is_ptr_type(alloca.Ty) != 0:
|
||||
pointee: mlirlite.LLVMType* = alloca.Ty.Pointee
|
||||
if pointee is not None:
|
||||
# Check the pointee type with match
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Array(elem_ty, count):
|
||||
# Array traversal
|
||||
return mlirlite.build_gep_array(builder, pointee, elem_ty, alloca, idx_val)
|
||||
case mlirlite.LLVMType.Ptr(inner_ty):
|
||||
# inner_ty being Ptr means alloca.Ty is a triple pointer, so [i]=val is a pointer
|
||||
# dereference assignment, not a __setitem__ call.
|
||||
if self.is_ptr_type(inner_ty) != 0:
|
||||
ptr_val_gsp: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_gsp is not None: return mlirlite.build_gep(builder, inner_ty, ptr_val_gsp, idx_val)
|
||||
return None
|
||||
# Custom struct (e.g. HashTable*, JsonValue*): return None (triggering the
|
||||
# __setitem__ overload) only if the class defines __setitem__; else plain GEP.
|
||||
cls_nm_ptr_chk: str = self._get_custom_struct_cls_nm(pool, inner_ty)
|
||||
if not (cls_nm_ptr_chk is None):
|
||||
si_lookup_gsp: str = pool.alloc(128)
|
||||
if si_lookup_gsp is not None:
|
||||
viperlib.snprintf(si_lookup_gsp, 128, "%s.__setitem__", cls_nm_ptr_chk)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, si_lookup_gsp) is not None: return None
|
||||
# bytes*/str*: the alloca is i8**, so GEP with an i8* stride (8 bytes) without loading;
|
||||
# pointer traversal applies only when inner_ty is not a custom struct.
|
||||
if trans is not None:
|
||||
ve_pe: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe) != 0:
|
||||
# bytes*/str*/T*: the variable holds a pointer value, so the subscript
|
||||
# means [ptr + i*8]; load the alloca then GEP, or the write misses the data.
|
||||
ptr_val_pe: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_pe is not None:
|
||||
# 64-bit scalar union (int64*/uint64*/sizet*): the 8-byte cell holds an i64 scalar,
|
||||
# so GEP must use Int64, not a pointer, or the store fails-fast as int->ptr.
|
||||
pe_annot: str = self.Gen.VarGen.var_annot_class_name(ve_pe)
|
||||
if pe_annot is not None and self._annot_is_64bit_scalar(pe_annot) != 0: return mlirlite.build_gep(builder, mlirlite.Int64(pool), ptr_val_pe, idx_val)
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Int(bits_pe):
|
||||
if bits_pe == 8:
|
||||
i8_ptr_pe: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
return mlirlite.build_gep(builder, i8_ptr_pe, ptr_val_pe, idx_val)
|
||||
case _:
|
||||
pass
|
||||
return mlirlite.build_gep(builder, pointee, ptr_val_pe, idx_val)
|
||||
return None
|
||||
# Plain pointer traversal: load the pointer value, then GEP
|
||||
ptr_val: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val is None: return None
|
||||
gep_r: mlirlite.Value* = mlirlite.build_gep(builder, inner_ty, ptr_val, idx_val)
|
||||
return gep_r
|
||||
case mlirlite.LLVMType.Struct(_fs_gsp, _fc_gsp, _lsn_gsp):
|
||||
# 值类型类实例(`d: dict = dict(bd)`):槽里是对象本身,`d[k] = v`
|
||||
# 只能走类的 `__setitem__`。定义了 __setitem__ 时返回 None,交由
|
||||
# 赋值路径的 __setitem__ 重载分支(self 用槽地址);否则按对象数组
|
||||
# GEP 会生成非法 IR,明确 fail-fast 而不是静默。
|
||||
cls_nm_val_gsp: str = self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||||
if not (cls_nm_val_gsp is None):
|
||||
si_lookup_val: str = pool.alloc(128)
|
||||
if si_lookup_val is not None:
|
||||
viperlib.snprintf(si_lookup_val, 128, "%s.__setitem__", cls_nm_val_gsp)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, si_lookup_val) is not None: return None
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"cannot index the value-type object '{nm.id}' of type '{_lsn_gsp}' (the class defines no __setitem__); use a pointer variable ('T*') or define __setitem__", "TypeError")
|
||||
return None
|
||||
case _:
|
||||
# Plain scalar pointee (e.g. i64/i32/i8): GEP directly to the idx-th element; union type
|
||||
# (IsPtrElement=1, pointee i8 but 8-byte pointer elements) uses an i8* stride instead.
|
||||
if trans is not None:
|
||||
ve_pe_cs: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if self.Gen.VarGen.var_is_ptr_element(ve_pe_cs) != 0:
|
||||
# T* (degraded to i8*, e.g. _list.vp elem_ptr) holds a data pointer whose elements
|
||||
# are 8-byte pointers: load the alloca then GEP with an i8* stride, or the write misses.
|
||||
ptr_val_cs: mlirlite.Value* = mlirlite.build_load(builder, pointee, alloca)
|
||||
if ptr_val_cs is not None:
|
||||
match pointee:
|
||||
case mlirlite.LLVMType.Int(bits_cs):
|
||||
if bits_cs == 8:
|
||||
i8_ptr_cs: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
return mlirlite.build_gep(builder, i8_ptr_cs, ptr_val_cs, idx_val)
|
||||
case _:
|
||||
pass
|
||||
return mlirlite.build_gep(builder, pointee, ptr_val_cs, idx_val)
|
||||
return None
|
||||
return mlirlite.build_gep(builder, pointee, alloca, idx_val)
|
||||
|
||||
# Generic path
|
||||
ptr_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.value, None, 0, trans)
|
||||
if ptr_val is None or ptr_val.Ty is None: return None
|
||||
if self.is_ptr_type(ptr_val.Ty) != 0:
|
||||
elem_ty2: mlirlite.LLVMType* = ptr_val.Ty.Pointee
|
||||
if elem_ty2 is not None:
|
||||
# Array type: double-index GEP (getelementptr [N x ty], ptr, i32 0, i32 idx)
|
||||
match elem_ty2:
|
||||
case mlirlite.LLVMType.Array(arr_elem_ty, arr_count):
|
||||
return mlirlite.build_gep_array(builder, elem_ty2, arr_elem_ty, ptr_val, idx_val)
|
||||
case _:
|
||||
pass
|
||||
# Custom struct (e.g. self._ht[key]): return None (triggering the HandlesAssign __setitem__
|
||||
# overload) only if the class defines __setitem__; otherwise do a plain GEP array access.
|
||||
cls_nm_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
|
||||
if not (cls_nm_gen is None):
|
||||
si_lookup_gen: str = pool.alloc(128)
|
||||
if si_lookup_gen is not None:
|
||||
viperlib.snprintf(si_lookup_gen, 128, "%s.__setitem__", cls_nm_gen)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, si_lookup_gen) is not None: return None
|
||||
# Pointer type: single-index GEP (getelementptr ty, ptr, idx)
|
||||
return mlirlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
|
||||
return None
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,523 +0,0 @@
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Projectrans.Utils as Utils
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenBase - ExprGenMixin 拆分(阶段 D):表达式基础工具(类型判断/指针层级/coerce)
|
||||
#
|
||||
# 最底层 Base(ExprGenMixin/Const/Fstr/Ops/Cdef/Name/Attr/Cls 均 1 级继承本类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ExprGenBase:
|
||||
"""表达式基础工具(类型判断/指针层级/coerce)"""
|
||||
|
||||
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
|
||||
Gen: Generator.Generator | t.CPtr
|
||||
DocKeys: bytes
|
||||
DocValues: bytes
|
||||
DocCount: int
|
||||
|
||||
# --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) ---
|
||||
DiagCDefineCount: int
|
||||
# --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) ---
|
||||
DiagNameValueCount: int
|
||||
# --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) ---
|
||||
DiagTranslateValueCount: int
|
||||
# ============================================================
|
||||
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
|
||||
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
# 从 AST 节点提取函数名
|
||||
# ============================================================
|
||||
def get_func_name(self, func_node: ast.AST | t.CPtr) -> str:
|
||||
"""从函数节点提取函数名"""
|
||||
if func_node is None:
|
||||
return None
|
||||
k: int = func_node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
|
||||
return nm.id
|
||||
elif k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
|
||||
return at.attr
|
||||
return None
|
||||
# ============================================================
|
||||
# 转义 LLVM IR 字符串字面量
|
||||
# ============================================================
|
||||
def escape_llvm_string(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
src: str) -> str:
|
||||
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
|
||||
if src is None:
|
||||
return None
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
alloc_size: t.CSizeT = 4 * slen + 8
|
||||
buf: str = pool.alloc(alloc_size)
|
||||
if buf is None:
|
||||
return None
|
||||
pos: t.CSizeT = 0
|
||||
buf[pos] = 'c'
|
||||
pos += 1
|
||||
buf[pos] = '"'
|
||||
pos += 1
|
||||
for i in range(slen):
|
||||
ch: t.CChar = src[i]
|
||||
if ch == '\0':
|
||||
break
|
||||
if ch == '\"' or ch == '\\' or ch < 0x20:
|
||||
buf[pos] = '\\'
|
||||
pos += 1
|
||||
hi: int = (ch >> 4) & 0xF
|
||||
lo: int = ch & 0xF
|
||||
buf[pos] = Utils.nibble_to_hex_char(hi, 1)
|
||||
pos += 1
|
||||
buf[pos] = Utils.nibble_to_hex_char(lo, 1)
|
||||
pos += 1
|
||||
else:
|
||||
buf[pos] = ch
|
||||
pos += 1
|
||||
buf[pos] = '\\'
|
||||
pos += 1
|
||||
buf[pos] = '0'
|
||||
pos += 1
|
||||
buf[pos] = '0'
|
||||
pos += 1
|
||||
buf[pos] = '"'
|
||||
pos += 1
|
||||
buf[pos] = '\0'
|
||||
return buf
|
||||
# ============================================================
|
||||
# 获取 LLVM 整数类型的位宽
|
||||
# ============================================================
|
||||
def get_llvm_type_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
|
||||
if ty is None:
|
||||
return 0
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Int(bits):
|
||||
return bits
|
||||
case _:
|
||||
return 0
|
||||
# ============================================================
|
||||
# 获取 LLVM 浮点类型的位宽
|
||||
# ============================================================
|
||||
def get_llvm_float_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
|
||||
if ty is None:
|
||||
return 0
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Float(bits):
|
||||
return bits
|
||||
case _:
|
||||
return 0
|
||||
# ============================================================
|
||||
# is_ptr_type - 检查类型是否为 Ptr
|
||||
#
|
||||
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
|
||||
# ============================================================
|
||||
def is_ptr_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
|
||||
if ty is None:
|
||||
return 0
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Ptr(pointee):
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
# ============================================================
|
||||
# get_ptr_level - 计算指针层级
|
||||
#
|
||||
# 返回值:
|
||||
# 0 = 非指针(数值/结构体等)
|
||||
# 1 = 一级指针(如 i8*, Struct*)
|
||||
# 2 = 二级指针(如 i8**, Struct**)
|
||||
# 3+ = 更高级指针
|
||||
#
|
||||
# 用于比较运算的类型安全检查:
|
||||
# - 数值(0) vs 数值(0) → 允许
|
||||
# - 一级指针(1) vs 一级指针(1) → 允许
|
||||
# - 数值(0) vs 指针(1+) → 禁止(fast-fail)
|
||||
# - 不同级指针(1 vs 2) → 禁止(fast-fail)
|
||||
# ============================================================
|
||||
def get_ptr_level(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针..."""
|
||||
if ty is None:
|
||||
return 0
|
||||
level: int = 0
|
||||
cur: llvmlite.LLVMType | t.CPtr = ty
|
||||
while cur is not None:
|
||||
# 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接
|
||||
# 更新 while 条件变量(cur = pointee)时自举编译出的 match 把赋值
|
||||
# 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case
|
||||
# 绑定到局部变量,match 外更新循环变量。
|
||||
nxt_cur: llvmlite.LLVMType | t.CPtr = None
|
||||
match cur:
|
||||
case llvmlite.LLVMType.Ptr(pointee):
|
||||
level += 1
|
||||
nxt_cur = pointee
|
||||
case _:
|
||||
break
|
||||
cur = nxt_cur
|
||||
return level
|
||||
# ============================================================
|
||||
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
|
||||
#
|
||||
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
|
||||
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
|
||||
# ============================================================
|
||||
def _is_zero_int_literal(self, node: ast.AST | t.CPtr) -> int:
|
||||
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
|
||||
if node is None:
|
||||
return 0
|
||||
if node.kind() != ast.ASTKind.Constant:
|
||||
return 0
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||||
if cn is None or cn.const_kind != ast.CONST_INT:
|
||||
return 0
|
||||
if cn.int_val == 0:
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# is_array_type - 检查类型是否为 Array
|
||||
#
|
||||
# 返回 1=是, 0=否。类型检查工具函数。
|
||||
# ============================================================
|
||||
def is_array_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
|
||||
if ty is None:
|
||||
return 0
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Array(_, _):
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
# ============================================================
|
||||
# _zext_index_to_i32 - 数组下标索引零扩展
|
||||
#
|
||||
# LLVM GEP 对窄整数索引默认符号扩展:i8 0xE2(226) 会被当成 -30,
|
||||
# 导致数组下标越界读(字节 >= 128 的乱码根因)。
|
||||
# 数组下标语义为非负,<32 位整数索引一律 zext 到 i32。
|
||||
# 仅用于数组 GEP;指针负偏移(p[-1])不经过此路径,不受影响。
|
||||
# ============================================================
|
||||
def _zext_index_to_i32(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""将 <32 位整数下标零扩展到 i32(数组下标非负语义)"""
|
||||
if idx_val is None or idx_val.Ty is None or builder is None:
|
||||
return idx_val
|
||||
idx_bits: int = self.get_llvm_type_bits(idx_val.Ty)
|
||||
if idx_bits != 0 and idx_bits < 32:
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(builder.Pool)
|
||||
return llvmlite.build_zext(builder, idx_val, i32_ty)
|
||||
return idx_val
|
||||
# ============================================================
|
||||
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
|
||||
#
|
||||
# 处理两种情况:
|
||||
# - ty 直接是 Struct → 检查是否注册
|
||||
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
|
||||
#
|
||||
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
|
||||
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
|
||||
# ============================================================
|
||||
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
ty: llvmlite.LLVMType | t.CPtr) -> str:
|
||||
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
|
||||
if ty is None:
|
||||
return None
|
||||
# 直接检查 ty 是否是注册的结构体
|
||||
cls_nm_direct: str = self.Gen.StructGen.get_class_name_by_type(pool, ty)
|
||||
if cls_nm_direct is not None:
|
||||
return cls_nm_direct
|
||||
# 如果 ty 是 Ptr,检查 pointee
|
||||
if self.is_ptr_type(ty) != 0:
|
||||
pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee
|
||||
if pointee_cs is not None:
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, pointee_cs)
|
||||
return None
|
||||
# ============================================================
|
||||
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
|
||||
#
|
||||
# 属性访问的 Subscript 基座(如 lst[i].x)需要区分:
|
||||
# - list[T] 变量:translate_subscript 走 __getitem__ 内联返回元素地址
|
||||
# - 普通结构体指针数组(如 pts: Point | t.CPtr):get_subscript_ptr 返回元素地址
|
||||
# 路由错误会让 list[T] 被当作结构体数组 GEP,访问错位崩溃。
|
||||
# ============================================================
|
||||
def _is_list_subscript_base(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
sub_node: ast.AST | t.CPtr) -> int:
|
||||
"""判断下标基座是否为 list[T] 变量(1=是)"""
|
||||
if sub_node is None or trans is None:
|
||||
return 0
|
||||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
|
||||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
|
||||
return 0
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||||
if nm.id is None:
|
||||
return 0
|
||||
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is None or alloca.Ty is None:
|
||||
return 0
|
||||
if self.is_ptr_type(alloca.Ty) == 0:
|
||||
return 0
|
||||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||||
if pointee is None:
|
||||
return 0
|
||||
match pointee:
|
||||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||||
lsn_ls: str = None
|
||||
match inner_ty:
|
||||
case llvmlite.LLVMType.Struct(_, _, lsn3):
|
||||
lsn_ls = lsn3
|
||||
case _:
|
||||
pass
|
||||
if lsn_ls is not None and string.strstr(lsn_ls, "list[") is not None:
|
||||
return 1
|
||||
case _:
|
||||
pass
|
||||
return 0
|
||||
# ============================================================
|
||||
# _sub_base_class_name - 解析下标基座变量的结构体类名
|
||||
# ============================================================
|
||||
def _sub_base_class_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
sub_node: ast.AST | t.CPtr) -> str:
|
||||
"""解析下标基座变量的结构体类名(无则 None)"""
|
||||
if sub_node is None or trans is None:
|
||||
return None
|
||||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
|
||||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
|
||||
return None
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||||
if nm.id is None:
|
||||
return None
|
||||
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is None or alloca.Ty is None:
|
||||
return None
|
||||
if self.is_ptr_type(alloca.Ty) == 0:
|
||||
return None
|
||||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||||
if pointee is None:
|
||||
return None
|
||||
if self.is_ptr_type(pointee) != 0:
|
||||
inner: llvmlite.LLVMType | t.CPtr = pointee.Pointee
|
||||
if inner is not None:
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, inner)
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||||
# ============================================================
|
||||
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
|
||||
#
|
||||
# 属性访问的 Subscript 基座路由依据:
|
||||
# - list[T] / OOP 定义 __getitem__ 的类(如 dict[key])→ translate_value
|
||||
# (translate_subscript 走 __getitem__ 内联/方法调用返回元素值或地址)
|
||||
# - 普通结构体指针数组(如 pts: Point | t.CPtr)→ get_subscript_ptr
|
||||
# (GEP 元素地址,避免结构体值 base=非指针 报错)
|
||||
# ============================================================
|
||||
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
sub_node: ast.AST | t.CPtr) -> int:
|
||||
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)"""
|
||||
if self._is_list_subscript_base(pool, trans, sub_node) != 0:
|
||||
return 1
|
||||
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
|
||||
if cls_ugi is None:
|
||||
return 0
|
||||
lookup_ugi: str = pool.alloc(128)
|
||||
if lookup_ugi is None:
|
||||
return 0
|
||||
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_ugi) is not None:
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||||
#
|
||||
# 用于支持 X|t.CPtr 类型变量的属性访问:
|
||||
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
|
||||
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
|
||||
# ============================================================
|
||||
def _deref_if_ptr_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针"""
|
||||
if obj_ptr is None or obj_ptr.Ty is None:
|
||||
return obj_ptr
|
||||
if self.is_ptr_type(obj_ptr.Ty) == 0:
|
||||
return obj_ptr
|
||||
pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
|
||||
if pointee is None:
|
||||
return obj_ptr
|
||||
if self.is_ptr_type(pointee) == 0:
|
||||
return obj_ptr
|
||||
# obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||||
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
|
||||
if loaded is None:
|
||||
return obj_ptr
|
||||
return loaded
|
||||
# ============================================================
|
||||
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
|
||||
# ============================================================
|
||||
def coerce_to_type(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
val: llvmlite.Value | t.CPtr,
|
||||
target_ty: llvmlite.LLVMType | t.CPtr,
|
||||
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""将 val 强制转换为 target_ty
|
||||
|
||||
整数: sext(扩展)/ trunc(截断)
|
||||
浮点: fpext(扩展)/ fptrunc(截断)
|
||||
int→float: sitofp
|
||||
float→int: fptosi
|
||||
|
||||
node: AST 节点(用于错误定位),可为 None
|
||||
"""
|
||||
if val is None or target_ty is None:
|
||||
return val
|
||||
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
|
||||
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
|
||||
match val.Ty:
|
||||
case llvmlite.LLVMType.Func(_, _, _):
|
||||
if self.is_ptr_type(target_ty) != 0:
|
||||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||||
return val
|
||||
case _:
|
||||
pass
|
||||
# 整数转换
|
||||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||||
target_bits: int = self.get_llvm_type_bits(target_ty)
|
||||
# 整数 → 指针: inttoptr(必须在纯整数转换之前检查,因为 ptr 的 bits 非零会误入 sext/trunc 路径)
|
||||
# 适用于: 跨模块方法返回 i64/i32(默认推断),目标变量是指针类型
|
||||
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
return llvmlite.build_inttoptr(builder, val, target_ty)
|
||||
# 指针 → 整数: ptrtoint(当目标明确是整数而非指针时)
|
||||
# 注意: target_bits == 8 时跳过 ptrtoint,走后面的 build_load 解引用首字符
|
||||
# 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针,
|
||||
# ptrtoint 会截断地址低 8 位(非 0),应 load 解引用取首字符 0
|
||||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and target_bits != 8 and self.is_ptr_type(target_ty) == 0:
|
||||
return llvmlite.build_ptrtoint(builder, val, target_ty)
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
if val_bits == target_bits:
|
||||
return val
|
||||
if val_bits < target_bits:
|
||||
# i1 (bool) → 更宽整数用 zext(bool 语义:1 保持 1,而非 sext 的 0xFF...)
|
||||
# 与 HandlesReturn 中 return 语句的 i1 扩展规则保持一致
|
||||
if val_bits == 1:
|
||||
return llvmlite.build_zext(builder, val, target_ty)
|
||||
return llvmlite.build_sext(builder, val, target_ty)
|
||||
return llvmlite.build_trunc(builder, val, target_ty)
|
||||
# 浮点转换
|
||||
val_fbits: int = self.get_llvm_float_bits(val.Ty)
|
||||
target_fbits: int = self.get_llvm_float_bits(target_ty)
|
||||
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
|
||||
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or \
|
||||
(self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
|
||||
self.Gen.TypeGenErr.fatal_error(node, "unsupported type conversion: float <-> ptr", "CastError")
|
||||
return val
|
||||
if val_fbits != 0 and target_fbits != 0:
|
||||
if val_fbits == target_fbits:
|
||||
return val
|
||||
if val_fbits < target_fbits:
|
||||
return llvmlite.build_fpext(builder, val, target_ty)
|
||||
return llvmlite.build_fptrunc(builder, val, target_ty)
|
||||
# int → float
|
||||
if val_bits != 0 and target_fbits != 0:
|
||||
return llvmlite.build_si2fp(builder, val, target_ty)
|
||||
# float → int
|
||||
if val_fbits != 0 and target_bits != 0:
|
||||
return llvmlite.build_fp2si(builder, val, target_ty)
|
||||
# 指针 → 非指针值: build_load 解引用
|
||||
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
|
||||
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
return llvmlite.build_load(builder, target_ty, val)
|
||||
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||||
return val
|
||||
# ============================================================
|
||||
# translate_ptr_arith - 指针算术: ptr + int / ptr - int
|
||||
#
|
||||
# 用 getelementptr(按 pointee 元素大小缩放索引),对齐 TPC
|
||||
# ExprGen.emit_binary_op / HandlesAugAssign.py。
|
||||
# 旧实现用 ptrtoint + add + inttoptr(按字节加):i8* 元素大小 1
|
||||
# 恰好正确,但 i32*/struct* 等指针 `p + 3` 只前进 3 字节(应 12),
|
||||
# 导致 StringIterTest Test 4/8 失败(指针偏移/指针算术)。
|
||||
#
|
||||
# 数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),
|
||||
# 避免以整个数组为步长(对齐 TPC ExprGen L53-55)。
|
||||
# ============================================================
|
||||
def translate_ptr_arith(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
ptr_val: llvmlite.Value | t.CPtr,
|
||||
int_val: llvmlite.Value | t.CPtr,
|
||||
op: int) -> llvmlite.Value | t.CPtr:
|
||||
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)"""
|
||||
if ptr_val is None or int_val is None or ptr_val.Ty is None:
|
||||
return None
|
||||
# 提取 pointee 类型(非指针 → 不处理返回 None)
|
||||
elem_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
match ptr_val.Ty:
|
||||
case llvmlite.LLVMType.Ptr(pointee):
|
||||
elem_ty = pointee
|
||||
case _:
|
||||
return None
|
||||
if elem_ty is None:
|
||||
return None
|
||||
# 索引统一到 i64(对齐 TPC zext 到 i64)
|
||||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
idx: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, int_val, i64_ty, None)
|
||||
if idx is None:
|
||||
return None
|
||||
# ptr - int → 负索引(LLVM GEP 索引为加法语义)
|
||||
if op == ast.OpKind.Sub:
|
||||
neg_idx: llvmlite.Value | t.CPtr = llvmlite.build_sub(
|
||||
builder, llvmlite.const_int64(pool, 0), idx)
|
||||
if neg_idx is None:
|
||||
return None
|
||||
idx = neg_idx
|
||||
# 数组 pointee → 先 decay 为元素指针(getelementptr [N x ty], [N x ty]* %p, i32 0, i32 0)
|
||||
if self.is_array_type(elem_ty) != 0:
|
||||
inner_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
match elem_ty:
|
||||
case llvmlite.LLVMType.Array(inner, _):
|
||||
inner_ty = inner
|
||||
case _:
|
||||
return None
|
||||
if inner_ty is None:
|
||||
return None
|
||||
decayed: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||||
builder, elem_ty, inner_ty, ptr_val, llvmlite.const_int64(pool, 0))
|
||||
if decayed is None:
|
||||
return None
|
||||
return llvmlite.build_gep(builder, inner_ty, decayed, idx)
|
||||
return llvmlite.build_gep(builder, elem_ty, ptr_val, idx)
|
||||
def InitExprGen(self) -> int:
|
||||
"""初始化 ExprGen 族字段(构造时由 Generator 调用)"""
|
||||
if self is None:
|
||||
return 1
|
||||
self.DocKeys = None
|
||||
self.DocValues = None
|
||||
self.DocCount = 0
|
||||
self.DiagCDefineCount = 0
|
||||
self.DiagNameValueCount = 0
|
||||
self.DiagTranslateValueCount = 0
|
||||
return 0
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,529 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
|
||||
import lib.core.Gen.ExprGenBase as ExprGenBase
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# ============================================================
|
||||
# docstring 登记表容量常量(与 ExprGenMixin 保持一致,本地副本避免
|
||||
# 跨模块 CDefine 查找 bug:ExprGenCls(C) 在 ExprGenMixin(M) 之前按
|
||||
# 文件序编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
|
||||
# ============================================================
|
||||
DOC_MAX: t.CDefine = 512
|
||||
DOC_KEY_SZ: t.CDefine = 192
|
||||
DOC_VAL_SZ: t.CDefine = 4096
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenCls - ExprGenMixin 拆分(阶段 D):类名推断/docstring 登记
|
||||
#
|
||||
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
"""类名推断/docstring 登记"""
|
||||
|
||||
# ============================================================
|
||||
# docstring 提取/登记/查找(__doc__ 魔法属性支持)
|
||||
#
|
||||
# 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有
|
||||
# App/includes 文件的 AST,提取函数/类/方法的 docstring 内容
|
||||
# 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性
|
||||
# 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR
|
||||
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
|
||||
# ============================================================
|
||||
|
||||
def _extract_docstring_from_body(self, body: list[ast.AST | t.CPtr] | t.CPtr) -> str:
|
||||
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
|
||||
if body is None or len(body) == 0:
|
||||
return None
|
||||
first: ast.AST | t.CPtr = body.get(0)
|
||||
if first is None or first.kind() != ast.ASTKind.Expr:
|
||||
return None
|
||||
ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(first)
|
||||
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant:
|
||||
return None
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(ex.value)
|
||||
if cn is None or cn.const_kind != ast.CONST_STR:
|
||||
return None
|
||||
return cn.str_val
|
||||
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
|
||||
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
|
||||
if module_sha1 is None or symbol is None or doc is None:
|
||||
return
|
||||
if string.strlen(doc) == 0:
|
||||
return
|
||||
if self.DocKeys is None:
|
||||
self.DocKeys = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_KEY_SZ)
|
||||
self.DocValues = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_VAL_SZ)
|
||||
if self.DocKeys is None or self.DocValues is None:
|
||||
return
|
||||
string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ)
|
||||
string.memset(self.DocValues, 0, DOC_MAX * DOC_VAL_SZ)
|
||||
if self.DocCount >= DOC_MAX:
|
||||
return
|
||||
ml: t.CSizeT = string.strlen(module_sha1)
|
||||
sl: t.CSizeT = string.strlen(symbol)
|
||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||
if key_buf is None:
|
||||
return
|
||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||
i: int
|
||||
for i in range(self.DocCount):
|
||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||
stdlib.free(key_buf)
|
||||
return
|
||||
string.strcpy(self.DocKeys + self.DocCount * DOC_KEY_SZ, key_buf)
|
||||
string.strcpy(self.DocValues + self.DocCount * DOC_VAL_SZ, doc)
|
||||
self.DocCount += 1
|
||||
stdlib.free(key_buf)
|
||||
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
|
||||
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
|
||||
if self.DocKeys is None or module_sha1 is None or symbol is None:
|
||||
return None
|
||||
ml: t.CSizeT = string.strlen(module_sha1)
|
||||
sl: t.CSizeT = string.strlen(symbol)
|
||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||
if key_buf is None:
|
||||
return None
|
||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||
i: int
|
||||
for i in range(self.DocCount):
|
||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||
stdlib.free(key_buf)
|
||||
return self.DocValues + i * DOC_VAL_SZ
|
||||
stdlib.free(key_buf)
|
||||
return None
|
||||
def extract_module_docstrings(self, tree: ast.AST | t.CPtr, module_sha1: str) -> None:
|
||||
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
|
||||
if tree is None or module_sha1 is None:
|
||||
return
|
||||
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
|
||||
if ch is None:
|
||||
return
|
||||
cn: t.CSizeT = len(ch)
|
||||
for ci in range(cn):
|
||||
child: ast.AST | t.CPtr = ch.get(ci)
|
||||
if child is None:
|
||||
continue
|
||||
kd: int = child.kind()
|
||||
if kd == ast.ASTKind.FunctionDef:
|
||||
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
|
||||
if fd is not None and fd.name is not None:
|
||||
fd_body: list[ast.AST | t.CPtr] | t.CPtr = fd.children
|
||||
fdoc: str = self._extract_docstring_from_body(fd_body)
|
||||
if fdoc is not None:
|
||||
self.register_docstring(module_sha1, fd.name, fdoc)
|
||||
elif kd == ast.ASTKind.ClassDef:
|
||||
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||||
if cd is None or cd.name is None:
|
||||
continue
|
||||
cd_body0: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||||
cdoc: str = self._extract_docstring_from_body(cd_body0)
|
||||
if cdoc is not None:
|
||||
self.register_docstring(module_sha1, cd.name, cdoc)
|
||||
# 类方法 docstring(key="{ClassName}.{method}")
|
||||
cd_body: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||||
if cd_body is not None:
|
||||
cbn: t.CSizeT = len(cd_body)
|
||||
for mi in range(cbn):
|
||||
m: ast.AST | t.CPtr = cd_body.get(mi)
|
||||
if m is None or m.kind() != ast.ASTKind.FunctionDef:
|
||||
continue
|
||||
mfd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m)
|
||||
if mfd is None or mfd.name is None:
|
||||
continue
|
||||
mdoc: str = self._extract_docstring_from_body(mfd.children)
|
||||
if mdoc is not None:
|
||||
cl_nl: t.CSizeT = string.strlen(cd.name)
|
||||
mt_nl: t.CSizeT = string.strlen(mfd.name)
|
||||
sym_buf: bytes = stdlib.malloc(cl_nl + mt_nl + 2)
|
||||
if sym_buf is not None:
|
||||
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
|
||||
self.register_docstring(module_sha1, sym_buf, mdoc)
|
||||
stdlib.free(sym_buf)
|
||||
def _resolve_doc_symbol_name(self, trans: HT.Translator | t.CPtr, name_id: str,
|
||||
out_mod: str, out_sym: str) -> int:
|
||||
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
|
||||
|
||||
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
|
||||
"""
|
||||
if trans is None or name_id is None or out_mod is None or out_sym is None:
|
||||
return 0
|
||||
if trans.ModuleSha1 is None:
|
||||
return 0
|
||||
# 1. 本地变量(函数变量/全局变量)
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
|
||||
se: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(name_id)
|
||||
if se is not None:
|
||||
if se.ModuleSha1 is not None:
|
||||
string.strcpy(out_mod, se.ModuleSha1)
|
||||
else:
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免
|
||||
# 跨 Mixin self 调用生成不存在符号)
|
||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id)
|
||||
if mod_sha1 is None:
|
||||
mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
|
||||
if mod_sha1 is not None:
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 4. 兜底当前模块
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
def _resolve_doc_symbol_attr(self, trans: HT.Translator | t.CPtr,
|
||||
at: ast.Attribute | t.CPtr,
|
||||
out_mod: str, out_sym: str) -> int:
|
||||
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
|
||||
|
||||
支持形态:remote.remote_func / remote.RemoteClass /
|
||||
remote.RemoteClass.remote_method / LocalClass.local_method 等。
|
||||
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
|
||||
"""
|
||||
if trans is None or at is None or out_mod is None or out_sym is None:
|
||||
return 0
|
||||
if trans.ModuleSha1 is None:
|
||||
return 0
|
||||
# 收集 Attribute 链(最外层 attr 在前)
|
||||
DOC_CHAIN_MAX: t.CInt = 8
|
||||
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
|
||||
if items is None:
|
||||
return 0
|
||||
n_items: int = 0
|
||||
cur: ast.AST | t.CPtr = (ast.AST | t.CPtr)(at)
|
||||
while cur is not None and cur.kind() == ast.ASTKind.Attribute and n_items < DOC_CHAIN_MAX:
|
||||
cat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cur)
|
||||
if cat.attr is None:
|
||||
break
|
||||
string.strcpy(items + n_items * 128, cat.attr)
|
||||
n_items += 1
|
||||
cur = cat.value
|
||||
# 最左应为 Name
|
||||
head_id: str = None
|
||||
if cur is not None and cur.kind() == ast.ASTKind.Name:
|
||||
hnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cur)
|
||||
head_id = hnm.id
|
||||
if head_id is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
# head 判定:模块名 or 变量/类
|
||||
is_mod: int = 0
|
||||
mod_sha1: str = None
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, head_id) is None:
|
||||
mod_sha1 = StubMergerStore.LookupSha1ByModName(head_id)
|
||||
if mod_sha1 is None:
|
||||
mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
|
||||
if mod_sha1 is not None:
|
||||
is_mod = 1
|
||||
if is_mod != 0:
|
||||
# 模块限定:symbol = items 反转(不含 head_id)
|
||||
if n_items == 0:
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, head_id)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
total: t.CSizeT = 0
|
||||
j: int
|
||||
for j in range(n_items):
|
||||
total += string.strlen(items + j * 128) + 1
|
||||
sym_buf: bytes = stdlib.malloc(total + 1)
|
||||
if sym_buf is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
sym_buf[0] = '\0'
|
||||
for j in range(n_items):
|
||||
if j > 0:
|
||||
string.strcat(sym_buf, ".")
|
||||
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, sym_buf)
|
||||
stdlib.free(sym_buf)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
# 变量/类:symbol = head_id + '.' + items 反转
|
||||
total2: t.CSizeT = string.strlen(head_id)
|
||||
for j in range(n_items):
|
||||
total2 += string.strlen(items + j * 128) + 1
|
||||
sym2: bytes = stdlib.malloc(total2 + 1)
|
||||
if sym2 is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
sym2[0] = '\0'
|
||||
string.strcat(sym2, head_id)
|
||||
for j in range(n_items):
|
||||
string.strcat(sym2, ".")
|
||||
string.strcat(sym2, items + (n_items - 1 - j) * 128)
|
||||
se2: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(head_id)
|
||||
if se2 is not None and se2.ModuleSha1 is not None:
|
||||
string.strcpy(out_mod, se2.ModuleSha1)
|
||||
else:
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, sym2)
|
||||
stdlib.free(sym2)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
# ============================================================
|
||||
# P3 迁移方法(来自 HandlesExprOps.vp:运算符重载 + 二元运算)
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
# infer_expr_class - 从表达式推断结构体类名(共享推断)
|
||||
#
|
||||
# 无注解赋值(HandlesAssign)与方法调用分派(CallGenMixin)共用:
|
||||
# 联合类型 X|t.CPtr 简化为 Ptr(i8),不含类名,需按表达式结构推断:
|
||||
# Call → 类实例化(func 为已注册结构体名);func 为 Attribute
|
||||
# 时是方法调用,经 vpi 返回注解缓存解析返回类名
|
||||
# Name → 源变量的 AnnotClassName
|
||||
# BinOp/UnaryOp → lhs 类名 + 存在对应 dunder 方法(返回同类对象)
|
||||
# Attribute → 字段注解类名(_resolve_attr_field_class)
|
||||
# 仅采用已注册结构体;无法精确推断返回 None(fail-fast 由调用方暴露)。
|
||||
# ============================================================
|
||||
def infer_expr_class(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> str:
|
||||
"""从表达式推断结构体类名,无法推断返回 None"""
|
||||
if node is None or trans is None:
|
||||
return None
|
||||
k: int = node.kind()
|
||||
# Call: 类实例化 / 方法调用
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
|
||||
if cl is None or cl.func is None:
|
||||
return None
|
||||
if cl.func.kind() == ast.ASTKind.Name:
|
||||
cand: str = ((ast.Name | t.CPtr)(cl.func)).id
|
||||
if cand is None:
|
||||
return None
|
||||
if self.Gen.StructGen.find_struct(cand) is not None:
|
||||
return cand
|
||||
return None
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
# 方法调用 a.cross(b):vpi 返回注解缓存精确解析返回类名
|
||||
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3')。
|
||||
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||||
if ca is None or ca.attr is None or ca.value is None:
|
||||
return None
|
||||
# 模块级函数调用(Module.func()):接收者是已导入模块名而非类对象
|
||||
# (CompilerFactory.get_perm_pool())。返回注解可能是联合类型
|
||||
# (memhub.MemBuddy | t.CPtr),查 vpi 模块函数返回注解(cls=None)
|
||||
# 提取返回类,解决链式方法调用 .alloc() 推断失败退化裸名 @alloc
|
||||
# (链接 undefined symbol: alloc 根因)。
|
||||
if ca.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm2: str = ((ast.Name | t.CPtr)(ca.value)).id
|
||||
if mod_nm2 is not None and trans._from_imports is not None:
|
||||
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(
|
||||
pool, trans._from_imports, mod_nm2)
|
||||
if mod_sha1_2 is not None:
|
||||
temp_dir2: str = None
|
||||
if self.Gen.CallGenBase.TempDir is not None:
|
||||
temp_dir2 = self.Gen.CallGenBase.TempDir
|
||||
if temp_dir2 is not None:
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
|
||||
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(
|
||||
mod_sha1_2, None, ca.attr)
|
||||
if annot_ret2 is not None:
|
||||
ret_cls2: str = self._extract_annot_class_name(pool, annot_ret2)
|
||||
if ret_cls2 is not None:
|
||||
return ret_cls2
|
||||
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
|
||||
if obj_cls is None:
|
||||
return None
|
||||
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0:
|
||||
return None
|
||||
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
|
||||
if cls_sha1 is None and trans.ModuleSha1 is not None:
|
||||
cls_sha1 = trans.ModuleSha1
|
||||
ret_cls: str = None
|
||||
if cls_sha1 is not None:
|
||||
temp_dir: str = None
|
||||
# 跨子对象访问经 self.Gen 取 CallGenBase.TempDir(双向引用)
|
||||
if self.Gen.CallGenBase.TempDir is not None:
|
||||
temp_dir = self.Gen.CallGenBase.TempDir
|
||||
if temp_dir is not None:
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
|
||||
annot_ret: str = VpiReader._lookup_vpi_ret_annot(
|
||||
cls_sha1, obj_cls, ca.attr)
|
||||
if annot_ret is not None:
|
||||
ret_cls = self._extract_annot_class_name(pool, annot_ret)
|
||||
return ret_cls
|
||||
return None
|
||||
# Name: 源变量注解
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||||
if nm is None or nm.id is None:
|
||||
return None
|
||||
ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
|
||||
trans.SymTab, nm.id)
|
||||
if ve is None:
|
||||
return None
|
||||
acn_n: str = ve.AnnotClassName
|
||||
if acn_n is None:
|
||||
return None
|
||||
# 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带
|
||||
# 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回
|
||||
# "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/
|
||||
# get_struct_sha1 查找都用裸类名(struct 注册名)。泛型名
|
||||
# (list[str] 含 '[')不截断(方括号内可能有点号)。
|
||||
acn_dot_n: str = string.strrchr(acn_n, 46)
|
||||
if acn_dot_n is not None and string.strchr(acn_n, 91) is None:
|
||||
acn_n = acn_dot_n + 1
|
||||
return acn_n
|
||||
# BinOp/UnaryOp: 运算符重载返回同类对象
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
|
||||
if bop is None or bop.left is None:
|
||||
return None
|
||||
dunder: str = self.Gen.ExprGenOps._binop_to_dunder(bop.op)
|
||||
if dunder is None:
|
||||
return None
|
||||
lhs_cls: str = self.infer_expr_class(pool, mod, bop.left, trans)
|
||||
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0:
|
||||
return None
|
||||
return lhs_cls
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
|
||||
if uop is None or uop.operand is None:
|
||||
return None
|
||||
dunder: str = self.Gen.ExprGenOps._unaryop_to_dunder(uop.op)
|
||||
if dunder is None:
|
||||
return None
|
||||
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
|
||||
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0:
|
||||
return None
|
||||
return opr_cls
|
||||
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
|
||||
if k == ast.ASTKind.Attribute:
|
||||
return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
|
||||
return None
|
||||
# ============================================================
|
||||
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder/方法
|
||||
#
|
||||
# 与 try_operator_overload 的查找一致:当前模块内定义或跨模块类有
|
||||
# SHA1(stub 中声明)即视为存在。仅在确实存在时采用推断类名,
|
||||
# 避免把原生运算误标为类类型。
|
||||
# ============================================================
|
||||
def _cls_has_dunder(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
class_name: str, dunder: str) -> int:
|
||||
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
|
||||
if class_name is None or dunder is None or mod is None:
|
||||
return 0
|
||||
lookup_name: str = pool.alloc(128)
|
||||
if lookup_name is None:
|
||||
return 0
|
||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None:
|
||||
return 1
|
||||
# 继承链祖先查找
|
||||
mro_ptr: t.CSizeT | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||||
mi: int = 1
|
||||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||||
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
|
||||
if anc is not None:
|
||||
anc_lookup: str = pool.alloc(128)
|
||||
if anc_lookup is not None:
|
||||
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None:
|
||||
return 1
|
||||
mi += 1
|
||||
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
|
||||
if self.Gen.StructGen.get_struct_sha1(class_name) is not None:
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _extract_annot_class_name - 从返回注解文本提取结构体类名
|
||||
#
|
||||
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
|
||||
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
|
||||
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
|
||||
# 多候选时取首个命中段。
|
||||
# ============================================================
|
||||
def _extract_annot_class_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
annot: str) -> str:
|
||||
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
|
||||
if pool is None or annot is None:
|
||||
return None
|
||||
alen: t.CSizeT = string.strlen(annot)
|
||||
if alen == 0:
|
||||
return None
|
||||
seg: str = pool.alloc(256)
|
||||
if seg is None:
|
||||
return None
|
||||
start: t.CSizeT = 0
|
||||
i: t.CSizeT
|
||||
for i in range(alen):
|
||||
if annot[i] == '|':
|
||||
s: t.CSizeT = start
|
||||
e: t.CSizeT = i
|
||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
|
||||
s += 1
|
||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
|
||||
e -= 1
|
||||
seglen: t.CSizeT = e - s
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
if self.Gen.StructGen.find_struct(seg) is not None:
|
||||
return seg
|
||||
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中
|
||||
# 时去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
|
||||
seg_dot2: str = string.strrchr(seg, 46)
|
||||
if seg_dot2 is not None and seg_dot2 != seg:
|
||||
seg_bare2: str = seg_dot2 + 1
|
||||
if self.Gen.StructGen.find_struct(seg_bare2) is not None:
|
||||
return seg_bare2
|
||||
start = i + 1
|
||||
# 最后一段(无 '|' 后缀)
|
||||
s: t.CSizeT = start
|
||||
e: t.CSizeT = alen
|
||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
|
||||
s += 1
|
||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
|
||||
e -= 1
|
||||
seglen: t.CSizeT = e - s
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
if self.Gen.StructGen.find_struct(seg) is not None:
|
||||
return seg
|
||||
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中时
|
||||
# 去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
|
||||
seg_dot3: str = string.strrchr(seg, 46)
|
||||
if seg_dot3 is not None and seg_dot3 != seg:
|
||||
seg_bare3: str = seg_dot3 + 1
|
||||
if self.Gen.StructGen.find_struct(seg_bare3) is not None:
|
||||
return seg_bare3
|
||||
return None
|
||||
@@ -1,411 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Projectrans.Utils as Utils
|
||||
|
||||
import lib.core.Gen.ExprGenBase as ExprGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenConst - ExprGenMixin 拆分(阶段 D):常量/全局字符串/list 字面量翻译
|
||||
#
|
||||
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
"""常量/全局字符串/list 字面量翻译"""
|
||||
|
||||
# ============================================================
|
||||
# 创建全局字符串常量
|
||||
# ============================================================
|
||||
def create_global_string(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
str_val: str,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""创建全局字符串常量并返回 i8* bitcast"""
|
||||
escaped: str = self.escape_llvm_string(pool, str_val)
|
||||
if escaped is None:
|
||||
return None
|
||||
|
||||
slen: t.CSizeT = string.strlen(str_val)
|
||||
count: int = slen + 1
|
||||
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)
|
||||
|
||||
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
|
||||
str_idx: int = trans._label_counter
|
||||
trans._label_counter += 1
|
||||
|
||||
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
|
||||
gv_name: str = pool.alloc(48)
|
||||
if gv_name is None:
|
||||
return None
|
||||
if trans.ModuleSha1 is not None:
|
||||
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
|
||||
else:
|
||||
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
|
||||
|
||||
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
|
||||
if gv is None:
|
||||
return None
|
||||
llvmlite.module_add_global(mod, gv)
|
||||
|
||||
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
|
||||
llvmlite.global_set_unnamed_addr(gv, 1)
|
||||
llvmlite.global_set_constant(gv, 1)
|
||||
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
|
||||
gv.Linkage = "internal"
|
||||
gv.Initializer = escaped
|
||||
|
||||
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
|
||||
gv_ref_name: str = pool.alloc(64)
|
||||
if gv_ref_name is None:
|
||||
return None
|
||||
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
|
||||
gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
|
||||
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
|
||||
bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
||||
return bc
|
||||
# ============================================================
|
||||
# 翻译常量表达式
|
||||
# ============================================================
|
||||
def translate_constant(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr = None,
|
||||
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译常量(int/str/bool)"""
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||||
if cn is None:
|
||||
return None
|
||||
ck: int = cn.const_kind
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
# 超出 i32 范围则用 i64(避免常量创建时被截断)
|
||||
iv: t.CInt64T = cn.int_val
|
||||
if iv > 2147483647 or iv < -2147483648:
|
||||
return llvmlite.const_int64(pool, iv)
|
||||
return llvmlite.const_int32(pool, iv)
|
||||
elif cn.const_kind == ast.CONST_FLOAT:
|
||||
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
|
||||
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
|
||||
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
|
||||
elif cn.const_kind == ast.CONST_CHAR:
|
||||
# 单引号单字符字面量:默认按字符值(i32,coerce_to_type 截断为 i8)。
|
||||
# 但当目标类型是指针(str/bytes/t.CPtr 等)时,对齐 TPC 引号推断
|
||||
# (HandlesExpr:"目标是 ptr 但值是 char → 走字符串路径"):物化为
|
||||
# 1 字符字符串。否则 coerce inttoptr 把字符值当指针(如 '"'=0x22),
|
||||
# 运行期 strlen 访问低地址 → ASan 崩溃(二代编 StringTest PyI 生成根因)。
|
||||
# 1a 阶段(declare_only=1)不 LoadDepVpi(vpi 未预生成)→ 依赖函数
|
||||
# found_func=None → translate_call 缺省参数补全不生效 → translate_value
|
||||
# 调用参数数 < define(如 target_ty 缺省)→ x64 ABI 被调方读第 9 参
|
||||
# 栈槽残留垃圾(0x4 等)。declare_only 不产出 IR,target_ty 无用,
|
||||
# 未对齐(8 对齐失败)即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
|
||||
# (SlotsBugTest 1a string.vp 崩溃根因;1b found_func 非 None 正常填充)。
|
||||
if target_ty is not None and (t.CUInt64T(target_ty) & 7) == 0 \
|
||||
and self.is_ptr_type(target_ty) != 0:
|
||||
cbuf: bytes = stdlib.malloc(2)
|
||||
if cbuf is None:
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
cbuf[0] = t.CChar(cn.int_val)
|
||||
cbuf[1] = '\0'
|
||||
r_cs: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, cbuf, trans)
|
||||
stdlib.free(cbuf)
|
||||
return r_cs
|
||||
cv: t.CInt64T = cn.int_val
|
||||
return llvmlite.const_int32(pool, cv)
|
||||
elif cn.const_kind == ast.CONST_STR:
|
||||
sv: str = cn.str_val
|
||||
if sv is None:
|
||||
return None
|
||||
r: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, sv, trans)
|
||||
return r
|
||||
elif cn.const_kind == ast.CONST_BOOL:
|
||||
if cn.int_val != 0:
|
||||
return llvmlite.const_int32(pool, 1)
|
||||
return llvmlite.const_int32(pool, 0)
|
||||
elif cn.const_kind == ast.CONST_NONE:
|
||||
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
if i8_ty is None:
|
||||
return None
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
if i8_ptr_ty is None:
|
||||
return None
|
||||
rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
|
||||
return rv_none
|
||||
return None
|
||||
# ============================================================
|
||||
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
|
||||
#
|
||||
# 用于 list 字面量 [a, b, c] 的元素类型推断
|
||||
# ============================================================
|
||||
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
value: llvmlite.Value | t.CPtr) -> str:
|
||||
"""从 LLVM Value 的类型推断 list 元素类型名
|
||||
|
||||
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
|
||||
绝不静默回退 "str"(掩盖上游 Value 类型缺失根因)。
|
||||
"""
|
||||
if value is None or value.Ty is None:
|
||||
return None
|
||||
ty: llvmlite.LLVMType | t.CPtr = value.Ty
|
||||
bits: int = self.get_llvm_type_bits(ty)
|
||||
if bits != 0:
|
||||
if bits == 32:
|
||||
return "int"
|
||||
if bits == 64:
|
||||
return "CSizeT"
|
||||
if bits == 8:
|
||||
return "CInt8T"
|
||||
if bits == 16:
|
||||
return "CInt16T"
|
||||
return None
|
||||
if self.is_ptr_type(ty) != 0:
|
||||
return "str"
|
||||
fbits: int = self.get_llvm_float_bits(ty)
|
||||
if fbits != 0:
|
||||
if fbits == 64:
|
||||
return "CDouble"
|
||||
if fbits == 32:
|
||||
return "CFloat"
|
||||
return None
|
||||
# ============================================================
|
||||
# _find_pool_var — 查找上下文中的 pool 变量
|
||||
#
|
||||
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
|
||||
# ============================================================
|
||||
def _find_pool_var(self, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""查找上下文中的 pool 变量,返回 alloca 或 None
|
||||
|
||||
查找顺序: pool → _mbuddy → mbuddy → mb
|
||||
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
|
||||
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
|
||||
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
|
||||
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
|
||||
"""
|
||||
if trans is None:
|
||||
return None
|
||||
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
|
||||
if alloca is not None:
|
||||
return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
|
||||
if alloca is not None:
|
||||
return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mbuddy")
|
||||
if alloca is not None:
|
||||
return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
|
||||
if alloca is not None:
|
||||
return alloca
|
||||
# 兜底: 创建当前模块 mangled 的 @_mbuddy 定义(linkonce_odr + zeroinitializer)
|
||||
# 对齐模块级全局带 SHA1 前缀命名:裸名 @_mbuddy 依赖链接合并共享
|
||||
# (--allow-multiple-definition),带前缀后每模块独立。linkonce_odr 允许
|
||||
# 链接器合并同模板重复特化定义,zeroinitializer 保证编译链接通过。
|
||||
# 运行时若该模块未在入口 main 赋值 _mbuddy(null pool 上动态建 list),
|
||||
# 由真实崩溃暴露(fail-fast 哲学,不掩盖上游未初始化)。
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
if pool is None or trans.Module is None or trans.SymTab is None:
|
||||
return None
|
||||
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
|
||||
existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
|
||||
if existing is not None:
|
||||
return existing
|
||||
# 创建 i8* 类型(t.CVoid | t.CPtr = i8*)
|
||||
i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb)
|
||||
# 当前模块 mangled 名(@<模块sha1>._mbuddy)
|
||||
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
|
||||
# 创建 linkonce_odr 定义(可被链接器合并,自给自足保证链接通过)
|
||||
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
|
||||
if gv_ext is not None:
|
||||
gv_ext.Linkage = "linkonce_odr"
|
||||
gv_ext.Initializer = "zeroinitializer"
|
||||
llvmlite.module_add_global(trans.Module, gv_ext)
|
||||
# 创建 Value 引用(@<模块sha1>._mbuddy, 类型为 i8**)
|
||||
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
|
||||
ref_name_fb: str = pool.alloc(96)
|
||||
if ref_name_fb is not None:
|
||||
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(见 make_global_ref)
|
||||
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0:
|
||||
viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
|
||||
else:
|
||||
viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
|
||||
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
||||
# 注册到模块作用域,避免重复创建
|
||||
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
||||
return gv_ref_fb
|
||||
# ============================================================
|
||||
# translate_list_literal — 翻译 list 字面量 [a, b, c]
|
||||
#
|
||||
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
|
||||
#
|
||||
# Args:
|
||||
# builder: IRBuilder
|
||||
# pool: 编译器内存池(MemBuddy)
|
||||
# mod: LLVMModule
|
||||
# node: ast.List 节点
|
||||
# elem_type_name: 元素类型名(如 "str", "int"),None 表示从元素推断
|
||||
# trans: Translator 对象
|
||||
#
|
||||
# Returns:
|
||||
# list 对象指针(Ptr(list[T] struct)),None 失败
|
||||
# ============================================================
|
||||
def translate_list_literal(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
elem_type_name: str,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
|
||||
if node is None or trans is None:
|
||||
return None
|
||||
|
||||
list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
|
||||
if list_node is None:
|
||||
return None
|
||||
|
||||
elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
|
||||
elts_count: t.CSizeT = 0
|
||||
if elts is not None:
|
||||
elts_count = len(elts)
|
||||
|
||||
# 推断元素类型
|
||||
inferred_type_name: str = elem_type_name
|
||||
first_elem_val: llvmlite.Value | t.CPtr = None
|
||||
if inferred_type_name is None:
|
||||
if elts_count > 0:
|
||||
first_elem_node: ast.AST | t.CPtr = elts.get(0)
|
||||
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
|
||||
if first_elem_val is None or first_elem_val.Ty is None:
|
||||
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
||||
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
|
||||
if inferred_type_name is None:
|
||||
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
||||
else:
|
||||
# 空 list 无元素可推断,保持默认 str(list[str])
|
||||
inferred_type_name = "str"
|
||||
|
||||
# 查找 pool 变量
|
||||
pool_alloca: llvmlite.Value | t.CPtr = self._find_pool_var(trans)
|
||||
if pool_alloca is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
|
||||
return None
|
||||
|
||||
# load pool 变量值(i8*)
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
|
||||
if pool_val is None:
|
||||
return None
|
||||
|
||||
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
|
||||
type_args: list[str] | t.CPtr = list[str](pool, 8)
|
||||
type_args.append(inferred_type_name)
|
||||
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
|
||||
if spec_name is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
|
||||
return None
|
||||
|
||||
# 查找 struct entry
|
||||
struct_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
|
||||
if struct_entry is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
|
||||
return None
|
||||
struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
|
||||
if struct_ty is None:
|
||||
return None
|
||||
|
||||
# alloca 临时 list 空间
|
||||
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||||
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
|
||||
if tmp is None:
|
||||
return None
|
||||
|
||||
# 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针
|
||||
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
|
||||
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
|
||||
# 避免为无 __new__ 的类生成过度声明)
|
||||
storage_ptr: llvmlite.Value | t.CPtr = tmp
|
||||
has_new_flag: int = 0
|
||||
if struct_entry is not None:
|
||||
has_new_flag = struct_entry.HasNew
|
||||
if has_new_flag != 0:
|
||||
new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||||
if new_args is None:
|
||||
return None
|
||||
string.memset(new_args, 0, 8 * 32)
|
||||
new_args[0] = t.CSizeT(pool_val)
|
||||
new_ptr: llvmlite.Value | t.CPtr = self.Gen.CallGenCall._call_method_on_ptr(
|
||||
pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
|
||||
# 调用 __before_init__(storage_ptr)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(
|
||||
pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
|
||||
|
||||
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
|
||||
init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||||
if init_args is None:
|
||||
return None
|
||||
string.memset(init_args, 0, 8 * 32)
|
||||
init_args[0] = t.CSizeT(pool_val)
|
||||
elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
|
||||
init_args[1] = t.CSizeT(elem_size_val)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(
|
||||
pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
|
||||
|
||||
# 对每个元素调用 append(storage_ptr, elem_val)
|
||||
if elts_count > 0:
|
||||
# 第一个元素可能已经翻译过
|
||||
if first_elem_val is not None:
|
||||
append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||||
if append_args is not None:
|
||||
string.memset(append_args, 0, 8 * 32)
|
||||
append_args[0] = t.CSizeT(first_elem_val)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(
|
||||
pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
||||
|
||||
# 翻译剩余元素
|
||||
i: t.CSizeT = 1
|
||||
while i < elts_count:
|
||||
elem_node_i: ast.AST | t.CPtr = elts.get(i)
|
||||
elem_val_i: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
||||
if elem_val_i is not None:
|
||||
ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||||
if ap_args is not None:
|
||||
string.memset(ap_args, 0, 8 * 32)
|
||||
ap_args[0] = t.CSizeT(elem_val_i)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(
|
||||
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
|
||||
i += 1
|
||||
|
||||
return storage_ptr
|
||||
@@ -1,502 +0,0 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Projectrans.Utils as Utils
|
||||
|
||||
|
||||
import lib.core.Gen.ExprGenBase as ExprGenBase
|
||||
|
||||
# ============================================================
|
||||
# f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块
|
||||
# CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序
|
||||
# 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
|
||||
# ============================================================
|
||||
FSTR_BUF_SIZE: t.CDefine = 1024
|
||||
FSTR_SPEC_MAX: t.CDefine = 64
|
||||
FSTR_FMT_MAX: t.CDefine = 64
|
||||
|
||||
# ============================================================
|
||||
# ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译
|
||||
#
|
||||
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
"""f-string/格式化值翻译"""
|
||||
|
||||
# ============================================================
|
||||
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
|
||||
#
|
||||
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
|
||||
# 栈缓冲。之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
|
||||
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
|
||||
# ============================================================
|
||||
def translate_joined_str(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""f-string → snprintf 生成 i8* 栈缓冲"""
|
||||
if node is None or builder is None or trans is None:
|
||||
return None
|
||||
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
|
||||
if js is None or js.values is None:
|
||||
return None
|
||||
|
||||
# 编译期格式串缓冲(pool 分配)
|
||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||||
if fmt_buf is None:
|
||||
return None
|
||||
fmt_buf[0] = '\0'
|
||||
|
||||
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
|
||||
arg_head: llvmlite.Value | t.CPtr = None
|
||||
arg_tail: llvmlite.Value | t.CPtr = None
|
||||
arg_count: int = 0
|
||||
|
||||
parts: list[ast.AST | t.CPtr] | t.CPtr = js.values
|
||||
pn: t.CSizeT = len(parts)
|
||||
for pi in range(pn):
|
||||
part: ast.AST | t.CPtr = parts.get(pi)
|
||||
if part is None:
|
||||
continue
|
||||
if part.kind() == ast.ASTKind.Constant:
|
||||
pcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
|
||||
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
|
||||
self._fstr_append_literal(fmt_buf, pcn.str_val)
|
||||
continue
|
||||
if part.kind() != ast.ASTKind.FormattedValue:
|
||||
continue
|
||||
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
|
||||
if fv.value is None:
|
||||
continue
|
||||
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, fv.value, None, 0, trans)
|
||||
if val is None:
|
||||
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
||||
continue
|
||||
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
|
||||
c_fmt: str = None
|
||||
if fv.format_spec is not None:
|
||||
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||
if c_fmt is None:
|
||||
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||
if c_fmt is None:
|
||||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||||
# 追加格式说明符
|
||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
|
||||
string.strcat(fmt_buf, c_fmt)
|
||||
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
|
||||
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
|
||||
builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None:
|
||||
cast_val = val
|
||||
cast_val.Next = None
|
||||
if arg_head is None:
|
||||
arg_head = cast_val
|
||||
else:
|
||||
arg_tail.Next = cast_val
|
||||
arg_tail = cast_val
|
||||
arg_count += 1
|
||||
|
||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||
def translate_formatted_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
|
||||
if node is None or builder is None or trans is None:
|
||||
return None
|
||||
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
|
||||
if fv is None or fv.value is None:
|
||||
return None
|
||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||||
if fmt_buf is None:
|
||||
return None
|
||||
fmt_buf[0] = '\0'
|
||||
arg_head: llvmlite.Value | t.CPtr = None
|
||||
arg_tail: llvmlite.Value | t.CPtr = None
|
||||
arg_count: int = 0
|
||||
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, fv.value, None, 0, trans)
|
||||
if val is None:
|
||||
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
||||
else:
|
||||
c_fmt: str = None
|
||||
if fv.format_spec is not None:
|
||||
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||
if c_fmt is None:
|
||||
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||
if c_fmt is None:
|
||||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
|
||||
string.strcat(fmt_buf, c_fmt)
|
||||
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
|
||||
builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None:
|
||||
cast_val = val
|
||||
cast_val.Next = None
|
||||
arg_head = cast_val
|
||||
arg_tail = cast_val
|
||||
arg_count = 1
|
||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||
def _fstr_finalize(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
fmt_buf: str,
|
||||
arg_head: llvmlite.Value | t.CPtr,
|
||||
arg_tail: llvmlite.Value | t.CPtr,
|
||||
arg_count: int,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf,返回 i8*"""
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
if fmt_buf is None or fmt_buf[0] == '\0':
|
||||
# 空 f-string(f"")→ 空字符串常量
|
||||
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
|
||||
# 格式串 → 全局字符串常量(i8*)
|
||||
fmt_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
|
||||
builder, pool, mod, fmt_buf, trans)
|
||||
if fmt_ptr is None:
|
||||
return None
|
||||
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
|
||||
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
|
||||
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
|
||||
if buf_alloca is None:
|
||||
return None
|
||||
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
buf_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||||
builder, buf_ty, i8_ty, buf_alloca, zero_i32)
|
||||
if buf_ptr is None:
|
||||
return None
|
||||
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
|
||||
size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
|
||||
buf_ptr.Next = size_val
|
||||
size_val.Next = fmt_ptr
|
||||
fmt_ptr.Next = arg_head
|
||||
if arg_tail is not None:
|
||||
arg_tail.Next = None
|
||||
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
||||
return buf_ptr
|
||||
def _fstr_append_literal(self, fmt_buf: str,
|
||||
lit: str) -> None:
|
||||
"""追加字面量到格式串(% 转义为 %%)"""
|
||||
if fmt_buf is None or lit is None:
|
||||
return
|
||||
cur_len: t.CSizeT = string.strlen(fmt_buf)
|
||||
rem: t.CSizeT = FSTR_BUF_SIZE - 1 - cur_len
|
||||
if rem == 0:
|
||||
return
|
||||
src_len: t.CSizeT = string.strlen(lit)
|
||||
si: t.CSizeT = 0
|
||||
while si < src_len and rem > 0:
|
||||
ch: t.CChar = lit[si]
|
||||
if ch == '%':
|
||||
if rem < 2:
|
||||
break
|
||||
fmt_buf[cur_len] = '%'
|
||||
fmt_buf[cur_len + 1] = '%'
|
||||
cur_len += 2
|
||||
rem -= 2
|
||||
else:
|
||||
fmt_buf[cur_len] = ch
|
||||
cur_len += 1
|
||||
rem -= 1
|
||||
si += 1
|
||||
fmt_buf[cur_len] = '\0'
|
||||
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
|
||||
"""追加原始字符串(不做 % 转义)"""
|
||||
if fmt_buf is None or s is None:
|
||||
return
|
||||
cur_len: t.CSizeT = string.strlen(fmt_buf)
|
||||
src_len: t.CSizeT = string.strlen(s)
|
||||
if cur_len + src_len < FSTR_BUF_SIZE:
|
||||
string.strcpy(fmt_buf + cur_len, s)
|
||||
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
spec_node: ast.AST | t.CPtr,
|
||||
val: llvmlite.Value | t.CPtr,
|
||||
arg_node: ast.AST | t.CPtr) -> str:
|
||||
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
|
||||
if spec_node is None or pool is None or val is None:
|
||||
return None
|
||||
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
|
||||
spec: str = pool.alloc(FSTR_SPEC_MAX)
|
||||
if spec is None:
|
||||
return None
|
||||
spec[0] = '\0'
|
||||
sk: int = spec_node.kind()
|
||||
if sk == ast.ASTKind.JoinedStr:
|
||||
jss: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(spec_node)
|
||||
if jss.values is None:
|
||||
return None
|
||||
vn: t.CSizeT = len(jss.values)
|
||||
for vi in range(vn):
|
||||
vpart: ast.AST | t.CPtr = jss.values.get(vi)
|
||||
if vpart is None:
|
||||
continue
|
||||
if vpart.kind() != ast.ASTKind.Constant:
|
||||
return None
|
||||
vcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(vpart)
|
||||
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None:
|
||||
return None
|
||||
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
|
||||
string.strcat(spec, vcn.str_val)
|
||||
elif sk == ast.ASTKind.Constant:
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(spec_node)
|
||||
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
|
||||
return None
|
||||
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
|
||||
else:
|
||||
return None
|
||||
slen: t.CSizeT = string.strlen(spec)
|
||||
if slen == 0:
|
||||
return None
|
||||
|
||||
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
|
||||
pos: t.CSizeT = 0
|
||||
align_char: t.CChar = 0
|
||||
if pos < slen:
|
||||
ch0: t.CChar = spec[pos]
|
||||
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
|
||||
align_char = ch0
|
||||
pos += 1
|
||||
elif pos + 1 < slen:
|
||||
ch1: t.CChar = spec[pos + 1]
|
||||
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
|
||||
align_char = ch1
|
||||
pos += 2
|
||||
c_flags: str = pool.alloc(FSTR_FMT_MAX)
|
||||
c_width: str = pool.alloc(32)
|
||||
c_prec: str = pool.alloc(32)
|
||||
if c_flags is None or c_width is None or c_prec is None:
|
||||
return None
|
||||
c_flags[0] = '\0'
|
||||
c_width[0] = '\0'
|
||||
c_prec[0] = '\0'
|
||||
# sign
|
||||
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
|
||||
if spec[pos] == '+':
|
||||
string.strcat(c_flags, "+")
|
||||
elif spec[pos] == ' ':
|
||||
string.strcat(c_flags, " ")
|
||||
pos += 1
|
||||
# alt '#'
|
||||
if pos < slen and spec[pos] == '#':
|
||||
string.strcat(c_flags, "#")
|
||||
pos += 1
|
||||
# zero '0'
|
||||
if pos < slen and spec[pos] == '0':
|
||||
string.strcat(c_flags, "0")
|
||||
pos += 1
|
||||
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
|
||||
if align_char == '<' or align_char == '^':
|
||||
string.strcat(c_flags, "-")
|
||||
# width digits
|
||||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||||
wl: t.CSizeT = string.strlen(c_width)
|
||||
if wl < 30:
|
||||
c_width[wl] = spec[pos]
|
||||
c_width[wl + 1] = '\0'
|
||||
pos += 1
|
||||
# comma ',' → C printf 不支持千分位,忽略
|
||||
if pos < slen and spec[pos] == ',':
|
||||
pos += 1
|
||||
# .precision
|
||||
if pos < slen and spec[pos] == '.':
|
||||
string.strcat(c_prec, ".")
|
||||
pos += 1
|
||||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||||
pl: t.CSizeT = string.strlen(c_prec)
|
||||
if pl < 30:
|
||||
c_prec[pl] = spec[pos]
|
||||
c_prec[pl + 1] = '\0'
|
||||
pos += 1
|
||||
if string.strlen(c_prec) == 1:
|
||||
return None # 只有 '.' 无效
|
||||
# type char
|
||||
fmt_type: t.CChar = 0
|
||||
if pos < slen:
|
||||
fmt_type = spec[pos]
|
||||
pos += 1
|
||||
if pos != slen:
|
||||
return None # 多余字符无法解析
|
||||
|
||||
# 3. 长度修饰(按值类型位宽)
|
||||
len_mod: str = pool.alloc(4)
|
||||
if len_mod is None:
|
||||
return None
|
||||
len_mod[0] = '\0'
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
if ibits == 64:
|
||||
string.strcpy(len_mod, "ll")
|
||||
elif ibits == 32:
|
||||
string.strcpy(len_mod, "l")
|
||||
elif ibits == 16:
|
||||
string.strcpy(len_mod, "h")
|
||||
elif ibits == 8:
|
||||
string.strcpy(len_mod, "hh")
|
||||
|
||||
# 4. 组装 C 格式串
|
||||
out: str = pool.alloc(FSTR_FMT_MAX)
|
||||
if out is None:
|
||||
return None
|
||||
out[0] = '\0'
|
||||
string.strcat(out, "%")
|
||||
string.strcat(out, c_flags)
|
||||
string.strcat(out, c_width)
|
||||
string.strcat(out, c_prec)
|
||||
if fmt_type == 0:
|
||||
# 无 type:按值类型推断
|
||||
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if ibits != 0:
|
||||
if ibits == 8:
|
||||
string.strcat(out, "c")
|
||||
elif ibits == 1:
|
||||
string.strcat(out, "d")
|
||||
else:
|
||||
string.strcat(out, len_mod)
|
||||
string.strcat(out, "d")
|
||||
elif fbits2 != 0:
|
||||
string.strcat(out, "f")
|
||||
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
|
||||
string.strcat(out, "s")
|
||||
else:
|
||||
string.strcat(out, "d")
|
||||
elif fmt_type == 'c':
|
||||
string.strcat(out, "c")
|
||||
elif fmt_type == 'f' or fmt_type == 'F':
|
||||
string.strcat(out, "f")
|
||||
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
|
||||
tmp_ch: str = pool.alloc(2)
|
||||
if tmp_ch is None:
|
||||
return None
|
||||
tmp_ch[0] = fmt_type
|
||||
tmp_ch[1] = '\0'
|
||||
string.strcat(out, tmp_ch)
|
||||
elif fmt_type == 's':
|
||||
string.strcat(out, "s")
|
||||
elif fmt_type == '%':
|
||||
string.strcat(out, "%")
|
||||
elif fmt_type == 'b':
|
||||
return None # 二进制不支持
|
||||
elif fmt_type == 'd' or fmt_type == 'i':
|
||||
string.strcat(out, len_mod)
|
||||
string.strcat(out, "d")
|
||||
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
|
||||
string.strcat(out, len_mod)
|
||||
tmp_ch2: str = pool.alloc(2)
|
||||
if tmp_ch2 is None:
|
||||
return None
|
||||
tmp_ch2[0] = fmt_type
|
||||
tmp_ch2[1] = '\0'
|
||||
string.strcat(out, tmp_ch2)
|
||||
else:
|
||||
return None
|
||||
return out
|
||||
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
val: llvmlite.Value | t.CPtr,
|
||||
arg_node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> str:
|
||||
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
|
||||
if val is None or pool is None:
|
||||
return None
|
||||
out: str = pool.alloc(16)
|
||||
if out is None:
|
||||
return None
|
||||
out[0] = '\0'
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if ibits == 1:
|
||||
return "%d" # bool
|
||||
if ibits == 8:
|
||||
return "%c" # 字符
|
||||
if ibits != 0:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
||||
if ibits == 64:
|
||||
string.strcpy(out, "%llu")
|
||||
else:
|
||||
string.strcpy(out, "%u")
|
||||
else:
|
||||
if ibits == 64:
|
||||
string.strcpy(out, "%lld")
|
||||
else:
|
||||
string.strcpy(out, "%d")
|
||||
return out
|
||||
if fbits != 0:
|
||||
return "%f"
|
||||
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
|
||||
return "%s"
|
||||
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||||
return None
|
||||
def _fstr_cast_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
val: llvmlite.Value | t.CPtr,
|
||||
c_fmt: str,
|
||||
arg_node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
|
||||
if val is None or c_fmt is None:
|
||||
return None
|
||||
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
|
||||
fmt_char: t.CChar = 0
|
||||
flen: t.CSizeT = string.strlen(c_fmt)
|
||||
fi: t.CSizeT = flen
|
||||
while fi > 0:
|
||||
fi -= 1
|
||||
ch: t.CChar = c_fmt[fi]
|
||||
if ch == '%':
|
||||
break
|
||||
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
|
||||
if ch != 'l' and ch != 'h':
|
||||
fmt_char = ch
|
||||
break
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if fmt_char == 's':
|
||||
return val
|
||||
if fmt_char == 'c':
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
if ibits != 0 and ibits < 32:
|
||||
return llvmlite.build_zext(builder, val, i32_ty)
|
||||
return val
|
||||
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
|
||||
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
|
||||
ibits = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
if ibits == 1:
|
||||
return llvmlite.build_zext(builder, val, i32_ty)
|
||||
if ibits == 8 or ibits == 16:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
||||
return llvmlite.build_zext(builder, val, i32_ty)
|
||||
return llvmlite.build_sext(builder, val, i32_ty)
|
||||
return val
|
||||
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
|
||||
or fmt_char == 'g' or fmt_char == 'G':
|
||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if fbits == 32:
|
||||
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
|
||||
return val
|
||||
return val
|
||||
@@ -1,163 +0,0 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import memhub
|
||||
import string
|
||||
import llvmlite
|
||||
import stdlib
|
||||
import stdio
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.core.Gen.ExprGenBase as ExprGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# docstring 登记表容量(__doc__ 魔法属性支持)
|
||||
#
|
||||
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
|
||||
# 函数/类/方法 docstring,幂等查重)
|
||||
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
|
||||
# DOC_VAL_SZ: docstring 内容缓冲宽度
|
||||
# ============================================================
|
||||
DOC_MAX: t.CDefine = 512
|
||||
DOC_KEY_SZ: t.CDefine = 192
|
||||
DOC_VAL_SZ: t.CDefine = 4096
|
||||
|
||||
# ============================================================
|
||||
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
|
||||
#
|
||||
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
|
||||
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
|
||||
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
|
||||
# ============================================================
|
||||
FSTR_BUF_SIZE: t.CDefine = 1024
|
||||
FSTR_SPEC_MAX: t.CDefine = 64
|
||||
FSTR_FMT_MAX: t.CDefine = 64
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenMixin - 表达式处理 Mixin(对象化 P3,wiki/14-oo-refactor.md)
|
||||
#
|
||||
# 从 HandlesExpr.vp(48 函数,无全局状态)与 HandlesExprOps.vp
|
||||
# 迁移:模块级纯函数 → 类方法(self 替代,参数保留含 builder/pool)。
|
||||
#
|
||||
# 迁移原则(与 P2 一致,行为零变化):
|
||||
# 1. 方法保留原函数签名(含 builder/pool 首参),仅函数体内
|
||||
# 本模块函数调用改为 self.xxx(...)
|
||||
# 2. 外部模块调用(HandlesExprCall.xxx / HandlesType.xxx 等)保留模块前缀
|
||||
# 3. HandlesExpr.vp / HandlesExprOps.vp 原函数保留为"委托":本地
|
||||
# 模块级单例 _g_gen(BuildPipeline/main 注入)cast 成 Generator 类型
|
||||
# 4. ExprHandle 类保留在 HandlesExpr.vp(translator handler 模式)
|
||||
#
|
||||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 翻译值表达式(RHS 分派)— 模块级版本
|
||||
# ============================================================
|
||||
def translate_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
funcs_ptr: t.CPtr = None,
|
||||
func_count: int = 0,
|
||||
trans: HT.Translator | t.CPtr = None,
|
||||
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
|
||||
|
||||
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
|
||||
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
|
||||
"""
|
||||
if trans is not None:
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
rv: llvmlite.Value | t.CPtr = self._translate_value_impl(
|
||||
builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty)
|
||||
if trans is not None:
|
||||
trans.TranslateDepth -= 1
|
||||
return rv
|
||||
|
||||
def _translate_value_impl(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
funcs_ptr: t.CPtr = None,
|
||||
func_count: int = 0,
|
||||
trans: HT.Translator | t.CPtr = None,
|
||||
target_ty: llvmlite.LLVMType | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""翻译值表达式为 LLVM Value"""
|
||||
if node is None:
|
||||
return None
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
rv: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
|
||||
return rv
|
||||
elif k == ast.ASTKind.Name:
|
||||
return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans)
|
||||
elif k == ast.ASTKind.BinOp:
|
||||
return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
|
||||
elif k == ast.ASTKind.Call:
|
||||
return self.Gen.CallGen.translate_call(pool, builder, mod, node,
|
||||
funcs_ptr, func_count, trans)
|
||||
elif k == ast.ASTKind.Compare:
|
||||
return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.UnaryOp:
|
||||
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.BoolOp:
|
||||
return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node,
|
||||
funcs_ptr, func_count, trans)
|
||||
elif k == ast.ASTKind.Subscript:
|
||||
return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Attribute:
|
||||
return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.List:
|
||||
return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
|
||||
elif k == ast.ASTKind.IfExp:
|
||||
return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Lambda:
|
||||
# 模块级委托:translate_lambda 定义在 FuncGenMixin(跨 Mixin
|
||||
# 方法不能 self. 直接调用,见 HandlesExpr.translate_lambda)
|
||||
return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.JoinedStr:
|
||||
return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.FormattedValue:
|
||||
# 解析器对 f"{expr:spec}"(单 FormattedValue 无字面量)返回裸 FormattedValue
|
||||
# 而非 JoinedStr(见 ast/parser.vp _parse_fstring),需单独翻译
|
||||
return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Await:
|
||||
# 协程 await(TPV-M1):await 目标是 async 函数调用时内联翻译
|
||||
# (子帧创建 + resume 到 DONE + 取子帧 ret 存父帧 await 槽 + 父挂起),
|
||||
# 返回子协程 ret 值(i32)。顶层 await(AnnAssign/Assign 的 value 直接
|
||||
# 是 Await)由 AsyncGen 段翻译特判处理,本分支覆盖表达式上下文中的
|
||||
# await(M1 覆盖有限场景,详见 AsyncGenMixin._emit_await)。
|
||||
aw: ast.Await | t.CPtr = (ast.Await | t.CPtr)(node)
|
||||
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
|
||||
aw_cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aw.value)
|
||||
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
|
||||
aw_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aw_cl.func)
|
||||
if aw_nm.id is not None and trans is not None:
|
||||
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0:
|
||||
return self.Gen.AsyncGen._emit_await(
|
||||
builder, pool, mod, trans, aw, aw_nm.id)
|
||||
return None
|
||||
return None
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,282 @@
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
|
||||
# FuncGenBase - split from FuncGenMixin (stage D): attrs / naming / mangle / create_function helpers.
|
||||
novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
"""Shared helpers for function attributes, naming, mangling and creation."""
|
||||
|
||||
# extract_func_attrs - extract c.Attribute attrs from decorator_list.
|
||||
# Supports @c.Attribute(t.attr.xxx()/t.attr.xxx/t.attr.llvm.xxx); returns LLVM IR attr string or None.
|
||||
def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract c.Attribute attrs from decorators; return the LLVM IR attr string."""
|
||||
if decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
|
||||
attrs_buf: str = pool.alloc(256)
|
||||
if attrs_buf is None: return None
|
||||
attrs_buf[0] = '\0'
|
||||
found_any: int = 0
|
||||
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
|
||||
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
|
||||
if dk == ast.ASTKind.Attribute:
|
||||
dan: str = self._get_attr_name_from_node(deco)
|
||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||
if self._append_llvm_attr(attrs_buf, dan) != 0: found_any = 1
|
||||
continue
|
||||
if dk != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None: continue
|
||||
cfk: int = call_node.func.kind()
|
||||
|
||||
# Detect the builtin Attribute(...) wrapper emitted by @__attribute__(...).
|
||||
if cfk != ast.ASTKind.Name: continue
|
||||
func_nm: ast.Name* = ast.Name*(call_node.func)
|
||||
if func_nm.id is None: continue
|
||||
if string.strcmp(func_nm.id, "Attribute") != 0: continue
|
||||
|
||||
# Iterate args and extract attrs
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(int(an)):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
# Extract attr name (t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None: continue
|
||||
# section string attr cannot be inlined on the signature line (llc: expected
|
||||
# string constant); it must go through attr group #N, handled by FuncGenDef.
|
||||
if string.strcmp(attr_name, "section") == 0: continue
|
||||
# Map to LLVM attr name and append
|
||||
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
||||
if ar != 0: found_any = 1
|
||||
|
||||
if found_any == 0: return None
|
||||
return attrs_buf
|
||||
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract @Attribute(attr.section(...)) as the attr-group text."""
|
||||
if pool is None or decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Name: continue
|
||||
func_nm: ast.Name* = ast.Name*(call_node.func)
|
||||
if func_nm.id is None or string.strcmp(func_nm.id, "Attribute") != 0: continue
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(int(an)):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
|
||||
sec_val: str = self._get_attr_call_arg_str(arg_node)
|
||||
if sec_val is None: continue
|
||||
sec_buf: str = pool.alloc(128)
|
||||
if sec_buf is None: return None
|
||||
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
|
||||
return sec_buf
|
||||
return None
|
||||
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
|
||||
"""Extract the attribute name from a Call or Attribute AST node."""
|
||||
if node is None: return None
|
||||
k: int = node.kind()
|
||||
|
||||
# Call node: t.attr.always_inline()
|
||||
if k == ast.ASTKind.Call:
|
||||
call: ast.Call* = ast.Call*(node)
|
||||
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
|
||||
at: ast.Attribute* = ast.Attribute*(call.func)
|
||||
return at.attr
|
||||
|
||||
# Attribute node: t.attr.packed or t.attr.llvm.nounwind
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(node)
|
||||
return at2.attr
|
||||
|
||||
return None
|
||||
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
|
||||
"""Return the first positional string constant of a t.attr.xxx(...) call."""
|
||||
if node is None or node.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.args is None or len(cl.args) == 0: return None
|
||||
a0: ast.AST* = cl.args.get(0)
|
||||
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
|
||||
cn: ast.Constant* = ast.Constant*(a0)
|
||||
if cn.const_kind != ast.CONST_STR: return None
|
||||
return cn.str_val
|
||||
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
||||
"""Map an attribute name to its LLVM name and append it; 1=appended, 0=unsupported."""
|
||||
if attrs_buf is None or attr_name is None: return 0
|
||||
|
||||
# First determine the mapped LLVM attr name
|
||||
llvm_name: str = None
|
||||
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
|
||||
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
|
||||
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
|
||||
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
|
||||
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
|
||||
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
|
||||
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
|
||||
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
|
||||
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
|
||||
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
|
||||
else:
|
||||
# packed/aligned/section/visibility/weak are unsupported as inline function attrs
|
||||
return 0
|
||||
|
||||
# Supported attr: append a space separator if the buffer is non-empty
|
||||
if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256)
|
||||
self._append_str(attrs_buf, llvm_name, 256)
|
||||
return 1
|
||||
def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None:
|
||||
"""Append a string to buf with a bounded write to avoid overflow."""
|
||||
if buf is None or s is None: return
|
||||
# Defensive: VPC default-arg registration is buggy (missing max_len falls to 0);
|
||||
# fall back to 256 so the bounded-write check does not drop all attr text.
|
||||
if max_len == 0: max_len = 256
|
||||
cur_len: sizet = string.strlen(buf)
|
||||
s_len: sizet = string.strlen(s)
|
||||
if cur_len + s_len + 1 > max_len: return # No remaining capacity: drop this append to avoid a bounds write (uncertain)
|
||||
string.strcpy(buf + cur_len, s)
|
||||
# _mangle_name - prepend a SHA1 prefix to a name.
|
||||
# Returns "sha1.name"; if ModuleSha1 is None or the name already has a SHA1 prefix, returns as-is.
|
||||
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
|
||||
"""Prepend the SHA1 prefix to a name (no main/export check)."""
|
||||
if trans is None or name is None: return name
|
||||
if trans.ModuleSha1 is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (aligned with the ">= 17" check).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1: str = trans.ModuleSha1
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None: return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# _mangle_name_with_sha1 - prepend the given SHA1 to a name.
|
||||
# Used for cross-module vtable inheritance: inherited methods mangle with the parent module's SHA1.
|
||||
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
||||
"""Prepend the given SHA1 to a name (no main/export check)."""
|
||||
if sha1 is None or name is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (same check as _mangle_name).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None: return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# _mangle_func_name - prepend a SHA1 prefix to a function name (with main/export checks).
|
||||
# No prefix for main, when has_export != 0 (t.CExport), when already prefixed, or when ModuleSha1 is None.
|
||||
def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str:
|
||||
"""Prepend the SHA1 prefix to a function name (checks main/export)."""
|
||||
if trans is None or name is None: return name
|
||||
if has_export != 0: return name
|
||||
if string.strcmp(name, "main") == 0: return name
|
||||
return self._mangle_name(trans, name)
|
||||
# _body_has_raise - whether the body contains raise (non-StopIteration).
|
||||
# Matches TPC HandlesFunctions.py _body_contains_raise; v1 only scans top-level raise, not nested ones.
|
||||
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
|
||||
"""Whether the body has a top-level non-StopIteration raise; 1=yes, 0=no."""
|
||||
if fd is None or fd.children is None: return 0
|
||||
body_f: list[ast.AST*]* = fd.children
|
||||
bn_f: sizet = len(body_f)
|
||||
for bi_f in range(int(bn_f)):
|
||||
stmt_f: ast.AST* = body_f.get(bi_f)
|
||||
if stmt_f is None: continue
|
||||
if stmt_f.kind() != ast.ASTKind.Raise: continue
|
||||
rn_f: ast.Raise* = ast.Raise*(stmt_f)
|
||||
if rn_f is None or rn_f.exc is None: continue
|
||||
ek_f: int = rn_f.exc.kind()
|
||||
is_stop_f: int = 0
|
||||
if ek_f == ast.ASTKind.Name:
|
||||
enm_f: ast.Name* = ast.Name*(rn_f.exc)
|
||||
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
elif ek_f == ast.ASTKind.Call:
|
||||
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
|
||||
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
|
||||
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
|
||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
if is_stop_f == 0: return 1
|
||||
return 0
|
||||
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
|
||||
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
|
||||
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
|
||||
if decorators is None: return 0
|
||||
rcr_dn: sizet = len(decorators)
|
||||
for rcr_i in range(int(rcr_dn)):
|
||||
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
||||
if rcr_deco is None: continue
|
||||
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
|
||||
rcr_attr: ast.Attribute* = None
|
||||
if rcr_deco.kind() == ast.ASTKind.Call:
|
||||
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
||||
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func)
|
||||
elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco)
|
||||
if rcr_attr is None or rcr_attr.attr is None: continue
|
||||
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` removed: use a return annotation `-> tuple[ty1, ty2, ...]` instead (multiple return values are packed into a struct return)", "TypeError")
|
||||
return 1
|
||||
return 0
|
||||
# Create LLVM function (simple version, declaration only)
|
||||
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
|
||||
"""Create an LLVM function and add its parameters."""
|
||||
if name is None or mod is None: return None
|
||||
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, name, ret_ty)
|
||||
if func is None: return None
|
||||
|
||||
if args_node is not None:
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# CVoid / void means no args: skip
|
||||
if arg.annotation is not None:
|
||||
if arg.annotation.kind() == ast.ASTKind.Name:
|
||||
at_cn: ast.Name* = ast.Name*(arg.annotation)
|
||||
if at_cn.id is not None and (string.strcmp(at_cn.id, "CVoid") == 0 or string.strcmp(at_cn.id, "void") == 0): continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||
if resolved is None:
|
||||
# [S4 fail-fast] Annotation present but unresolvable: no silent i32 fallback.
|
||||
# This fn lacks trans/imports context, so failure surfaces upstream.
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"function '{name}' param '{arg.arg}' type annotation failed to resolve", "NameError")
|
||||
return None
|
||||
param_ty = resolved
|
||||
pname: str = pool.alloc(32)
|
||||
if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||
mlirlite.add_param(pool, func, param_ty, pname)
|
||||
|
||||
return func
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,445 +0,0 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import stdio
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
|
||||
# ============================================================
|
||||
# FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具
|
||||
#
|
||||
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||||
# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class FuncGenBase(CallGenBase.CallGenBase):
|
||||
"""函数属性 / 命名 / mangle / create_function 基础工具"""
|
||||
|
||||
# ============================================================
|
||||
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
|
||||
#
|
||||
# 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式
|
||||
# 也支持 @c.Attribute(t.attr.llvm.xxx) 形式
|
||||
#
|
||||
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
|
||||
# ============================================================
|
||||
def extract_func_attrs(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
decorator_list: list[ast.AST | t.CPtr] | t.CPtr,
|
||||
imported_modules: str) -> str:
|
||||
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
|
||||
if decorator_list is None:
|
||||
return None
|
||||
dn: t.CSizeT = len(decorator_list)
|
||||
if dn == 0:
|
||||
return None
|
||||
|
||||
attrs_buf: str = pool.alloc(256)
|
||||
if attrs_buf is None:
|
||||
return None
|
||||
attrs_buf[0] = '\0'
|
||||
found_any: int = 0
|
||||
|
||||
# [ATTR-DIAG] 运行时诊断:定位 @c.Attribute 属性为何未进入 IR(stackrealign)
|
||||
cur_file: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if cur_file is None:
|
||||
cur_file = "?"
|
||||
stdio.printf("[ATTR] extract_func_attrs: file='%s' dlist_len=%d\n", cur_file, dn)
|
||||
for di in range(dn):
|
||||
deco: ast.AST | t.CPtr = decorator_list.get(di)
|
||||
if deco is None:
|
||||
stdio.printf("[ATTR] deco[%d]=None\n", di)
|
||||
continue
|
||||
dk: int = deco.kind()
|
||||
stdio.printf("[ATTR] deco[%d] kind=%d\n", di, dk)
|
||||
if dk != ast.ASTKind.Call:
|
||||
continue
|
||||
call_node: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco)
|
||||
if call_node.func is None:
|
||||
stdio.printf("[ATTR] call.func=None\n")
|
||||
continue
|
||||
cfk: int = call_node.func.kind()
|
||||
stdio.printf("[ATTR] call.func kind=%d\n", cfk)
|
||||
if cfk != ast.ASTKind.Attribute:
|
||||
continue
|
||||
|
||||
# 检测 c.Attribute
|
||||
func_attr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(call_node.func)
|
||||
if func_attr.attr is None:
|
||||
stdio.printf("[ATTR] func_attr.attr=None\n")
|
||||
continue
|
||||
stdio.printf("[ATTR] func_attr.attr='%s'\n", func_attr.attr)
|
||||
if string.strcmp(func_attr.attr, "Attribute") != 0:
|
||||
continue
|
||||
if func_attr.value is None:
|
||||
stdio.printf("[ATTR] func_attr.value=None\n")
|
||||
continue
|
||||
fvk: int = func_attr.value.kind()
|
||||
stdio.printf("[ATTR] func_attr.value kind=%d\n", fvk)
|
||||
if fvk != ast.ASTKind.Name:
|
||||
continue
|
||||
c_name: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_attr.value)
|
||||
if c_name.id is None:
|
||||
stdio.printf("[ATTR] c_name.id=None\n")
|
||||
continue
|
||||
stdio.printf("[ATTR] c_name.id='%s'\n", c_name.id)
|
||||
if string.strcmp(c_name.id, "c") != 0:
|
||||
continue
|
||||
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
|
||||
stdio.printf("[ATTR] is_module_imported('c')=%d\n", imp_c)
|
||||
if imp_c == 0:
|
||||
continue
|
||||
|
||||
# 遍历参数提取属性
|
||||
if call_node.args is None:
|
||||
stdio.printf("[ATTR] call.args=None\n")
|
||||
continue
|
||||
dbuf: bytes = pool.alloc(1024)
|
||||
if dbuf is not None:
|
||||
dbuf[0] = '\0'
|
||||
dpos: t.CSizeT = deco.dump(dbuf, 1024, 0)
|
||||
if dpos < 1024:
|
||||
dbuf[dpos] = '\0'
|
||||
stdio.printf("[ATTR] deco.dump='%s'\n", dbuf)
|
||||
args_list: list[ast.AST | t.CPtr] | t.CPtr = call_node.args
|
||||
an: t.CSizeT = len(args_list)
|
||||
stdio.printf("[ATTR] args_len=%d\n", an)
|
||||
if call_node.keywords is not None:
|
||||
kwl: t.CSizeT = len(call_node.keywords)
|
||||
stdio.printf("[ATTR] keywords_len=%d\n", kwl)
|
||||
for kwi in range(kwl):
|
||||
kwnd: ast.AST | t.CPtr = call_node.keywords.get(kwi)
|
||||
if kwnd is not None:
|
||||
stdio.printf("[ATTR] kw[%d] kind=%d\n", kwi, kwnd.kind())
|
||||
for ai in range(an):
|
||||
arg_node: ast.AST | t.CPtr = args_list.get(ai)
|
||||
if arg_node is None:
|
||||
stdio.printf("[ATTR] arg[%d]=None\n", ai)
|
||||
continue
|
||||
stdio.printf("[ATTR] arg[%d] kind=%d\n", ai, arg_node.kind())
|
||||
|
||||
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None:
|
||||
stdio.printf("[ATTR] arg[%d] name=None\n", ai)
|
||||
continue
|
||||
|
||||
# 映射到 LLVM 属性名并追加
|
||||
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
||||
stdio.printf("[ATTR] arg[%d] name='%s' append=%d buf='%s' blen=%zu b0=0x%02x\n",
|
||||
ai, attr_name, ar, attrs_buf, string.strlen(attrs_buf), attrs_buf[0])
|
||||
if ar != 0:
|
||||
found_any = 1
|
||||
# 追加成功后,如果不是最后一个属性,添加空格分隔
|
||||
# 在下一次追加前由 _append_str 处理
|
||||
|
||||
stdio.printf("[ATTR] found_any=%d return='%s'\n", found_any, attrs_buf)
|
||||
if found_any == 0:
|
||||
return None
|
||||
return attrs_buf
|
||||
def _get_attr_name_from_node(self, node: ast.AST | t.CPtr) -> str:
|
||||
"""从 AST 节点提取属性名
|
||||
|
||||
支持:
|
||||
- Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline"
|
||||
- Attribute: t.attr.packed -> "packed"
|
||||
- Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind"
|
||||
"""
|
||||
if node is None:
|
||||
return None
|
||||
k: int = node.kind()
|
||||
|
||||
# Call 节点: t.attr.always_inline()
|
||||
if k == ast.ASTKind.Call:
|
||||
call: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
|
||||
if call.func is None or call.func.kind() != ast.ASTKind.Attribute:
|
||||
return None
|
||||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(call.func)
|
||||
return at.attr
|
||||
|
||||
# Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||||
return at2.attr
|
||||
|
||||
return None
|
||||
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
||||
"""将属性名映射到 LLVM 属性名并追加到缓冲区
|
||||
|
||||
返回 1 表示已追加,0 表示不支持该属性
|
||||
"""
|
||||
if attrs_buf is None or attr_name is None:
|
||||
return 0
|
||||
|
||||
# 先确定映射的 LLVM 属性名
|
||||
llvm_name: str = None
|
||||
if string.strcmp(attr_name, "always_inline") == 0:
|
||||
llvm_name = "alwaysinline"
|
||||
elif string.strcmp(attr_name, "noinline") == 0:
|
||||
llvm_name = "noinline"
|
||||
elif string.strcmp(attr_name, "noreturn") == 0:
|
||||
llvm_name = "noreturn"
|
||||
elif string.strcmp(attr_name, "pure") == 0:
|
||||
llvm_name = "readonly"
|
||||
elif string.strcmp(attr_name, "const") == 0:
|
||||
llvm_name = "readnone"
|
||||
elif string.strcmp(attr_name, "nounwind") == 0:
|
||||
llvm_name = "nounwind"
|
||||
elif string.strcmp(attr_name, "noredzone") == 0:
|
||||
llvm_name = "noredzone"
|
||||
elif string.strcmp(attr_name, "stackrealign") == 0:
|
||||
# 动态重对齐栈(LLVM 序言 and rsp,-16)。入口函数(_start)由 bootloader
|
||||
# jmp 进入(rsp≡0 mod 16),而 llc 按 call 约定假设入口 rsp≡8 → 帧整体
|
||||
# 偏 8 → 所有调用 rsp≡8 → 变参函数保存 xmm 的 movaps #GP。stackrealign
|
||||
# 使序言动态对齐,与入口约定无关(sched started 后 movaps #GP 根因)。
|
||||
# 注意:stackrealign 是 LLVM 字符串属性,必须带引号("stackrealign"),
|
||||
# 与 noinline/nounwind 等枚举属性(无引号)区分,否则 llc 报
|
||||
# "expected '{' in function body"。
|
||||
llvm_name = '"stackrealign"'
|
||||
elif string.strcmp(attr_name, "willreturn") == 0:
|
||||
llvm_name = "willreturn"
|
||||
elif string.strcmp(attr_name, "mustprogress") == 0:
|
||||
llvm_name = "mustprogress"
|
||||
else:
|
||||
# packed/aligned/section/visibility/weak 等不支持作为函数内联属性
|
||||
return 0
|
||||
|
||||
# 支持的属性:如果缓冲区非空,先追加空格分隔
|
||||
if attrs_buf[0] != '\0':
|
||||
self._append_str(attrs_buf, " ", 256)
|
||||
self._append_str(attrs_buf, llvm_name, 256)
|
||||
return 1
|
||||
def _append_str(self, buf: str, s: str,
|
||||
max_len: t.CSizeT = 256) -> None:
|
||||
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
|
||||
if buf is None or s is None:
|
||||
return
|
||||
# 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0),
|
||||
# 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。
|
||||
if max_len == 0:
|
||||
max_len = 256
|
||||
cur_len: t.CSizeT = string.strlen(buf)
|
||||
s_len: t.CSizeT = string.strlen(s)
|
||||
stdio.printf("[ATTR] _append_str: max_len=%zu cur=%zu slen=%zu s='%s'\n", max_len, cur_len, s_len, s)
|
||||
if cur_len + s_len + 1 > max_len:
|
||||
return # 无剩余容量:丢弃本次追加,避免越界写
|
||||
string.strcpy(buf + cur_len, s)
|
||||
# ============================================================
|
||||
# _mangle_name - 为名称添加 SHA1 前缀
|
||||
#
|
||||
# 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带
|
||||
# SHA1 前缀(16 hex + '.'),则原样返回。
|
||||
# ============================================================
|
||||
def _mangle_name(self, trans: HT.Translator | t.CPtr, name: str) -> str:
|
||||
"""为任意名称添加 SHA1 前缀(不做 main/export 检查)"""
|
||||
if trans is None or name is None:
|
||||
return name
|
||||
if trans.ModuleSha1 is None:
|
||||
return name
|
||||
name_len: t.CSizeT = string.strlen(name)
|
||||
# 已带 SHA1 前缀(16 hex + '.')则跳过
|
||||
if name_len > 17:
|
||||
is_sha1: int = 1
|
||||
for i in range(16):
|
||||
c: t.CChar = name[i]
|
||||
if not (('0' <= c <= '9') or ('a' <= c <= 'f')):
|
||||
is_sha1 = 0
|
||||
break
|
||||
if is_sha1 != 0 and name[16] == '.':
|
||||
return name
|
||||
sha1: str = trans.ModuleSha1
|
||||
sha1_len: t.CSizeT = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None:
|
||||
return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# ============================================================
|
||||
# _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀
|
||||
#
|
||||
# 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling,
|
||||
# 而非当前模块的 SHA1。
|
||||
# ============================================================
|
||||
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
||||
"""用指定 SHA1 为名称添加前缀(不做 main/export 检查)"""
|
||||
if sha1 is None or name is None:
|
||||
return name
|
||||
name_len: t.CSizeT = string.strlen(name)
|
||||
# 已带 SHA1 前缀(16 hex + '.')则跳过
|
||||
if name_len > 17:
|
||||
is_sha1: int = 1
|
||||
for i in range(16):
|
||||
c2: t.CChar = name[i]
|
||||
if not (('0' <= c2 <= '9') or ('a' <= c2 <= 'f')):
|
||||
is_sha1 = 0
|
||||
break
|
||||
if is_sha1 != 0 and name[16] == '.':
|
||||
return name
|
||||
sha1_len: t.CSizeT = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None:
|
||||
return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# ============================================================
|
||||
# _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查)
|
||||
#
|
||||
# 规则:
|
||||
# - main 函数不加前缀(程序入口点)
|
||||
# - has_export 非 0 时不加前缀(t.CExport 导出函数)
|
||||
# - 已带 SHA1 前缀的不再加
|
||||
# - ModuleSha1 为 None 时不加前缀
|
||||
# ============================================================
|
||||
def _mangle_func_name(self, trans: HT.Translator | t.CPtr, name: str,
|
||||
has_export: int) -> str:
|
||||
"""为函数名添加 SHA1 前缀(检查 main/export)"""
|
||||
if trans is None or name is None:
|
||||
return name
|
||||
if has_export != 0:
|
||||
return name
|
||||
if string.strcmp(name, "main") == 0:
|
||||
return name
|
||||
return self._mangle_name(trans, name)
|
||||
# ============================================================
|
||||
# _body_has_raise - 函数体是否含 raise(非 StopIteration)
|
||||
#
|
||||
# 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名
|
||||
# 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体
|
||||
# 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。
|
||||
# ============================================================
|
||||
def _body_has_raise(self, fd: ast.FunctionDef | t.CPtr) -> int:
|
||||
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
|
||||
if fd is None or fd.children is None:
|
||||
return 0
|
||||
body_f: list[ast.AST | t.CPtr] | t.CPtr = fd.children
|
||||
bn_f: t.CSizeT = len(body_f)
|
||||
for bi_f in range(bn_f):
|
||||
stmt_f: ast.AST | t.CPtr = body_f.get(bi_f)
|
||||
if stmt_f is None:
|
||||
continue
|
||||
if stmt_f.kind() != ast.ASTKind.Raise:
|
||||
continue
|
||||
rn_f: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(stmt_f)
|
||||
if rn_f is None or rn_f.exc is None:
|
||||
continue
|
||||
ek_f: int = rn_f.exc.kind()
|
||||
is_stop_f: int = 0
|
||||
if ek_f == ast.ASTKind.Name:
|
||||
enm_f: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn_f.exc)
|
||||
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0:
|
||||
is_stop_f = 1
|
||||
elif ek_f == ast.ASTKind.Call:
|
||||
ecl_f: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn_f.exc)
|
||||
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
|
||||
ecn_f: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl_f.func)
|
||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0:
|
||||
is_stop_f = 1
|
||||
if is_stop_f == 0:
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _resolve_creturn_ret_ty - @c.CReturn(ty1, ty2, ...) → 多返回 struct 类型
|
||||
#
|
||||
# 对齐 TPC DeclarationGenerator:CReturn 声明函数返回多个值(如
|
||||
# GetMemoryStats @c.CReturn(t.CUInt64T, t.CUInt64T, t.CUInt64T) →
|
||||
# {i64, i64, i64})。pre_scan 前向声明与 translate_function_def 定义
|
||||
# 两阶段必须一致,否则 declare i32 + define {i64,i64,i64} → llc
|
||||
# "ret doesn't match function result type"。
|
||||
# ============================================================
|
||||
def _resolve_creturn_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
fd: ast.FunctionDef | t.CPtr,
|
||||
imported_modules: str,
|
||||
from_imports: str,
|
||||
trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""解析 @c.CReturn 装饰器为 struct 返回类型;无 CReturn 返回 None"""
|
||||
if fd is None or fd.decorator_list is None:
|
||||
return None
|
||||
cr_head: llvmlite.ParamNode | t.CPtr = None
|
||||
cr_last: llvmlite.ParamNode | t.CPtr = None
|
||||
cr_count: int = 0
|
||||
cr_dn: t.CSizeT = len(fd.decorator_list)
|
||||
for cdi in range(cr_dn):
|
||||
deco_cr: ast.AST | t.CPtr = fd.decorator_list.get(cdi)
|
||||
if deco_cr is None or deco_cr.kind() != ast.ASTKind.Call:
|
||||
continue
|
||||
deco_call_cr: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco_cr)
|
||||
if deco_call_cr.func is None or deco_call_cr.func.kind() != ast.ASTKind.Attribute:
|
||||
continue
|
||||
deco_fa_cr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(deco_call_cr.func)
|
||||
if deco_fa_cr.attr is None or string.strcmp(deco_fa_cr.attr, "CReturn") != 0:
|
||||
continue
|
||||
cr_args: list[ast.AST | t.CPtr] | t.CPtr = deco_call_cr.args
|
||||
if cr_args is None:
|
||||
continue
|
||||
cr_an: t.CSizeT = len(cr_args)
|
||||
for cai in range(cr_an):
|
||||
cr_aty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||||
pool, cr_args.get(cai), imported_modules, from_imports, trans, 0)
|
||||
if cr_aty is None:
|
||||
continue
|
||||
cr_pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, cr_aty)
|
||||
if cr_pn is not None:
|
||||
if cr_head is None:
|
||||
cr_head = cr_pn
|
||||
else:
|
||||
cr_last.Next = cr_pn
|
||||
cr_last = cr_pn
|
||||
cr_count += 1
|
||||
if cr_head is not None and cr_count > 0:
|
||||
return llvmlite.Struct(pool, cr_head, cr_count, None)
|
||||
return None
|
||||
# ============================================================
|
||||
# 创建 LLVM 函数(简单版本,仅声明)
|
||||
# ============================================================
|
||||
def create_function(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
name: str,
|
||||
args_node: ast.AST | t.CPtr,
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Function | t.CPtr:
|
||||
"""创建 LLVM 函数并添加参数"""
|
||||
if name is None or mod is None:
|
||||
return None
|
||||
|
||||
func: llvmlite.Function | t.CPtr = llvmlite.create_function(pool, mod, name, ret_ty)
|
||||
if func is None:
|
||||
return None
|
||||
|
||||
if args_node is not None:
|
||||
ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args
|
||||
an: t.CSizeT = len(alist)
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
for ai in range(an):
|
||||
arg: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# t.CVoid 表示空参:跳过
|
||||
if arg.annotation is not None:
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at_cf: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(arg.annotation)
|
||||
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0:
|
||||
continue
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||||
pool, arg.annotation, None, None, None, 0)
|
||||
if resolved is not None:
|
||||
param_ty = resolved
|
||||
pname: str = pool.alloc(32)
|
||||
if pname is not None:
|
||||
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||
llvmlite.add_param(pool, func, param_ty, pname)
|
||||
|
||||
return func
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+925
-418
File diff suppressed because it is too large.
Load diff
+332
-193
@@ -1,193 +1,332 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
|
||||
|
||||
# ============================================================
|
||||
# GenTypes - Gen 侧共享数据类型与常量(去除 Handles 层迁移目标)
|
||||
#
|
||||
# 原分布于 Handles 目录的数据类与常量集中于此,供 Gen 子对象、
|
||||
# 管线与保留实体文件引用。删除 Handles 目录后统一经本模块访问。
|
||||
# ============================================================
|
||||
|
||||
# ============================================================
|
||||
# 类编译期元数据(原 HandlesType.CLASS_META_*)
|
||||
# ============================================================
|
||||
CLASS_META_PROVIDES: t.CDefine = 1
|
||||
CLASS_META_REQUIRES: t.CDefine = 2
|
||||
CLASS_META_REQUIRE_MUST: t.CDefine = 3
|
||||
|
||||
# ============================================================
|
||||
# 属性/静态/类方法元数据(原 HandlesClassDef.META_*)
|
||||
# ============================================================
|
||||
META_STATIC: t.CDefine = 1
|
||||
META_CLASS: t.CDefine = 2
|
||||
META_PROP_GETTER: t.CDefine = 4
|
||||
META_PROP_SETTER: t.CDefine = 8
|
||||
META_PROP_DELETER: t.CDefine = 16
|
||||
|
||||
# ============================================================
|
||||
# 结构体容量(原 HandlesStruct.FIELD_MAX/FIELD_NAME_MAX/PARENT_MAX/MRO_MAX)
|
||||
# ============================================================
|
||||
FIELD_MAX: t.CDefine = 256
|
||||
FIELD_NAME_MAX: t.CDefine = 64
|
||||
PARENT_MAX: t.CDefine = 64
|
||||
MRO_MAX: t.CDefine = 64
|
||||
|
||||
# ============================================================
|
||||
# for 迭代序列种类(原 HandlesFor.SEQ_KIND_*)
|
||||
# ============================================================
|
||||
SEQ_KIND_RANGE: t.CDefine = 1
|
||||
SEQ_KIND_STR: t.CDefine = 2
|
||||
SEQ_KIND_LIST: t.CDefine = 3
|
||||
|
||||
# ============================================================
|
||||
# TraceFrame - 仿 Python Traceback 调用栈帧(原 HandlesType.TraceFrame)
|
||||
# ============================================================
|
||||
class TraceFrame:
|
||||
"""仿 Python Traceback 调用栈帧(push_trace/pop_trace 使用)"""
|
||||
file_name: str
|
||||
lineno: int
|
||||
col_start: int
|
||||
col_end: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
# VarEntry - 变量表条目(原 HandlesVar.VarEntry)
|
||||
# ============================================================
|
||||
class VarEntry:
|
||||
"""变量表条目"""
|
||||
Name: str
|
||||
Alloca: llvmlite.Value | t.CPtr
|
||||
Used: t.CInt
|
||||
AnnotClassName: str # 原始类型注解的类名(str 别名在结构体字段中触发编译器 bug,改用显式联合类型)
|
||||
IsPtrElement: t.CInt # 标志: 1=注解为 bytes|t.CPtr 或 str|t.CPtr, 下标按 8 字节步长
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Scope - 作用域节点(原 HandlesVar.Scope)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class Scope:
|
||||
"""作用域节点"""
|
||||
Parent: Scope | t.CPtr
|
||||
Vars: VarEntry | t.CPtr
|
||||
VarCount: t.CInt
|
||||
Kind: t.CInt
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SymbolTable - 嵌套作用域符号表(原 HandlesVar.SymbolTable)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class SymbolTable:
|
||||
"""嵌套作用域符号表"""
|
||||
Pool: memhub.MemBuddy | t.CPtr
|
||||
Root: Scope | t.CPtr
|
||||
Current: Scope | t.CPtr
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FieldEntry - 字段信息条目(原 HandlesStruct.FieldEntry)
|
||||
# ============================================================
|
||||
class FieldEntry:
|
||||
Name: str # 字段名(字符串)
|
||||
Index: int # 字段在结构体中的索引
|
||||
Ty: llvmlite.LLVMType | t.CPtr # 字段的 LLVM 类型
|
||||
DefaultVal: ast.AST | t.CPtr # 默认值 AST 节点(None=无默认值)
|
||||
AnnotClassName: str # 原始类型注解的类名(str 别名在结构体字段中触发编译器 bug,改用显式联合类型)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StructEntry - 结构体注册条目(原 HandlesStruct.StructEntry)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class StructEntry:
|
||||
Name: str # 类名
|
||||
Ty: llvmlite.LLVMType | t.CPtr # LLVM StructType
|
||||
FieldCount: int # 字段数量
|
||||
Fields: FieldEntry | t.CPtr # 字段数组(FIELD_MAX 个槽位)
|
||||
IsUnion: int # 1=联合体 / 0=普通结构体
|
||||
IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体
|
||||
HasInit: int # 1=有__init__方法 / 0=无
|
||||
HasNew: int # 1=有__new__方法 / 0=无
|
||||
HasVTable: int # 1=有虚表 / 0=无虚表
|
||||
IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记
|
||||
ParentName: str # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用)
|
||||
ParentNames: t.CSizeT | t.CPtr # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝)
|
||||
ParentCount: t.CInt # 父类数量(0=无父类,1=单继承,>1=多继承)
|
||||
VTableMethodCount: int # 虚表中的方法数量
|
||||
VTableMethods: str # 虚表方法名数组(每个方法名 str,VTableMethodCount 个)
|
||||
ModuleSha1: str # 定义该类的模块 SHA1(None=无 SHA1)
|
||||
MethodsEmitted: int # 1=方法体已翻译 / 0=未译(Phase 1a mark_only 未译,Phase B 据此补译)
|
||||
SpecTriggerSha1: str # 泛型特化类的触发文件 SHA1(None=非特化类)。特化类 ModuleSha1=模板
|
||||
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
|
||||
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
|
||||
# → 链接 undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EnumMember - 枚举成员(原 HandlesEnum.EnumMember)
|
||||
# ============================================================
|
||||
class EnumMember:
|
||||
Name: str # 成员名(字符串)
|
||||
Value: t.CInt64T # 成员的整数值
|
||||
Ty: llvmlite.LLVMType | t.CPtr # 成员的 LLVM 类型(用于混用类型场景)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EnumEntry - 枚举类型条目(原 HandlesEnum.EnumEntry)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class EnumEntry:
|
||||
Name: str # 枚举类名
|
||||
BaseTy: llvmlite.LLVMType | t.CPtr # 基准类型(所有成员类型中最大的)
|
||||
MemberCount: int # 成员数量
|
||||
Members: EnumMember | t.CPtr # 成员数组(ENUM_MEMBER_MAX 个槽位)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AsmOperand - 内联汇编操作数条目(原 HandlesExprCall.AsmOperand)
|
||||
# ============================================================
|
||||
class AsmOperand:
|
||||
"""内联汇编操作数条目"""
|
||||
Value: llvmlite.Value | t.CPtr # 输入值或输出 alloca
|
||||
Constraint: str # 约束字符串 (如 "r", "=r")
|
||||
IsOutput: int # 1=输出, 0=输入
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncEntry - 函数表条目(原 HandlesExprCall.FuncEntry)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class FuncEntry:
|
||||
"""函数表条目"""
|
||||
Name: str
|
||||
Func: llvmlite.Function | t.CPtr
|
||||
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值
|
||||
DefaultCount: int # 默认值数量
|
||||
ParamCount: int # 形参数量(不含 self)
|
||||
# TypeStr: 函数类型文本(stdlib 深拷贝,跨文件安全)
|
||||
# 1b 每文件 reset_to(mark1) 回收 pool 内 Function 对象后,
|
||||
# vtable 生成 find_func_global 读类型改用此文本,避免悬空。
|
||||
TypeStr: str
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncDefaultsEntry - 默认参数表条目(原 HandlesExprCall.FuncDefaultsEntry)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class FuncDefaultsEntry:
|
||||
"""默认参数表条目"""
|
||||
Name: str
|
||||
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
|
||||
DefaultCount: int
|
||||
ParamCount: int # 形参数量(不含 self)
|
||||
# 常量快照(stdlib 分配,跨文件永久存活):
|
||||
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
|
||||
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
|
||||
ConstKinds: t.CInt | t.CPtr # const_kind 数组(DefaultCount 个 i32)
|
||||
ConstInts: t.CInt64T | t.CPtr # int_val 数组(DefaultCount 个 i64)
|
||||
ConstStrs: t.CSizeT | t.CPtr # str_val 指针数组(DefaultCount 个 i8*)
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
# TypeSpec: unified type descriptor (Debugger/_Type.md section 6).
|
||||
# Alias TSpec because the in-module class name is also TypeSpec (same name would be a static method).
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
|
||||
|
||||
# ============================================================
|
||||
# GenTypes - Gen-side shared data types and constants (migrated out of the Handles layer)
|
||||
|
||||
# ============================================================
|
||||
# Class compile-time metadata (formerly HandlesType.CLASS_META_*)
|
||||
define CLASS_META_PROVIDES = 1
|
||||
define CLASS_META_REQUIRES = 2
|
||||
define CLASS_META_REQUIRE_MUST = 3
|
||||
|
||||
# ============================================================
|
||||
# Attribute/static/class-method metadata (formerly HandlesClassDef.META_*)
|
||||
define META_STATIC = 1
|
||||
define META_CLASS = 2
|
||||
define META_PROP_GETTER = 4
|
||||
define META_PROP_SETTER = 8
|
||||
define META_PROP_DELETER = 16
|
||||
|
||||
# ============================================================
|
||||
# Struct capacities (formerly HandlesStruct.FIELD_MAX/...)
|
||||
define FIELD_MAX = 256
|
||||
define FIELD_NAME_MAX = 64
|
||||
define PARENT_MAX = 64
|
||||
define MRO_MAX = 64
|
||||
|
||||
# ============================================================
|
||||
# for-iteration sequence kinds (formerly HandlesFor.SEQ_KIND_*)
|
||||
define SEQ_KIND_RANGE = 1
|
||||
define SEQ_KIND_STR = 2
|
||||
define SEQ_KIND_LIST = 3
|
||||
|
||||
# ============================================================
|
||||
# TraceFrame - Python-Traceback-like call frame (formerly HandlesType.TraceFrame)
|
||||
class TraceFrame:
|
||||
"""Python-Traceback-like call frame used by push_trace/pop_trace"""
|
||||
file_name: str
|
||||
lineno: int
|
||||
col_start: int
|
||||
col_end: int
|
||||
|
||||
|
||||
# ---- 生命期标签(LifeTag):有序,越小越"短命" ----
|
||||
# 逃逸检查只在 LIFE_FRAME 上触发;控制流汇合取更短者(Meet = min)。
|
||||
# 详见 Debugger/ownership-and-new.md §11。
|
||||
define LIFE_VALUE = 0 # 值语义(标量/聚合按值拷贝),不参与逃逸
|
||||
define LIFE_FRAME = 1 # 本帧栈存储:局部变量地址、数组退让、形参副本地址
|
||||
define LIFE_HEAP = 2 # 堆,hlife(释放靠手动/区域)
|
||||
define LIFE_STATIC = 3 # 模块级全局 / 字符串常量 / 函数地址
|
||||
define LIFE_UNKNOWN = 4 # 未知(extern 返回等),保守放行
|
||||
|
||||
# ============================================================
|
||||
# VarEntry - variable table entry (formerly HandlesVar.VarEntry)
|
||||
class VarEntry:
|
||||
"""Variable table entry"""
|
||||
Name: str
|
||||
Alloca: mlirlite.Value*
|
||||
Used: int
|
||||
# TypeSpec (sole type carrier since S3): filled by add_var via SpecFromLLVMType from
|
||||
# alloca.Ty's pointee (PermPool, cross-file safe); annotation side augments sign/source/elem.
|
||||
Spec: TSpec.TypeSpec*
|
||||
# 1=load/store of this variable carries LLVM volatile (annotation union has t.CVolatile).
|
||||
# Appended at the end so existing field offsets are unchanged.
|
||||
IsVolatile: int
|
||||
# 1=this variable is rawptr[t.CVolatile | T] (volatile pointee, like C's volatile T*):
|
||||
# its dereferences carry volatile. Unlike IsVolatile, which limits the pointer variable itself.
|
||||
PointeeVolatile: int
|
||||
# 1=this variable is a const object (annotation has t.CConst and the type is not a
|
||||
# pointer): assignment to it anywhere in scope fails fast. Pointer CConst only limits the pointee.
|
||||
IsConst: int
|
||||
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
||||
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
||||
Life: int
|
||||
# Byte order of this variable, from the `T | t.BigEndian` / `T | t.LittleEndian` annotation union:
|
||||
# 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so existing field offsets
|
||||
# are unchanged; local loads/stores bswap when 1 (16/32/64-bit ints only). Mirrors FieldEntry.ByteOrder.
|
||||
ByteOrder: int
|
||||
# 注:P2 的"已失效"状态**不放在这里** —— 给这个热结构加字段会改变布局、牵连整条变量注册
|
||||
# 路径(现场:任何工程编译时都在"函数形参注册刚结束"处 access-violation)。改由
|
||||
# VarGenMixin 的独立失效槽表 _g_freed_slots 保存(键 = 变量槽 alloca,槽本身唯一)。
|
||||
|
||||
# ============================================================
|
||||
# Scope - scope node (formerly HandlesVar.Scope)
|
||||
novirtual class Scope:
|
||||
"""Scope node"""
|
||||
Parent: Scope*
|
||||
Vars: VarEntry*
|
||||
VarCount: int
|
||||
Kind: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SymbolTable - nested-scope symbol table (formerly HandlesVar.SymbolTable)
|
||||
novirtual class SymbolTable:
|
||||
"""Nested-scope symbol table"""
|
||||
Pool: memhub.MemBuddy*
|
||||
Root: Scope*
|
||||
Current: Scope*
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FieldEntry - field info entry (formerly HandlesStruct.FieldEntry)
|
||||
class FieldEntry:
|
||||
Name: str # Field name (string)
|
||||
Index: int # Field index within the struct
|
||||
Ty: mlirlite.LLVMType* # Field LLVM type
|
||||
DefaultVal: ast.AST* # Default-value AST node (None=no default)
|
||||
# TypeSpec (sole type carrier since S3): filled by add_field via SpecFromLLVMType from
|
||||
# field_ty (PermPool, safe across 1b reset); annotation side adds sign/source.
|
||||
Spec: TSpec.TypeSpec*
|
||||
# 1=load/store of this field via an object carries LLVM volatile (field annotation union
|
||||
# has t.CVolatile, e.g. Spinlock.locked). Appended at the end so offsets are unchanged.
|
||||
IsVolatile: int
|
||||
# 1=this field is rawptr[t.CVolatile | T] (volatile pointee): dereferences via it
|
||||
# (self.ptr[i] / self.ptr.field) carry volatile. Same distinction as VarEntry.PointeeVolatile.
|
||||
PointeeVolatile: int
|
||||
# 1=this field is a const object (annotation has t.CConst and the type is not a pointer):
|
||||
# assigning it via an object fails fast, same as VarEntry.IsConst.
|
||||
IsConst: int
|
||||
# Byte order of this field, from the `X | t.BigEndian` / `X | t.LittleEndian` annotation
|
||||
# union: 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so offsets
|
||||
# are unchanged; consumers bswap on field load/store when 1 (16/32/64-bit ints only).
|
||||
ByteOrder: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StructEntry - struct registration entry (formerly HandlesStruct.StructEntry)
|
||||
novirtual class StructEntry:
|
||||
Name: str # Class name
|
||||
Ty: mlirlite.LLVMType* # LLVM StructType
|
||||
FieldCount: int # Field count
|
||||
Fields: FieldEntry* # Field array (FIELD_MAX slots)
|
||||
IsUnion: int # 1=union / 0=normal struct
|
||||
IsPacked: int # 1=@c.Attribute(t.attr.packed) packed struct (no padding)
|
||||
Align: int # @c.Attribute(t.attr.aligned(N)) alignment in bytes (0=default)
|
||||
IsOOP: int # 1=OOP struct (has methods) / 0=plain memory struct
|
||||
HasInit: int # 1=has __init__ / 0=no
|
||||
HasNew: int # 1=has __new__ / 0=no
|
||||
HasVTable: int # 1=has vtable / 0=no vtable
|
||||
IsNoVTable: int # 1=explicitly @t.NoVTable / 0=not marked
|
||||
ParentName: str # Parent name (None=none; first parent for multiple inheritance)
|
||||
ParentNames: sizet* # Multi-inheritance parent name array (str pointers; stdlib-deep-copied)
|
||||
ParentCount: int # Parent count (0=none, 1=single, >1=multiple)
|
||||
VTableMethodCount: int # Number of methods in the vtable
|
||||
VTableMethods: str # Vtable method name array (VTableMethodCount str)
|
||||
ModuleSha1: str # Module SHA1 defining this class (None=no SHA1)
|
||||
MethodsEmitted: int # 1=method bodies translated / 0=not (Phase 1a mark_only; Phase B fills in)
|
||||
SpecTriggerSha1: str # Trigger-file SHA1 for a generic specialization (None=not specialized).
|
||||
# ModuleSha1 is the template sha1; reset_module_methods_emitted resets
|
||||
# MethodsEmitted by trigger sha1 to avoid leftover 1 / undefined symbol.
|
||||
IsForwardDecl: int # 1=forward-declaration placeholder (Ty is opaque named struct, FieldCount=0).
|
||||
# The definition side later fills the same Ty in place and clears this flag.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EnumMember - enum member (formerly HandlesEnum.EnumMember)
|
||||
class EnumMember:
|
||||
Name: str # Member name (string)
|
||||
Value: int64 # Member integer value
|
||||
Ty: mlirlite.LLVMType* # Member LLVM type (for mixed-type cases)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EnumEntry - enum type entry (formerly HandlesEnum.EnumEntry)
|
||||
novirtual class EnumEntry:
|
||||
Name: str # Enum class name
|
||||
BaseTy: mlirlite.LLVMType* # Base type (largest among member types)
|
||||
MemberCount: int # Member count
|
||||
Members: EnumMember* # Member array (ENUM_MEMBER_MAX slots)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AsmOperand - inline-asm operand entry (formerly HandlesExprCall.AsmOperand)
|
||||
class AsmOperand:
|
||||
"""Inline-asm operand entry"""
|
||||
Value: mlirlite.Value* # Input value or output alloca
|
||||
Constraint: str # Constraint string (e.g. "r", "=r")
|
||||
IsOutput: int # 1=output, 0=input
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncEntry - function table entry (formerly HandlesExprCall.FuncEntry)
|
||||
novirtual class FuncEntry:
|
||||
"""Function table entry"""
|
||||
Name: str
|
||||
Func: mlirlite.Function*
|
||||
Defaults: t.CPtr # Defaults list node pointer (list[AST*]); None=no defaults
|
||||
DefaultCount: int # Default count
|
||||
ParamCount: int # Param count (excluding self)
|
||||
# TypeSpec (dual-write since S3): the full signature of Kind=TS_FUNC (Ret/Params/...)
|
||||
# built by update_global_func_spec after add_param; PermPool-resident, safe across 1b reset.
|
||||
Spec: TSpec.TypeSpec*
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncDefaultsEntry - default-argument entry (formerly HandlesExprCall.FuncDefaultsEntry)
|
||||
novirtual class FuncDefaultsEntry:
|
||||
"""Default-argument table entry"""
|
||||
Name: str
|
||||
ModuleSha1: str # Module SHA1 defining this function (dedupes qualified names)
|
||||
Defaults: t.CPtr # Defaults list node pointer (list[AST*]) (in pool, may be reset)
|
||||
DefaultCount: int
|
||||
ParamCount: int # Param count (excluding self)
|
||||
# Constant snapshot (stdlib-allocated, permanent): extracted at registration so
|
||||
# cross-module calls rebuild constant IR without touching a reset AST.
|
||||
ConstKinds: int* # const_kind array (DefaultCount i32)
|
||||
ConstInts: int64* # int_val array (DefaultCount i64)
|
||||
ConstStrs: sizet* # str_val pointer array (DefaultCount i8*)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# REPL session (M2/M3): ctx buffer layout, session table entries, and result header.
|
||||
define REPL_CTX_MARKER = 1397571916 # Host-side object marker (self-comparison; not a ctx header field)
|
||||
define REPL_CTX_MAGIC = 0x5245504C # 'REPL' (ctx header +0, little-endian bytes R E P L)
|
||||
define REPL_CTX_VERSION = 2 # ctx header +4 (1 -> 2: function pointer slot area appended at the tail)
|
||||
define REPL_SLOT_SIZE = 16 # Bytes per session variable (stride)
|
||||
define REPL_SLOT_BASE = 64 # Slot area start = fixed header bytes (after the +40..63 reserved area)
|
||||
define REPL_CTX_PARAM = "__repl_ctx__" # Wrapper-template ctx parameter name (same source as Repl.vp)
|
||||
define REPL_VAR_CAP = 64 # Max session variables (var area = 64 + 64*16 = 1088 bytes)
|
||||
# Session function pointer slot area (M3): starts right after the var area, 16B stride (low 8 bytes hold the address)
|
||||
define REPL_FUNC_BASE = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
|
||||
define REPL_FUNC_SLOT_SIZE = 16 # Bytes per session function (stride)
|
||||
define REPL_FUNC_CAP = 32 # Max session functions (func area = 32*16 = 512 bytes)
|
||||
# Total ctx bytes = 64 + 64*16 + 32*16 = 1600 (host Repl._REPL_CTX_SIZE must match)
|
||||
define REPL_CTX_SIZE = 1600
|
||||
# Max session function params (fixed-size signature token array; overflow errors at registration)
|
||||
define REPL_FUNC_PARAM_MAX = 8
|
||||
# Fragment entry function name prefix (Repl.vp generates "__repl_frag_%d"); distinguishes
|
||||
# the fragment entry function (writes session vars) from the hoisted session function body.
|
||||
define REPL_ENTRY_PREFIX = "__repl_frag_"
|
||||
# Session module SHA1 (M4): fragments use this stable value so type names are byte-identical
|
||||
# across fragments, which is the key for classes defined earlier to be visible later.
|
||||
define REPL_SESSION_SHA1 = "71f67b7cb405f0e4"
|
||||
|
||||
# ctx header field offsets (one-to-one with ReplResult field order/width)
|
||||
define REPL_RES_KIND_OFF = 8 # i32
|
||||
define REPL_RES_A_OFF = 16 # u64
|
||||
define REPL_RES_B_OFF = 24 # u64
|
||||
define REPL_RES_VARCOUNT_OFF = 32 # i32
|
||||
define REPL_RES_LASTFRAG_OFF = 36 # i32
|
||||
|
||||
# Result kind (the host echoes accordingly)
|
||||
define REPL_RES_NONE = 0 # No result for this fragment (e.g. declaration only)
|
||||
define REPL_RES_INT = 1 # Integer (read result_a bit pattern)
|
||||
define REPL_RES_FLOAT = 2 # float64 (read result_a's double bit pattern)
|
||||
define REPL_RES_STR = 3 # str/bytes (read result_a pointer + result_b length)
|
||||
define REPL_RES_OBJ = 4 # Struct/other pointer (read result_a address)
|
||||
|
||||
# Session variable type tags (ReplVarEntry.TyTag)
|
||||
define REPL_TY_INT = 1
|
||||
define REPL_TY_FLOAT = 2
|
||||
define REPL_TY_STR = 3 # i8* (str/bytes)
|
||||
define REPL_TY_PTR = 4 # Other pointer (Bits=pointee width / TyName=struct name)
|
||||
define REPL_TY_STRUCT = 5 # Struct value (inlined in the slot per declared type; <= slot capacity)
|
||||
|
||||
# Session variable storage kind (ReplVarEntry.Kind)
|
||||
define REPL_KIND_UNTYPED = 0 # Slot registered but not yet typed (unassigned / no annotation)
|
||||
define REPL_KIND_SCALAR = 1 # Value inlined in the slot (scalar/pointer/<=16B struct value)
|
||||
|
||||
# Session function signature return-type token (M3; used only for ReplFuncEntry.RetTag)
|
||||
define REPL_TY_VOID = 0 # No return value (void; not used as a param token)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ReplVarEntry - REPL session variable entry (survives across fragments; table in PermPool)
|
||||
novirtual class ReplVarEntry:
|
||||
"""REPL session variable entry: name + ctx slot offset + type info"""
|
||||
Name: str # Variable name (PermPool deep copy, NUL-terminated)
|
||||
TyName: str # Struct name (TyTag=PTR with struct pointee / TyTag=STRUCT); else None
|
||||
Offset: int # Slot offset within ctx (REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
|
||||
Kind: int # REPL_KIND_*
|
||||
TyTag: int # REPL_TY_*
|
||||
Bits: int # Scalar bit width; for TyTag=PTR the pointee width (0=unknown)
|
||||
|
||||
|
||||
# ReplFuncEntry - REPL session function entry (M3; survives fragments, in PermPool).
|
||||
# Maps name -> symbol + fragment no + slot offset + signature token for indirect calls.
|
||||
novirtual class ReplFuncEntry:
|
||||
"""REPL session function entry: name + export symbol + ctx function-pointer slot + signature token"""
|
||||
Name: str # Session function name (= the bare name the user wrote; PermPool copy)
|
||||
SymName: str # Export symbol name in the fragment DLL (PermPool copy; M3 = bare name)
|
||||
FragNo: int # Fragment number defining this function (updated on redefinition)
|
||||
SlotOff: int # Function pointer slot offset within ctx (REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
|
||||
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
|
||||
RetBits: int # Same measure as ReplVarEntry.Bits (0 for VOID)
|
||||
ParamCount: int # Param count (NOT including the injected ctx first param)
|
||||
ParamTags: str # PermPool byte array (1 tag per param, REPL_FUNC_PARAM_MAX entries)
|
||||
ParamBits: str # PermPool byte array (1 bits byte per param, REPL_FUNC_PARAM_MAX entries)
|
||||
|
||||
|
||||
# ReplCtx - REPL session context (host-built, translator-read). trans.ReplCtx != None
|
||||
# enables REPL mode and guards all Gen-side interception points; otherwise the old path is used.
|
||||
novirtual class ReplCtx:
|
||||
"""REPL mode context (host-owned, translator-read-only)"""
|
||||
Marker: int # REPL_CTX_MARKER (guards against a non-ReplCtx object)
|
||||
Table: ReplVarEntry* # Session table array (PermPool, capacity Cap)
|
||||
Count: int # Registered entry count (= allocated slots)
|
||||
Cap: int # Capacity (REPL_VAR_CAP)
|
||||
CtxParam: str # Fragment function ctx param name (PermPool copy)
|
||||
SlotSize: int # Slot stride (REPL_SLOT_SIZE)
|
||||
# Shadow entry (host-built, PermPool): only classifies bare-expression results; it
|
||||
# occupies no session slot and is not counted; Name is fixed to "<expr>".
|
||||
Scratch: ReplVarEntry*
|
||||
# Permanent pool provided by the host: cross-fragment data (session names, struct
|
||||
# names) must be deep-copied here, since a fragment's trans.Pool is reset each line.
|
||||
Perm: memhub.MemBuddy*
|
||||
# Session function table (M3; host-writes, Gen-reads): capacity FuncCap, FuncCount
|
||||
# registered. Entries are appended only after a fragment DLL loads and GetProcAddress hits.
|
||||
FuncTable: ReplFuncEntry*
|
||||
FuncCount: int
|
||||
FuncCap: int # Capacity (REPL_FUNC_CAP)
|
||||
|
||||
|
||||
# ReplResult - ctx header (host reads by field; a fragment writes by REPL_RES_*_OFF).
|
||||
# Field order/width is the ctx layout: Magic, Version, Kind, Reserved0, A, B, VarCount, LastFrag.
|
||||
novirtual class ReplResult:
|
||||
"""Fixed ctx header (first 40 of the 64 header bytes; +40..63 reserved)"""
|
||||
Magic: int # REPL_CTX_MAGIC ('REPL')
|
||||
Version: int # REPL_CTX_VERSION
|
||||
Kind: int # REPL_RES_*
|
||||
Reserved0: int # Reserved (0)
|
||||
A: int64 # result_a
|
||||
B: int64 # result_b
|
||||
VarCount: int # Allocated slot count (host-written, for inspection)
|
||||
LastFrag: int # Most recent fragment number (host-written, for inspection)
|
||||
+456
-468
@@ -1,468 +1,456 @@
|
||||
import string
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import stdlib
|
||||
import memhub
|
||||
import llvmlite
|
||||
import lib.core.Gen.BaseGenMixin as BaseGenMixin
|
||||
import lib.core.Gen.TypeGenBase as TypeGenBase
|
||||
import lib.core.Gen.TypeGenMixin as TypeGenMixin
|
||||
import lib.core.Gen.TypeGenCDef as TypeGenCDef
|
||||
import lib.core.Gen.TypeGenCType as TypeGenCType
|
||||
import lib.core.Gen.TypeGenResolve as TypeGenResolve
|
||||
import lib.core.Gen.TypeGenErr as TypeGenErr
|
||||
import lib.core.Gen.StructGenMixin as StructGenMixin
|
||||
import lib.core.Gen.ExprGenBase as ExprGenBase
|
||||
import lib.core.Gen.ExprGenMixin as ExprGenMixin
|
||||
import lib.core.Gen.ExprGenConst as ExprGenConst
|
||||
import lib.core.Gen.ExprGenFstr as ExprGenFstr
|
||||
import lib.core.Gen.ExprGenOps as ExprGenOps
|
||||
import lib.core.Gen.ExprGenCdef as ExprGenCdef
|
||||
import lib.core.Gen.ExprGenName as ExprGenName
|
||||
import lib.core.Gen.ExprGenAttr as ExprGenAttr
|
||||
import lib.core.Gen.ExprGenCls as ExprGenCls
|
||||
import lib.core.Gen.CallGenBase as CallGenBase
|
||||
import lib.core.Gen.CallGenCBuiltin as CallGenCBuiltin
|
||||
import lib.core.Gen.CallGenLeafKit as CallGenLeafKit
|
||||
import lib.core.Gen.CallGenCtor as CallGenCtor
|
||||
import lib.core.Gen.CallGenCall as CallGenCall
|
||||
import lib.core.Gen.CallGenMixin as CallGenMixin
|
||||
import lib.core.Gen.CallGenDispatch as CallGenDispatch
|
||||
import lib.core.Gen.CallGenLib as CallGenLib
|
||||
import lib.core.Gen.CallGenVt as CallGenVt
|
||||
import lib.core.Gen.CallGenVa as CallGenVa
|
||||
import lib.core.Gen.CallGenProp as CallGenProp
|
||||
import lib.core.Gen.CallGenFd as CallGenFd
|
||||
import lib.core.Gen.FuncGenBase as FuncGenBase
|
||||
import lib.core.Gen.FuncGenDef as FuncGenDef
|
||||
import lib.core.Gen.FuncGenMixin as FuncGenMixin
|
||||
import lib.core.Gen.ClassGenBase as ClassGenBase
|
||||
import lib.core.Gen.ClassGenMixin as ClassGenMixin
|
||||
import lib.core.Gen.ClassGenEnum as ClassGenEnum
|
||||
import lib.core.Gen.ClassGenGeneric as ClassGenGeneric
|
||||
import lib.core.Gen.ClassGenVTable as ClassGenVTable
|
||||
import lib.core.Gen.ClassGenDef as ClassGenDef
|
||||
import lib.core.Gen.ImportsGenMixin as ImportsGenMixin
|
||||
import lib.core.Gen.StmtGenCifMixin as StmtGenCifMixin
|
||||
import lib.core.Gen.StmtGenDeclMixin as StmtGenDeclMixin
|
||||
import lib.core.Gen.StmtGenMixin as StmtGenMixin
|
||||
import lib.core.Gen.MatchGenMixin as MatchGenMixin
|
||||
import lib.core.Gen.AssignGenMixin as AssignGenMixin
|
||||
import lib.core.Gen.AsyncGenMixin as AsyncGenMixin
|
||||
import lib.core.Gen.VarGenMixin as VarGenMixin
|
||||
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Generator - 生成器(双向引用组合架构)
|
||||
#
|
||||
# 不再 46 路多继承 Mixin;改为组合:Generator 持所有 GenMixin
|
||||
# 子对象指针(永久池分配,全生命周期),每个 Mixin 持 Gen 回指。
|
||||
# 跨子对象访问 = self.Gen.<Mixin 字段>.<字段/方法>(链式有风险,
|
||||
# 统一用中间变量取子对象再访问)。高亮可推导(显式类型注解)。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class Generator:
|
||||
"""生成器:持所有 GenMixin 子对象指针(双向引用组合)"""
|
||||
|
||||
# ---- 子对象指针(46 个,永久池) ----
|
||||
BaseGen: BaseGenMixin.BaseGenMixin | t.CPtr
|
||||
TypeGenBase: TypeGenBase.TypeGenBase | t.CPtr
|
||||
TypeGen: TypeGenMixin.TypeGenMixin | t.CPtr
|
||||
TypeGenCDef: TypeGenCDef.TypeGenCDef | t.CPtr
|
||||
TypeGenCType: TypeGenCType.TypeGenCType | t.CPtr
|
||||
TypeGenResolve: TypeGenResolve.TypeGenResolve | t.CPtr
|
||||
TypeGenErr: TypeGenErr.TypeGenErr | t.CPtr
|
||||
StructGen: StructGenMixin.StructGenMixin | t.CPtr
|
||||
ExprGenBase: ExprGenBase.ExprGenBase | t.CPtr
|
||||
ExprGen: ExprGenMixin.ExprGenMixin | t.CPtr
|
||||
ExprGenConst: ExprGenConst.ExprGenConst | t.CPtr
|
||||
ExprGenFstr: ExprGenFstr.ExprGenFstr | t.CPtr
|
||||
ExprGenOps: ExprGenOps.ExprGenOps | t.CPtr
|
||||
ExprGenCdef: ExprGenCdef.ExprGenCdef | t.CPtr
|
||||
ExprGenName: ExprGenName.ExprGenName | t.CPtr
|
||||
ExprGenAttr: ExprGenAttr.ExprGenAttr | t.CPtr
|
||||
ExprGenCls: ExprGenCls.ExprGenCls | t.CPtr
|
||||
CallGenBase: CallGenBase.CallGenBase | t.CPtr
|
||||
CallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin | t.CPtr
|
||||
CallGenLeafKit: CallGenLeafKit.CallGenLeafKit | t.CPtr
|
||||
CallGenCtor: CallGenCtor.CallGenCtor | t.CPtr
|
||||
CallGenCall: CallGenCall.CallGenCall | t.CPtr
|
||||
CallGen: CallGenMixin.CallGenMixin | t.CPtr
|
||||
CallGenDispatch: CallGenDispatch.CallGenDispatch | t.CPtr
|
||||
CallGenLib: CallGenLib.CallGenLib | t.CPtr
|
||||
CallGenVt: CallGenVt.CallGenVt | t.CPtr
|
||||
CallGenVa: CallGenVa.CallGenVa | t.CPtr
|
||||
CallGenProp: CallGenProp.CallGenProp | t.CPtr
|
||||
CallGenFd: CallGenFd.CallGenFd | t.CPtr
|
||||
FuncGenBase: FuncGenBase.FuncGenBase | t.CPtr
|
||||
FuncGenDef: FuncGenDef.FuncGenDef | t.CPtr
|
||||
FuncGen: FuncGenMixin.FuncGenMixin | t.CPtr
|
||||
ClassGenBase: ClassGenBase.ClassGenBase | t.CPtr
|
||||
ClassGen: ClassGenMixin.ClassGenMixin | t.CPtr
|
||||
ClassGenEnum: ClassGenEnum.ClassGenEnum | t.CPtr
|
||||
ClassGenGeneric: ClassGenGeneric.ClassGenGeneric | t.CPtr
|
||||
ClassGenVTable: ClassGenVTable.ClassGenVTable | t.CPtr
|
||||
ClassGenDef: ClassGenDef.ClassGenDef | t.CPtr
|
||||
ImportsGen: ImportsGenMixin.ImportsGenMixin | t.CPtr
|
||||
StmtGenCif: StmtGenCifMixin.StmtGenCifMixin | t.CPtr
|
||||
StmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin | t.CPtr
|
||||
StmtGen: StmtGenMixin.StmtGenMixin | t.CPtr
|
||||
MatchGen: MatchGenMixin.MatchGenMixin | t.CPtr
|
||||
AssignGen: AssignGenMixin.AssignGenMixin | t.CPtr
|
||||
AsyncGen: AsyncGenMixin.AsyncGenMixin | t.CPtr
|
||||
VarGen: VarGenMixin.VarGenMixin | t.CPtr
|
||||
TranslatorGen: TranslatorGenMixin.TranslatorGenMixin | t.CPtr
|
||||
|
||||
def __init__(self, pool: memhub.MemBuddy | t.CPtr):
|
||||
if self is None:
|
||||
return
|
||||
# ---- alloc 46 个子对象 + 回指(永久池,fail-fast) ----
|
||||
SubBaseGen: BaseGenMixin.BaseGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||
if SubBaseGen is None:
|
||||
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||
SubBaseGen.Gen = self
|
||||
self.BaseGen = SubBaseGen
|
||||
SubTypeGenBase: TypeGenBase.TypeGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenBase.TypeGenBase.__sizeof__())
|
||||
if SubTypeGenBase is None:
|
||||
stdio.printf("FAIL: TypeGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
|
||||
SubTypeGenBase.Gen = self
|
||||
self.TypeGenBase = SubTypeGenBase
|
||||
SubTypeGen: TypeGenMixin.TypeGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
|
||||
if SubTypeGen is None:
|
||||
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
|
||||
SubTypeGen.Gen = self
|
||||
self.TypeGen = SubTypeGen
|
||||
SubTypeGenCDef: TypeGenCDef.TypeGenCDef | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
|
||||
if SubTypeGenCDef is None:
|
||||
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
|
||||
SubTypeGenCDef.Gen = self
|
||||
self.TypeGenCDef = SubTypeGenCDef
|
||||
SubTypeGenCType: TypeGenCType.TypeGenCType | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenCType.TypeGenCType.__sizeof__())
|
||||
if SubTypeGenCType is None:
|
||||
stdio.printf("FAIL: TypeGenCType alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
|
||||
SubTypeGenCType.Gen = self
|
||||
self.TypeGenCType = SubTypeGenCType
|
||||
SubTypeGenResolve: TypeGenResolve.TypeGenResolve | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
|
||||
if SubTypeGenResolve is None:
|
||||
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
|
||||
SubTypeGenResolve.Gen = self
|
||||
self.TypeGenResolve = SubTypeGenResolve
|
||||
SubTypeGenErr: TypeGenErr.TypeGenErr | t.CPtr = CompilerFactory.get_perm_pool().alloc(TypeGenErr.TypeGenErr.__sizeof__())
|
||||
if SubTypeGenErr is None:
|
||||
stdio.printf("FAIL: TypeGenErr alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
|
||||
SubTypeGenErr.Gen = self
|
||||
self.TypeGenErr = SubTypeGenErr
|
||||
SubStructGen: StructGenMixin.StructGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StructGenMixin.StructGenMixin.__sizeof__())
|
||||
if SubStructGen is None:
|
||||
stdio.printf("FAIL: StructGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
|
||||
SubStructGen.Gen = self
|
||||
self.StructGen = SubStructGen
|
||||
SubExprGenBase: ExprGenBase.ExprGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenBase.ExprGenBase.__sizeof__())
|
||||
if SubExprGenBase is None:
|
||||
stdio.printf("FAIL: ExprGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
|
||||
SubExprGenBase.Gen = self
|
||||
self.ExprGenBase = SubExprGenBase
|
||||
SubExprGen: ExprGenMixin.ExprGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
|
||||
if SubExprGen is None:
|
||||
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
|
||||
SubExprGen.Gen = self
|
||||
self.ExprGen = SubExprGen
|
||||
SubExprGenConst: ExprGenConst.ExprGenConst | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenConst.ExprGenConst.__sizeof__())
|
||||
if SubExprGenConst is None:
|
||||
stdio.printf("FAIL: ExprGenConst alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
|
||||
SubExprGenConst.Gen = self
|
||||
self.ExprGenConst = SubExprGenConst
|
||||
SubExprGenFstr: ExprGenFstr.ExprGenFstr | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
|
||||
if SubExprGenFstr is None:
|
||||
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
|
||||
SubExprGenFstr.Gen = self
|
||||
self.ExprGenFstr = SubExprGenFstr
|
||||
SubExprGenOps: ExprGenOps.ExprGenOps | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenOps.ExprGenOps.__sizeof__())
|
||||
if SubExprGenOps is None:
|
||||
stdio.printf("FAIL: ExprGenOps alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
|
||||
SubExprGenOps.Gen = self
|
||||
self.ExprGenOps = SubExprGenOps
|
||||
SubExprGenCdef: ExprGenCdef.ExprGenCdef | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
|
||||
if SubExprGenCdef is None:
|
||||
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
|
||||
SubExprGenCdef.Gen = self
|
||||
self.ExprGenCdef = SubExprGenCdef
|
||||
SubExprGenName: ExprGenName.ExprGenName | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenName.ExprGenName.__sizeof__())
|
||||
if SubExprGenName is None:
|
||||
stdio.printf("FAIL: ExprGenName alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
|
||||
SubExprGenName.Gen = self
|
||||
self.ExprGenName = SubExprGenName
|
||||
SubExprGenAttr: ExprGenAttr.ExprGenAttr | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
|
||||
if SubExprGenAttr is None:
|
||||
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
|
||||
SubExprGenAttr.Gen = self
|
||||
self.ExprGenAttr = SubExprGenAttr
|
||||
SubExprGenCls: ExprGenCls.ExprGenCls | t.CPtr = CompilerFactory.get_perm_pool().alloc(ExprGenCls.ExprGenCls.__sizeof__())
|
||||
if SubExprGenCls is None:
|
||||
stdio.printf("FAIL: ExprGenCls alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
|
||||
SubExprGenCls.Gen = self
|
||||
self.ExprGenCls = SubExprGenCls
|
||||
SubCallGenBase: CallGenBase.CallGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenBase.CallGenBase.__sizeof__())
|
||||
if SubCallGenBase is None:
|
||||
stdio.printf("FAIL: CallGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
|
||||
SubCallGenBase.Gen = self
|
||||
self.CallGenBase = SubCallGenBase
|
||||
SubCallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
||||
if SubCallGenCBuiltin is None:
|
||||
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
||||
SubCallGenCBuiltin.Gen = self
|
||||
self.CallGenCBuiltin = SubCallGenCBuiltin
|
||||
SubCallGenLeafKit: CallGenLeafKit.CallGenLeafKit | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
||||
if SubCallGenLeafKit is None:
|
||||
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
||||
SubCallGenLeafKit.Gen = self
|
||||
self.CallGenLeafKit = SubCallGenLeafKit
|
||||
SubCallGenCtor: CallGenCtor.CallGenCtor | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenCtor.CallGenCtor.__sizeof__())
|
||||
if SubCallGenCtor is None:
|
||||
stdio.printf("FAIL: CallGenCtor alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
|
||||
SubCallGenCtor.Gen = self
|
||||
self.CallGenCtor = SubCallGenCtor
|
||||
SubCallGenCall: CallGenCall.CallGenCall | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenCall.CallGenCall.__sizeof__())
|
||||
if SubCallGenCall is None:
|
||||
stdio.printf("FAIL: CallGenCall alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
|
||||
SubCallGenCall.Gen = self
|
||||
self.CallGenCall = SubCallGenCall
|
||||
SubCallGen: CallGenMixin.CallGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenMixin.CallGenMixin.__sizeof__())
|
||||
if SubCallGen is None:
|
||||
stdio.printf("FAIL: CallGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
|
||||
SubCallGen.Gen = self
|
||||
self.CallGen = SubCallGen
|
||||
SubCallGenDispatch: CallGenDispatch.CallGenDispatch | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
|
||||
if SubCallGenDispatch is None:
|
||||
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
|
||||
SubCallGenDispatch.Gen = self
|
||||
self.CallGenDispatch = SubCallGenDispatch
|
||||
SubCallGenLib: CallGenLib.CallGenLib | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenLib.CallGenLib.__sizeof__())
|
||||
if SubCallGenLib is None:
|
||||
stdio.printf("FAIL: CallGenLib alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
|
||||
SubCallGenLib.Gen = self
|
||||
self.CallGenLib = SubCallGenLib
|
||||
SubCallGenVt: CallGenVt.CallGenVt | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenVt.CallGenVt.__sizeof__())
|
||||
if SubCallGenVt is None:
|
||||
stdio.printf("FAIL: CallGenVt alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
|
||||
SubCallGenVt.Gen = self
|
||||
self.CallGenVt = SubCallGenVt
|
||||
SubCallGenVa: CallGenVa.CallGenVa | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenVa.CallGenVa.__sizeof__())
|
||||
if SubCallGenVa is None:
|
||||
stdio.printf("FAIL: CallGenVa alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
|
||||
SubCallGenVa.Gen = self
|
||||
self.CallGenVa = SubCallGenVa
|
||||
SubCallGenProp: CallGenProp.CallGenProp | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenProp.CallGenProp.__sizeof__())
|
||||
if SubCallGenProp is None:
|
||||
stdio.printf("FAIL: CallGenProp alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
|
||||
SubCallGenProp.Gen = self
|
||||
self.CallGenProp = SubCallGenProp
|
||||
SubCallGenFd: CallGenFd.CallGenFd | t.CPtr = CompilerFactory.get_perm_pool().alloc(CallGenFd.CallGenFd.__sizeof__())
|
||||
if SubCallGenFd is None:
|
||||
stdio.printf("FAIL: CallGenFd alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
|
||||
SubCallGenFd.Gen = self
|
||||
self.CallGenFd = SubCallGenFd
|
||||
SubFuncGenBase: FuncGenBase.FuncGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(FuncGenBase.FuncGenBase.__sizeof__())
|
||||
if SubFuncGenBase is None:
|
||||
stdio.printf("FAIL: FuncGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
|
||||
SubFuncGenBase.Gen = self
|
||||
self.FuncGenBase = SubFuncGenBase
|
||||
SubFuncGenDef: FuncGenDef.FuncGenDef | t.CPtr = CompilerFactory.get_perm_pool().alloc(FuncGenDef.FuncGenDef.__sizeof__())
|
||||
if SubFuncGenDef is None:
|
||||
stdio.printf("FAIL: FuncGenDef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
|
||||
SubFuncGenDef.Gen = self
|
||||
self.FuncGenDef = SubFuncGenDef
|
||||
SubFuncGen: FuncGenMixin.FuncGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
|
||||
if SubFuncGen is None:
|
||||
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
|
||||
SubFuncGen.Gen = self
|
||||
self.FuncGen = SubFuncGen
|
||||
SubClassGenBase: ClassGenBase.ClassGenBase | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenBase.ClassGenBase.__sizeof__())
|
||||
if SubClassGenBase is None:
|
||||
stdio.printf("FAIL: ClassGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
|
||||
SubClassGenBase.Gen = self
|
||||
self.ClassGenBase = SubClassGenBase
|
||||
SubClassGen: ClassGenMixin.ClassGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
|
||||
if SubClassGen is None:
|
||||
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
|
||||
SubClassGen.Gen = self
|
||||
self.ClassGen = SubClassGen
|
||||
SubClassGenEnum: ClassGenEnum.ClassGenEnum | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
|
||||
if SubClassGenEnum is None:
|
||||
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
|
||||
SubClassGenEnum.Gen = self
|
||||
self.ClassGenEnum = SubClassGenEnum
|
||||
SubClassGenGeneric: ClassGenGeneric.ClassGenGeneric | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
||||
if SubClassGenGeneric is None:
|
||||
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
||||
SubClassGenGeneric.Gen = self
|
||||
self.ClassGenGeneric = SubClassGenGeneric
|
||||
SubClassGenVTable: ClassGenVTable.ClassGenVTable | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
|
||||
if SubClassGenVTable is None:
|
||||
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
|
||||
SubClassGenVTable.Gen = self
|
||||
self.ClassGenVTable = SubClassGenVTable
|
||||
SubClassGenDef: ClassGenDef.ClassGenDef | t.CPtr = CompilerFactory.get_perm_pool().alloc(ClassGenDef.ClassGenDef.__sizeof__())
|
||||
if SubClassGenDef is None:
|
||||
stdio.printf("FAIL: ClassGenDef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
|
||||
SubClassGenDef.Gen = self
|
||||
self.ClassGenDef = SubClassGenDef
|
||||
SubImportsGen: ImportsGenMixin.ImportsGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
||||
if SubImportsGen is None:
|
||||
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
||||
SubImportsGen.Gen = self
|
||||
self.ImportsGen = SubImportsGen
|
||||
SubStmtGenCif: StmtGenCifMixin.StmtGenCifMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
||||
if SubStmtGenCif is None:
|
||||
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
||||
SubStmtGenCif.Gen = self
|
||||
self.StmtGenCif = SubStmtGenCif
|
||||
SubStmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
||||
if SubStmtGenDecl is None:
|
||||
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
||||
SubStmtGenDecl.Gen = self
|
||||
self.StmtGenDecl = SubStmtGenDecl
|
||||
SubStmtGen: StmtGenMixin.StmtGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
|
||||
if SubStmtGen is None:
|
||||
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
|
||||
SubStmtGen.Gen = self
|
||||
self.StmtGen = SubStmtGen
|
||||
SubMatchGen: MatchGenMixin.MatchGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
|
||||
if SubMatchGen is None:
|
||||
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
|
||||
SubMatchGen.Gen = self
|
||||
self.MatchGen = SubMatchGen
|
||||
SubAssignGen: AssignGenMixin.AssignGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
|
||||
if SubAssignGen is None:
|
||||
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
|
||||
SubAssignGen.Gen = self
|
||||
self.AssignGen = SubAssignGen
|
||||
SubVarGen: VarGenMixin.VarGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(VarGenMixin.VarGenMixin.__sizeof__())
|
||||
if SubVarGen is None:
|
||||
stdio.printf("FAIL: VarGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
|
||||
SubVarGen.Gen = self
|
||||
self.VarGen = SubVarGen
|
||||
SubTranslatorGen: TranslatorGenMixin.TranslatorGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
||||
if SubTranslatorGen is None:
|
||||
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
||||
SubTranslatorGen.Gen = self
|
||||
self.TranslatorGen = SubTranslatorGen
|
||||
SubAsyncGen: AsyncGenMixin.AsyncGenMixin | t.CPtr = CompilerFactory.get_perm_pool().alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
|
||||
if SubAsyncGen is None:
|
||||
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubAsyncGen, 0, AsyncGenMixin.AsyncGenMixin.__sizeof__())
|
||||
SubAsyncGen.Gen = self
|
||||
self.AsyncGen = SubAsyncGen
|
||||
|
||||
# ---- 初始化各子对象字段(Init* 方法内联调用) ----
|
||||
SubBaseGen.InitBaseGen(pool)
|
||||
SubTypeGenBase.InitTypeGen()
|
||||
SubCallGenBase.InitCallGen()
|
||||
SubStructGen.InitStructGen()
|
||||
SubClassGenBase.InitClassGen()
|
||||
SubExprGenBase.InitExprGen()
|
||||
SubImportsGen.InitImportsGen()
|
||||
SubStmtGenCif.InitStmtGen()
|
||||
SubAssignGen.InitAssignGen()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局生成器单例(组合架构兼容层)
|
||||
# ============================================================
|
||||
_g_gen: Generator | t.CPtr = None
|
||||
import string
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
from stdint import *
|
||||
import stdio
|
||||
import stdlib
|
||||
import memhub
|
||||
import mlirlite
|
||||
import lib.core.Gen.BaseGenMixin as BaseGenMixin
|
||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
|
||||
import lib.core.Gen.Type.TypeGenCDef as TypeGenCDef
|
||||
import lib.core.Gen.Type.TypeGenCType as TypeGenCType
|
||||
import lib.core.Gen.Type.TypeGenResolve as TypeGenResolve
|
||||
import lib.core.Gen.Type.TypeGenErr as TypeGenErr
|
||||
import lib.core.Gen.StructGenMixin as StructGenMixin
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
import lib.core.Gen.Expr.ExprGenMixin as ExprGenMixin
|
||||
import lib.core.Gen.Expr.ExprGenConst as ExprGenConst
|
||||
import lib.core.Gen.Expr.ExprGenFstr as ExprGenFstr
|
||||
import lib.core.Gen.Expr.ExprGenOps as ExprGenOps
|
||||
import lib.core.Gen.Expr.ExprGenCdef as ExprGenCdef
|
||||
import lib.core.Gen.Expr.ExprGenName as ExprGenName
|
||||
import lib.core.Gen.Expr.ExprGenAttr as ExprGenAttr
|
||||
import lib.core.Gen.Expr.ExprGenCls as ExprGenCls
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
import lib.core.Gen.Call.CallGenCBuiltin as CallGenCBuiltin
|
||||
import lib.core.Gen.Call.CallGenLeafKit as CallGenLeafKit
|
||||
import lib.core.Gen.Call.CallGenCtor as CallGenCtor
|
||||
import lib.core.Gen.Call.CallGenCall as CallGenCall
|
||||
import lib.core.Gen.Call.CallGenMixin as CallGenMixin
|
||||
import lib.core.Gen.Call.CallGenDispatch as CallGenDispatch
|
||||
import lib.core.Gen.Call.CallGenLib as CallGenLib
|
||||
import lib.core.Gen.Call.CallGenVt as CallGenVt
|
||||
import lib.core.Gen.Call.CallGenVa as CallGenVa
|
||||
import lib.core.Gen.Call.CallGenProp as CallGenProp
|
||||
import lib.core.Gen.Call.CallGenFd as CallGenFd
|
||||
import lib.core.Gen.Func.FuncGenBase as FuncGenBase
|
||||
import lib.core.Gen.Func.FuncGenDef as FuncGenDef
|
||||
import lib.core.Gen.Func.FuncGenMixin as FuncGenMixin
|
||||
import lib.core.Gen.Class.ClassGenBase as ClassGenBase
|
||||
import lib.core.Gen.Class.ClassGenMixin as ClassGenMixin
|
||||
import lib.core.Gen.Class.ClassGenEnum as ClassGenEnum
|
||||
import lib.core.Gen.Class.ClassGenGeneric as ClassGenGeneric
|
||||
import lib.core.Gen.Class.ClassGenVTable as ClassGenVTable
|
||||
import lib.core.Gen.Class.ClassGenDef as ClassGenDef
|
||||
import lib.core.Gen.ImportsGenMixin as ImportsGenMixin
|
||||
import lib.core.Gen.Stmt.StmtGenCifMixin as StmtGenCifMixin
|
||||
import lib.core.Gen.Stmt.StmtGenDeclMixin as StmtGenDeclMixin
|
||||
import lib.core.Gen.Stmt.StmtGenMixin as StmtGenMixin
|
||||
import lib.core.Gen.MatchGenMixin as MatchGenMixin
|
||||
import lib.core.Gen.AssignGenMixin as AssignGenMixin
|
||||
import lib.core.Gen.AsyncGenMixin as AsyncGenMixin
|
||||
import lib.core.Gen.VarGenMixin as VarGenMixin
|
||||
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
|
||||
|
||||
|
||||
# Generator - generator (bidirectional-reference composition architecture)
|
||||
novirtual class Generator:
|
||||
"""Generator holding pointers to all GenMixin sub-objects (bidirectional composition)."""
|
||||
|
||||
# ---- sub-object pointers (46, permanent pool) ----
|
||||
BaseGen: BaseGenMixin.BaseGenMixin*
|
||||
TypeGenBase: TypeGenBase.TypeGenBase*
|
||||
TypeGen: TypeGenMixin.TypeGenMixin*
|
||||
TypeGenCDef: TypeGenCDef.TypeGenCDef*
|
||||
TypeGenCType: TypeGenCType.TypeGenCType*
|
||||
TypeGenResolve: TypeGenResolve.TypeGenResolve*
|
||||
TypeGenErr: TypeGenErr.TypeGenErr*
|
||||
StructGen: StructGenMixin.StructGenMixin*
|
||||
ExprGenBase: ExprGenBase.ExprGenBase*
|
||||
ExprGen: ExprGenMixin.ExprGenMixin*
|
||||
ExprGenConst: ExprGenConst.ExprGenConst*
|
||||
ExprGenFstr: ExprGenFstr.ExprGenFstr*
|
||||
ExprGenOps: ExprGenOps.ExprGenOps*
|
||||
ExprGenCdef: ExprGenCdef.ExprGenCdef*
|
||||
ExprGenName: ExprGenName.ExprGenName*
|
||||
ExprGenAttr: ExprGenAttr.ExprGenAttr*
|
||||
ExprGenCls: ExprGenCls.ExprGenCls*
|
||||
CallGenBase: CallGenBase.CallGenBase*
|
||||
CallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin*
|
||||
CallGenLeafKit: CallGenLeafKit.CallGenLeafKit*
|
||||
CallGenCtor: CallGenCtor.CallGenCtor*
|
||||
CallGenCall: CallGenCall.CallGenCall*
|
||||
CallGen: CallGenMixin.CallGenMixin*
|
||||
CallGenDispatch: CallGenDispatch.CallGenDispatch*
|
||||
CallGenLib: CallGenLib.CallGenLib*
|
||||
CallGenVt: CallGenVt.CallGenVt*
|
||||
CallGenVa: CallGenVa.CallGenVa*
|
||||
CallGenProp: CallGenProp.CallGenProp*
|
||||
CallGenFd: CallGenFd.CallGenFd*
|
||||
FuncGenBase: FuncGenBase.FuncGenBase*
|
||||
FuncGenDef: FuncGenDef.FuncGenDef*
|
||||
FuncGen: FuncGenMixin.FuncGenMixin*
|
||||
ClassGenBase: ClassGenBase.ClassGenBase*
|
||||
ClassGen: ClassGenMixin.ClassGenMixin*
|
||||
ClassGenEnum: ClassGenEnum.ClassGenEnum*
|
||||
ClassGenGeneric: ClassGenGeneric.ClassGenGeneric*
|
||||
ClassGenVTable: ClassGenVTable.ClassGenVTable*
|
||||
ClassGenDef: ClassGenDef.ClassGenDef*
|
||||
ImportsGen: ImportsGenMixin.ImportsGenMixin*
|
||||
StmtGenCif: StmtGenCifMixin.StmtGenCifMixin*
|
||||
StmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin*
|
||||
StmtGen: StmtGenMixin.StmtGenMixin*
|
||||
MatchGen: MatchGenMixin.MatchGenMixin*
|
||||
AssignGen: AssignGenMixin.AssignGenMixin*
|
||||
AsyncGen: AsyncGenMixin.AsyncGenMixin*
|
||||
VarGen: VarGenMixin.VarGenMixin*
|
||||
TranslatorGen: TranslatorGenMixin.TranslatorGenMixin*
|
||||
|
||||
def __init__(self, pool: memhub.MemBuddy*):
|
||||
if self is None: return
|
||||
# ---- alloc 46 sub-objects + back-pointers (permanent pool, fail-fast) ----
|
||||
SubBaseGen: BaseGenMixin.BaseGenMixin* = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||
if SubBaseGen is None:
|
||||
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||
SubBaseGen.Gen = self
|
||||
self.BaseGen = SubBaseGen
|
||||
SubTypeGenBase: TypeGenBase.TypeGenBase* = CompilerFactory.factory.PermPool.alloc(TypeGenBase.TypeGenBase.__sizeof__())
|
||||
if SubTypeGenBase is None:
|
||||
stdio.printf("FAIL: TypeGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
|
||||
SubTypeGenBase.Gen = self
|
||||
self.TypeGenBase = SubTypeGenBase
|
||||
SubTypeGen: TypeGenMixin.TypeGenMixin* = CompilerFactory.factory.PermPool.alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
|
||||
if SubTypeGen is None:
|
||||
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
|
||||
SubTypeGen.Gen = self
|
||||
self.TypeGen = SubTypeGen
|
||||
SubTypeGenCDef: TypeGenCDef.TypeGenCDef* = CompilerFactory.factory.PermPool.alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
|
||||
if SubTypeGenCDef is None:
|
||||
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
|
||||
SubTypeGenCDef.Gen = self
|
||||
self.TypeGenCDef = SubTypeGenCDef
|
||||
SubTypeGenCType: TypeGenCType.TypeGenCType* = CompilerFactory.factory.PermPool.alloc(TypeGenCType.TypeGenCType.__sizeof__())
|
||||
if SubTypeGenCType is None:
|
||||
stdio.printf("FAIL: TypeGenCType alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
|
||||
SubTypeGenCType.Gen = self
|
||||
self.TypeGenCType = SubTypeGenCType
|
||||
SubTypeGenResolve: TypeGenResolve.TypeGenResolve* = CompilerFactory.factory.PermPool.alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
|
||||
if SubTypeGenResolve is None:
|
||||
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
|
||||
SubTypeGenResolve.Gen = self
|
||||
self.TypeGenResolve = SubTypeGenResolve
|
||||
SubTypeGenErr: TypeGenErr.TypeGenErr* = CompilerFactory.factory.PermPool.alloc(TypeGenErr.TypeGenErr.__sizeof__())
|
||||
if SubTypeGenErr is None:
|
||||
stdio.printf("FAIL: TypeGenErr alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
|
||||
SubTypeGenErr.Gen = self
|
||||
self.TypeGenErr = SubTypeGenErr
|
||||
SubStructGen: StructGenMixin.StructGenMixin* = CompilerFactory.factory.PermPool.alloc(StructGenMixin.StructGenMixin.__sizeof__())
|
||||
if SubStructGen is None:
|
||||
stdio.printf("FAIL: StructGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
|
||||
SubStructGen.Gen = self
|
||||
self.StructGen = SubStructGen
|
||||
SubExprGenBase: ExprGenBase.ExprGenBase* = CompilerFactory.factory.PermPool.alloc(ExprGenBase.ExprGenBase.__sizeof__())
|
||||
if SubExprGenBase is None:
|
||||
stdio.printf("FAIL: ExprGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
|
||||
SubExprGenBase.Gen = self
|
||||
self.ExprGenBase = SubExprGenBase
|
||||
SubExprGen: ExprGenMixin.ExprGenMixin* = CompilerFactory.factory.PermPool.alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
|
||||
if SubExprGen is None:
|
||||
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
|
||||
SubExprGen.Gen = self
|
||||
self.ExprGen = SubExprGen
|
||||
SubExprGenConst: ExprGenConst.ExprGenConst* = CompilerFactory.factory.PermPool.alloc(ExprGenConst.ExprGenConst.__sizeof__())
|
||||
if SubExprGenConst is None:
|
||||
stdio.printf("FAIL: ExprGenConst alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
|
||||
SubExprGenConst.Gen = self
|
||||
self.ExprGenConst = SubExprGenConst
|
||||
SubExprGenFstr: ExprGenFstr.ExprGenFstr* = CompilerFactory.factory.PermPool.alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
|
||||
if SubExprGenFstr is None:
|
||||
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
|
||||
SubExprGenFstr.Gen = self
|
||||
self.ExprGenFstr = SubExprGenFstr
|
||||
SubExprGenOps: ExprGenOps.ExprGenOps* = CompilerFactory.factory.PermPool.alloc(ExprGenOps.ExprGenOps.__sizeof__())
|
||||
if SubExprGenOps is None:
|
||||
stdio.printf("FAIL: ExprGenOps alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
|
||||
SubExprGenOps.Gen = self
|
||||
self.ExprGenOps = SubExprGenOps
|
||||
SubExprGenCdef: ExprGenCdef.ExprGenCdef* = CompilerFactory.factory.PermPool.alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
|
||||
if SubExprGenCdef is None:
|
||||
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
|
||||
SubExprGenCdef.Gen = self
|
||||
self.ExprGenCdef = SubExprGenCdef
|
||||
SubExprGenName: ExprGenName.ExprGenName* = CompilerFactory.factory.PermPool.alloc(ExprGenName.ExprGenName.__sizeof__())
|
||||
if SubExprGenName is None:
|
||||
stdio.printf("FAIL: ExprGenName alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
|
||||
SubExprGenName.Gen = self
|
||||
self.ExprGenName = SubExprGenName
|
||||
SubExprGenAttr: ExprGenAttr.ExprGenAttr* = CompilerFactory.factory.PermPool.alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
|
||||
if SubExprGenAttr is None:
|
||||
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
|
||||
SubExprGenAttr.Gen = self
|
||||
self.ExprGenAttr = SubExprGenAttr
|
||||
SubExprGenCls: ExprGenCls.ExprGenCls* = CompilerFactory.factory.PermPool.alloc(ExprGenCls.ExprGenCls.__sizeof__())
|
||||
if SubExprGenCls is None:
|
||||
stdio.printf("FAIL: ExprGenCls alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
|
||||
SubExprGenCls.Gen = self
|
||||
self.ExprGenCls = SubExprGenCls
|
||||
SubCallGenBase: CallGenBase.CallGenBase* = CompilerFactory.factory.PermPool.alloc(CallGenBase.CallGenBase.__sizeof__())
|
||||
if SubCallGenBase is None:
|
||||
stdio.printf("FAIL: CallGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
|
||||
SubCallGenBase.Gen = self
|
||||
self.CallGenBase = SubCallGenBase
|
||||
SubCallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin* = CompilerFactory.factory.PermPool.alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
||||
if SubCallGenCBuiltin is None:
|
||||
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
||||
SubCallGenCBuiltin.Gen = self
|
||||
self.CallGenCBuiltin = SubCallGenCBuiltin
|
||||
SubCallGenLeafKit: CallGenLeafKit.CallGenLeafKit* = CompilerFactory.factory.PermPool.alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
||||
if SubCallGenLeafKit is None:
|
||||
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
||||
SubCallGenLeafKit.Gen = self
|
||||
self.CallGenLeafKit = SubCallGenLeafKit
|
||||
SubCallGenCtor: CallGenCtor.CallGenCtor* = CompilerFactory.factory.PermPool.alloc(CallGenCtor.CallGenCtor.__sizeof__())
|
||||
if SubCallGenCtor is None:
|
||||
stdio.printf("FAIL: CallGenCtor alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
|
||||
SubCallGenCtor.Gen = self
|
||||
self.CallGenCtor = SubCallGenCtor
|
||||
SubCallGenCall: CallGenCall.CallGenCall* = CompilerFactory.factory.PermPool.alloc(CallGenCall.CallGenCall.__sizeof__())
|
||||
if SubCallGenCall is None:
|
||||
stdio.printf("FAIL: CallGenCall alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
|
||||
SubCallGenCall.Gen = self
|
||||
self.CallGenCall = SubCallGenCall
|
||||
SubCallGen: CallGenMixin.CallGenMixin* = CompilerFactory.factory.PermPool.alloc(CallGenMixin.CallGenMixin.__sizeof__())
|
||||
if SubCallGen is None:
|
||||
stdio.printf("FAIL: CallGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
|
||||
SubCallGen.Gen = self
|
||||
self.CallGen = SubCallGen
|
||||
SubCallGenDispatch: CallGenDispatch.CallGenDispatch* = CompilerFactory.factory.PermPool.alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
|
||||
if SubCallGenDispatch is None:
|
||||
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
|
||||
SubCallGenDispatch.Gen = self
|
||||
self.CallGenDispatch = SubCallGenDispatch
|
||||
SubCallGenLib: CallGenLib.CallGenLib* = CompilerFactory.factory.PermPool.alloc(CallGenLib.CallGenLib.__sizeof__())
|
||||
if SubCallGenLib is None:
|
||||
stdio.printf("FAIL: CallGenLib alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
|
||||
SubCallGenLib.Gen = self
|
||||
self.CallGenLib = SubCallGenLib
|
||||
SubCallGenVt: CallGenVt.CallGenVt* = CompilerFactory.factory.PermPool.alloc(CallGenVt.CallGenVt.__sizeof__())
|
||||
if SubCallGenVt is None:
|
||||
stdio.printf("FAIL: CallGenVt alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
|
||||
SubCallGenVt.Gen = self
|
||||
self.CallGenVt = SubCallGenVt
|
||||
SubCallGenVa: CallGenVa.CallGenVa* = CompilerFactory.factory.PermPool.alloc(CallGenVa.CallGenVa.__sizeof__())
|
||||
if SubCallGenVa is None:
|
||||
stdio.printf("FAIL: CallGenVa alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
|
||||
SubCallGenVa.Gen = self
|
||||
self.CallGenVa = SubCallGenVa
|
||||
SubCallGenProp: CallGenProp.CallGenProp* = CompilerFactory.factory.PermPool.alloc(CallGenProp.CallGenProp.__sizeof__())
|
||||
if SubCallGenProp is None:
|
||||
stdio.printf("FAIL: CallGenProp alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
|
||||
SubCallGenProp.Gen = self
|
||||
self.CallGenProp = SubCallGenProp
|
||||
SubCallGenFd: CallGenFd.CallGenFd* = CompilerFactory.factory.PermPool.alloc(CallGenFd.CallGenFd.__sizeof__())
|
||||
if SubCallGenFd is None:
|
||||
stdio.printf("FAIL: CallGenFd alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
|
||||
SubCallGenFd.Gen = self
|
||||
self.CallGenFd = SubCallGenFd
|
||||
SubFuncGenBase: FuncGenBase.FuncGenBase* = CompilerFactory.factory.PermPool.alloc(FuncGenBase.FuncGenBase.__sizeof__())
|
||||
if SubFuncGenBase is None:
|
||||
stdio.printf("FAIL: FuncGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
|
||||
SubFuncGenBase.Gen = self
|
||||
self.FuncGenBase = SubFuncGenBase
|
||||
SubFuncGenDef: FuncGenDef.FuncGenDef* = CompilerFactory.factory.PermPool.alloc(FuncGenDef.FuncGenDef.__sizeof__())
|
||||
if SubFuncGenDef is None:
|
||||
stdio.printf("FAIL: FuncGenDef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
|
||||
SubFuncGenDef.Gen = self
|
||||
self.FuncGenDef = SubFuncGenDef
|
||||
SubFuncGen: FuncGenMixin.FuncGenMixin* = CompilerFactory.factory.PermPool.alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
|
||||
if SubFuncGen is None:
|
||||
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
|
||||
SubFuncGen.Gen = self
|
||||
self.FuncGen = SubFuncGen
|
||||
SubClassGenBase: ClassGenBase.ClassGenBase* = CompilerFactory.factory.PermPool.alloc(ClassGenBase.ClassGenBase.__sizeof__())
|
||||
if SubClassGenBase is None:
|
||||
stdio.printf("FAIL: ClassGenBase alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
|
||||
SubClassGenBase.Gen = self
|
||||
self.ClassGenBase = SubClassGenBase
|
||||
SubClassGen: ClassGenMixin.ClassGenMixin* = CompilerFactory.factory.PermPool.alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
|
||||
if SubClassGen is None:
|
||||
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
|
||||
SubClassGen.Gen = self
|
||||
self.ClassGen = SubClassGen
|
||||
SubClassGenEnum: ClassGenEnum.ClassGenEnum* = CompilerFactory.factory.PermPool.alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
|
||||
if SubClassGenEnum is None:
|
||||
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
|
||||
SubClassGenEnum.Gen = self
|
||||
self.ClassGenEnum = SubClassGenEnum
|
||||
SubClassGenGeneric: ClassGenGeneric.ClassGenGeneric* = CompilerFactory.factory.PermPool.alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
||||
if SubClassGenGeneric is None:
|
||||
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
||||
SubClassGenGeneric.Gen = self
|
||||
self.ClassGenGeneric = SubClassGenGeneric
|
||||
SubClassGenVTable: ClassGenVTable.ClassGenVTable* = CompilerFactory.factory.PermPool.alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
|
||||
if SubClassGenVTable is None:
|
||||
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
|
||||
SubClassGenVTable.Gen = self
|
||||
self.ClassGenVTable = SubClassGenVTable
|
||||
SubClassGenDef: ClassGenDef.ClassGenDef* = CompilerFactory.factory.PermPool.alloc(ClassGenDef.ClassGenDef.__sizeof__())
|
||||
if SubClassGenDef is None:
|
||||
stdio.printf("FAIL: ClassGenDef alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
|
||||
SubClassGenDef.Gen = self
|
||||
self.ClassGenDef = SubClassGenDef
|
||||
SubImportsGen: ImportsGenMixin.ImportsGenMixin* = CompilerFactory.factory.PermPool.alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
||||
if SubImportsGen is None:
|
||||
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
||||
SubImportsGen.Gen = self
|
||||
self.ImportsGen = SubImportsGen
|
||||
SubStmtGenCif: StmtGenCifMixin.StmtGenCifMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
||||
if SubStmtGenCif is None:
|
||||
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
||||
SubStmtGenCif.Gen = self
|
||||
self.StmtGenCif = SubStmtGenCif
|
||||
SubStmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
||||
if SubStmtGenDecl is None:
|
||||
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
||||
SubStmtGenDecl.Gen = self
|
||||
self.StmtGenDecl = SubStmtGenDecl
|
||||
SubStmtGen: StmtGenMixin.StmtGenMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
|
||||
if SubStmtGen is None:
|
||||
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
|
||||
SubStmtGen.Gen = self
|
||||
self.StmtGen = SubStmtGen
|
||||
SubMatchGen: MatchGenMixin.MatchGenMixin* = CompilerFactory.factory.PermPool.alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
|
||||
if SubMatchGen is None:
|
||||
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
|
||||
SubMatchGen.Gen = self
|
||||
self.MatchGen = SubMatchGen
|
||||
SubAssignGen: AssignGenMixin.AssignGenMixin* = CompilerFactory.factory.PermPool.alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
|
||||
if SubAssignGen is None:
|
||||
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
|
||||
SubAssignGen.Gen = self
|
||||
self.AssignGen = SubAssignGen
|
||||
SubVarGen: VarGenMixin.VarGenMixin* = CompilerFactory.factory.PermPool.alloc(VarGenMixin.VarGenMixin.__sizeof__())
|
||||
if SubVarGen is None:
|
||||
stdio.printf("FAIL: VarGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
|
||||
SubVarGen.Gen = self
|
||||
self.VarGen = SubVarGen
|
||||
SubTranslatorGen: TranslatorGenMixin.TranslatorGenMixin* = CompilerFactory.factory.PermPool.alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
||||
if SubTranslatorGen is None:
|
||||
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
||||
SubTranslatorGen.Gen = self
|
||||
self.TranslatorGen = SubTranslatorGen
|
||||
SubAsyncGen: AsyncGenMixin.AsyncGenMixin* = CompilerFactory.factory.PermPool.alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
|
||||
if SubAsyncGen is None:
|
||||
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
|
||||
stdlib.exit(1)
|
||||
string.memset(SubAsyncGen, 0, AsyncGenMixin.AsyncGenMixin.__sizeof__())
|
||||
SubAsyncGen.Gen = self
|
||||
self.AsyncGen = SubAsyncGen
|
||||
|
||||
# ---- initialize sub-object fields (Init* methods called inline) ----
|
||||
SubBaseGen.InitBaseGen(pool)
|
||||
SubTypeGenBase.InitTypeGen()
|
||||
SubCallGenBase.InitCallGen()
|
||||
SubStructGen.InitStructGen()
|
||||
SubClassGenBase.InitClassGen()
|
||||
SubExprGenBase.InitExprGen()
|
||||
SubImportsGen.InitImportsGen()
|
||||
SubStmtGenCif.InitStmtGen()
|
||||
SubAssignGen.InitAssignGen()
|
||||
|
||||
|
||||
# global generator singleton (composition-architecture compatibility layer)
|
||||
_g_gen: Generator* = None
|
||||
+465
-598
File diff suppressed because it is too large.
Load diff
+1121
-975
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,474 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.core.Gen.Stmt.StmtGenCifMixin as StmtGenCifMixin
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - StmtGen split 2/3: declaration/enum/nonlocal access
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Constants
|
||||
define ENUM_MAX = 64
|
||||
define ENUM_MEMBER_MAX = 128
|
||||
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - declaration/enum/nonlocal (mid layer)
|
||||
novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
"""Declaration/enum/nonlocal access (StmtGen split 2/3, extends Cif)"""
|
||||
|
||||
def translate_global(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a global statement and record the global variable names"""
|
||||
gn: ast.Global* = ast.Global*(node)
|
||||
if gn is None: return 0
|
||||
names: list[ast.AST*]* = gn.names
|
||||
if names is None: return 0
|
||||
n: sizet = len(names)
|
||||
for i in range(int(n)):
|
||||
nm_node: ast.AST* = names.get(i)
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(nm_node)
|
||||
if nm.id is not None: GenApi.add_global_name(trans, nm.id)
|
||||
return 0
|
||||
|
||||
def translate_nonlocal(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a nonlocal statement and record names with their types"""
|
||||
nl: ast.Nonlocal* = ast.Nonlocal*(node)
|
||||
if nl is None: return 0
|
||||
names: list[ast.AST*]* = nl.names
|
||||
if names is None: return 0
|
||||
n: sizet = len(names)
|
||||
for i in range(int(n)):
|
||||
nm_node: ast.AST* = names.get(i)
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(nm_node)
|
||||
if nm.id is not None:
|
||||
# Look up the variable alloca in the parent scope to get its type
|
||||
var_ty: mlirlite.LLVMType* = None
|
||||
var_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if var_alloca is not None and var_alloca.Ty is not None:
|
||||
var_ty = var_alloca.Ty.Pointee
|
||||
# Treat Void as invalid (Pointee access can return Void); report and abort
|
||||
# rather than emit illegal IR like "bitcast ... to void*" / "load void, void*".
|
||||
if var_ty is not None:
|
||||
match var_ty:
|
||||
case mlirlite.LLVMType.Void():
|
||||
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal variable {nm.id} has an invalid type (Void/unresolvable)", "TypeError")
|
||||
case _:
|
||||
pass
|
||||
GenApi.add_nonlocal_name(trans, nm.id, var_ty)
|
||||
return 0
|
||||
|
||||
def translate_expr_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate an expression statement (e.g. a printf call)"""
|
||||
ex: ast.Expr* = ast.Expr*(node)
|
||||
if ex is None: return 0
|
||||
call_node: ast.AST* = ex.value
|
||||
if call_node is None: return 0
|
||||
ck: int = call_node.kind()
|
||||
if ck != ast.ASTKind.Call:
|
||||
# REPL mode (M2): bare expression statements in a fragment body (e.g. 1+2 / x / x + 1)
|
||||
# are written to the ctx result area. Guarded by trans.ReplCtx; non-REPL behavior unchanged.
|
||||
if trans is not None and trans.ReplCtx is not None and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0: self.Gen.VarGen.repl_expr_result(trans, call_node)
|
||||
return 0
|
||||
|
||||
cl: ast.Call* = ast.Call*(call_node)
|
||||
func_node: ast.AST* = cl.func
|
||||
if func_node is None: return 0
|
||||
|
||||
func_name: str = self.Gen.ExprGenBase.get_func_name(func_node)
|
||||
if func_name is None:
|
||||
# Function name resolution failed: report and abort (fail-fast)
|
||||
self.Gen.TypeGenErr.fatal_error(call_node, "failed to resolve the function name of the expression statement", "SyntaxError")
|
||||
|
||||
# printf / print take special paths (print is type-aware formatting, not mapped to printf)
|
||||
if string.strcmp(func_name, "printf") == 0: self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||
elif string.strcmp(func_name, "print") == 0: self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||
else:
|
||||
# Generic call (result discarded). M3: a bare-name call to a session function also
|
||||
# writes the result to ctx (classified by the session table's return token).
|
||||
if trans is not None and trans.ReplCtx is not None and func_node.kind() == ast.ASTKind.Name and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
|
||||
sfn_ent: GenTypes.ReplFuncEntry* = self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
|
||||
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
|
||||
sfn_v: mlirlite.Value* = self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
|
||||
if sfn_v is not None:
|
||||
ctx_v: mlirlite.Value* = self.Gen.VarGen.repl_ctx_ptr(trans, trans.ReplCtx, call_node)
|
||||
if ctx_v is not None:
|
||||
self.Gen.VarGen.repl_emit_result(trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits, call_node)
|
||||
return 0
|
||||
self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
|
||||
return 0
|
||||
|
||||
def pre_scan_allocas(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Pre-scan AnnAssign statements and create their entry allocas early"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.AnnAssign: return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
|
||||
if k == ast.ASTKind.If:
|
||||
ifn: ast.If* = ast.If*(node)
|
||||
if ifn.children is not None:
|
||||
bn_pre_i: sizet = len(ifn.children)
|
||||
for bi_pre_i in range(int(bn_pre_i)): self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
|
||||
if ifn.orelse is not None:
|
||||
bn_pre_e: sizet = len(ifn.orelse)
|
||||
for bi_pre_e in range(int(bn_pre_e)): self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
|
||||
return 0
|
||||
if k == ast.ASTKind.For:
|
||||
for_n: ast.For* = ast.For*(node)
|
||||
if for_n.children is not None:
|
||||
bn_pre_f: sizet = len(for_n.children)
|
||||
for bi_pre_f in range(int(bn_pre_f)): self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
|
||||
if for_n.orelse is not None:
|
||||
bn_pre_fe: sizet = len(for_n.orelse)
|
||||
for bi_pre_fe in range(int(bn_pre_fe)): self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
|
||||
return 0
|
||||
if k == ast.ASTKind.While:
|
||||
wh_n: ast.While* = ast.While*(node)
|
||||
if wh_n.children is not None:
|
||||
bn_pre_w: sizet = len(wh_n.children)
|
||||
for bi_pre_w in range(int(bn_pre_w)): self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
|
||||
if wh_n.orelse is not None:
|
||||
bn_pre_we: sizet = len(wh_n.orelse)
|
||||
for bi_pre_we in range(int(bn_pre_we)): self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
|
||||
return 0
|
||||
if k == ast.ASTKind.Try:
|
||||
tr_n: ast.Try* = ast.Try*(node)
|
||||
if tr_n.children is not None:
|
||||
bn_pre_t: sizet = len(tr_n.children)
|
||||
for bi_pre_t in range(int(bn_pre_t)): self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
|
||||
if tr_n.handlers is not None:
|
||||
bn_pre_th: sizet = len(tr_n.handlers)
|
||||
for bi_pre_th in range(int(bn_pre_th)):
|
||||
eh_pre: ast.AST* = tr_n.handlers.get(bi_pre_th)
|
||||
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
|
||||
ehc_pre: ast.ExceptHandler* = ast.ExceptHandler*(eh_pre)
|
||||
if ehc_pre.children is not None:
|
||||
bn_pre_ehb: sizet = len(ehc_pre.children)
|
||||
for bi_pre_ehb in range(int(bn_pre_ehb)): self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
|
||||
if tr_n.orelse is not None:
|
||||
bn_pre_to: sizet = len(tr_n.orelse)
|
||||
for bi_pre_to in range(int(bn_pre_to)): self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
|
||||
if tr_n.finalbody is not None:
|
||||
bn_pre_tf: sizet = len(tr_n.finalbody)
|
||||
for bi_pre_tf in range(int(bn_pre_tf)): self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
|
||||
return 0
|
||||
if k == ast.ASTKind.With:
|
||||
wth: ast.With* = ast.With*(node)
|
||||
if wth.children is not None:
|
||||
bn_pre_wb: sizet = len(wth.children)
|
||||
for bi_pre_wb in range(int(bn_pre_wb)): self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
|
||||
return 0
|
||||
if k == ast.ASTKind.Match:
|
||||
mt_n: ast.Match* = ast.Match*(node)
|
||||
if mt_n.cases is not None:
|
||||
bn_pre_mc: sizet = len(mt_n.cases)
|
||||
for bi_pre_mc in range(int(bn_pre_mc)):
|
||||
mc_pre: ast.AST* = mt_n.cases.get(bi_pre_mc)
|
||||
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
|
||||
mcc_pre: ast.MatchCase* = ast.MatchCase*(mc_pre)
|
||||
if mcc_pre.children is not None:
|
||||
bn_pre_mcb: sizet = len(mcc_pre.children)
|
||||
for bi_pre_mcb in range(int(bn_pre_mcb)): self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
|
||||
return 0
|
||||
return 0
|
||||
|
||||
def pre_scan_scope_decls(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Pre-scan Global/Nonlocal declarations and register scope names early"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Global: return self.translate_global(trans, node)
|
||||
if k == ast.ASTKind.Nonlocal: return self.translate_nonlocal(trans, node)
|
||||
return 0
|
||||
|
||||
def translate_break(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a break statement: branch to the break target"""
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
func: mlirlite.Function* = trans._cur_func
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
|
||||
if builder is None or func is None: return 0
|
||||
|
||||
# Emit br to the break target (_break_bb set by the loop / switch / match case side)
|
||||
if trans._break_bb is None:
|
||||
# fail-fast: with no loop / switch / match there is nowhere to jump; never silently no-op
|
||||
self.Gen.TypeGenErr.fatal_error(node, "break is only allowed inside a loop or a switch/match case body (there is no target to break out of)", "SyntaxError")
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._break_bb)
|
||||
|
||||
# Create a dead-code BB for following statements (code after break is unreachable)
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
||||
dead_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||
mlirlite.position_at_end(builder, dead_bb)
|
||||
return 0
|
||||
|
||||
def translate_fallthrough(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a fallthrough statement: emits no IR, consumed by the switch side"""
|
||||
if trans is None: return 0
|
||||
if trans._in_switch_case == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "fallthrough is only allowed inside a switch case body (fallthrough is completely forbidden in match; " "the semantics are falling through to the next case)", "SyntaxError")
|
||||
return 0
|
||||
|
||||
def translate_continue(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a continue statement: branch to the loop cond/incr block"""
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
func: mlirlite.Function* = trans._cur_func
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
|
||||
if builder is None or func is None: return 0
|
||||
|
||||
# Emit br to the continue target
|
||||
if trans._continue_bb is None:
|
||||
# fail-fast: a continue outside a loop has nowhere to jump; never silently no-op
|
||||
self.Gen.TypeGenErr.fatal_error(node, "continue is only allowed inside a loop body (there is no enclosing loop to continue)", "SyntaxError")
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._continue_bb)
|
||||
|
||||
# Create a dead-code BB for following statements
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
||||
dead_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||
mlirlite.position_at_end(builder, dead_bb)
|
||||
return 0
|
||||
|
||||
def get_stmt_kind_name(self, node: ast.AST*) -> str:
|
||||
"""Return the statement type name"""
|
||||
if node is None: return None
|
||||
return node.type_name()
|
||||
|
||||
# ============================================================
|
||||
# Cluster D: nonlocal/closure env access (migrated from HandlesNonlocal.vp)
|
||||
|
||||
def _is_void_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Return 1 if the type is Void, else 0"""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Void():
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
|
||||
def get_env_ptr_var(self, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""Get or create the _env_ptr variable holding the closure env pointer"""
|
||||
env_var: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
|
||||
if env_var is not None: return env_var
|
||||
# Create an alloca to store env
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
alloca: mlirlite.Value* = mlirlite.build_alloca(builder, i8_ptr_ty)
|
||||
if alloca is None: return None
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", alloca)
|
||||
return alloca
|
||||
|
||||
def _load_raw_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""Load the raw pointer (i8*) of a nonlocal variable from the closure env"""
|
||||
idx: int = GenApi.get_nonlocal_index(trans, name)
|
||||
if idx < 0: return None
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||
|
||||
# 1. Load env_ptr
|
||||
env_alloca: mlirlite.Value* = self.get_env_ptr_var(trans)
|
||||
if env_alloca is None: return None
|
||||
env_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, env_alloca)
|
||||
if env_ptr is None: return None
|
||||
|
||||
# 2. GEP to offset (idx * 8) - env is an array of pointers, 8 bytes per slot
|
||||
offset_val: mlirlite.Value* = mlirlite.const_int32(pool, idx * 8)
|
||||
addr: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, env_ptr, offset_val)
|
||||
if addr is None: return None
|
||||
|
||||
# 3. Bitcast to i8** and load the raw pointer
|
||||
ptr_addr: mlirlite.Value* = mlirlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
|
||||
if ptr_addr is None: return None
|
||||
return mlirlite.build_load(builder, i8_ptr_ty, ptr_addr)
|
||||
|
||||
def load_nonlocal_var(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""Load the value of a nonlocal variable from the closure env"""
|
||||
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
|
||||
if raw_ptr is None: return None
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
|
||||
# Get the variable type (from the type info stored at registration)
|
||||
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||
# fail-fast on a missing/invalid type, avoiding illegal IR like "load void, void*"
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal variable {name} is missing its type; cannot load", "TypeError")
|
||||
|
||||
# bitcast raw i8* to var_ty*
|
||||
var_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, var_ty)
|
||||
var_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||
if var_ptr is None: return None
|
||||
return mlirlite.build_load(builder, var_ty, var_ptr)
|
||||
|
||||
def get_nonlocal_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""Get the storage pointer (var_ty*) of a nonlocal variable for store operations"""
|
||||
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
|
||||
if raw_ptr is None: return None
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
|
||||
# Get the variable type
|
||||
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||
# Missing/invalid type aborts (fail-fast) to avoid illegal "bitcast ... to void*" IR
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal variable {name} is missing its type; cannot load", "TypeError")
|
||||
|
||||
# bitcast raw i8* to var_ty*
|
||||
var_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, var_ty)
|
||||
return mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||
|
||||
# ============================================================
|
||||
# Cluster E: enum registry (migrated from HandlesEnum.vp)
|
||||
|
||||
def init_enum_table(self, pool: memhub.MemBuddy*) -> int:
|
||||
"""Initialize the enum registry; returns 1 on success"""
|
||||
if self.EnumTable is not None: return 1
|
||||
|
||||
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
||||
self.EnumTable = CompilerFactory.factory.PermPool.alloc(entry_size * ENUM_MAX)
|
||||
if self.EnumTable is None: return 0
|
||||
string.memset(self.EnumTable, 0, entry_size * ENUM_MAX)
|
||||
self.EnumCount = 0
|
||||
return 1
|
||||
|
||||
def _get_enum_entry(self, i: int) -> GenTypes.EnumEntry*:
|
||||
"""Get the i-th enum entry"""
|
||||
if self.EnumTable is None or i < 0 or i >= ENUM_MAX: return None
|
||||
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
||||
addr: uint64 = uint64(self.EnumTable) + i * entry_size
|
||||
return GenTypes.EnumEntry*(addr)
|
||||
|
||||
def _get_enum_member(self, enum_entry: GenTypes.EnumEntry*, i: int) -> GenTypes.EnumMember*:
|
||||
"""Get the i-th enum member entry"""
|
||||
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX: return None
|
||||
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
||||
addr: uint64 = uint64(enum_entry.Members) + i * member_size
|
||||
return GenTypes.EnumMember*(addr)
|
||||
|
||||
def register_enum(self, pool: memhub.MemBuddy*, name: str, base_ty: mlirlite.LLVMType*) -> GenTypes.EnumEntry*:
|
||||
"""Register an enum type and return its EnumEntry pointer"""
|
||||
if self.init_enum_table(pool) == 0: return None
|
||||
|
||||
# Check whether it is already registered
|
||||
existing: GenTypes.EnumEntry* = self.find_enum(name)
|
||||
if existing is not None: return existing
|
||||
|
||||
if self.EnumCount >= ENUM_MAX:
|
||||
VLogger.error(f"table full, cannot register {name}", "ENUM")
|
||||
return None
|
||||
|
||||
entry: GenTypes.EnumEntry* = self._get_enum_entry(self.EnumCount)
|
||||
if entry is None: return None
|
||||
|
||||
# Allocate the member array (permanent pool: survives mark/reset like EnumTable)
|
||||
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
||||
entry.Members = CompilerFactory.factory.PermPool.alloc(member_size * ENUM_MEMBER_MAX)
|
||||
if entry.Members is None: return None
|
||||
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
|
||||
|
||||
# Copy the class name (permanent-pool deep copy)
|
||||
name_len: sizet = string.strlen(name)
|
||||
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
||||
if name_buf is not None:
|
||||
string.strcpy(name_buf, name)
|
||||
entry.Name = name_buf
|
||||
|
||||
# Deep-copy BaseTy: LLVMType is pool-allocated and would dangle after reset under
|
||||
# StdlibAlloc=1. StdlibAlloc is a StructGenMixin field, so delegate via the module.
|
||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
base_ty_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
|
||||
if base_ty_copy is not None: entry.BaseTy = base_ty_copy
|
||||
else: entry.BaseTy = base_ty
|
||||
else: entry.BaseTy = base_ty
|
||||
entry.MemberCount = 0
|
||||
|
||||
self.EnumCount += 1
|
||||
return entry
|
||||
|
||||
def add_enum_member(self, pool: memhub.MemBuddy*, enum_entry: GenTypes.EnumEntry*, member_name: str, member_val: int64, member_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Add a member to an enum; returns the member index or -1 on failure"""
|
||||
if enum_entry is None or member_name is None or member_ty is None: return -1
|
||||
|
||||
# Dedup: skip an existing member with the same name (cross-module enum merging, e.g.
|
||||
# win32.fileio and fat32_types both define FRESULT).
|
||||
for ei_d in range(int(enum_entry.MemberCount)):
|
||||
ex_d: GenTypes.EnumMember* = self._get_enum_member(enum_entry, ei_d)
|
||||
if ex_d is not None and ex_d.Name is not None:
|
||||
if string.strcmp(ex_d.Name, member_name) == 0: return ei_d
|
||||
|
||||
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
|
||||
VLogger.error(f"member table full for {enum_entry.Name}", "ENUM")
|
||||
return -1
|
||||
|
||||
idx: int = enum_entry.MemberCount
|
||||
me: GenTypes.EnumMember* = self._get_enum_member(enum_entry, idx)
|
||||
if me is None: return -1
|
||||
|
||||
# Copy the member name (permanent-pool deep copy: the member table survives mark/reset)
|
||||
name_len: sizet = string.strlen(member_name)
|
||||
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
||||
if name_buf is not None:
|
||||
string.strcpy(name_buf, member_name)
|
||||
me.Name = name_buf
|
||||
|
||||
me.Value = member_val
|
||||
me.Ty = member_ty
|
||||
|
||||
enum_entry.MemberCount = idx + 1
|
||||
return idx
|
||||
|
||||
def find_enum(self, name: str) -> GenTypes.EnumEntry*:
|
||||
"""Look up an enum by class name; returns EnumEntry or None"""
|
||||
if name is None or self.EnumTable is None: return None
|
||||
for i in range(int(self.EnumCount)):
|
||||
entry: GenTypes.EnumEntry* = self._get_enum_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0: return entry
|
||||
return None
|
||||
|
||||
def lookup_enum_member(self, enum_name: str, member_name: str) -> GenTypes.EnumMember*:
|
||||
"""Look up a member by enum class name and member name; returns EnumMember or None"""
|
||||
if enum_name is None or member_name is None: return None
|
||||
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
|
||||
if entry is None: return None
|
||||
for mi in range(int(entry.MemberCount)):
|
||||
me: GenTypes.EnumMember* = self._get_enum_member(entry, mi)
|
||||
if me is not None and me.Name is not None:
|
||||
if string.strcmp(me.Name, member_name) == 0: return me
|
||||
return None
|
||||
|
||||
def is_enum_class(self, name: str) -> int:
|
||||
"""Return 1 if the class name is a registered enum, else 0"""
|
||||
if name is None: return 0
|
||||
if self.find_enum(name) is not None: return 1
|
||||
return 0
|
||||
|
||||
def get_enum_base_type(self, enum_name: str) -> mlirlite.LLVMType*:
|
||||
"""Get the base type for an enum class name"""
|
||||
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
|
||||
if entry is not None: return entry.BaseTy
|
||||
return None
|
||||
@@ -0,0 +1,634 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.Call.CallGenFd as CallGenFd
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.core.Gen.Stmt.StmtGenDeclMixin as StmtGenDeclMixin
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# StmtGenMixin - statement/flow translation Mixin (P6 object refactor group A, top-level)
|
||||
|
||||
|
||||
# StmtGenMixin - statement dispatch hub (top level)
|
||||
novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
"""Statement/flow translation Mixin (top level, extends Decl)"""
|
||||
|
||||
def translate_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a single statement with a depth guard; returns the number of new variables"""
|
||||
if trans is not None:
|
||||
# --traceback: statement-level DWARF location (no-op outside a DI-attached
|
||||
# define). Every instruction then carries ", !dbg !N".
|
||||
if node is not None and trans._cur_builder is not None:
|
||||
DebugInfoGen.SetStatement(trans.Pool, trans._cur_builder, node.lineno, node.col_offset + 1)
|
||||
# 注意:**不要**用当前语句位置覆盖栈帧(那会把“函数定义帧”与错误帧叠成同一位置、
|
||||
# 被去重合并,反而丢掉 `_start` 那帧)。栈帧保留“被调函数的定义位置”,
|
||||
# “当前语句/具体表达式”由错误帧给出(见 TypeGenErr 的 CurArg 兜底)。
|
||||
# 仅在语句边界清掉上一条语句遗留的实参位置,避免兜底定位串味。
|
||||
if node is not None:
|
||||
self.Gen.TypeGenBase.set_cur_arg(None, 0, 0)
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "statement translation nested too deeply (likely an AST self-reference cycle or a translation bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
mret: int = self._translate_stmt_impl(trans, node)
|
||||
if trans is not None: trans.TranslateDepth -= 1
|
||||
return mret
|
||||
|
||||
def _translate_stmt_impl(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a single statement; returns the number of new variables"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
|
||||
if k == ast.ASTKind.Expr: return self.translate_expr_stmt(trans, node)
|
||||
elif k == ast.ASTKind.Assign: return self.Gen.AssignGen.translate_assign(trans, node)
|
||||
elif k == ast.ASTKind.AnnAssign: return self.Gen.AssignGen.translate_annassign(trans, node)
|
||||
elif k == ast.ASTKind.FunctionDef:
|
||||
# nested function def: hoist to a top-level function and build a closure
|
||||
# 函数体翻译 = 下潜进一个源码上下文:推一帧(def 位置),供 traceback 回溯。
|
||||
nfd_t: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||
tr_nf: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
tr_nl: int = 0
|
||||
if nfd_t is not None: tr_nl = nfd_t.lineno
|
||||
# §13 NLL:记录当前函数体(谓词 NllFrameConfined 要遍历它)
|
||||
prev_nf: ast.AST* = self.Gen.StructGen.NllCurFunc
|
||||
self.Gen.StructGen.NllCurFunc = node
|
||||
self.Gen.TypeGenBase.push_trace(tr_nf, tr_nl)
|
||||
rc_nf: int = self.Gen.FuncGen.translate_nested_function_def(trans, node)
|
||||
self.Gen.TypeGenBase.pop_trace()
|
||||
self.Gen.StructGen.NllCurFunc = prev_nf
|
||||
return rc_nf
|
||||
elif k == ast.ASTKind.Return: return self.Gen.StmtGenCif.translate_return(trans, node)
|
||||
elif k == ast.ASTKind.If: return self.Gen.StmtGenCif.translate_if(trans, node)
|
||||
elif k == ast.ASTKind.While:
|
||||
# 循环生命期汇合:循环可能 0 次执行,出口状态须与进入前状态 Meet(见 §11 控制流 Meet)
|
||||
snap_wh: sizet* = self.Gen.VarGen.life_capture(trans)
|
||||
# willFree 失效槽表:同点位快照,出口与进入前状态**求交**(循环体可能 0 次执行,
|
||||
# 故体内 free 不得被当成"循环后已失效")
|
||||
freed_snap_wh: sizet* = self.Gen.VarGen.freed_capture(trans)
|
||||
rc_wh: int = self.Gen.StmtGenCif.translate_while(trans, node)
|
||||
self.Gen.VarGen.life_join(snap_wh)
|
||||
self.Gen.VarGen.freed_join(freed_snap_wh)
|
||||
return rc_wh
|
||||
elif k == ast.ASTKind.AugAssign: return self.Gen.AssignGen.translate_augassign(trans, node)
|
||||
elif k == ast.ASTKind.For:
|
||||
# 同上:for 的各迭代形态(range/指针/数组/容器/解糖)统一在此收口
|
||||
snap_fr: sizet* = self.Gen.VarGen.life_capture(trans)
|
||||
freed_snap_fr: sizet* = self.Gen.VarGen.freed_capture(trans)
|
||||
rc_fr: int = self.Gen.StmtGenCif.translate_for(trans, node)
|
||||
self.Gen.VarGen.life_join(snap_fr)
|
||||
self.Gen.VarGen.freed_join(freed_snap_fr)
|
||||
return rc_fr
|
||||
elif k == ast.ASTKind.ClassDef: return self.Gen.ClassGenDef.translate_class_def(trans, node)
|
||||
elif k == ast.ASTKind.Import: return self.Gen.ImportsGen.translate_import(trans, node)
|
||||
elif k == ast.ASTKind.ImportFrom:
|
||||
self.Gen.ImportsGen.translate_import_from_module(trans, node)
|
||||
return self.Gen.ImportsGen.translate_import_from_names(trans, node)
|
||||
elif k == ast.ASTKind.Global: return self.translate_global(trans, node)
|
||||
elif k == ast.ASTKind.Nonlocal: return self.translate_nonlocal(trans, node)
|
||||
elif k == ast.ASTKind.Pass: return 0
|
||||
elif k == ast.ASTKind.Break: return self.translate_break(trans, node)
|
||||
elif k == ast.ASTKind.Continue: return self.translate_continue(trans, node)
|
||||
elif k == ast.ASTKind.FallThrough: return self.translate_fallthrough(trans, node)
|
||||
elif k == ast.ASTKind.Match: return self.Gen.MatchGen.translate_match(trans, node)
|
||||
elif k == ast.ASTKind.Delete: return self.translate_delete(trans, node)
|
||||
elif k == ast.ASTKind.Raise: return self.translate_raise(trans, node)
|
||||
elif k == ast.ASTKind.Assert: return self.translate_assert(trans, node)
|
||||
elif k == ast.ASTKind.Try: return self.translate_try(trans, node)
|
||||
elif k == ast.ASTKind.With: return self.translate_with(trans, node)
|
||||
# unknown statement type: fail-fast to expose an upstream parse problem
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"unknown statement type kind={k}", "SyntaxError")
|
||||
return 0
|
||||
|
||||
def translate_delete(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a del statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
dnode: ast.Delete* = ast.Delete*(node)
|
||||
if dnode is None or dnode.targets is None: return 0
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
if pool is None or mod is None or builder is None: return 0
|
||||
tn: sizet = len(dnode.targets)
|
||||
for ti in range(int(tn)):
|
||||
tgt: ast.AST* = dnode.targets.get(ti)
|
||||
if tgt is None: continue
|
||||
# del obj -> __delete__ (matches TPC HandlesDelete.py; no-op if undefined)
|
||||
if tgt.kind() == ast.ASTKind.Name:
|
||||
# §2.4 判据5:slife 对象由编译器独占(出口自动 __del__ + free),手动 del 会让析构跑两次
|
||||
nm_del: ast.Name* = ast.Name*(tgt)
|
||||
if nm_del is not None and nm_del.id is not None:
|
||||
del_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_del.id)
|
||||
if self.Gen.VarGen._SlifeOwned(del_slot) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(tgt, f"cannot `del {nm_del.id}`: it is a compiler-owned `slife` object (freed automatically at scope exit); a manual del would double-free it", "LifetimeError")
|
||||
del_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tgt, None, 0, trans)
|
||||
if del_obj is not None:
|
||||
del_st_nm: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(del_obj)
|
||||
if del_st_nm is not None:
|
||||
del_cls_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, del_st_nm)
|
||||
if del_cls_nm is not None: self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, del_obj, "__delete__", trans)
|
||||
# del obj.attr -> property deleter; falls back to __del__ (matches TPC)
|
||||
elif tgt.kind() == ast.ASTKind.Attribute:
|
||||
del_at: ast.Attribute* = ast.Attribute*(tgt)
|
||||
if del_at is not None:
|
||||
del_prop: int = self.Gen.CallGenProp._try_property_deleter(pool, builder, mod, del_at, trans)
|
||||
if del_prop == 0:
|
||||
del_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, del_at, None, 0, trans)
|
||||
if del_av is not None: self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, del_av, "__del__", trans)
|
||||
# del obj[key] -> __delitem__(obj, key) (matches TPC); ignoring Subscript
|
||||
# targets would silently leave del ineffective.
|
||||
elif tgt.kind() == ast.ASTKind.Subscript:
|
||||
dl_sub: ast.Subscript* = ast.Subscript*(tgt)
|
||||
if dl_sub is not None and dl_sub.value is not None and dl_sub.slice is not None:
|
||||
dl_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, dl_sub.value, None, 0, trans)
|
||||
dl_idx: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, dl_sub.slice, None, 0, trans)
|
||||
if dl_obj is not None and dl_idx is not None:
|
||||
dl_st: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(dl_obj)
|
||||
if dl_st is not None:
|
||||
dl_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, dl_st)
|
||||
if dl_cls is not None:
|
||||
dl_lookup: str = pool.alloc(128)
|
||||
if dl_lookup is not None:
|
||||
viperlib.snprintf(dl_lookup, 128, "%s.__delitem__", dl_cls)
|
||||
dl_fn: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, dl_lookup)
|
||||
if dl_fn is not None:
|
||||
dl_ret: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(dl_fn)
|
||||
dl_fn_name: str = mlirlite.function_get_name(dl_fn)
|
||||
if dl_ret is not None and dl_fn_name is not None:
|
||||
# self param (container) aligned to __delitem__'s first param
|
||||
dl_self: mlirlite.Value* = dl_obj
|
||||
dl_first: mlirlite.Param* = mlirlite.function_get_param_head(dl_fn)
|
||||
if dl_first is not None:
|
||||
dl_self_ty: mlirlite.LLVMType* = mlirlite.param_get_ty(dl_first)
|
||||
if dl_self_ty is not None: dl_self = mlirlite.build_bitcast(builder, dl_obj, dl_self_ty)
|
||||
if dl_self is not None:
|
||||
# param list: self -> key (separate null terminator)
|
||||
dl_self.Next = dl_idx
|
||||
dl_idx.Next = None
|
||||
mlirlite.build_call(builder, dl_fn_name, dl_self, 2, dl_ret, 0)
|
||||
return 0
|
||||
|
||||
# translate_assert - translate `assert cond[, msg]` into a single testcheck.check(...) call:
|
||||
# a true condition is silent (ok_msg=None), a false one reports FAIL via the standard path
|
||||
# (no printf/exit/exception; the checker prints nothing when the condition holds).
|
||||
def translate_assert(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate an assert statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
asn: ast.Assert* = ast.Assert*(node)
|
||||
if asn is None or asn.test is None: return 0
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
if pool is None or builder is None or mod is None: return 0
|
||||
|
||||
# Resolve the message argument: omitted -> "assertion failed". A non-string literal is
|
||||
# rejected here (clear report); a non-string expression is caught by the call's int->ptr rule.
|
||||
msg_node: ast.AST* = asn.msg
|
||||
if msg_node is None:
|
||||
msg_node = ast.Constant(pool, ast.CONST_STR, 0, 0.0, "assertion failed", node.lineno, node.col_offset)
|
||||
if msg_node is None: return 0
|
||||
elif msg_node.kind() == ast.ASTKind.Constant:
|
||||
msg_c: ast.Constant* = ast.Constant*(msg_node)
|
||||
if msg_c is None or msg_c.const_kind != ast.CONST_STR:
|
||||
self.Gen.TypeGenErr.fatal_error(msg_node, "assert message must be a string (str)", "TypeError")
|
||||
return 0
|
||||
|
||||
# Synthesize `testcheck.check(<test>, None, <msg>)` and reuse the normal call translation
|
||||
# (cross-module name resolution + argument coercion stay in one place).
|
||||
none_node: ast.AST* = ast.Constant(pool, ast.CONST_NONE, 0, 0.0, None, node.lineno, node.col_offset)
|
||||
if none_node is None: return 0
|
||||
args_list: list[ast.AST*]* = list[ast.AST*](pool, 8)
|
||||
if args_list is None: return 0
|
||||
args_list.append(asn.test)
|
||||
args_list.append(none_node)
|
||||
args_list.append(msg_node)
|
||||
mod_name_node: ast.AST* = ast.Name(pool, "testcheck", ast.ASTCtx.Load)
|
||||
if mod_name_node is None: return 0
|
||||
check_attr: ast.AST* = ast.Attribute(pool, mod_name_node, "check", ast.ASTCtx.Load)
|
||||
if check_attr is None: return 0
|
||||
call_node: ast.AST* = ast.Call(pool, check_attr, args_list, None)
|
||||
if call_node is None: return 0
|
||||
self.Gen.ExprGen.translate_value(builder, pool, mod, call_node, trans._funcs, trans._func_count, trans)
|
||||
return 0
|
||||
|
||||
# translate_try - try/except translation. Exception = (i32 code, i8* msg);
|
||||
# except X statically expands X and all descendant exception codes.
|
||||
def translate_try(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a try/except statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
tr: ast.Try* = ast.Try*(node)
|
||||
if tr is None: return 0
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
func: mlirlite.Function* = trans._cur_func
|
||||
if pool is None or builder is None or func is None: return 0
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) != 0: return 0
|
||||
|
||||
# ---- 1. exception code/message alloca (current try context) ----
|
||||
i32_ty_t: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ty_t: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_t: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_t)
|
||||
if i32_ty_t is None or i8_ptr_ty_t is None: return 0
|
||||
exception_code: mlirlite.Value* = mlirlite.build_alloca(builder, i32_ty_t)
|
||||
eh_message: mlirlite.Value* = mlirlite.build_alloca(builder, i8_ptr_ty_t)
|
||||
if exception_code is None or eh_message is None: return 0
|
||||
init_code_t: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
init_msg_t: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty_t, "null")
|
||||
if init_code_t is not None: mlirlite.build_store(builder, init_code_t, exception_code)
|
||||
if init_msg_t is not None: mlirlite.build_store(builder, init_msg_t, eh_message)
|
||||
|
||||
# ---- 2. block layout (unique names; create_block does not auto-number) ----
|
||||
lc_t: int = trans._label_counter
|
||||
trans._label_counter = lc_t + 1
|
||||
bb_buf_t: str = pool.alloc(64)
|
||||
if bb_buf_t is None: return 0
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.dispatch.%d", lc_t)
|
||||
dispatch_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.after.%d", lc_t)
|
||||
after_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.unhandled.%d", lc_t)
|
||||
unhandled_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.body.%d", lc_t)
|
||||
try_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
if dispatch_bb is None or after_bb is None or unhandled_bb is None or try_bb is None: return 0
|
||||
else_bb: mlirlite.BasicBlock* = None
|
||||
if tr.orelse is not None and len(tr.orelse) > 0:
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.else.%d", lc_t)
|
||||
else_bb = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
n_handlers_t: sizet = 0
|
||||
if tr.handlers is not None: n_handlers_t = len(tr.handlers)
|
||||
# NOTE: list[ptr](pool, N)'s second arg is elem_size, not capacity; pointer
|
||||
# elements must pass 8, else append strides wrongly and reads garbage.
|
||||
except_bbs: list[mlirlite.BasicBlock*]* = list[mlirlite.BasicBlock*](pool, 8)
|
||||
match_bbs: list[mlirlite.BasicBlock*]* = list[mlirlite.BasicBlock*](pool, 8)
|
||||
if except_bbs is None or match_bbs is None: return 0
|
||||
for i_t in range(int(n_handlers_t)):
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.except.%d.%d", lc_t, i_t)
|
||||
eb_t: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.match.%d.%d", lc_t, i_t)
|
||||
mb_t: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
except_bbs.append(eb_t)
|
||||
match_bbs.append(mb_t)
|
||||
|
||||
# ---- 3. save outer try context, set this try context ----
|
||||
old_dispatch_t: mlirlite.BasicBlock* = trans._eh_dispatch_bb
|
||||
old_code_t: mlirlite.Value* = trans._eh_code_ptr
|
||||
old_msg_t: mlirlite.Value* = trans._eh_msg_ptr
|
||||
trans._eh_dispatch_bb = dispatch_bb
|
||||
trans._eh_code_ptr = exception_code
|
||||
trans._eh_msg_ptr = eh_message
|
||||
|
||||
# ---- 4. try body ----
|
||||
mlirlite.build_br(builder, try_bb)
|
||||
mlirlite.position_at_end(builder, try_bb)
|
||||
if tr.children is not None:
|
||||
tb_n: sizet = len(tr.children)
|
||||
for tbi in range(int(tb_n)):
|
||||
stmt_t: ast.AST* = tr.children.get(tbi)
|
||||
if stmt_t is not None: self.translate_stmt(trans, stmt_t)
|
||||
# try body completed: clear the code, go to else (if any) or After
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
if init_code_t is not None: mlirlite.build_store(builder, init_code_t, exception_code)
|
||||
if else_bb is not None: mlirlite.build_br(builder, else_bb)
|
||||
else: mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 5. DispatchBB: code check chain ----
|
||||
mlirlite.position_at_end(builder, dispatch_bb)
|
||||
if n_handlers_t == 0:
|
||||
# no except (invalid try; defensive): swallow
|
||||
mlirlite.build_br(builder, unhandled_bb)
|
||||
else:
|
||||
eh_code_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty_t, exception_code)
|
||||
next_check_t: mlirlite.BasicBlock* = None
|
||||
for i_t in range(int(n_handlers_t)):
|
||||
hdr_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
|
||||
match_bb_t: mlirlite.BasicBlock* = match_bbs.get(i_t)
|
||||
if i_t > 0 and next_check_t is not None:
|
||||
mlirlite.position_at_end(builder, next_check_t)
|
||||
eh_code_val = mlirlite.build_load(builder, i32_ty_t, exception_code)
|
||||
if hdr_t is None or hdr_t.type is None:
|
||||
# bare except: unconditional match
|
||||
mlirlite.build_br(builder, match_bb_t)
|
||||
else:
|
||||
# expand the match code set (handler.type + all descendant classes)
|
||||
hname_t: str = None
|
||||
htype_t: ast.AST* = hdr_t.type
|
||||
if htype_t.kind() == ast.ASTKind.Name: hname_t = (ast.Name*(htype_t)).id
|
||||
elif htype_t.kind() == ast.ASTKind.Attribute: hname_t = (ast.Attribute*(htype_t)).attr
|
||||
# match code buffer must be int* (i32 stride): collect_descendant_codes
|
||||
# writes 4-byte entries; reading as sizet* yields garbage.
|
||||
codes_buf_t: int* = pool.alloc(64 * 4)
|
||||
if codes_buf_t is None: return 0
|
||||
n_codes_t: int = 0
|
||||
if hname_t is not None and self.Gen.ClassGenBase.is_exception_name(hname_t) != 0: n_codes_t = self.Gen.ClassGenBase.collect_descendant_codes(hname_t, codes_buf_t, 64)
|
||||
else:
|
||||
c0_t: int = 1
|
||||
if hname_t is not None:
|
||||
c0_t = self.Gen.ClassGenBase.lookup_exception_code(hname_t)
|
||||
if c0_t == 0: c0_t = 1
|
||||
codes_buf_t[0] = c0_t
|
||||
n_codes_t = 1
|
||||
match_cond_t: mlirlite.Value* = None
|
||||
for ci_t in range(int(n_codes_t)):
|
||||
code_val_t: mlirlite.Value* = mlirlite.const_int32(pool, codes_buf_t[ci_t])
|
||||
is_match_t: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, eh_code_val, code_val_t)
|
||||
if match_cond_t is None: match_cond_t = is_match_t
|
||||
elif is_match_t is not None: match_cond_t = mlirlite.build_or(builder, match_cond_t, is_match_t)
|
||||
if i_t < n_handlers_t - 1:
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.check.%d.%d", lc_t, i_t)
|
||||
next_check_t = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
else: next_check_t = unhandled_bb
|
||||
mlirlite.build_cond_br(builder, match_cond_t, match_bb_t, next_check_t)
|
||||
# MatchBB: bind handler var (e = message ptr), jump to ExceptBB
|
||||
mlirlite.position_at_end(builder, match_bb_t)
|
||||
if hdr_t is not None and hdr_t.name is not None and hdr_t.name[0] != '\0':
|
||||
# Same-named handler vars reuse the entry alloca to avoid a non-
|
||||
# dominating alloca ("does not dominate all uses").
|
||||
h_ent_t: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, hdr_t.name)
|
||||
if h_ent_t is not None and h_ent_t.Alloca is not None: h_alloca_t: mlirlite.Value* = h_ent_t.Alloca
|
||||
else:
|
||||
h_alloca_t = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_t)
|
||||
if h_alloca_t is not None: self.Gen.VarGen.define_var(trans.SymTab, hdr_t.name, h_alloca_t)
|
||||
if h_alloca_t is not None:
|
||||
h_msg_t: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty_t, eh_message)
|
||||
if h_msg_t is not None: mlirlite.build_store(builder, h_msg_t, h_alloca_t)
|
||||
except_bb_t: mlirlite.BasicBlock* = except_bbs.get(i_t)
|
||||
mlirlite.build_br(builder, except_bb_t)
|
||||
|
||||
# ---- 6. UnhandledBB: no except matched -> propagate or swallow ----
|
||||
mlirlite.position_at_end(builder, unhandled_bb)
|
||||
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None and trans._eh_code_out_param is not None:
|
||||
un_msg_t: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty_t, eh_message)
|
||||
un_code_t: mlirlite.Value* = mlirlite.build_load(builder, i32_ty_t, exception_code)
|
||||
if un_msg_t is not None: mlirlite.build_store(builder, un_msg_t, trans._eh_msg_out_param)
|
||||
if un_code_t is not None: mlirlite.build_store(builder, un_code_t, trans._eh_code_out_param)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
else:
|
||||
if init_code_t is not None: mlirlite.build_store(builder, init_code_t, exception_code)
|
||||
mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 7. except bodies ----
|
||||
for i_t in range(int(n_handlers_t)):
|
||||
hdr2_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
|
||||
except_bb2_t: mlirlite.BasicBlock* = except_bbs.get(i_t)
|
||||
mlirlite.position_at_end(builder, except_bb2_t)
|
||||
if hdr2_t is not None and hdr2_t.children is not None:
|
||||
hn2_t: sizet = len(hdr2_t.children)
|
||||
for hi2 in range(int(hn2_t)):
|
||||
stmt2_t: ast.AST* = hdr2_t.children.get(hi2)
|
||||
if stmt2_t is not None: self.translate_stmt(trans, stmt2_t)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 8. else body ----
|
||||
if else_bb is not None:
|
||||
mlirlite.position_at_end(builder, else_bb)
|
||||
if tr.orelse is not None:
|
||||
en_t: sizet = len(tr.orelse)
|
||||
for ei in range(int(en_t)):
|
||||
stmt3_t: ast.AST* = tr.orelse.get(ei)
|
||||
if stmt3_t is not None: self.translate_stmt(trans, stmt3_t)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 9. AfterBB + restore outer try context ----
|
||||
mlirlite.position_at_end(builder, after_bb)
|
||||
trans._eh_dispatch_bb = old_dispatch_t
|
||||
trans._eh_code_ptr = old_code_t
|
||||
trans._eh_msg_ptr = old_msg_t
|
||||
return 0
|
||||
|
||||
def translate_raise(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a raise statement and dispatch the exception; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
if pool is None or builder is None or mod is None: return 0
|
||||
rn: ast.Raise* = ast.Raise*(node)
|
||||
if rn is None or rn.exc is None:
|
||||
# bare raise (no exc): v1 has no active exception; just return default
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# OOP iter protocol: raise StopIteration -> set __stop_iter_flag__ = 1, then
|
||||
# return the default value.
|
||||
ek: int = rn.exc.kind()
|
||||
is_stop_iter: int = 0
|
||||
if ek == ast.ASTKind.Name:
|
||||
enm: ast.Name* = ast.Name*(rn.exc)
|
||||
if enm.id is not None and string.strcmp(enm.id, "StopIteration") == 0: is_stop_iter = 1
|
||||
elif ek == ast.ASTKind.Call:
|
||||
ecl: ast.Call* = ast.Call*(rn.exc)
|
||||
if ecl.func is not None and ecl.func.kind() == ast.ASTKind.Name:
|
||||
ecn: ast.Name* = ast.Name*(ecl.func)
|
||||
if ecn.id is not None and string.strcmp(ecn.id, "StopIteration") == 0: is_stop_iter = 1
|
||||
if is_stop_iter != 0:
|
||||
if trans._stop_iter_flag_ptr is not None:
|
||||
i1_ty_r: mlirlite.LLVMType* = mlirlite.Int1(pool)
|
||||
if i1_ty_r is not None:
|
||||
one_i1_r: mlirlite.Value* = mlirlite.ConstInt(pool, i1_ty_r, 1, "1")
|
||||
if one_i1_r is not None: mlirlite.build_store(builder, one_i1_r, trans._stop_iter_flag_ptr)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# ---- non-StopIteration: compute exception code and message ----
|
||||
exc_code: int = 1
|
||||
exc_msg: mlirlite.Value* = None
|
||||
if ek == ast.ASTKind.Constant:
|
||||
cst_r: ast.Constant* = ast.Constant*(rn.exc)
|
||||
if cst_r is not None and cst_r.const_kind == ast.CONST_INT: exc_code = int(cst_r.int_val)
|
||||
elif ek == ast.ASTKind.Name:
|
||||
enm2: ast.Name* = ast.Name*(rn.exc)
|
||||
if enm2.id is not None:
|
||||
exc_code = self.Gen.ClassGenBase.lookup_exception_code(enm2.id)
|
||||
if exc_code == 0: exc_code = 1
|
||||
elif ek == ast.ASTKind.Call:
|
||||
ecl2: ast.Call* = ast.Call*(rn.exc)
|
||||
if ecl2.func is not None and ecl2.func.kind() == ast.ASTKind.Name:
|
||||
ecn2: ast.Name* = ast.Name*(ecl2.func)
|
||||
if ecn2.id is not None:
|
||||
exc_code = self.Gen.ClassGenBase.lookup_exception_code(ecn2.id)
|
||||
if exc_code == 0: exc_code = 1
|
||||
if ecl2.args is not None and len(ecl2.args) > 0: exc_msg = self.Gen.ExprGen.translate_value(builder, pool, mod, ecl2.args.get(0), None, 0, trans)
|
||||
|
||||
# ---- exception dispatch ----
|
||||
# 1. inside try: write to the try's eh_code/eh_message alloca, branch to DispatchBB
|
||||
if trans._eh_dispatch_bb is not None and trans._eh_code_ptr is not None:
|
||||
code_val_r: mlirlite.Value* = mlirlite.const_int32(pool, exc_code)
|
||||
if code_val_r is not None: mlirlite.build_store(builder, code_val_r, trans._eh_code_ptr)
|
||||
if exc_msg is None: exc_msg = mlirlite.ConstNull(pool, mlirlite.Ptr(pool, mlirlite.Int8(pool)), "null")
|
||||
if exc_msg is not None and trans._eh_msg_ptr is not None: mlirlite.build_store(builder, exc_msg, trans._eh_msg_ptr)
|
||||
mlirlite.build_br(builder, trans._eh_dispatch_bb)
|
||||
return 0
|
||||
# 2. function has EH out params: write them (propagate to caller)
|
||||
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None and trans._eh_code_out_param is not None:
|
||||
code_val_r2: mlirlite.Value* = mlirlite.const_int32(pool, exc_code)
|
||||
if code_val_r2 is not None: mlirlite.build_store(builder, code_val_r2, trans._eh_code_out_param)
|
||||
if exc_msg is None: exc_msg = mlirlite.ConstNull(pool, mlirlite.Ptr(pool, mlirlite.Int8(pool)), "null")
|
||||
if exc_msg is not None: mlirlite.build_store(builder, exc_msg, trans._eh_msg_out_param)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
# 3. no try and no EH params: just return the default
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# _eh_ret_default - return the function's default zero value on the exception
|
||||
# path (void->ret_void, ptr->null, float->0.0, else zero); skipped if terminated.
|
||||
def _eh_ret_default(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*) -> int:
|
||||
"""Return the current function's default zero value on the exception path"""
|
||||
if trans is None or builder is None: return 0
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) != 0: return 0
|
||||
cur_func: mlirlite.Function* = trans._cur_func
|
||||
if cur_func is None: return 0
|
||||
ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(cur_func)
|
||||
if ret_ty is None: return 0
|
||||
# P1:异常/传播出口**也是函数出口** —— 这里是"异常路径上真正离开函数"的唯一落点
|
||||
# (translate_raise 的三条分支 + try 的 UnhandledBB 传播分支都汇聚到它),所以在此逆序释放
|
||||
# 本函数登记的 slife。**不能**改在 raise 语句本身释放:被本函数 except 捕获时对象仍在用
|
||||
# (那一步只 br 到 DispatchBB,不走这里);同理 EH 返回值不会是 slife 对象(逃逸检查已拒)。
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, trans.Module, trans)
|
||||
match ret_ty:
|
||||
case mlirlite.LLVMType.Void():
|
||||
mlirlite.build_ret_void(builder)
|
||||
case mlirlite.LLVMType.Ptr(_pe):
|
||||
null_v: mlirlite.Value* = mlirlite.ConstNull(pool, ret_ty, "null")
|
||||
if null_v is not None: mlirlite.build_ret(builder, null_v)
|
||||
case mlirlite.LLVMType.Float(_fb):
|
||||
zero_f: mlirlite.Value* = mlirlite.ConstFloat(pool, ret_ty, 0.0)
|
||||
if zero_f is not None: mlirlite.build_ret(builder, zero_f)
|
||||
case _:
|
||||
zero_v: mlirlite.Value* = mlirlite.ConstZero(pool, ret_ty)
|
||||
if zero_v is not None: mlirlite.build_ret(builder, zero_v)
|
||||
return 0
|
||||
|
||||
# translate_with - with statement translation: build context, call __enter__,
|
||||
# bind asname, translate body, then call __exit__ in reverse order.
|
||||
def translate_with(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate a with statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
wt: ast.With* = ast.With*(node)
|
||||
if wt is None: return 0
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
if pool is None or builder is None or mod is None: return 0
|
||||
items: list[ast.AST*]* = wt.items
|
||||
if items is None: return 0
|
||||
n: sizet = len(items)
|
||||
|
||||
# save the outer __with_ctx (restored after nested with exits)
|
||||
old_ctx_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "__with_ctx")
|
||||
|
||||
# record each item's class name (used to type __with_ctx on __exit__)
|
||||
ctx_class_buf: sizet* = pool.alloc(8 * n)
|
||||
if ctx_class_buf is None: return 0
|
||||
string.memset(ctx_class_buf, 0, 8 * n)
|
||||
|
||||
# 1. all items: build context, call __enter__, bind asname
|
||||
for i in range(int(n)):
|
||||
item_node: ast.AST* = items.get(i)
|
||||
if item_node is None: continue
|
||||
if item_node.kind() != ast.ASTKind.WithItem: continue
|
||||
item: ast.WithItem* = ast.WithItem*(item_node)
|
||||
if item is None or item.context_expr is None: continue
|
||||
# translate context_expr (object construction, e.g. MemPool(arena, 4096))
|
||||
ctx_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, item.context_expr, trans._funcs, trans._func_count, trans)
|
||||
if ctx_val is None: continue
|
||||
# Extract the class name (module-prefixed for Attribute, e.g. "memhub.MemPool");
|
||||
# cross-module calls fall back to AnnotClassName by module name.
|
||||
cls_name: str = None
|
||||
ce: ast.AST* = item.context_expr
|
||||
if ce.kind() == ast.ASTKind.Call:
|
||||
cfn: ast.AST* = (ast.Call*(ce)).func
|
||||
if cfn is not None:
|
||||
if cfn.kind() == ast.ASTKind.Attribute:
|
||||
ca: ast.Attribute* = ast.Attribute*(cfn)
|
||||
if ca.value is not None and ca.value.kind() == ast.ASTKind.Name:
|
||||
cmn: ast.Name* = ast.Name*(ca.value)
|
||||
if cmn.id is not None:
|
||||
ml2: sizet = string.strlen(cmn.id) + string.strlen(ca.attr) + 2
|
||||
mbuf: str = pool.alloc(ml2)
|
||||
if mbuf is not None:
|
||||
viperlib.snprintf(mbuf, ml2, "%s.%s", cmn.id, ca.attr)
|
||||
cls_name = mbuf
|
||||
if cls_name is None: cls_name = ca.attr
|
||||
elif cfn.kind() == ast.ASTKind.Name: cls_name = (ast.Name*(cfn)).id
|
||||
# store the context object in __with_ctx (used to resolve self)
|
||||
ctx_alloca: mlirlite.Value* = mlirlite.build_alloca(builder, ctx_val.Ty)
|
||||
if ctx_alloca is None: continue
|
||||
mlirlite.build_store(builder, ctx_val, ctx_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", ctx_alloca)
|
||||
if cls_name is not None:
|
||||
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_name)
|
||||
ctx_class_buf[i] = sizet(cls_name)
|
||||
# call __enter__()
|
||||
enter_val: mlirlite.Value* = self._with_ctx_call(trans, pool, builder, mod, "__enter__")
|
||||
# bind asname (__enter__ return value, or the context object on miss)
|
||||
if item.optional_vars is not None:
|
||||
ov: ast.AST* = item.optional_vars
|
||||
if ov.kind() == ast.ASTKind.Name:
|
||||
asname: str = (ast.Name*(ov)).id
|
||||
if asname is not None:
|
||||
bind_val: mlirlite.Value* = enter_val
|
||||
if bind_val is None: bind_val = ctx_val
|
||||
if bind_val is not None:
|
||||
asn_alloca: mlirlite.Value* = mlirlite.build_alloca(builder, bind_val.Ty)
|
||||
if asn_alloca is not None:
|
||||
mlirlite.build_store(builder, bind_val, asn_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, asname, asn_alloca)
|
||||
if cls_name is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, asname, cls_name)
|
||||
# FakeDuck: __provides__ metadata -> emit a _push_provider call (matches TPC);
|
||||
# the class name is de-qualified before the __provides__ lookup.
|
||||
fd_cls_bare: str = cls_name
|
||||
if cls_name is not None:
|
||||
fd_dot: str = string.strrchr(cls_name, '.')
|
||||
if fd_dot is not None: fd_cls_bare = fd_dot + 1
|
||||
if fd_cls_bare is not None: self.Gen.CallGenFd._emit_provider_push(trans, pool, builder, mod, fd_cls_bare, ctx_val)
|
||||
|
||||
# 2. translate body
|
||||
body: list[ast.AST*]* = wt.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
for bi in range(int(bn)):
|
||||
st: ast.AST* = body.get(bi)
|
||||
if st is not None: self.translate_stmt(trans, st)
|
||||
|
||||
# 2.5 FakeDuck: __provides__ metadata -> emit _pop_provider calls in reverse.
|
||||
for pi_pop in range(int(n)):
|
||||
if ctx_class_buf[pi_pop] != 0:
|
||||
cls_pop: str = rawptr(ctx_class_buf[pi_pop])
|
||||
fd_cls_bare_p: str = cls_pop
|
||||
if cls_pop is not None:
|
||||
fd_dot_p: str = string.strrchr(cls_pop, '.')
|
||||
if fd_dot_p is not None: fd_cls_bare_p = fd_dot_p + 1
|
||||
if fd_cls_bare_p is not None: self.Gen.CallGenFd._emit_provider_pop(trans, pool, builder, mod, fd_cls_bare_p)
|
||||
|
||||
# 3. call __exit__() in reverse order (Python with semantics: LIFO)
|
||||
for i in range(int(n)):
|
||||
ci: int = int(n - 1 - i)
|
||||
if ctx_class_buf[ci] != 0:
|
||||
cls_ci: str = rawptr(ctx_class_buf[ci])
|
||||
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_ci)
|
||||
self._with_ctx_call(trans, pool, builder, mod, "__exit__")
|
||||
|
||||
# 4. restore the outer __with_ctx
|
||||
if old_ctx_alloca is not None: self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", old_ctx_alloca)
|
||||
return 0
|
||||
|
||||
def _with_ctx_call(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, method_name: str) -> mlirlite.Value*:
|
||||
"""Build and translate a __with_ctx.<method_name>() call (__enter__/__exit__)"""
|
||||
if pool is None or method_name is None: return None
|
||||
name_node: ast.AST* = ast.Name(pool, "__with_ctx", ast.ASTCtx.Load)
|
||||
if name_node is None: return None
|
||||
attr_node: ast.AST* = ast.Attribute(pool, name_node, method_name, ast.ASTCtx.Load)
|
||||
if attr_node is None: return None
|
||||
call_node: ast.AST* = ast.Call(pool, attr_node, None, None)
|
||||
if call_node is None: return None
|
||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, call_node, trans._funcs, trans._func_count, trans)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,590 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.core.Gen.StmtGenCifMixin as StmtGenCifMixin
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - StmtGen 拆分 2/3:声明/枚举/nonlocal 访问
|
||||
#
|
||||
# 按功能域从 StmtGenMixin.vp 拆出(500~1500 行/文件架构重构,试点):
|
||||
# translate_global/nonlocal/expr_stmt/pre_scan、break/continue、
|
||||
# nonlocal 闭包 env 访问、枚举注册表。继承 StmtGenCifMixin。
|
||||
#
|
||||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量(与 HandlesEnum 保持一致,本地副本避免跨模块 CDefine 查找 bug)
|
||||
# ============================================================
|
||||
ENUM_MAX: t.CDefine = 64
|
||||
ENUM_MEMBER_MAX: t.CDefine = 128
|
||||
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - 声明/枚举/nonlocal(中层级)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
"""声明/枚举/nonlocal 访问(StmtGen 拆分 2/3,继承 Cif)"""
|
||||
|
||||
def translate_global(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 global 语句:记录 global 变量名"""
|
||||
gn: ast.Global | t.CPtr = (ast.Global | t.CPtr)(node)
|
||||
if gn is None:
|
||||
return 0
|
||||
names: list[ast.AST | t.CPtr] | t.CPtr = gn.names
|
||||
if names is None:
|
||||
return 0
|
||||
n: t.CSizeT = len(names)
|
||||
for i in range(n):
|
||||
nm_node: ast.AST | t.CPtr = names.get(i)
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node)
|
||||
if nm.id is not None:
|
||||
HT.add_global_name(trans, nm.id)
|
||||
return 0
|
||||
|
||||
def translate_nonlocal(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 nonlocal 语句:记录 nonlocal 变量名及其类型
|
||||
|
||||
类型信息通过查找父作用域的 alloca 获取(用于 env 指针数组方案的类型恢复)。
|
||||
"""
|
||||
nl: ast.Nonlocal | t.CPtr = (ast.Nonlocal | t.CPtr)(node)
|
||||
if nl is None:
|
||||
return 0
|
||||
names: list[ast.AST | t.CPtr] | t.CPtr = nl.names
|
||||
if names is None:
|
||||
return 0
|
||||
n: t.CSizeT = len(names)
|
||||
for i in range(n):
|
||||
nm_node: ast.AST | t.CPtr = names.get(i)
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node)
|
||||
if nm.id is not None:
|
||||
# 查找父作用域中的变量 alloca,获取类型信息
|
||||
var_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
var_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
|
||||
trans.SymTab, nm.id)
|
||||
if var_alloca is not None and var_alloca.Ty is not None:
|
||||
var_ty = var_alloca.Ty.Pointee
|
||||
# Void 类型视为无效(Pointee 访问异常时可能返回 Void),
|
||||
# 类型无效直接报错终止(fail-fast),避免生成
|
||||
# "bitcast ... to void*" / "load void, void*" 非法 IR
|
||||
if var_ty is not None:
|
||||
match var_ty:
|
||||
case llvmlite.LLVMType.Void():
|
||||
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal 变量 '{nm.id}' 类型无效(Void/无法解析)", "TypeError")
|
||||
case _:
|
||||
pass
|
||||
HT.add_nonlocal_name(trans, nm.id, var_ty)
|
||||
return 0
|
||||
|
||||
def translate_expr_stmt(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译表达式语句(printf 调用等)"""
|
||||
ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node)
|
||||
if ex is None:
|
||||
return 0
|
||||
call_node: ast.AST | t.CPtr = ex.value
|
||||
if call_node is None:
|
||||
return 0
|
||||
ck: int = call_node.kind()
|
||||
if ck != ast.ASTKind.Call:
|
||||
return 0
|
||||
|
||||
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(call_node)
|
||||
func_node: ast.AST | t.CPtr = cl.func
|
||||
if func_node is None:
|
||||
return 0
|
||||
|
||||
func_name: str = self.Gen.ExprGenBase.get_func_name(func_node)
|
||||
if func_name is None:
|
||||
# 函数名解析失败:直接报错终止(fail-fast)
|
||||
self.Gen.TypeGenErr.fatal_error(call_node, "表达式语句的函数名解析失败", "SyntaxError")
|
||||
|
||||
# printf / print 走特殊路径(print 是类型感知格式化,不映射到 printf)
|
||||
if string.strcmp(func_name, "printf") == 0:
|
||||
self.Gen.CallGenLeafKit.translate_printf_call(
|
||||
trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||
elif string.strcmp(func_name, "print") == 0:
|
||||
self.Gen.CallGenLeafKit.translate_print_call(
|
||||
trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||
else:
|
||||
# 通用函数调用(返回值丢弃)
|
||||
self.Gen.CallGen.translate_call(
|
||||
trans.Pool, trans._cur_builder, trans.Module, call_node,
|
||||
trans._funcs, trans._func_count, trans)
|
||||
return 0
|
||||
|
||||
def pre_scan_allocas(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""预扫描语句中的 AnnAssign,提前创建 alloca
|
||||
|
||||
递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部
|
||||
也必须提前注册到 entry alloca——否则闭包隐式捕获扫描(step 8c)
|
||||
在分支局部未注册时会误判为外层函数捕获(Terminal _build_path 的
|
||||
i/j/cwd_len 被当 nonlocal 捕获 → 循环计数器走 env/父函数存储 →
|
||||
跨函数 SSA 错乱,llc "instruction expected to be numbered")。
|
||||
|
||||
返回新增的变量数(调用方均忽略返回值)。
|
||||
"""
|
||||
if node is None:
|
||||
return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.AnnAssign:
|
||||
return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
|
||||
if k == ast.ASTKind.If:
|
||||
ifn: ast.If | t.CPtr = (ast.If | t.CPtr)(node)
|
||||
if ifn.children is not None:
|
||||
bn_pre_i: t.CSizeT = len(ifn.children)
|
||||
for bi_pre_i in range(bn_pre_i):
|
||||
self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
|
||||
if ifn.orelse is not None:
|
||||
bn_pre_e: t.CSizeT = len(ifn.orelse)
|
||||
for bi_pre_e in range(bn_pre_e):
|
||||
self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
|
||||
return 0
|
||||
if k == ast.ASTKind.For:
|
||||
for_n: ast.For | t.CPtr = (ast.For | t.CPtr)(node)
|
||||
if for_n.children is not None:
|
||||
bn_pre_f: t.CSizeT = len(for_n.children)
|
||||
for bi_pre_f in range(bn_pre_f):
|
||||
self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
|
||||
if for_n.orelse is not None:
|
||||
bn_pre_fe: t.CSizeT = len(for_n.orelse)
|
||||
for bi_pre_fe in range(bn_pre_fe):
|
||||
self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
|
||||
return 0
|
||||
if k == ast.ASTKind.While:
|
||||
wh_n: ast.While | t.CPtr = (ast.While | t.CPtr)(node)
|
||||
if wh_n.children is not None:
|
||||
bn_pre_w: t.CSizeT = len(wh_n.children)
|
||||
for bi_pre_w in range(bn_pre_w):
|
||||
self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
|
||||
if wh_n.orelse is not None:
|
||||
bn_pre_we: t.CSizeT = len(wh_n.orelse)
|
||||
for bi_pre_we in range(bn_pre_we):
|
||||
self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
|
||||
return 0
|
||||
if k == ast.ASTKind.Try:
|
||||
tr_n: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node)
|
||||
if tr_n.children is not None:
|
||||
bn_pre_t: t.CSizeT = len(tr_n.children)
|
||||
for bi_pre_t in range(bn_pre_t):
|
||||
self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
|
||||
if tr_n.handlers is not None:
|
||||
bn_pre_th: t.CSizeT = len(tr_n.handlers)
|
||||
for bi_pre_th in range(bn_pre_th):
|
||||
eh_pre: ast.AST | t.CPtr = tr_n.handlers.get(bi_pre_th)
|
||||
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
|
||||
ehc_pre: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(eh_pre)
|
||||
if ehc_pre.children is not None:
|
||||
bn_pre_ehb: t.CSizeT = len(ehc_pre.children)
|
||||
for bi_pre_ehb in range(bn_pre_ehb):
|
||||
self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
|
||||
if tr_n.orelse is not None:
|
||||
bn_pre_to: t.CSizeT = len(tr_n.orelse)
|
||||
for bi_pre_to in range(bn_pre_to):
|
||||
self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
|
||||
if tr_n.finalbody is not None:
|
||||
bn_pre_tf: t.CSizeT = len(tr_n.finalbody)
|
||||
for bi_pre_tf in range(bn_pre_tf):
|
||||
self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
|
||||
return 0
|
||||
if k == ast.ASTKind.With:
|
||||
wth: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
|
||||
if wth.children is not None:
|
||||
bn_pre_wb: t.CSizeT = len(wth.children)
|
||||
for bi_pre_wb in range(bn_pre_wb):
|
||||
self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
|
||||
return 0
|
||||
if k == ast.ASTKind.Match:
|
||||
mt_n: ast.Match | t.CPtr = (ast.Match | t.CPtr)(node)
|
||||
if mt_n.cases is not None:
|
||||
bn_pre_mc: t.CSizeT = len(mt_n.cases)
|
||||
for bi_pre_mc in range(bn_pre_mc):
|
||||
mc_pre: ast.AST | t.CPtr = mt_n.cases.get(bi_pre_mc)
|
||||
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
|
||||
mcc_pre: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(mc_pre)
|
||||
if mcc_pre.children is not None:
|
||||
bn_pre_mcb: t.CSizeT = len(mcc_pre.children)
|
||||
for bi_pre_mcb in range(bn_pre_mcb):
|
||||
self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
|
||||
return 0
|
||||
return 0
|
||||
|
||||
def pre_scan_scope_decls(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""预扫描 Global/Nonlocal 声明,提前注册作用域名称"""
|
||||
if node is None:
|
||||
return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Global:
|
||||
return self.translate_global(trans, node)
|
||||
if k == ast.ASTKind.Nonlocal:
|
||||
return self.translate_nonlocal(trans, node)
|
||||
return 0
|
||||
|
||||
def translate_break(self, trans: HT.Translator | t.CPtr) -> int:
|
||||
"""翻译 break 语句:跳转到循环 end 块"""
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
func: llvmlite.Function | t.CPtr = trans._cur_func
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
|
||||
if builder is None or func is None:
|
||||
return 0
|
||||
|
||||
# 发射 br 到 break 目标
|
||||
if trans._break_bb is not None:
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, trans._break_bb)
|
||||
|
||||
# 创建死代码 BB,用于后续语句(break 后面的代码不可达)
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
||||
dead_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||||
llvmlite.position_at_end(builder, dead_bb)
|
||||
return 0
|
||||
|
||||
def translate_continue(self, trans: HT.Translator | t.CPtr) -> int:
|
||||
"""翻译 continue 语句:跳转到循环 cond/incr 块"""
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
func: llvmlite.Function | t.CPtr = trans._cur_func
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
|
||||
if builder is None or func is None:
|
||||
return 0
|
||||
|
||||
# 发射 br 到 continue 目标
|
||||
if trans._continue_bb is not None:
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, trans._continue_bb)
|
||||
|
||||
# 创建死代码 BB,用于后续语句
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
||||
dead_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||||
llvmlite.position_at_end(builder, dead_bb)
|
||||
return 0
|
||||
|
||||
def get_stmt_kind_name(self, node: ast.AST | t.CPtr) -> str:
|
||||
"""获取语句类型名"""
|
||||
if node is None:
|
||||
return None
|
||||
return node.type_name()
|
||||
|
||||
# ============================================================
|
||||
# 簇 D:nonlocal/闭包 env 访问(迁移自 HandlesNonlocal.vp)
|
||||
# ============================================================
|
||||
|
||||
def _is_void_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""判断是否为 Void 类型,是则返回 1,否则 0"""
|
||||
if ty is None:
|
||||
return 0
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Void():
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
|
||||
def get_env_ptr_var(self, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""获取或创建 _env_ptr 变量(存储闭包 env 指针)"""
|
||||
env_var: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
|
||||
trans.SymTab, "_env_ptr")
|
||||
if env_var is not None:
|
||||
return env_var
|
||||
# 创建 alloca 存储 env
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i8_ptr_ty)
|
||||
if alloca is None:
|
||||
return None
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", alloca)
|
||||
return alloca
|
||||
|
||||
def _load_raw_var_ptr(self, trans: HT.Translator | t.CPtr,
|
||||
name: str) -> llvmlite.Value | t.CPtr:
|
||||
"""从闭包 env 加载 nonlocal 变量的原始指针(i8*)
|
||||
|
||||
返回 i8* 类型的 Value,调用方需 bitcast 为具体类型指针后 load/store。
|
||||
"""
|
||||
idx: int = HT.get_nonlocal_index(trans, name)
|
||||
if idx < 0:
|
||||
return None
|
||||
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
|
||||
|
||||
# 1. 加载 env_ptr
|
||||
env_alloca: llvmlite.Value | t.CPtr = self.get_env_ptr_var(trans)
|
||||
if env_alloca is None:
|
||||
return None
|
||||
env_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
|
||||
if env_ptr is None:
|
||||
return None
|
||||
|
||||
# 2. GEP to offset (idx * 8) — env 存储指针数组,每槽 8 字节
|
||||
offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, idx * 8)
|
||||
addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
|
||||
if addr is None:
|
||||
return None
|
||||
|
||||
# 3. Bitcast to i8** and load the raw pointer
|
||||
ptr_addr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
|
||||
if ptr_addr is None:
|
||||
return None
|
||||
return llvmlite.build_load(builder, i8_ptr_ty, ptr_addr)
|
||||
|
||||
def load_nonlocal_var(self, trans: HT.Translator | t.CPtr,
|
||||
name: str) -> llvmlite.Value | t.CPtr:
|
||||
"""从闭包 env 加载 nonlocal 变量值
|
||||
|
||||
通过 HT.get_nonlocal_type 获取变量类型,bitcast 原始指针后 load。
|
||||
"""
|
||||
raw_ptr: llvmlite.Value | t.CPtr = self._load_raw_var_ptr(trans, name)
|
||||
if raw_ptr is None:
|
||||
return None
|
||||
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
|
||||
# 获取变量类型(从注册时存储的类型信息)
|
||||
var_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, name)
|
||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "load void, void*" 非法 IR
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
|
||||
|
||||
# bitcast raw i8* to var_ty*
|
||||
var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty)
|
||||
var_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||
if var_ptr is None:
|
||||
return None
|
||||
return llvmlite.build_load(builder, var_ty, var_ptr)
|
||||
|
||||
def get_nonlocal_var_ptr(self, trans: HT.Translator | t.CPtr,
|
||||
name: str) -> llvmlite.Value | t.CPtr:
|
||||
"""获取 nonlocal 变量的存储指针(var_ty*),用于 store 操作
|
||||
|
||||
返回的指针指向父函数的 alloca,store 操作对父函数可见。
|
||||
"""
|
||||
raw_ptr: llvmlite.Value | t.CPtr = self._load_raw_var_ptr(trans, name)
|
||||
if raw_ptr is None:
|
||||
return None
|
||||
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
|
||||
# 获取变量类型
|
||||
var_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, name)
|
||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "bitcast ... to void*" 非法 IR
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
|
||||
|
||||
# bitcast raw i8* to var_ty*
|
||||
var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty)
|
||||
return llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||
|
||||
# ============================================================
|
||||
# 簇 E:枚举注册表(迁移自 HandlesEnum.vp)
|
||||
# ============================================================
|
||||
|
||||
def init_enum_table(self, pool: memhub.MemBuddy | t.CPtr) -> int:
|
||||
"""初始化枚举注册表,返回 1 成功
|
||||
|
||||
表用 stdlib 分配(跨 Phase1 1b 的 mark/reset_to 存活——枚举表是
|
||||
跨文件持久表,pool 分配会被 reset 回收导致 Phase B 读垃圾,
|
||||
enum_count 出现 512 等垃圾值 / entry.Name 解引用崩溃)。
|
||||
"""
|
||||
if self.EnumTable is not None:
|
||||
return 1
|
||||
|
||||
entry_size: t.CSizeT = GenTypes.EnumEntry.__sizeof__()
|
||||
self.EnumTable = CompilerFactory.get_perm_pool().alloc(entry_size * ENUM_MAX)
|
||||
if self.EnumTable is None:
|
||||
return 0
|
||||
string.memset(self.EnumTable, 0, entry_size * ENUM_MAX)
|
||||
self.EnumCount = 0
|
||||
return 1
|
||||
|
||||
def _get_enum_entry(self, i: int) -> GenTypes.EnumEntry | t.CPtr:
|
||||
"""获取第 i 个枚举条目"""
|
||||
if self.EnumTable is None or i < 0 or i >= ENUM_MAX:
|
||||
return None
|
||||
entry_size: t.CSizeT = GenTypes.EnumEntry.__sizeof__()
|
||||
addr: t.CUInt64T = t.CUInt64T(self.EnumTable) + i * entry_size
|
||||
return (GenTypes.EnumEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||||
|
||||
def _get_enum_member(self, enum_entry: GenTypes.EnumEntry | t.CPtr,
|
||||
i: int) -> GenTypes.EnumMember | t.CPtr:
|
||||
"""获取枚举中第 i 个成员条目"""
|
||||
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX:
|
||||
return None
|
||||
member_size: t.CSizeT = GenTypes.EnumMember.__sizeof__()
|
||||
addr: t.CUInt64T = t.CUInt64T(enum_entry.Members) + i * member_size
|
||||
return (GenTypes.EnumMember | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||||
|
||||
def register_enum(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
name: str,
|
||||
base_ty: llvmlite.LLVMType | t.CPtr) -> GenTypes.EnumEntry | t.CPtr:
|
||||
"""注册枚举类型,返回 EnumEntry 指针"""
|
||||
if self.init_enum_table(pool) == 0:
|
||||
return None
|
||||
|
||||
# 检查是否已注册
|
||||
existing: GenTypes.EnumEntry | t.CPtr = self.find_enum(name)
|
||||
if existing is not None:
|
||||
return existing
|
||||
|
||||
if self.EnumCount >= ENUM_MAX:
|
||||
fb_et: str = VLogger.fmt_buf()
|
||||
if fb_et is not None:
|
||||
viperlib.snprintf(fb_et, 1024, "table full, cannot register %s", name)
|
||||
VLogger.error(fb_et, "ENUM")
|
||||
return None
|
||||
|
||||
entry: GenTypes.EnumEntry | t.CPtr = self._get_enum_entry(self.EnumCount)
|
||||
if entry is None:
|
||||
return None
|
||||
|
||||
# 分配成员数组(永久池:跨 mark/reset 存活,对齐 EnumTable 永久池化)
|
||||
member_size: t.CSizeT = GenTypes.EnumMember.__sizeof__()
|
||||
entry.Members = CompilerFactory.get_perm_pool().alloc(member_size * ENUM_MEMBER_MAX)
|
||||
if entry.Members is None:
|
||||
return None
|
||||
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
|
||||
|
||||
# 复制类名(永久池深拷贝)
|
||||
name_len: t.CSizeT = string.strlen(name)
|
||||
name_buf: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
|
||||
if name_buf is not None:
|
||||
string.strcpy(name_buf, name)
|
||||
entry.Name = name_buf
|
||||
|
||||
# BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1(Phase2 mark2 后)
|
||||
# 时 reset 回收悬空 → Phase B 枚举访问 ConstInt(悬空 BaseTy) 生成 void
|
||||
# 值(DecoratorMarkerTest `store void 2` llc 报错根因)。Phase1 1a
|
||||
# (StdlibAlloc=0,mark1 前)保持 pool 存活,无需拷贝。
|
||||
# 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接
|
||||
# self.StdlibAlloc(NoVTable 按本 Mixin 字段布局编译),走模块级委托。
|
||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
base_ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
|
||||
CompilerFactory.get_perm_pool(), base_ty)
|
||||
if base_ty_copy is not None:
|
||||
entry.BaseTy = base_ty_copy
|
||||
else:
|
||||
entry.BaseTy = base_ty
|
||||
else:
|
||||
entry.BaseTy = base_ty
|
||||
entry.MemberCount = 0
|
||||
|
||||
self.EnumCount += 1
|
||||
return entry
|
||||
|
||||
def add_enum_member(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
enum_entry: GenTypes.EnumEntry | t.CPtr,
|
||||
member_name: str,
|
||||
member_val: t.CInt64T,
|
||||
member_ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""向枚举添加成员,返回成员索引(-1 失败)"""
|
||||
if enum_entry is None or member_name is None or member_ty is None:
|
||||
return -1
|
||||
|
||||
# 去重:同名成员已存在则跳过(跨模块同名枚举合并注册 / 同模块
|
||||
# 重复处理幂等——如 w32.fileio 与 fat32_types 都有 FRESULT)
|
||||
for ei_d in range(enum_entry.MemberCount):
|
||||
ex_d: GenTypes.EnumMember | t.CPtr = self._get_enum_member(
|
||||
enum_entry, ei_d)
|
||||
if ex_d is not None and ex_d.Name is not None:
|
||||
if string.strcmp(ex_d.Name, member_name) == 0:
|
||||
return ei_d
|
||||
|
||||
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
|
||||
fb_em: str = VLogger.fmt_buf()
|
||||
if fb_em is not None:
|
||||
viperlib.snprintf(fb_em, 1024, "member table full for %s", enum_entry.Name)
|
||||
VLogger.error(fb_em, "ENUM")
|
||||
return -1
|
||||
|
||||
idx: int = enum_entry.MemberCount
|
||||
me: GenTypes.EnumMember | t.CPtr = self._get_enum_member(enum_entry, idx)
|
||||
if me is None:
|
||||
return -1
|
||||
|
||||
# 复制成员名(永久池深拷贝:成员表跨 mark/reset 存活)
|
||||
name_len: t.CSizeT = string.strlen(member_name)
|
||||
name_buf: str = CompilerFactory.get_perm_pool().alloc(name_len + 1)
|
||||
if name_buf is not None:
|
||||
string.strcpy(name_buf, member_name)
|
||||
me.Name = name_buf
|
||||
|
||||
me.Value = member_val
|
||||
me.Ty = member_ty
|
||||
|
||||
enum_entry.MemberCount = idx + 1
|
||||
return idx
|
||||
|
||||
def find_enum(self, name: str) -> GenTypes.EnumEntry | t.CPtr:
|
||||
"""按枚举类名查找,返回 EnumEntry 或 None"""
|
||||
if name is None or self.EnumTable is None:
|
||||
return None
|
||||
for i in range(self.EnumCount):
|
||||
entry: GenTypes.EnumEntry | t.CPtr = self._get_enum_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0:
|
||||
return entry
|
||||
return None
|
||||
|
||||
def lookup_enum_member(self, enum_name: str,
|
||||
member_name: str) -> GenTypes.EnumMember | t.CPtr:
|
||||
"""按枚举类名和成员名查找,返回 EnumMember 或 None"""
|
||||
if enum_name is None or member_name is None:
|
||||
return None
|
||||
entry: GenTypes.EnumEntry | t.CPtr = self.find_enum(enum_name)
|
||||
if entry is None:
|
||||
return None
|
||||
for mi in range(entry.MemberCount):
|
||||
me: GenTypes.EnumMember | t.CPtr = self._get_enum_member(entry, mi)
|
||||
if me is not None and me.Name is not None:
|
||||
if string.strcmp(me.Name, member_name) == 0:
|
||||
return me
|
||||
return None
|
||||
|
||||
def is_enum_class(self, name: str) -> int:
|
||||
"""检查类名是否为已注册枚举,返回 1=是 / 0=否"""
|
||||
if name is None:
|
||||
return 0
|
||||
if self.find_enum(name) is not None:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def get_enum_base_type(self, enum_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""按枚举类名获取基准类型"""
|
||||
entry: GenTypes.EnumEntry | t.CPtr = self.find_enum(enum_name)
|
||||
if entry is not None:
|
||||
return entry.BaseTy
|
||||
return None
|
||||
@@ -1,758 +0,0 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.CallGenFd as CallGenFd
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.core.Gen.StmtGenDeclMixin as StmtGenDeclMixin
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenMixin - 语句/流程翻译 Mixin(P6 对象化重构 A 组,拆分后顶层)
|
||||
#
|
||||
# 按功能域拆分(500~1500 行/文件架构重构,试点):
|
||||
# 顶层保留语句分派中枢簇 A(translate_stmt/try/raise/with/delete/
|
||||
# _eh_ret_default/_with_ctx_call);global/nonlocal/枚举/CIf 已拆至
|
||||
# StmtGenDeclMixin / StmtGenCifMixin(本类继承链 1/3、2/3)。
|
||||
# 外部引用(Generator.vp import + HandlesBody 委托)零改动。
|
||||
#
|
||||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenMixin - 语句分派中枢(顶层)
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
"""语句/流程翻译 Mixin(拆分后顶层,继承 Decl)"""
|
||||
|
||||
def translate_stmt(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译单条语句,返回新增的变量数(深度守卫包装器,原逻辑见 _translate_stmt_impl)"""
|
||||
if trans is not None:
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
mret: int = self._translate_stmt_impl(trans, node)
|
||||
if trans is not None:
|
||||
trans.TranslateDepth -= 1
|
||||
return mret
|
||||
|
||||
def _translate_stmt_impl(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译单条语句,返回新增的变量数"""
|
||||
if node is None:
|
||||
return 0
|
||||
k: int = node.kind()
|
||||
|
||||
if k == ast.ASTKind.Expr:
|
||||
return self.translate_expr_stmt(trans, node)
|
||||
elif k == ast.ASTKind.Assign:
|
||||
return self.Gen.AssignGen.translate_assign(trans, node)
|
||||
elif k == ast.ASTKind.AnnAssign:
|
||||
return self.Gen.AssignGen.translate_annassign(trans, node)
|
||||
elif k == ast.ASTKind.FunctionDef:
|
||||
# 嵌套函数定义:提升为顶层函数 + 创建闭包
|
||||
return self.Gen.FuncGen.translate_nested_function_def(trans, node)
|
||||
elif k == ast.ASTKind.Return:
|
||||
return self.Gen.StmtGenCif.translate_return(trans, node)
|
||||
elif k == ast.ASTKind.If:
|
||||
return self.Gen.StmtGenCif.translate_if(trans, node)
|
||||
elif k == ast.ASTKind.While:
|
||||
return self.Gen.StmtGenCif.translate_while(trans, node)
|
||||
elif k == ast.ASTKind.AugAssign:
|
||||
return self.Gen.AssignGen.translate_augassign(trans, node)
|
||||
elif k == ast.ASTKind.For:
|
||||
return self.Gen.StmtGenCif.translate_for(trans, node)
|
||||
elif k == ast.ASTKind.ClassDef:
|
||||
return self.Gen.ClassGenDef.translate_class_def(trans, node)
|
||||
elif k == ast.ASTKind.Import:
|
||||
return self.Gen.ImportsGen.translate_import(trans, node)
|
||||
elif k == ast.ASTKind.ImportFrom:
|
||||
self.Gen.ImportsGen.translate_import_from_module(trans, node)
|
||||
return self.Gen.ImportsGen.translate_import_from_names(trans, node)
|
||||
elif k == ast.ASTKind.Global:
|
||||
return self.translate_global(trans, node)
|
||||
elif k == ast.ASTKind.Nonlocal:
|
||||
return self.translate_nonlocal(trans, node)
|
||||
elif k == ast.ASTKind.Pass:
|
||||
return 0
|
||||
elif k == ast.ASTKind.Break:
|
||||
return self.translate_break(trans)
|
||||
elif k == ast.ASTKind.Continue:
|
||||
return self.translate_continue(trans)
|
||||
elif k == ast.ASTKind.Match:
|
||||
return self.Gen.MatchGen.translate_match(trans, node)
|
||||
elif k == ast.ASTKind.Delete:
|
||||
return self.translate_delete(trans, node)
|
||||
elif k == ast.ASTKind.Raise:
|
||||
return self.translate_raise(trans, node)
|
||||
elif k == ast.ASTKind.Try:
|
||||
return self.translate_try(trans, node)
|
||||
elif k == ast.ASTKind.With:
|
||||
return self.translate_with(trans, node)
|
||||
# 未知语句类型:直接报错终止(fail-fast),暴露上游解析问题
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"未知语句类型 kind={k}", "SyntaxError")
|
||||
return 0
|
||||
|
||||
def translate_delete(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 del 语句,返回 0"""
|
||||
if trans is None or node is None:
|
||||
return 0
|
||||
dnode: ast.Delete | t.CPtr = (ast.Delete | t.CPtr)(node)
|
||||
if dnode is None or dnode.targets is None:
|
||||
return 0
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
if pool is None or mod is None or builder is None:
|
||||
return 0
|
||||
tn: t.CSizeT = len(dnode.targets)
|
||||
for ti in range(tn):
|
||||
tgt: ast.AST | t.CPtr = dnode.targets.get(ti)
|
||||
if tgt is None:
|
||||
continue
|
||||
# del obj.attr → property deleter
|
||||
if tgt.kind() == ast.ASTKind.Attribute:
|
||||
del_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(tgt)
|
||||
if del_at is not None:
|
||||
self.Gen.CallGenProp._try_property_deleter(
|
||||
pool, builder, mod, del_at, trans)
|
||||
# del obj[key] → __delitem__(obj, key)
|
||||
# (对齐 TPC HandlesDelete.py L14-25;此前 Subscript 目标被静默
|
||||
# 忽略,HashTableTest Test 11 的 del ht3[del_buf] 未执行 → len 不变)
|
||||
elif tgt.kind() == ast.ASTKind.Subscript:
|
||||
dl_sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(tgt)
|
||||
if dl_sub is not None and dl_sub.value is not None and dl_sub.slice is not None:
|
||||
dl_obj: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, dl_sub.value, None, 0, trans)
|
||||
dl_idx: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, dl_sub.slice, None, 0, trans)
|
||||
if dl_obj is not None and dl_idx is not None:
|
||||
dl_st: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type_from_value(
|
||||
dl_obj)
|
||||
if dl_st is not None:
|
||||
dl_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, dl_st)
|
||||
if dl_cls is not None:
|
||||
dl_lookup: str = pool.alloc(128)
|
||||
if dl_lookup is not None:
|
||||
viperlib.snprintf(dl_lookup, 128, "%s.__delitem__", dl_cls)
|
||||
dl_fn: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module(
|
||||
mod, dl_lookup)
|
||||
if dl_fn is not None:
|
||||
dl_ret: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(dl_fn)
|
||||
dl_fn_name: str = llvmlite.function_get_name(dl_fn)
|
||||
if dl_ret is not None and dl_fn_name is not None:
|
||||
# self 参数(容器)按 __delitem__ 首参类型对齐
|
||||
dl_self: llvmlite.Value | t.CPtr = dl_obj
|
||||
dl_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(dl_fn)
|
||||
if dl_first is not None:
|
||||
dl_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(dl_first)
|
||||
if dl_self_ty is not None:
|
||||
dl_self = llvmlite.build_bitcast(builder, dl_obj, dl_self_ty)
|
||||
if dl_self is not None:
|
||||
# 参数链表: self → key(独立 null 结尾)
|
||||
dl_self.Next = dl_idx
|
||||
dl_idx.Next = None
|
||||
llvmlite.build_call(
|
||||
builder, dl_fn_name, dl_self, 2, dl_ret, 0)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_try - try/except 语句翻译
|
||||
#
|
||||
# 对齐 TPC HandlesTry.py L59-214(v1 迁移)。异常 = (i32 code, i8* msg)
|
||||
# 二元组;except X 编译期静态展开 X 及全部子孙异常类编码。布局:
|
||||
# pre → TryBB → DispatchBB(编码检查链)→ MatchBB → ExceptBB → AfterBB
|
||||
# try 体正常结束 → store 0 →(else 若有)→ AfterBB
|
||||
# DispatchBB 无匹配 → UnhandledBB:函数有 EH 输出参数则写入并返回
|
||||
# (向上传播),否则 store 0 → AfterBB(吞掉)
|
||||
# try 上下文(DispatchBB + 两个 alloca)写入 trans._eh_*,translate_raise
|
||||
# 与调用侧 EH 检测(CallGenMixin._emit_eh_guarded_call)据此重定向;
|
||||
# 退出时恢复外层上下文(嵌套 try 支持)。
|
||||
# v1 未实现 finalbody(TestFBProject 未使用;finally+return 交互复杂,
|
||||
# 后续按 TPC HandlesTry.py L188-214 的 pending_ret 方案扩展)。
|
||||
# ============================================================
|
||||
def translate_try(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 try/except 语句,返回 0"""
|
||||
if trans is None or node is None:
|
||||
return 0
|
||||
tr: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node)
|
||||
if tr is None:
|
||||
return 0
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
func: llvmlite.Function | t.CPtr = trans._cur_func
|
||||
if pool is None or builder is None or func is None:
|
||||
return 0
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) != 0:
|
||||
return 0
|
||||
|
||||
# ---- 1. 异常编码/消息 alloca(当前 try 上下文)----
|
||||
i32_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
i8_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_t)
|
||||
if i32_ty_t is None or i8_ptr_ty_t is None:
|
||||
return 0
|
||||
exception_code: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i32_ty_t)
|
||||
eh_message: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i8_ptr_ty_t)
|
||||
if exception_code is None or eh_message is None:
|
||||
return 0
|
||||
init_code_t: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
init_msg_t: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty_t, "null")
|
||||
if init_code_t is not None:
|
||||
llvmlite.build_store(builder, init_code_t, exception_code)
|
||||
if init_msg_t is not None:
|
||||
llvmlite.build_store(builder, init_msg_t, eh_message)
|
||||
|
||||
# ---- 2. 块布局(块名唯一化,create_block 不自动编号)----
|
||||
lc_t: int = trans._label_counter
|
||||
trans._label_counter = lc_t + 1
|
||||
bb_buf_t: str = pool.alloc(64)
|
||||
if bb_buf_t is None:
|
||||
return 0
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.dispatch.%d", lc_t)
|
||||
dispatch_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.after.%d", lc_t)
|
||||
after_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.unhandled.%d", lc_t)
|
||||
unhandled_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.body.%d", lc_t)
|
||||
try_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
if dispatch_bb is None or after_bb is None or unhandled_bb is None or try_bb is None:
|
||||
return 0
|
||||
else_bb: llvmlite.BasicBlock | t.CPtr = None
|
||||
if tr.orelse is not None and len(tr.orelse) > 0:
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.else.%d", lc_t)
|
||||
else_bb = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
n_handlers_t: t.CSizeT = 0
|
||||
if tr.handlers is not None:
|
||||
n_handlers_t = len(tr.handlers)
|
||||
# 注意:list[ptr](pool, N) 第二参数是 elem_size 不是容量!
|
||||
# 指针元素必须显式传 8(T.__sizeof__ 对联合类型不可靠),
|
||||
# 否则 append 按 N 字节步长错位存取 → 读出垃圾指针(崩溃)。
|
||||
except_bbs: list[llvmlite.BasicBlock | t.CPtr] | t.CPtr = \
|
||||
list[llvmlite.BasicBlock | t.CPtr](pool, 8)
|
||||
match_bbs: list[llvmlite.BasicBlock | t.CPtr] | t.CPtr = \
|
||||
list[llvmlite.BasicBlock | t.CPtr](pool, 8)
|
||||
if except_bbs is None or match_bbs is None:
|
||||
return 0
|
||||
for i_t in range(n_handlers_t):
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.except.%d.%d", lc_t, i_t)
|
||||
eb_t: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.match.%d.%d", lc_t, i_t)
|
||||
mb_t: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
except_bbs.append(eb_t)
|
||||
match_bbs.append(mb_t)
|
||||
|
||||
# ---- 3. 保存外层 try 上下文,设置本 try 上下文 ----
|
||||
old_dispatch_t: llvmlite.BasicBlock | t.CPtr = trans._eh_dispatch_bb
|
||||
old_code_t: llvmlite.Value | t.CPtr = trans._eh_code_ptr
|
||||
old_msg_t: llvmlite.Value | t.CPtr = trans._eh_msg_ptr
|
||||
trans._eh_dispatch_bb = dispatch_bb
|
||||
trans._eh_code_ptr = exception_code
|
||||
trans._eh_msg_ptr = eh_message
|
||||
|
||||
# ---- 4. Try 体 ----
|
||||
llvmlite.build_br(builder, try_bb)
|
||||
llvmlite.position_at_end(builder, try_bb)
|
||||
if tr.children is not None:
|
||||
tb_n: t.CSizeT = len(tr.children)
|
||||
for tbi in range(tb_n):
|
||||
stmt_t: ast.AST | t.CPtr = tr.children.get(tbi)
|
||||
if stmt_t is not None:
|
||||
self.translate_stmt(trans, stmt_t)
|
||||
# 正常走完 try 体:清异常码,进 else(若有)或 After
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
if init_code_t is not None:
|
||||
llvmlite.build_store(builder, init_code_t, exception_code)
|
||||
if else_bb is not None:
|
||||
llvmlite.build_br(builder, else_bb)
|
||||
else:
|
||||
llvmlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 5. DispatchBB:编码检查链 ----
|
||||
llvmlite.position_at_end(builder, dispatch_bb)
|
||||
if n_handlers_t == 0:
|
||||
# 无 except(非法 try,防御):直接吞掉
|
||||
llvmlite.build_br(builder, unhandled_bb)
|
||||
else:
|
||||
eh_code_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, i32_ty_t, exception_code)
|
||||
next_check_t: llvmlite.BasicBlock | t.CPtr = None
|
||||
for i_t in range(n_handlers_t):
|
||||
hdr_t: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(
|
||||
tr.handlers.get(i_t))
|
||||
match_bb_t: llvmlite.BasicBlock | t.CPtr = match_bbs.get(i_t)
|
||||
if i_t > 0 and next_check_t is not None:
|
||||
llvmlite.position_at_end(builder, next_check_t)
|
||||
eh_code_val = llvmlite.build_load(
|
||||
builder, i32_ty_t, exception_code)
|
||||
if hdr_t is None or hdr_t.type is None:
|
||||
# 裸 except:无条件匹配
|
||||
llvmlite.build_br(builder, match_bb_t)
|
||||
else:
|
||||
# 展开匹配编码集(handler.type 自身 + 全部子孙异常类)
|
||||
hname_t: str = None
|
||||
htype_t: ast.AST | t.CPtr = hdr_t.type
|
||||
if htype_t.kind() == ast.ASTKind.Name:
|
||||
hname_t = ((ast.Name | t.CPtr)(htype_t)).id
|
||||
elif htype_t.kind() == ast.ASTKind.Attribute:
|
||||
hname_t = ((ast.Attribute | t.CPtr)(htype_t)).attr
|
||||
# 匹配编码缓冲:必须是 t.CInt*(i32 步长)——collect_descendant_codes
|
||||
# 按 out_codes[i](4 字节)写入;若用 t.CSizeT*(8 字节)读,
|
||||
# 多码时读出 (code[i+1]<<32)|code[i] 的 64 位拼接垃圾(单码时
|
||||
# 高位恰为 0 掩盖;Test 2/3/8 父类多码捕获失败的根因)。
|
||||
codes_buf_t: t.CInt | t.CPtr = pool.alloc(64 * 4)
|
||||
if codes_buf_t is None:
|
||||
return 0
|
||||
n_codes_t: int = 0
|
||||
if hname_t is not None and self.Gen.ClassGenBase.is_exception_name(hname_t) != 0:
|
||||
n_codes_t = self.Gen.ClassGenBase.collect_descendant_codes(
|
||||
hname_t, codes_buf_t, 64)
|
||||
else:
|
||||
c0_t: int = 1
|
||||
if hname_t is not None:
|
||||
c0_t = self.Gen.ClassGenBase.lookup_exception_code(hname_t)
|
||||
if c0_t == 0:
|
||||
c0_t = 1
|
||||
codes_buf_t[0] = c0_t
|
||||
n_codes_t = 1
|
||||
match_cond_t: llvmlite.Value | t.CPtr = None
|
||||
for ci_t in range(n_codes_t):
|
||||
code_val_t: llvmlite.Value | t.CPtr = llvmlite.const_int32(
|
||||
pool, codes_buf_t[ci_t])
|
||||
is_match_t: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_EQ, eh_code_val, code_val_t)
|
||||
if match_cond_t is None:
|
||||
match_cond_t = is_match_t
|
||||
elif is_match_t is not None:
|
||||
match_cond_t = llvmlite.build_or(
|
||||
builder, match_cond_t, is_match_t)
|
||||
if i_t < n_handlers_t - 1:
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.check.%d.%d", lc_t, i_t)
|
||||
next_check_t = llvmlite.create_block(pool, func, bb_buf_t)
|
||||
else:
|
||||
next_check_t = unhandled_bb
|
||||
llvmlite.build_cond_br(
|
||||
builder, match_cond_t, match_bb_t, next_check_t)
|
||||
# MatchBB:绑定 handler 变量(e = 消息指针),跳 ExceptBB
|
||||
llvmlite.position_at_end(builder, match_bb_t)
|
||||
if hdr_t is not None and hdr_t.name is not None and hdr_t.name[0] != '\0':
|
||||
# 同名 handler 变量复用函数入口 alloca(find_var 首条命中、
|
||||
# 重复 define_var 不 shadow——多 try 共用 `as e` 时各 MatchBB
|
||||
# 各自 store 消息,各 ExceptBB 读同一入口 alloca,避免
|
||||
# 非支配 alloca 触发 "does not dominate all uses")。
|
||||
h_ent_t: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
|
||||
trans.SymTab, hdr_t.name)
|
||||
if h_ent_t is not None and h_ent_t.Alloca is not None:
|
||||
h_alloca_t: llvmlite.Value | t.CPtr = h_ent_t.Alloca
|
||||
else:
|
||||
h_alloca_t = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_t)
|
||||
if h_alloca_t is not None:
|
||||
self.Gen.VarGen.define_var(trans.SymTab, hdr_t.name, h_alloca_t)
|
||||
if h_alloca_t is not None:
|
||||
h_msg_t: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, i8_ptr_ty_t, eh_message)
|
||||
if h_msg_t is not None:
|
||||
llvmlite.build_store(builder, h_msg_t, h_alloca_t)
|
||||
except_bb_t: llvmlite.BasicBlock | t.CPtr = except_bbs.get(i_t)
|
||||
llvmlite.build_br(builder, except_bb_t)
|
||||
|
||||
# ---- 6. UnhandledBB:无 except 匹配 → 向上传播或吞掉 ----
|
||||
llvmlite.position_at_end(builder, unhandled_bb)
|
||||
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \
|
||||
and trans._eh_code_out_param is not None:
|
||||
un_msg_t: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, i8_ptr_ty_t, eh_message)
|
||||
un_code_t: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, i32_ty_t, exception_code)
|
||||
if un_msg_t is not None:
|
||||
llvmlite.build_store(builder, un_msg_t, trans._eh_msg_out_param)
|
||||
if un_code_t is not None:
|
||||
llvmlite.build_store(builder, un_code_t, trans._eh_code_out_param)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
else:
|
||||
if init_code_t is not None:
|
||||
llvmlite.build_store(builder, init_code_t, exception_code)
|
||||
llvmlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 7. Except 体 ----
|
||||
for i_t in range(n_handlers_t):
|
||||
hdr2_t: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(
|
||||
tr.handlers.get(i_t))
|
||||
except_bb2_t: llvmlite.BasicBlock | t.CPtr = except_bbs.get(i_t)
|
||||
llvmlite.position_at_end(builder, except_bb2_t)
|
||||
if hdr2_t is not None and hdr2_t.children is not None:
|
||||
hn2_t: t.CSizeT = len(hdr2_t.children)
|
||||
for hi2 in range(hn2_t):
|
||||
stmt2_t: ast.AST | t.CPtr = hdr2_t.children.get(hi2)
|
||||
if stmt2_t is not None:
|
||||
self.translate_stmt(trans, stmt2_t)
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 8. Else 体 ----
|
||||
if else_bb is not None:
|
||||
llvmlite.position_at_end(builder, else_bb)
|
||||
if tr.orelse is not None:
|
||||
en_t: t.CSizeT = len(tr.orelse)
|
||||
for ei in range(en_t):
|
||||
stmt3_t: ast.AST | t.CPtr = tr.orelse.get(ei)
|
||||
if stmt3_t is not None:
|
||||
self.translate_stmt(trans, stmt3_t)
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 9. AfterBB + 恢复外层 try 上下文 ----
|
||||
llvmlite.position_at_end(builder, after_bb)
|
||||
trans._eh_dispatch_bb = old_dispatch_t
|
||||
trans._eh_code_ptr = old_code_t
|
||||
trans._eh_msg_ptr = old_msg_t
|
||||
return 0
|
||||
|
||||
def translate_raise(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 raise 语句,返回 0
|
||||
|
||||
对齐 TPC HandlesRaise.py 的 try-except 语义(v1 迁移):
|
||||
- StopIteration:置 __stop_iter_flag__ = 1 后返回默认值(迭代结束协议)
|
||||
- 非 StopIteration:
|
||||
1. 处于 try(trans._eh_dispatch_bb 非空):异常编码/消息写入当前
|
||||
try 的 eh_code/eh_message alloca,分支到 DispatchBB 由 except 链裁决
|
||||
2. 函数有 EH 输出参数(_cur_func_has_eh):编码/消息写入
|
||||
__eh_code_out__/__eh_msg_out__,返回默认值(异常向调用方传播)
|
||||
3. 两者皆无:仅返回默认值(v1 语义:异常被吞掉)
|
||||
"""
|
||||
if trans is None or node is None:
|
||||
return 0
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
|
||||
if pool is None or builder is None or mod is None:
|
||||
return 0
|
||||
rn: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(node)
|
||||
if rn is None or rn.exc is None:
|
||||
# bare raise(无 exc):v1 无活动异常可再抛,仅返回默认值
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# OOP iter 协议:raise StopIteration → 置 __stop_iter_flag__ = 1
|
||||
# (对齐 TPC HandlesRaise.py L76-100),随后按迭代结束返回默认值。
|
||||
ek: int = rn.exc.kind()
|
||||
is_stop_iter: int = 0
|
||||
if ek == ast.ASTKind.Name:
|
||||
enm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn.exc)
|
||||
if enm.id is not None and string.strcmp(enm.id, "StopIteration") == 0:
|
||||
is_stop_iter = 1
|
||||
elif ek == ast.ASTKind.Call:
|
||||
ecl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn.exc)
|
||||
if ecl.func is not None and ecl.func.kind() == ast.ASTKind.Name:
|
||||
ecn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl.func)
|
||||
if ecn.id is not None and string.strcmp(ecn.id, "StopIteration") == 0:
|
||||
is_stop_iter = 1
|
||||
if is_stop_iter != 0:
|
||||
if trans._stop_iter_flag_ptr is not None:
|
||||
i1_ty_r: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
|
||||
if i1_ty_r is not None:
|
||||
one_i1_r: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
|
||||
pool, i1_ty_r, 1, "1")
|
||||
if one_i1_r is not None:
|
||||
llvmlite.build_store(
|
||||
builder, one_i1_r, trans._stop_iter_flag_ptr)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# ---- 非 StopIteration:计算异常编码与消息 ----
|
||||
exc_code: int = 1
|
||||
exc_msg: llvmlite.Value | t.CPtr = None
|
||||
if ek == ast.ASTKind.Constant:
|
||||
cst_r: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rn.exc)
|
||||
if cst_r is not None and cst_r.const_kind == ast.CONST_INT:
|
||||
exc_code = cst_r.int_val
|
||||
elif ek == ast.ASTKind.Name:
|
||||
enm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn.exc)
|
||||
if enm2.id is not None:
|
||||
exc_code = self.Gen.ClassGenBase.lookup_exception_code(enm2.id)
|
||||
if exc_code == 0:
|
||||
exc_code = 1
|
||||
elif ek == ast.ASTKind.Call:
|
||||
ecl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn.exc)
|
||||
if ecl2.func is not None and ecl2.func.kind() == ast.ASTKind.Name:
|
||||
ecn2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl2.func)
|
||||
if ecn2.id is not None:
|
||||
exc_code = self.Gen.ClassGenBase.lookup_exception_code(ecn2.id)
|
||||
if exc_code == 0:
|
||||
exc_code = 1
|
||||
if ecl2.args is not None and len(ecl2.args) > 0:
|
||||
exc_msg = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, ecl2.args.get(0), None, 0, trans)
|
||||
|
||||
# ---- 异常分发 ----
|
||||
# 1. try 内:写入 try 的 eh_code/eh_message alloca,分支 DispatchBB
|
||||
if trans._eh_dispatch_bb is not None and trans._eh_code_ptr is not None:
|
||||
code_val_r: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, exc_code)
|
||||
if code_val_r is not None:
|
||||
llvmlite.build_store(builder, code_val_r, trans._eh_code_ptr)
|
||||
if exc_msg is None:
|
||||
exc_msg = llvmlite.ConstNull(
|
||||
pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null")
|
||||
if exc_msg is not None and trans._eh_msg_ptr is not None:
|
||||
llvmlite.build_store(builder, exc_msg, trans._eh_msg_ptr)
|
||||
llvmlite.build_br(builder, trans._eh_dispatch_bb)
|
||||
return 0
|
||||
# 2. 函数有 EH 输出参数:写入 out 参数(异常向调用方传播)
|
||||
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \
|
||||
and trans._eh_code_out_param is not None:
|
||||
code_val_r2: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, exc_code)
|
||||
if code_val_r2 is not None:
|
||||
llvmlite.build_store(builder, code_val_r2, trans._eh_code_out_param)
|
||||
if exc_msg is None:
|
||||
exc_msg = llvmlite.ConstNull(
|
||||
pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null")
|
||||
if exc_msg is not None:
|
||||
llvmlite.build_store(builder, exc_msg, trans._eh_msg_out_param)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
# 3. 无 try 无 EH 参数:仅返回默认值
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _eh_ret_default - 异常路径返回当前函数的默认零值
|
||||
#
|
||||
# 对齐 TPC HandlesTry.py UnhandledBB / HandlesRaise.py 的返回生成
|
||||
# (void→ret_void,ptr→null,float→0.0,其余→零值)。当前块已
|
||||
# 终结(前一条是 raise/return)时跳过,避免在终止块后插指令。
|
||||
# ============================================================
|
||||
def _eh_ret_default(self, trans: HT.Translator | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr) -> int:
|
||||
"""异常路径返回当前函数默认零值,返回 0"""
|
||||
if trans is None or builder is None:
|
||||
return 0
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) != 0:
|
||||
return 0
|
||||
cur_func: llvmlite.Function | t.CPtr = trans._cur_func
|
||||
if cur_func is None:
|
||||
return 0
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func)
|
||||
if ret_ty is None:
|
||||
return 0
|
||||
match ret_ty:
|
||||
case llvmlite.LLVMType.Void():
|
||||
llvmlite.build_ret_void(builder)
|
||||
case llvmlite.LLVMType.Ptr(_pe):
|
||||
null_v: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null")
|
||||
if null_v is not None:
|
||||
llvmlite.build_ret(builder, null_v)
|
||||
case llvmlite.LLVMType.Float(_fb):
|
||||
zero_f: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, ret_ty, 0.0)
|
||||
if zero_f is not None:
|
||||
llvmlite.build_ret(builder, zero_f)
|
||||
case _:
|
||||
zero_v: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, ret_ty)
|
||||
if zero_v is not None:
|
||||
llvmlite.build_ret(builder, zero_v)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_with - with 语句翻译
|
||||
#
|
||||
# with expr() as x: body
|
||||
# → 1. 构造 context 对象(expr() 调用)
|
||||
# 2. 调用 __enter__()(沿继承链 MRO 解析,如 MemPool → MemManager)
|
||||
# 3. x 绑定 __enter__ 返回值(缺失时退回 context 对象)
|
||||
# 4. 翻译 body
|
||||
# 5. 逆序调用 __exit__()(无异常传播,__exit__ 仅做清理如 reset)
|
||||
#
|
||||
# 实现:context 对象存入临时变量 __with_ctx,方法调用复用
|
||||
# translate_call 的 Attribute 分派 + _translate_method_call 的 MRO 回退
|
||||
# (与普通 obj.method() 调用一致)。嵌套 with 时保存外层 __with_ctx,
|
||||
# 退出后恢复,避免内层覆盖导致外层 __exit__ 解析到错误对象。
|
||||
# 对齐 TPC HandlesWith 语义(不含 __provides__ provider 机制)。
|
||||
# ============================================================
|
||||
def translate_with(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
"""翻译 with 语句,返回 0"""
|
||||
if trans is None or node is None:
|
||||
return 0
|
||||
wt: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
|
||||
if wt is None:
|
||||
return 0
|
||||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
|
||||
if pool is None or builder is None or mod is None:
|
||||
return 0
|
||||
items: list[ast.AST | t.CPtr] | t.CPtr = wt.items
|
||||
if items is None:
|
||||
return 0
|
||||
n: t.CSizeT = len(items)
|
||||
|
||||
# 保存外层 __with_ctx(嵌套 with 时退出后恢复)
|
||||
old_ctx_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
|
||||
trans.SymTab, "__with_ctx")
|
||||
|
||||
# 记录每个 item 的类名(__exit__ 调用时设置 __with_ctx 类型用)
|
||||
ctx_class_buf: t.CSizeT | t.CPtr = pool.alloc(8 * n)
|
||||
if ctx_class_buf is None:
|
||||
return 0
|
||||
string.memset(ctx_class_buf, 0, 8 * n)
|
||||
|
||||
# 1. 全部 item:构造 context 对象 + 调用 __enter__ + asname 绑定
|
||||
for i in range(n):
|
||||
item_node: ast.AST | t.CPtr = items.get(i)
|
||||
if item_node is None:
|
||||
continue
|
||||
if item_node.kind() != ast.ASTKind.WithItem:
|
||||
continue
|
||||
item: ast.WithItem | t.CPtr = (ast.WithItem | t.CPtr)(item_node)
|
||||
if item is None or item.context_expr is None:
|
||||
continue
|
||||
# 翻译 context_expr(对象构造,如 MemPool(arena, 4096))
|
||||
ctx_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, item.context_expr,
|
||||
trans._funcs, trans._func_count, trans)
|
||||
if ctx_val is None:
|
||||
continue
|
||||
# 提取类名(注解类名,Attribute 时带模块前缀,如 "memhub.MemPool"——
|
||||
# 与 AnnAssign 的 extract_class_name_from_annotation 一致,跨模块
|
||||
# 方法调用靠 AnnotClassName 回退按模块名查 SHA1)
|
||||
cls_name: str = None
|
||||
ce: ast.AST | t.CPtr = item.context_expr
|
||||
if ce.kind() == ast.ASTKind.Call:
|
||||
cfn: ast.AST | t.CPtr = ((ast.Call | t.CPtr)(ce)).func
|
||||
if cfn is not None:
|
||||
if cfn.kind() == ast.ASTKind.Attribute:
|
||||
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cfn)
|
||||
if ca.value is not None and ca.value.kind() == ast.ASTKind.Name:
|
||||
cmn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ca.value)
|
||||
if cmn.id is not None:
|
||||
ml2: t.CSizeT = string.strlen(cmn.id) + string.strlen(ca.attr) + 2
|
||||
mbuf: str = pool.alloc(ml2)
|
||||
if mbuf is not None:
|
||||
viperlib.snprintf(mbuf, ml2, "%s.%s", cmn.id, ca.attr)
|
||||
cls_name = mbuf
|
||||
if cls_name is None:
|
||||
cls_name = ca.attr
|
||||
elif cfn.kind() == ast.ASTKind.Name:
|
||||
cls_name = ((ast.Name | t.CPtr)(cfn)).id
|
||||
# context 对象存入 __with_ctx(方法调用解析 self 用)
|
||||
ctx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
|
||||
builder, ctx_val.Ty)
|
||||
if ctx_alloca is None:
|
||||
continue
|
||||
llvmlite.build_store(builder, ctx_val, ctx_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", ctx_alloca)
|
||||
if cls_name is not None:
|
||||
self.Gen.VarGen.set_var_annot_class_name(
|
||||
trans.SymTab, "__with_ctx", cls_name)
|
||||
ctx_class_buf[i] = t.CSizeT(cls_name)
|
||||
# 调用 __enter__()
|
||||
enter_val: llvmlite.Value | t.CPtr = self._with_ctx_call(
|
||||
trans, pool, builder, mod, "__enter__")
|
||||
# asname 绑定(__enter__ 返回值,缺失时退回 context 对象)
|
||||
if item.optional_vars is not None:
|
||||
ov: ast.AST | t.CPtr = item.optional_vars
|
||||
if ov.kind() == ast.ASTKind.Name:
|
||||
asname: str = ((ast.Name | t.CPtr)(ov)).id
|
||||
if asname is not None:
|
||||
bind_val: llvmlite.Value | t.CPtr = enter_val
|
||||
if bind_val is None:
|
||||
bind_val = ctx_val
|
||||
if bind_val is not None:
|
||||
asn_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
|
||||
builder, bind_val.Ty)
|
||||
if asn_alloca is not None:
|
||||
llvmlite.build_store(builder, bind_val, asn_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, asname, asn_alloca)
|
||||
if cls_name is not None:
|
||||
self.Gen.VarGen.set_var_annot_class_name(
|
||||
trans.SymTab, asname, cls_name)
|
||||
# FakeDuck: __provides__ 元数据 → 生成 _push_provider 调用
|
||||
# (对齐 TPC HandlesWith.py L138-159;经 gen 单例委托 CallGenMixin。
|
||||
# 类名去模块前缀后查 __provides__ 表,如 memhub.MemBuddy → MemBuddy)
|
||||
fd_cls_bare: str = cls_name
|
||||
if cls_name is not None:
|
||||
fd_dot: str = string.strrchr(cls_name, '.')
|
||||
if fd_dot is not None:
|
||||
fd_cls_bare = fd_dot + 1
|
||||
if fd_cls_bare is not None:
|
||||
self.Gen.CallGenFd._emit_provider_push(
|
||||
trans, pool, builder, mod, fd_cls_bare, ctx_val)
|
||||
|
||||
# 2. 翻译 body
|
||||
body: list[ast.AST | t.CPtr] | t.CPtr = wt.children
|
||||
if body is not None:
|
||||
bn: t.CSizeT = len(body)
|
||||
for bi in range(bn):
|
||||
st: ast.AST | t.CPtr = body.get(bi)
|
||||
if st is not None:
|
||||
self.translate_stmt(trans, st)
|
||||
|
||||
# 2.5 FakeDuck: __provides__ 元数据 → 生成 _pop_provider 调用
|
||||
# (对齐 TPC HandlesWith.py L167-174;逆序弹栈,经 gen 单例委托)
|
||||
for pi_pop in range(n):
|
||||
if ctx_class_buf[pi_pop] != 0:
|
||||
cls_pop: str = (str | t.CPtr)(t.CVoid(ctx_class_buf[pi_pop], t.CPtr))
|
||||
fd_cls_bare_p: str = cls_pop
|
||||
if cls_pop is not None:
|
||||
fd_dot_p: str = string.strrchr(cls_pop, '.')
|
||||
if fd_dot_p is not None:
|
||||
fd_cls_bare_p = fd_dot_p + 1
|
||||
if fd_cls_bare_p is not None:
|
||||
self.Gen.CallGenFd._emit_provider_pop(
|
||||
trans, pool, builder, mod, fd_cls_bare_p)
|
||||
|
||||
# 3. 逆序调用 __exit__()(Python with 语义:后进先出)
|
||||
for i in range(n):
|
||||
ci: int = n - 1 - i
|
||||
if ctx_class_buf[ci] != 0:
|
||||
cls_ci: str = (str | t.CPtr)(t.CVoid(ctx_class_buf[ci], t.CPtr))
|
||||
self.Gen.VarGen.set_var_annot_class_name(
|
||||
trans.SymTab, "__with_ctx", cls_ci)
|
||||
self._with_ctx_call(trans, pool, builder, mod, "__exit__")
|
||||
|
||||
# 4. 恢复外层 __with_ctx
|
||||
if old_ctx_alloca is not None:
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", old_ctx_alloca)
|
||||
return 0
|
||||
|
||||
def _with_ctx_call(self, trans: HT.Translator | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
method_name: str) -> llvmlite.Value | t.CPtr:
|
||||
"""构造并翻译 __with_ctx.<method_name>() 调用(__enter__/__exit__)"""
|
||||
if pool is None or method_name is None:
|
||||
return None
|
||||
name_node: ast.AST | t.CPtr = ast.Name(pool, "__with_ctx", ast.ASTCtx.Load)
|
||||
if name_node is None:
|
||||
return None
|
||||
attr_node: ast.AST | t.CPtr = ast.Attribute(
|
||||
pool, name_node, method_name, ast.ASTCtx.Load)
|
||||
if attr_node is None:
|
||||
return None
|
||||
call_node: ast.AST | t.CPtr = ast.Call(pool, attr_node, None, None)
|
||||
if call_node is None:
|
||||
return None
|
||||
return self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, call_node,
|
||||
trans._funcs, trans._func_count, trans)
|
||||
+3496
-1559
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1130
-919
File diff suppressed because it is too large.
Load diff
Loaded 100 of 176 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user