Compare commits
544 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34900d2748 | ||
|
|
749b518b90 | ||
|
|
c07fce0477 | ||
|
|
7f8476dd33 | ||
|
|
57afefb069 | ||
|
|
0629ccb226 | ||
|
|
6e4620edc6 | ||
|
|
2410267c8e | ||
|
|
bc12aae8c1 | ||
|
|
6578412437 | ||
|
|
ce696c9b4e | ||
|
|
3befa1be59 | ||
|
|
a9174f09c5 | ||
|
|
b86cc097d9 | ||
|
|
0e9f434ad8 | ||
|
|
e868b825bb | ||
|
|
dd2a569d76 | ||
|
|
059192f1ae | ||
|
|
3db6d8352a | ||
|
|
00db78d89e | ||
|
|
d9eb3ec175 | ||
|
|
4ed8f80774 | ||
|
|
43095609c3 | ||
|
|
b9cccbf9ae | ||
|
|
8689d62f02 | ||
|
|
17d3cef4bd | ||
|
|
ecac668bef | ||
|
|
078f1fe5c1 | ||
|
|
b9752e1e98 | ||
|
|
f8c1c78df1 | ||
|
|
b8e0338da8 | ||
|
|
091a393d1d | ||
|
|
d7590fb029 | ||
|
|
c199881b67 | ||
|
|
fb3b6a3674 | ||
|
|
4e814b7d53 | ||
|
|
8a6dd7af7f | ||
|
|
d08095299e | ||
|
|
bf5859be7e | ||
|
|
18d3b4809f | ||
|
|
a82e876f93 | ||
|
|
1f7b9e3922 | ||
|
|
fac1bc782d | ||
|
|
ea452bd33c | ||
|
|
ddf2007553 | ||
|
|
e7a7896710 | ||
|
|
69a78d0a54 | ||
|
|
cc4e4631bd | ||
|
|
8034854ad4 | ||
|
|
733fd7d18e | ||
|
|
6e55bfc406 | ||
|
|
72192c7e6a | ||
|
|
b303fd532a | ||
|
|
aa264cee2d | ||
|
|
3743146121 | ||
|
|
8de5c3faff | ||
|
|
083586464b | ||
|
|
0f3d5a8127 | ||
|
|
3e77a55de3 | ||
|
|
65c4558cac | ||
|
|
43f7330fda | ||
|
|
1d735e49d2 | ||
|
|
fea82991c3 | ||
|
|
b3bf7f86cd | ||
|
|
3c06f0d590 | ||
|
|
cadc53f174 | ||
|
|
0ed471d336 | ||
|
|
7234b0d200 | ||
|
|
fa3aec8353 | ||
|
|
bd9d71c713 | ||
|
|
8dbfc377ad | ||
|
|
82d9c787f3 | ||
|
|
87d444c9e9 | ||
|
|
fa26aa1651 | ||
|
|
cbf4a61a17 | ||
|
|
0e265a2cc5 | ||
|
|
e65f6174a7 | ||
|
|
83299a20a6 | ||
|
|
97c0e15e72 | ||
|
|
4eab936224 | ||
|
|
78be6a7f5f | ||
|
|
bc02b3d90a | ||
|
|
aa0ee9b6a6 | ||
|
|
0610a27535 | ||
|
|
f4bbb18e47 | ||
|
|
25e6651055 | ||
|
|
38a00c6e55 | ||
|
|
818c9289fc | ||
|
|
ba03b226c8 | ||
|
|
8f2c27ba41 | ||
|
|
ef59848d18 | ||
|
|
4905743e0a | ||
|
|
09d9dbba1e | ||
|
|
aae25f5e55 | ||
|
|
af2c1d6d01 | ||
|
|
4e993570cd | ||
|
|
6bbf53241d | ||
|
|
0801df41dc | ||
|
|
24f1b33737 | ||
|
|
536bc37c94 | ||
|
|
351bd39814 | ||
|
|
33ca795e0c | ||
|
|
d57c4d88c6 | ||
|
|
328f88bbd5 | ||
|
|
ea5b430297 | ||
|
|
5f01343bba | ||
|
|
bf8693bb80 | ||
|
|
e69048ad3d | ||
|
|
8480d55faf | ||
|
|
7d1c4c18a0 | ||
|
|
07c64779d8 | ||
|
|
d10b182479 | ||
|
|
1be8b07ced | ||
|
|
3a289ccace | ||
|
|
bf0253c9f9 | ||
|
|
d060c10096 | ||
|
|
1ed3c77310 | ||
|
|
cfe5e9ea92 | ||
|
|
1e9d37a64e | ||
|
|
eb4fc653f2 | ||
|
|
b4d943a671 | ||
|
|
c20c4d776f | ||
|
|
84a95945ea | ||
|
|
0421ad8456 | ||
|
|
2a25b1c2b3 | ||
|
|
c4db61c7f5 | ||
|
|
c0e9dedc90 | ||
|
|
319984f0e0 | ||
|
|
56a511854a | ||
|
|
625858aa6f | ||
|
|
6d3253e4ea | ||
|
|
4c1b9016fb | ||
|
|
2338eae5f6 | ||
|
|
2415042a2a | ||
|
|
ad287faeb1 | ||
|
|
69c75355f4 | ||
|
|
84f1b433de | ||
|
|
2e8944916c | ||
|
|
17ec89e457 | ||
|
|
6b333e84aa | ||
|
|
009239713e | ||
|
|
1812cff2d0 | ||
|
|
a0eb03b239 | ||
|
|
5b2b878f24 | ||
|
|
edab81ed7e | ||
|
|
96dc907613 | ||
|
|
592f9c6988 | ||
|
|
331f71f26f | ||
|
|
db7323771f | ||
|
|
f4b320ff09 | ||
|
|
08bed7dd74 | ||
|
|
c118427118 | ||
|
|
3e0dc25070 | ||
|
|
489d8056b9 | ||
|
|
a357987597 | ||
|
|
b754e803ec | ||
|
|
a5f6f2596f | ||
|
|
238ea0e453 | ||
|
|
e74b26f84c | ||
|
|
020032b578 | ||
|
|
777eea719c | ||
|
|
66346ee507 | ||
|
|
125e65e261 | ||
|
|
2479c2dc22 | ||
|
|
003768cdea | ||
|
|
ca8fa2593a | ||
|
|
fab52057ba | ||
|
|
0b1abe7b26 | ||
|
|
22b5d4695c | ||
|
|
b13aee0668 | ||
|
|
b14161c184 | ||
|
|
6c2ed9a3ed | ||
|
|
b6c1e7a961 | ||
|
|
8800ebac66 | ||
|
|
1b2a9dec9a | ||
|
|
a8c78a6e2e | ||
|
|
d06a5f3eb4 | ||
|
|
01c8ec1769 | ||
|
|
b913b2840e | ||
|
|
edc391f292 | ||
|
|
ec0dda4621 | ||
|
|
bd129baa0a | ||
|
|
6f524130d0 | ||
|
|
e63bdd6da2 | ||
|
|
67f4acf91f | ||
|
|
3c4769c58d | ||
|
|
b8da3c7a73 | ||
|
|
931352d4bf | ||
|
|
e4996f02d7 | ||
|
|
372d601875 | ||
|
|
a5078d6cd7 | ||
|
|
f5fd7241ea | ||
|
|
f8deae06dd | ||
|
|
ba759b59d0 | ||
|
|
bb43f4097a | ||
|
|
fde153aab6 | ||
|
|
51ff879a07 | ||
|
|
e89c19ee60 | ||
|
|
f172e82f10 | ||
|
|
91d2740fbf | ||
|
|
75b7d11fb1 | ||
|
|
61d8105e2c | ||
|
|
d9c2ec696d | ||
|
|
1a34e412a5 | ||
|
|
4bf0fa65ef | ||
|
|
268dc2466a | ||
|
|
0cccb3dec9 | ||
|
|
9df508f7c0 | ||
|
|
cd8d622fc9 | ||
|
|
9f9d5a6081 | ||
|
|
8073b67c96 | ||
|
|
f3e7b39bc9 | ||
|
|
d485c22b08 | ||
|
|
ae6dab8e50 | ||
|
|
5d83d626f6 | ||
|
|
17096867f7 | ||
|
|
93cd20e933 | ||
|
|
740d2595ee | ||
|
|
c3a08a446f | ||
|
|
dd9ba65328 | ||
|
|
894539681b | ||
|
|
cada51e297 | ||
|
|
7b1a825380 | ||
|
|
c1b46e4384 | ||
|
|
d270f47328 | ||
|
|
4e87b5170a | ||
|
|
d36cd1dbc7 | ||
|
|
d74ff4dc2b | ||
|
|
8086487ab7 | ||
|
|
769376b055 | ||
|
|
650a49dcab | ||
|
|
fccf439117 | ||
|
|
6c07747a3b | ||
|
|
45e70333fd | ||
|
|
a275068193 | ||
|
|
74982d6fef | ||
|
|
435b92b4a4 | ||
|
|
d270e885cc | ||
|
|
42f7219c3f | ||
|
|
175f558ce0 | ||
|
|
58d410b347 | ||
|
|
081b41db60 | ||
|
|
bf484aa216 | ||
|
|
20d0bf9f2a | ||
|
|
1647580a41 | ||
|
|
acd6ab685b | ||
|
|
0cc2e516ac | ||
|
|
92c9813b3e | ||
|
|
013c7d2009 | ||
|
|
ffa713e332 | ||
|
|
a7c650a458 | ||
|
|
079e31c474 | ||
|
|
7813bec6e9 | ||
|
|
26c4380c2b | ||
|
|
bf121485ad | ||
|
|
cb00dcc52c | ||
|
|
d2dc34cdd3 | ||
|
|
a09394cacd | ||
|
|
0036d55eea | ||
|
|
b4f4483809 | ||
|
|
a97b26415e | ||
|
|
2d8c6f0a41 | ||
|
|
3189be4d58 | ||
|
|
0681d2f37e | ||
|
|
230583f42d | ||
|
|
1fb6b05715 | ||
|
|
8700bbc4f1 | ||
|
|
60336d1c5f | ||
|
|
c66dad7ece | ||
|
|
355332d6d8 | ||
|
|
6eae24bcf3 | ||
|
|
7c39bd1f5b | ||
|
|
fe383ca326 | ||
|
|
b9ac22d136 | ||
|
|
113f30ffab | ||
|
|
541c119fc3 | ||
|
|
11cc78d780 | ||
|
|
aa5b969961 | ||
|
|
cd1fa4c4d8 | ||
|
|
39de8c4003 | ||
|
|
9c81fa1f63 | ||
|
|
c98a1274d9 | ||
|
|
450c29e052 | ||
|
|
da85049eb9 | ||
|
|
168510222f | ||
|
|
961c5e99e1 | ||
|
|
c085bef46e | ||
|
|
1f49bb6d7c | ||
|
|
7788346152 | ||
|
|
372d6804e4 | ||
|
|
9485d03e18 | ||
|
|
ca0a2b9fdd | ||
|
|
78f9c9a610 | ||
|
|
25b498c8fa | ||
|
|
437b1e9490 | ||
|
|
59d0dfddc7 | ||
|
|
895d114730 | ||
|
|
7aa9487f2f | ||
|
|
af7962a2ca | ||
|
|
d939728938 | ||
|
|
fa4927e1fb | ||
|
|
821e937837 | ||
|
|
d1931cc499 | ||
|
|
0168315076 | ||
|
|
4b5cb100f7 | ||
|
|
c76096bbcb | ||
|
|
3c81157ede | ||
|
|
eba88b32ba | ||
|
|
a3da74c416 | ||
|
|
c247cf9527 | ||
|
|
e9e1f5703d | ||
|
|
e9be14ca0e | ||
|
|
6a861d82e6 | ||
|
|
ef4c0d3be3 | ||
|
|
6afab6268c | ||
|
|
6b88ffbaed | ||
|
|
ce103975b8 | ||
|
|
f95b691b61 | ||
|
|
3e89a97fdb | ||
|
|
bd550e3c0e | ||
|
|
f88036274a | ||
|
|
089c825b0c | ||
|
|
e39e1c7205 | ||
|
|
36df29ba71 | ||
|
|
d0935fe533 | ||
|
|
5c1e8985a1 | ||
|
|
47771d8dbc | ||
|
|
a12c4da93d | ||
|
|
b793e72189 | ||
|
|
c822f5f9f7 | ||
|
|
0a6a11f35a | ||
|
|
3f789913f5 | ||
|
|
4fb7549243 | ||
|
|
7bd02ef91a | ||
|
|
fe37d8bae0 | ||
|
|
5dd0948c65 | ||
|
|
1d4c431b23 | ||
|
|
5d54bbb1cd | ||
|
|
b895f1fc99 | ||
|
|
ffc067f852 | ||
|
|
7e7b362e47 | ||
|
|
3d66981cc1 | ||
|
|
8b918f37d6 | ||
|
|
3772b8ba6e | ||
|
|
4ab6791e78 | ||
|
|
d355facf25 | ||
|
|
c0ff3615ee | ||
|
|
60795adb5e | ||
|
|
3b1ae9889d | ||
|
|
2bef762ba2 | ||
|
|
1ccda1ca5c | ||
|
|
b46757f492 | ||
|
|
2689694de9 | ||
|
|
e6ff2b46e2 | ||
|
|
3b75974731 | ||
|
|
3fba8e4adf | ||
|
|
f6e687ae66 | ||
|
|
f05222caec | ||
|
|
8c6384f242 | ||
|
|
7c9f73ccc7 | ||
|
|
93f8c70d19 | ||
|
|
5209cdb032 | ||
|
|
203858f417 | ||
|
|
d8ffc18ffc | ||
|
|
591d31414f | ||
|
|
50e92b4abe | ||
|
|
0cb552a1ca | ||
|
|
6bc5b72c71 | ||
|
|
ca1a23dff5 | ||
|
|
206d512c10 | ||
|
|
7451f6ca4a | ||
|
|
343efc6379 | ||
|
|
1daec8ff63 | ||
|
|
c7320e84de | ||
|
|
d0fd53fda3 | ||
|
|
ea315a58cb | ||
|
|
2d3889111b | ||
|
|
0f3dfb85d4 | ||
|
|
00b3449443 | ||
|
|
5d4759bd55 | ||
|
|
bdac4a809d | ||
|
|
91a10e6a8e | ||
|
|
8b58488bbd | ||
|
|
9874d8048d | ||
|
|
995d14e1d0 | ||
|
|
207254fe50 | ||
|
|
31fbc3ae64 | ||
|
|
a0a777e1aa | ||
|
|
ee62b0c96c | ||
|
|
f731332360 | ||
|
|
9dadd9f1df | ||
|
|
cc8b16b9ca | ||
|
|
64880e7d69 | ||
|
|
ee2d86e86f | ||
|
|
371e940c83 | ||
|
|
5fc5ba2e66 | ||
|
|
8ca0e79bd0 | ||
|
|
af6b0d7823 | ||
|
|
e02cd0a977 | ||
|
|
9571aefbe0 | ||
|
|
457cd24986 | ||
|
|
5b6a1d75f8 | ||
|
|
dd32ce56b1 | ||
|
|
f01da12e16 | ||
|
|
bc20661343 | ||
|
|
e8c7fcfd32 | ||
|
|
a6ed8d55f8 | ||
|
|
3fbce4c0f9 | ||
|
|
e2ec14bbb9 | ||
|
|
7f9c91e2aa | ||
|
|
3b399f9d8b | ||
|
|
bcc467b2b5 | ||
|
|
ff92a05858 | ||
|
|
b22c023301 | ||
|
|
ff51552838 | ||
|
|
10e83f306f | ||
|
|
edcef56f05 | ||
|
|
d397f4c497 | ||
|
|
5da98dac49 | ||
|
|
f3e9eb0b05 | ||
|
|
e2e6221f25 | ||
|
|
f2e65c6c54 | ||
|
|
436a12db03 | ||
|
|
970ba92720 | ||
|
|
d12b145bee | ||
|
|
5c5f3015f7 | ||
|
|
03e54f2c12 | ||
|
|
b3a04c4a34 | ||
|
|
78f81fec73 | ||
|
|
7c7c198953 | ||
|
|
f9376bf2d0 | ||
|
|
d491d91dd2 | ||
|
|
425e382368 | ||
|
|
52d7b8b6f5 | ||
|
|
7adecd8f5d | ||
|
|
6f234dbf2c | ||
|
|
0691800c84 | ||
|
|
469349be07 | ||
|
|
f6a974a767 | ||
|
|
a2c5e861c4 | ||
|
|
994c1432a6 | ||
|
|
a52db30af8 | ||
|
|
d795000176 | ||
|
|
4bc9f00d95 | ||
|
|
23a2d3a446 | ||
|
|
22d8e0048b | ||
|
|
5b9885fc40 | ||
|
|
ac791e5c38 | ||
|
|
affc4c016f | ||
|
|
bbc7a4ddf0 | ||
|
|
119ef50689 | ||
|
|
23e3f43d44 | ||
|
|
004313fcbd | ||
|
|
2685d60e38 | ||
|
|
2799e9f452 | ||
|
|
b9d05bb6fe | ||
|
|
507ff73061 | ||
|
|
bd377de198 | ||
|
|
6133b643d8 | ||
|
|
87a44e3117 | ||
|
|
daa62d9f78 | ||
|
|
6d19096438 | ||
|
|
c8e44702cb | ||
|
|
2b2407b82f | ||
|
|
47cd581ca5 | ||
|
|
d781e83bd1 | ||
|
|
23a5dd0dff | ||
|
|
aab2c39fc9 | ||
|
|
9b1b444000 | ||
|
|
b7fba64e53 | ||
|
|
80437fc077 | ||
|
|
01d0ff4eba | ||
|
|
e0d1b94053 | ||
|
|
ba309ea6c5 | ||
|
|
40b84ffd74 | ||
|
|
fe1ba06698 | ||
|
|
99c9a1f204 | ||
|
|
3f0a4a74f7 | ||
|
|
6041f2df84 | ||
|
|
6d8fbafb54 | ||
|
|
d1fefe5a69 | ||
|
|
87ffb437d8 | ||
|
|
aba3e208d2 | ||
|
|
1c15a9784b | ||
|
|
bba30621f1 | ||
|
|
e42dfd9abb | ||
|
|
cdd26cde58 | ||
|
|
f0ad108e2c | ||
|
|
ab0ce0d49a | ||
|
|
20a896b206 | ||
|
|
b9584e1071 | ||
|
|
bc03c9a8b7 | ||
|
|
10b3f188cc | ||
|
|
cd9c5e7c79 | ||
|
|
b0137289b9 | ||
|
|
8a159b35f0 | ||
|
|
af44641bb9 | ||
|
|
1e4633c44d | ||
|
|
b941ae5f1f | ||
|
|
8505256510 | ||
|
|
428f4ad530 | ||
|
|
a6e7bcb015 | ||
|
|
e7b1af043f | ||
|
|
a046685b2c | ||
|
|
fb30295e62 | ||
|
|
d390960657 | ||
|
|
eeda441dac | ||
|
|
4d773ed5ed | ||
|
|
2218f1d49e | ||
|
|
5513a34e80 | ||
|
|
5319fb8c98 | ||
|
|
562e3c1e46 | ||
|
|
d55bb95d4d | ||
|
|
f9d7195413 | ||
|
|
1009daf3fa | ||
|
|
c1d3044c77 | ||
|
|
7f84ecbe3b | ||
|
|
9ccc6782c7 | ||
|
|
c6d5538ead | ||
|
|
753f74d485 | ||
|
|
b23d7909fb | ||
|
|
e35b8ca636 | ||
|
|
3b0aeea6f2 | ||
|
|
3009d8468f | ||
|
|
68e2359ad2 | ||
|
|
8b45094e20 | ||
|
|
2acc070984 | ||
|
|
01054c726f | ||
|
|
b5893d4ebb | ||
|
|
150f497ed0 | ||
|
|
fef9c2efbf | ||
|
|
c43b27356b | ||
|
|
1594e5456a | ||
|
|
891daa1164 | ||
|
|
0654e00ab1 | ||
|
|
673ada6a4e | ||
|
|
b45c768cc5 | ||
|
|
184e4ea1a1 | ||
|
|
f1e10e849f | ||
|
|
aebb9857eb | ||
|
|
8381b25f66 | ||
|
|
7238fecc7f | ||
|
|
6981206096 | ||
|
|
066480fbb8 |
298 changed files with 24581 additions and 11933 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
llvm/_version.py export-subst
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,4 +2,7 @@
|
||||||
build
|
build
|
||||||
_build
|
_build
|
||||||
*.pyc
|
*.pyc
|
||||||
|
*.so
|
||||||
/llvm/_intrinsic_ids.py
|
/llvm/_intrinsic_ids.py
|
||||||
|
llvm_
|
||||||
|
newbinding/api/*
|
||||||
24
.travis.yml
Normal file
24
.travis.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
language: python
|
||||||
|
|
||||||
|
python:
|
||||||
|
- "2.6"
|
||||||
|
- "2.7"
|
||||||
|
- "3.2"
|
||||||
|
- "3.3"
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
|
install:
|
||||||
|
- wget "http://repo.continuum.io/pkgs/free/linux-64/llvm-3.2-0.tar.bz2"
|
||||||
|
- tar -xjf llvm-3.2-0.tar.bz2
|
||||||
|
- PATH+=":`pwd`/bin"
|
||||||
|
- export LD_LIBRARY_PATH="`pwd`/lib"
|
||||||
|
- export LLVM_CONFIG_PATH="`pwd`/bin/llvm-config"
|
||||||
|
- $LLVM_CONFIG_PATH --cflags # test llvm-config
|
||||||
|
- export LLVMPY_DYNLINK=1
|
||||||
|
- python setup.py install -q
|
||||||
|
|
||||||
|
script: cd ~; python -c "import sys;import llvm;sys.exit(0 if llvm.test() == 0 else 1)"
|
||||||
|
|
||||||
107
CHANGELOG
107
CHANGELOG
|
|
@ -1,14 +1,88 @@
|
||||||
|
2014-04-28 0.12.5:
|
||||||
|
---------------------
|
||||||
|
* Fixes memory leaks (#92)
|
||||||
|
* Fixes tarball (#99)
|
||||||
|
|
||||||
0.7, in progress:
|
2014-03-20 0.12.4:
|
||||||
|
---------------------
|
||||||
|
* Add dylib_import_library and friends
|
||||||
|
* Fix BasicBlock downcast
|
||||||
|
* Module hashing
|
||||||
|
* Fix test script
|
||||||
|
|
||||||
|
2014-02-18 0.12.3:
|
||||||
|
---------------------
|
||||||
|
* Fix deprecation message for py2.6
|
||||||
|
* Fix llvm_cbuilder for using deprecated_alloca
|
||||||
|
* Merged PR #88 by cantora
|
||||||
|
* Merged PR #94 by cgohlke
|
||||||
|
|
||||||
|
2014-02-04 0.12.2:
|
||||||
|
---------------------
|
||||||
|
* enhance wrapper efficiency by moving some capsule code into C++
|
||||||
|
* fix unclosed file handler in avx_support
|
||||||
|
* multiple-dimension insert_value, extract_value
|
||||||
|
* various minor fixes
|
||||||
|
|
||||||
|
2013-11-11 0.12.1:
|
||||||
|
---------------------
|
||||||
|
* various bug fixes
|
||||||
|
|
||||||
|
2013-08-28 0.12.0:
|
||||||
|
---------------------
|
||||||
|
* update to LLVM 3.3 and maintain compatibility with LLVM 3.2
|
||||||
|
* add LLRT for minimal support for 64-bit divmod on 32-bit platform
|
||||||
|
* start to adopt MCJIT (not quite usable on win32)
|
||||||
|
* various bug fixes
|
||||||
|
|
||||||
|
2013-03-05 0.11.1:
|
||||||
|
--------------------
|
||||||
|
* fix test when cc is not available
|
||||||
|
* fix Python 3 division (Hernan Grecco) (Issue #59)
|
||||||
|
* add relocation enums and add reloc argument for TargetMachine
|
||||||
|
|
||||||
|
|
||||||
|
2013-03-01 0.11.0:
|
||||||
|
--------------------
|
||||||
|
* fix Python 3 support on Windows
|
||||||
|
* New llvm binding
|
||||||
|
|
||||||
|
|
||||||
|
2013-02-01 0.10.2:
|
||||||
|
--------------------
|
||||||
|
* change default to link dynamically to LLVM use:
|
||||||
|
$ export LLVMPY_DYNLINK=0 # link statically
|
||||||
|
$ export LLVMPY_DYNLINK=1 # link dynamically
|
||||||
|
$ unset LLVMPY_DYNLINK # tries to link dynamically if LLVM shared
|
||||||
|
# objects are found and statically otherwise
|
||||||
|
* fix llpython for Python 2.6 support
|
||||||
|
|
||||||
|
|
||||||
|
2013-01-25 0.10.1:
|
||||||
|
--------------------
|
||||||
|
* fix support for Python 2.6
|
||||||
|
|
||||||
|
|
||||||
|
2013-01-18 0.10.0:
|
||||||
|
--------------------
|
||||||
|
* Add LLVM 3.2 support.
|
||||||
|
* New TargetData class.
|
||||||
|
* Fixed windows issue (Issue #42).
|
||||||
|
* Add ExecutionEngine.add_global_mapping and .get_pointer_to_global.
|
||||||
|
* Improved TargetMachine class and added code-model constants (CM_*).
|
||||||
|
* Added llvm.passes.build_pass_managers as a simpler way to build PassManagers.
|
||||||
|
|
||||||
|
|
||||||
|
in progress, 0.7:
|
||||||
|
-----------------
|
||||||
* Add llvm.core.Argument.alignment property.
|
* Add llvm.core.Argument.alignment property.
|
||||||
* Migrate to LLVM 2.8.
|
* Migrate to LLVM 2.8.
|
||||||
* Fix ffi link issue on darwin (Albert Mietus) (Issue #29).
|
* Fix ffi link issue on darwin (Albert Mietus) (Issue #29).
|
||||||
* LLVM tutorial ported (Max Shawabkeh) (Issue #33).
|
* LLVM tutorial ported (Max Shawabkeh) (Issue #33).
|
||||||
|
|
||||||
|
|
||||||
0.6, 31-Aug-2010:
|
2010-08-31 0.6:
|
||||||
|
-----------------
|
||||||
* Add and remove function attributes (Krzysztof Goj) (Issue #21).
|
* Add and remove function attributes (Krzysztof Goj) (Issue #21).
|
||||||
* Wrap fadd,fsub,fmul (Aaron S Lav) (Issue #31).
|
* Wrap fadd,fsub,fmul (Aaron S Lav) (Issue #31).
|
||||||
* Migrate to LLVM 2.7.
|
* Migrate to LLVM 2.7.
|
||||||
|
|
@ -27,16 +101,16 @@
|
||||||
* Migrate to LLVM 2.5.
|
* Migrate to LLVM 2.5.
|
||||||
|
|
||||||
|
|
||||||
0.5, 22-Nov-2008:
|
2008-11-22 0.5:
|
||||||
|
-----------------
|
||||||
* Added vicmp, vfcmp instructions and constant expressions.
|
* Added vicmp, vfcmp instructions and constant expressions.
|
||||||
* Builds on FreeBSD.
|
* Builds on FreeBSD.
|
||||||
* Updated documentation.
|
* Updated documentation.
|
||||||
* Migrate to LLVM 2.4.
|
* Migrate to LLVM 2.4.
|
||||||
|
|
||||||
|
|
||||||
0.4, 21-Nov-2008:
|
2008-11-21 0.4:
|
||||||
|
-----------------
|
||||||
* Code cleanup, added license headers.
|
* Code cleanup, added license headers.
|
||||||
* Added llvm.core.load_library_permanently() (Issue #12).
|
* Added llvm.core.load_library_permanently() (Issue #12).
|
||||||
* Fix comparison using != (Issue #11).
|
* Fix comparison using != (Issue #11).
|
||||||
|
|
@ -45,8 +119,8 @@
|
||||||
* Added viewCFG methods to Function (Paulo Silva).
|
* Added viewCFG methods to Function (Paulo Silva).
|
||||||
|
|
||||||
|
|
||||||
0.3, 8-Sep-2008:
|
2008-09-08 0.3:
|
||||||
|
-----------------
|
||||||
* Passes added.
|
* Passes added.
|
||||||
* Assembly support: create modules from .ll files.
|
* Assembly support: create modules from .ll files.
|
||||||
* Various bug fixes.
|
* Various bug fixes.
|
||||||
|
|
@ -58,13 +132,13 @@
|
||||||
* Updated documentation.
|
* Updated documentation.
|
||||||
|
|
||||||
|
|
||||||
0.2.1, 18-Jun-2008:
|
2008-06-28 0.2.1:
|
||||||
|
-------------------
|
||||||
* Build cleanly with LLVM 2.3 and 2.3svn.
|
* Build cleanly with LLVM 2.3 and 2.3svn.
|
||||||
|
|
||||||
|
|
||||||
0.2, 15-Jun-2008:
|
2008-06-15 0.2:
|
||||||
|
-----------------
|
||||||
* Independent package, need not be unpacked into llvm/bindings.
|
* Independent package, need not be unpacked into llvm/bindings.
|
||||||
* Fixed ownership issues with Module/ModuleProvider.
|
* Fixed ownership issues with Module/ModuleProvider.
|
||||||
* Modules, values and types can be stringified, to get their LLVM
|
* Modules, values and types can be stringified, to get their LLVM
|
||||||
|
|
@ -79,7 +153,6 @@
|
||||||
* Lots of cleanup.
|
* Lots of cleanup.
|
||||||
|
|
||||||
|
|
||||||
0.1, 10-May-2008:
|
2008-05-10 0.1:
|
||||||
|
-----------------
|
||||||
* Initial release.
|
* Initial release
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
include CHANGELOG LICENSE README setup.py MANIFEST.in
|
include CHANGELOG LICENSE README.rst setup.py MANIFEST.in versioneer.py
|
||||||
recursive-include llvm *
|
recursive-include llvm *
|
||||||
|
recursive-include llvmpy *
|
||||||
recursive-include www *
|
recursive-include www *
|
||||||
recursive-include test *
|
recursive-include test *
|
||||||
|
recursive-include tools *
|
||||||
|
|
|
||||||
40
README.rst
40
README.rst
|
|
@ -10,27 +10,55 @@ http://www.llvmpy.org
|
||||||
Versions
|
Versions
|
||||||
--------
|
--------
|
||||||
|
|
||||||
This package has only been tested with LLVM 3.1, and Python 2.7, (not Python 3.x).
|
This package has been tested with LLVM 3.2, Python 2.6, 2.7 and 3.3.
|
||||||
|
Other Python versions may work.
|
||||||
|
|
||||||
Quickstart
|
Quickstart
|
||||||
----------
|
----------
|
||||||
|
|
||||||
1. Get 3.1 version of LLVM, build it. Make sure ``--enable-pic`` is passed to
|
1. Get and extract LLVM 3.2 source tarball from
|
||||||
LLVM's ``configure``.
|
`llvm.org <http://llvm.org/releases/download.html#3.2>`_. Then, ``cd`` into
|
||||||
|
the extracted directory.
|
||||||
|
|
||||||
2. Get llvm-py and install it::
|
2. Run ``./configure --enable-optimized --prefix=LLVM_INSTALL_PATH``.
|
||||||
|
|
||||||
|
**Note**: Without the ``--enable-optimized`` flag, debug build will be
|
||||||
|
selected. Unless you are developing LLVM or llvmpy, it is recommended
|
||||||
|
that the flag is used to reduce build time and binary size.
|
||||||
|
|
||||||
|
**Note**: Use prefix to select the installation path. It is recommended
|
||||||
|
to separate your custom build from the default system package. Please
|
||||||
|
replace ``LLVM_INSTALL_PATH`` with your own path.
|
||||||
|
|
||||||
|
3. Run ``REQUIRES_RTTI=1 make install`` to build and install.
|
||||||
|
|
||||||
|
**Note**: With LLVM 3.2, the default build configuration has C++ RTTI
|
||||||
|
disabled. However, llvmpy requires RTTI.
|
||||||
|
|
||||||
|
**Note**: Use ``make -j2 install`` to enable concurrent build.
|
||||||
|
Replace ``2`` with the actual number of processor you have.
|
||||||
|
|
||||||
|
4. Get llvm-py and install it::
|
||||||
|
|
||||||
$ git clone git@github.com:llvmpy/llvmpy.git
|
$ git clone git@github.com:llvmpy/llvmpy.git
|
||||||
$ cd llvmpy
|
$ cd llvmpy
|
||||||
$ python setup.py install
|
$ LLVM_CONFIG_PATH=LLVM_INSTALL_PATH/bin/llvm-config python setup.py install
|
||||||
|
|
||||||
Run the tests::
|
Run the tests::
|
||||||
|
|
||||||
$ python -c "import llvm; llvm.test()"
|
$ python -c "import llvm; llvm.test()"
|
||||||
|
|
||||||
3. See documentation at 'http://www.llvmpy.org/pages.html' and examples
|
5. See documentation at 'http://www.llvmpy.org' and examples
|
||||||
under 'test'.
|
under 'test'.
|
||||||
|
|
||||||
|
Common Build Problems
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
1. If llvmpy cannot be imported due to "undefined symbol:
|
||||||
|
_ZTIN4llvm24PassRegistrationListenerE", it is because RTTI is not enabled
|
||||||
|
when building LLVM. "_ZTIN4llvm24PassRegistrationListenerE" is the typeinfo
|
||||||
|
of PassRegistrationListener class.
|
||||||
|
|
||||||
LICENSE
|
LICENSE
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
||||||
144
README_LLVM_CBUILDER.md
Normal file
144
README_LLVM_CBUILDER.md
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
LLVM CBuilder
|
||||||
|
=============
|
||||||
|
|
||||||
|
A few short examples:
|
||||||
|
(TODO: add more later)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from llvm.core import *
|
||||||
|
from llvm_cbuilder import *
|
||||||
|
import llvm_cbuilder.shortnames as C
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Square(CDefinition):
|
||||||
|
_name_ = 'square'
|
||||||
|
_retty_ = C.double
|
||||||
|
_argtys_ = [ ('x', C.double) ]
|
||||||
|
|
||||||
|
def body(self, x):
|
||||||
|
y = x * x
|
||||||
|
self.ret(y)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
m = Module.new('my_module')
|
||||||
|
llvm_square = Square()(m)
|
||||||
|
print(m)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
; ModuleID = 'my_module'
|
||||||
|
|
||||||
|
define double @square(double %x) {
|
||||||
|
decl:
|
||||||
|
%0 = fmul double %x, %x
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
class IsPrime(CDefinition):
|
||||||
|
_name_ = 'isprime'
|
||||||
|
_retty_ = C.int
|
||||||
|
_argtys_ = [('x', C.int)]
|
||||||
|
|
||||||
|
def body(self, x):
|
||||||
|
false = zero = self.constant(C.int, 0)
|
||||||
|
true = one = self.constant(C.int, 1)
|
||||||
|
|
||||||
|
two = self.constant(C.int, 2)
|
||||||
|
|
||||||
|
with self.ifelse( x <= two ) as ifelse:
|
||||||
|
with ifelse.then():
|
||||||
|
self.ret(true)
|
||||||
|
|
||||||
|
with self.ifelse( (x % two) == zero ) as ifelse:
|
||||||
|
with ifelse.then():
|
||||||
|
self.ret(false)
|
||||||
|
|
||||||
|
idx = self.var(C.int, 3, name='idx')
|
||||||
|
|
||||||
|
with self.loop() as loop:
|
||||||
|
with loop.condition() as setcond:
|
||||||
|
setcond( idx < x )
|
||||||
|
|
||||||
|
with loop.body():
|
||||||
|
with self.ifelse( (x % idx ) == zero ) as ifelse:
|
||||||
|
with ifelse.then():
|
||||||
|
self.ret(false)
|
||||||
|
idx += two
|
||||||
|
|
||||||
|
self.ret(true)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
define i32 @isprime(i32 %x) {
|
||||||
|
decl:
|
||||||
|
%0 = icmp sle i32 %x, 2
|
||||||
|
br i1 %0, label %if.then, label %if.end
|
||||||
|
|
||||||
|
if.then: ; preds = %loop.body, %loop.cond, %if.end, %decl
|
||||||
|
%merge = phi i32 [ 1, %decl ], [ 0, %if.end ], [ 1, %loop.cond ], [ 0, %loop.body ]
|
||||||
|
ret i32 %merge
|
||||||
|
|
||||||
|
if.end: ; preds = %decl
|
||||||
|
%1 = srem i32 %x, 2
|
||||||
|
%2 = icmp eq i32 %1, 0
|
||||||
|
br i1 %2, label %if.then, label %if.end4
|
||||||
|
|
||||||
|
if.end4: ; preds = %if.end
|
||||||
|
br label %loop.cond
|
||||||
|
|
||||||
|
loop.cond: ; preds = %if.end7, %if.end4
|
||||||
|
%idx.0 = phi i32 [ 3, %if.end4 ], [ %6, %if.end7 ]
|
||||||
|
%3 = icmp slt i32 %idx.0, %x
|
||||||
|
br i1 %3, label %loop.body, label %if.then
|
||||||
|
|
||||||
|
loop.body: ; preds = %loop.cond
|
||||||
|
%4 = srem i32 %x, %idx.0
|
||||||
|
%5 = icmp eq i32 %4, 0
|
||||||
|
br i1 %5, label %if.then, label %if.end7
|
||||||
|
|
||||||
|
if.end7: ; preds = %loop.body
|
||||||
|
%6 = add i32 %idx.0, 2
|
||||||
|
br label %loop.cond
|
||||||
|
}
|
||||||
|
|
||||||
|
; ModuleID = 'my_module'
|
||||||
|
|
||||||
|
define i32 @isprime(i32 %x) {
|
||||||
|
decl:
|
||||||
|
%0 = icmp sle i32 %x, 2
|
||||||
|
br i1 %0, label %if.then, label %if.end
|
||||||
|
|
||||||
|
if.then: ; preds = %loop.body, %loop.cond, %if.end, %decl
|
||||||
|
%merge = phi i32 [ 1, %decl ], [ 0, %if.end ], [ 1, %loop.cond ], [ 0, %loop.body ]
|
||||||
|
ret i32 %merge
|
||||||
|
|
||||||
|
if.end: ; preds = %decl
|
||||||
|
%1 = srem i32 %x, 2
|
||||||
|
%2 = icmp eq i32 %1, 0
|
||||||
|
br i1 %2, label %if.then, label %if.end4
|
||||||
|
|
||||||
|
if.end4: ; preds = %if.end
|
||||||
|
br label %loop.cond
|
||||||
|
|
||||||
|
loop.cond: ; preds = %if.end7, %if.end4
|
||||||
|
%idx.0 = phi i32 [ 3, %if.end4 ], [ %6, %if.end7 ]
|
||||||
|
%3 = icmp slt i32 %idx.0, %x
|
||||||
|
br i1 %3, label %loop.body, label %if.then
|
||||||
|
|
||||||
|
loop.body: ; preds = %loop.cond
|
||||||
|
%4 = srem i32 %x, %idx.0
|
||||||
|
%5 = icmp eq i32 %4, 0
|
||||||
|
br i1 %5, label %if.then, label %if.end7
|
||||||
|
|
||||||
|
if.end7: ; preds = %loop.body
|
||||||
|
%6 = add i32 %idx.0, 2
|
||||||
|
br label %loop.cond
|
||||||
|
}
|
||||||
|
```
|
||||||
6
buildscripts/condarecipe/bld.bat
Normal file
6
buildscripts/condarecipe/bld.bat
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
set LLVMPY_DYNLINK=0
|
||||||
|
set INCLUDE=%LIBRARY_INC%
|
||||||
|
set LIBPATH=%LIBRARY_LIB%
|
||||||
|
set LIB=%LIBRARY_LIB%
|
||||||
|
%PYTHON% setup.py install
|
||||||
|
if errorlevel 1 exit 1
|
||||||
13
buildscripts/condarecipe/build.sh
Normal file
13
buildscripts/condarecipe/build.sh
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ (`uname` == Linux) && (`uname -m` != armv6l) ]]
|
||||||
|
then
|
||||||
|
export CC=gcc
|
||||||
|
#gcc44
|
||||||
|
export CXX=g++
|
||||||
|
#g++44
|
||||||
|
fi
|
||||||
|
|
||||||
|
export LLVMPY_DYNLINK=$DISTRO_BUILD
|
||||||
|
|
||||||
|
$PYTHON setup.py install
|
||||||
28
buildscripts/condarecipe/meta.yaml
Normal file
28
buildscripts/condarecipe/meta.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package:
|
||||||
|
name: llvmpy
|
||||||
|
version: 99.9.9
|
||||||
|
|
||||||
|
source:
|
||||||
|
git_url: git@github.com:llvmpy/llvmpy.git
|
||||||
|
# git_tag: 0.12.0
|
||||||
|
|
||||||
|
requirements:
|
||||||
|
build:
|
||||||
|
- llvm
|
||||||
|
- python
|
||||||
|
#- chrpath [linux]
|
||||||
|
run:
|
||||||
|
- llvm [unix]
|
||||||
|
- python
|
||||||
|
|
||||||
|
test:
|
||||||
|
imports:
|
||||||
|
- llvm
|
||||||
|
- llvmpy
|
||||||
|
- llvmpy._api
|
||||||
|
- llvmpy._capsule
|
||||||
|
- llpython
|
||||||
|
- llvm_array
|
||||||
|
- llvm_cbuilder
|
||||||
|
|
||||||
|
|
||||||
22
buildscripts/condarecipe/run_test.py
Normal file
22
buildscripts/condarecipe/run_test.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import sys
|
||||||
|
import platform
|
||||||
|
import llvm
|
||||||
|
|
||||||
|
from llvm.core import Module
|
||||||
|
from llvm.ee import EngineBuilder
|
||||||
|
from llvm.utils import check_intrinsics
|
||||||
|
|
||||||
|
m = Module.new('fjoidajfa')
|
||||||
|
eb = EngineBuilder.new(m)
|
||||||
|
target = eb.select_target()
|
||||||
|
|
||||||
|
print('target.triple=%r' % target.triple)
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
s = {'64bit': 'x86_64', '32bit': 'x86'}[platform.architecture()[0]]
|
||||||
|
assert target.triple.startswith(s + '-apple-darwin')
|
||||||
|
|
||||||
|
assert llvm.test(verbosity=2, run_isolated=False) == 0
|
||||||
|
#check_intrinsics.main()
|
||||||
|
|
||||||
|
print('llvm.__version__: %s' % llvm.__version__)
|
||||||
|
#assert llvm.__version__ == '0.12.0'
|
||||||
|
|
@ -30,7 +30,7 @@ from subprocess import Popen, PIPE, CalledProcessError, check_call
|
||||||
pages_dir = 'gh-pages'
|
pages_dir = 'gh-pages'
|
||||||
html_dir = '_build/html'
|
html_dir = '_build/html'
|
||||||
pdf_dir = '_build/latex'
|
pdf_dir = '_build/latex'
|
||||||
pages_repo = 'git@github.com:llvmpy/llvmpy-doc.git'
|
pages_repo = 'https://github.com/llvmpy/llvmpy-doc.git'
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# Functions
|
# Functions
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,23 @@
|
||||||
# All configuration values have a default; values that are commented out
|
# All configuration values have a default; values that are commented out
|
||||||
# serve to show the default.
|
# serve to show the default.
|
||||||
|
|
||||||
import sys, os
|
import sys, os, glob
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
#sys.path.insert(0, os.path.abspath('../..'))
|
||||||
|
|
||||||
|
# Support sphinx.ext.autodoc to extract docstrings from modules without installing
|
||||||
|
# complete package.
|
||||||
|
# The python modules depend on _core, so we must build entire package first though.
|
||||||
|
built_lib = glob.glob('../../build/lib.*-%d.%d/' % sys.version_info[:2])
|
||||||
|
if not built_lib:
|
||||||
|
sys.stderr.write("WARNING: To build complete documentation you must build "
|
||||||
|
"package first\n")
|
||||||
|
else:
|
||||||
|
# lib dir has platform suffix
|
||||||
|
sys.path.insert(0, os.path.abspath(built_lib[0]))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -25,7 +36,7 @@ import sys, os
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.mathjax']
|
extensions = ['sphinx.ext.mathjax', 'sphinx.ext.autodoc']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
@ -41,16 +52,22 @@ master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'llvmpy'
|
project = u'llvmpy'
|
||||||
copyright = u'2012, Mahadevan R (2008-2010), Continuum Analytics (2012)'
|
copyright = u'2013, Mahadevan R (2008-2010), Continuum Analytics (2012-2013)'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
try:
|
||||||
version = '0.8'
|
import llvm
|
||||||
# The full version, including alpha/beta/rc tags.
|
version_strs = llvm.__version__.split('.')
|
||||||
release = '0.8.2'
|
# The short X.Y version.
|
||||||
|
version = '.'.join(version_strs[:2])
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '%s.%s' % (version, '-'.join(version_strs[2].split('-')[:2]))
|
||||||
|
except ImportError:
|
||||||
|
version = 'unknown-version'
|
||||||
|
release = 'unknown-release'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
|
|
||||||
9
docs/source/doc/core.rst
Normal file
9
docs/source/doc/core.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
********************************
|
||||||
|
llvm.core
|
||||||
|
********************************
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
llvm.core.*
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
A More Complicated Function
|
A More Complicated Function
|
||||||
====================
|
===========================
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Both LLVM and llvmpy are distributed under (different) permissive open
|
||||||
source licenses. llvmpy uses the `new BSD
|
source licenses. llvmpy uses the `new BSD
|
||||||
license <http://opensource.org/licenses/bsd-license.php>`_. More
|
license <http://opensource.org/licenses/bsd-license.php>`_. More
|
||||||
information is available
|
information is available
|
||||||
`here <https://github.com/numba/llvmpy/blob/master/LICENSE>`_.
|
`here <https://github.com/llvmpy/llvmpy/blob/master/LICENSE>`_.
|
||||||
|
|
||||||
Platforms
|
Platforms
|
||||||
---------
|
---------
|
||||||
|
|
@ -38,12 +38,11 @@ supported, for a variety of reasons.
|
||||||
Versions
|
Versions
|
||||||
--------
|
--------
|
||||||
|
|
||||||
llvmpy 0.8.2 requires version 3.1 of LLVM. It may not work with
|
llvmpy 0.11.2 uses LLVM 3.2 (or at least 3.1). It may not work with
|
||||||
previous versions.
|
previous versions.
|
||||||
|
|
||||||
llvmpy has been built and tested with Python 2.7. It should work with
|
llvmpy has been built and tested with Python 2.7 and 3.2. It should work with
|
||||||
earlier versions. It has not been tried with Python 3.x (patches
|
earlier versions.
|
||||||
welcome).
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
|
@ -66,22 +65,6 @@ It does not matter which compiler LLVM itself was built with (``g++``,
|
||||||
``llvm-g++`` or any other); llvmpy can be built with any compiler. It
|
``llvm-g++`` or any other); llvmpy can be built with any compiler. It
|
||||||
has been tried only with gcc/g++ though.
|
has been tried only with gcc/g++ though.
|
||||||
|
|
||||||
|
|
||||||
LLVM and ``--enable-pic``
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
The result of an LLVM build is a set of static libraries and object
|
|
||||||
files. The llvmpy contains an extension package that is built into a
|
|
||||||
shared object (\_core.so) which links to these static libraries and
|
|
||||||
object files. It is therefore required that the LLVM libraries and
|
|
||||||
object files be built with the ``-fPIC`` option (generate position
|
|
||||||
independent code). Be sure to use the ``--enable-pic`` option while
|
|
||||||
configuring LLVM (default is no PIC), like this:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ ~/llvm ./configure --enable-pic --enable-optimized
|
|
||||||
|
|
||||||
llvm-config
|
llvm-config
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
@ -99,13 +82,34 @@ is different from that of 'root', so even if ``llvm-config`` is in your
|
||||||
Steps
|
Steps
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Get 3.1 version of LLVM, build it. Make sure '--enable-pic' is passed to
|
1. Get and extract LLVM 3.2 source tarball from
|
||||||
LLVM's 'configure'.
|
`llvm.org <http://llvm.org/releases/download.html#3.2>`_. Then, ``cd`` into
|
||||||
|
the extracted directory.
|
||||||
|
|
||||||
Get llvmpy and install it:
|
2. Run ``./configure --enable-optimized --prefix=LLVM_INSTALL_PATH``.
|
||||||
|
|
||||||
|
**Note**: Without the ``--enable-optimized`` flag, debug build will be
|
||||||
|
selected. Unless you are developing LLVM or llvmpy, it is recommended
|
||||||
|
that the flag is used to reduce build time and binary size.
|
||||||
|
|
||||||
.. code-block:: bash
|
**Note**: Use prefix to select the installation path. It is recommended
|
||||||
|
to separate your custom build from the default system package. Please
|
||||||
|
replace ``LLVM_INSTALL_PATH`` with your own path.
|
||||||
|
|
||||||
$ git clone git@github.com:numba/llvmpy.git
|
3. Run ``REQUIRES_RTTI=1 make`` to build.
|
||||||
$ cd llvmpy $ python setup.py install
|
|
||||||
|
**Note**: With LLVM 3.2, the default build configuration has C++ RTTI
|
||||||
|
disabled. However, llvmpy requires RTTI.
|
||||||
|
|
||||||
|
4. Get llvm-py and install it::
|
||||||
|
|
||||||
|
$ git clone git@github.com:llvmpy/llvmpy.git
|
||||||
|
$ cd llvmpy
|
||||||
|
$ LLVM_CONFIG_PATH=LLVM_INSTALL_PATH/bin/llvm-config python setup.py install
|
||||||
|
|
||||||
|
Run the tests::
|
||||||
|
|
||||||
|
$ python -c "import llvm; llvm.test()"
|
||||||
|
|
||||||
|
5. See documentation at 'http://www.llvmpy.org' and examples
|
||||||
|
under 'test'.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ Written by `Chris Lattner <mailto:sabre@nondot.org>`_ and `Max
|
||||||
Shawabkeh <http://max99x.com>`_
|
Shawabkeh <http://max99x.com>`_
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
=========
|
============
|
||||||
|
|
||||||
Welcome to the "Implementing a language with LLVM" tutorial. This
|
Welcome to the "Implementing a language with LLVM" tutorial. This
|
||||||
tutorial runs through the implementation of a simple language, showing
|
tutorial runs through the implementation of a simple language, showing
|
||||||
|
|
@ -115,13 +115,13 @@ following simple example computes `Fibonacci
|
||||||
numbers <http://en.wikipedia.org/wiki/Fibonacci_number>`_:
|
numbers <http://en.wikipedia.org/wiki/Fibonacci_number>`_:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: text
|
||||||
|
|
||||||
# Compute the x'th fibonacci number.
|
# Compute the x'th fibonacci number.
|
||||||
def fib(x):
|
def fib(x)
|
||||||
if x < 3:
|
if x < 3
|
||||||
return 1
|
return 1
|
||||||
else:
|
else
|
||||||
return fib(x-1)+fib(x-2)
|
return fib(x-1)+fib(x-2)
|
||||||
|
|
||||||
# This expression will compute the 40th number.
|
# This expression will compute the 40th number.
|
||||||
|
|
@ -226,7 +226,7 @@ the line.
|
||||||
|
|
||||||
# Regular expressions that tokens and comments of our language.
|
# Regular expressions that tokens and comments of our language.
|
||||||
REGEX_NUMBER = re.compile('[0-9]+(?:.[0-9]+)?')
|
REGEX_NUMBER = re.compile('[0-9]+(?:.[0-9]+)?')
|
||||||
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]\ *')
|
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]*')
|
||||||
REGEX_COMMENT = re.compile('#.*')
|
REGEX_COMMENT = re.compile('#.*')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -772,7 +772,7 @@ Lexer
|
||||||
|
|
||||||
# Regular expressions that tokens and comments of our language.
|
# Regular expressions that tokens and comments of our language.
|
||||||
REGEX_NUMBER = re.compile('[0-9]+(?:\.[0-9]+)?')
|
REGEX_NUMBER = re.compile('[0-9]+(?:\.[0-9]+)?')
|
||||||
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9] *')
|
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]*')
|
||||||
REGEX_COMMENT = re.compile('#.*')
|
REGEX_COMMENT = re.compile('#.*')
|
||||||
|
|
||||||
def Tokenize(string):
|
def Tokenize(string):
|
||||||
|
|
@ -811,7 +811,7 @@ Lexer
|
||||||
yield CharacterToken(string[0])
|
yield CharacterToken(string[0])
|
||||||
string = string[1:]
|
string = string[1:]
|
||||||
|
|
||||||
yield EOFToken()
|
yield EOFToken()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1058,7 +1058,7 @@ Main driver code.
|
||||||
'+': 20,
|
'+': 20,
|
||||||
'-': 20,
|
'-': 20,
|
||||||
'*': 40
|
'*': 40
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run the main "interpreter loop".
|
# Run the main "interpreter loop".
|
||||||
while True:
|
while True:
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,8 @@ Kaleidoscope looks something like this:
|
||||||
br i1 %ifcond, label %then, label %else
|
br i1 %ifcond, label %then, label %else
|
||||||
|
|
||||||
then: ; preds = %entry
|
then: ; preds = %entry
|
||||||
%calltmp1 = call double @bar()
|
%calltmp = call double @foo()
|
||||||
|
br label %ifcont
|
||||||
|
|
||||||
else: ; preds = %entry
|
else: ; preds = %entry
|
||||||
%calltmp1 = call double @bar()
|
%calltmp1 = call double @bar()
|
||||||
|
|
|
||||||
|
|
@ -847,7 +847,7 @@ Lexer
|
||||||
|
|
||||||
# Regular expressions that tokens and comments of our language.
|
# Regular expressions that tokens and comments of our language.
|
||||||
REGEX_NUMBER = re.compile('[0-9]+(?:\.[0-9]+)?')
|
REGEX_NUMBER = re.compile('[0-9]+(?:\.[0-9]+)?')
|
||||||
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9] *')
|
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]*')
|
||||||
REGEX_COMMENT = re.compile('#.*')
|
REGEX_COMMENT = re.compile('#.*')
|
||||||
|
|
||||||
def Tokenize(string):
|
def Tokenize(string):
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,8 @@ to talk about how LLVM represents stack variables.
|
||||||
In LLVM, all memory accesses are explicit with load/store instructions,
|
In LLVM, all memory accesses are explicit with load/store instructions,
|
||||||
and it is carefully designed not to have (or need) an "address-of"
|
and it is carefully designed not to have (or need) an "address-of"
|
||||||
operator. Notice how the type of the @G/@H global variables is actually
|
operator. Notice how the type of the @G/@H global variables is actually
|
||||||
"i32\ *" even though the variable is defined as "i32". What this means
|
"i32\*" even though the variable is defined as "i32". What this means
|
||||||
is that @G defines* space* for an i32 in the global data area, but its
|
is that @G defines *space* for an i32 in the global data area, but its
|
||||||
*name* actually refers to the address for that space. Stack variables
|
*name* actually refers to the address for that space. Stack variables
|
||||||
work the same way, except that instead of being declared with global
|
work the same way, except that instead of being declared with global
|
||||||
variable definitions, they are declared with the `LLVM alloca
|
variable definitions, they are declared with the `LLVM alloca
|
||||||
|
|
@ -133,11 +133,11 @@ instruction <http://www.llvm.org/docs/LangRef.html#i_alloca>`_:
|
||||||
|
|
||||||
define i32 @example() {
|
define i32 @example() {
|
||||||
entry:
|
entry:
|
||||||
%X = alloca i32 ; type of %X is i32 *.
|
%X = alloca i32 ; type of %X is i32*
|
||||||
...
|
...
|
||||||
%tmp = load i32* %X ; load the stack value %X from the stack.
|
%tmp = load i32* %X ; load the stack value %X from the stack
|
||||||
%tmp2 = add i32 %tmp, 1 ; increment it store i32 %tmp2,
|
%tmp2 = add i32 %tmp, 1 ; increment it
|
||||||
i32* %X ; store it back
|
store i32 %tmp2, i32* %X ; store it back
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -295,7 +295,7 @@ variables is a useful thing regardless of whether you will be mutating
|
||||||
them. Here's a motivating example that shows how we could use these:
|
them. Here's a motivating example that shows how we could use these:
|
||||||
|
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: none
|
||||||
|
|
||||||
# Define ':' for sequencing: as a low-precedence operator that ignores operands
|
# Define ':' for sequencing: as a low-precedence operator that ignores operands
|
||||||
# and just returns the RHS.
|
# and just returns the RHS.
|
||||||
|
|
@ -352,7 +352,7 @@ function that ensures that the allocas are created in the entry block of
|
||||||
the function:
|
the function:
|
||||||
|
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: python
|
||||||
|
|
||||||
# Creates an alloca instruction in the entry block of the function. This is used
|
# Creates an alloca instruction in the entry block of the function. This is used
|
||||||
# for mutable variables.
|
# for mutable variables.
|
||||||
|
|
@ -475,7 +475,7 @@ It is interesting to see what the code looks like before and after the
|
||||||
mem2reg optimization runs. For example, this is the before/after code
|
mem2reg optimization runs. For example, this is the before/after code
|
||||||
for our recursive fib function. Before the optimization:
|
for our recursive fib function. Before the optimization:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: llvm
|
||||||
|
|
||||||
define double @fib(double %x) {
|
define double @fib(double %x) {
|
||||||
entry:
|
entry:
|
||||||
|
|
@ -515,7 +515,7 @@ still just make the PHI.
|
||||||
|
|
||||||
Here is the code after the mem2reg pass runs:
|
Here is the code after the mem2reg pass runs:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: llvm
|
||||||
|
|
||||||
define double @fib(double %x) {
|
define double @fib(double %x) {
|
||||||
entry:
|
entry:
|
||||||
|
|
@ -651,7 +651,7 @@ Now that we have an assignment operator, we can mutate loop variables
|
||||||
and arguments. For example, we can now run code like this:
|
and arguments. For example, we can now run code like this:
|
||||||
|
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: none
|
||||||
|
|
||||||
# Function to print a double.
|
# Function to print a double.
|
||||||
extern printd(x)
|
extern printd(x)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
*************************************************
|
***************************************************
|
||||||
Chapter 8: Conclusion and other useful LLVM tidbits
|
Chapter 8: Conclusion and other useful LLVM tidbits
|
||||||
*************************************************
|
***************************************************
|
||||||
|
|
||||||
Written by Chris Lattner
|
Written by Chris Lattner
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ generating LLVM IR. These are some of the more subtle things that may not be obv
|
||||||
but are very useful if you want to take advantage of LLVM's capabilities.
|
but are very useful if you want to take advantage of LLVM's capabilities.
|
||||||
|
|
||||||
Properties of the LLVM IR
|
Properties of the LLVM IR
|
||||||
========================
|
=========================
|
||||||
|
|
||||||
We have a couple common questions about code in the LLVM IR form - let's
|
We have a couple common questions about code in the LLVM IR form - let's
|
||||||
just get these out of the way right now, shall we?
|
just get these out of the way right now, shall we?
|
||||||
|
|
@ -164,7 +164,7 @@ This can make sense for specialized domains such as an in-kernel language.
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Safety Guarantees
|
Safety Guarantees
|
||||||
----------------
|
-----------------
|
||||||
|
|
||||||
Many of the languages above are also "safe" languages: it is
|
Many of the languages above are also "safe" languages: it is
|
||||||
impossible for a program written in Java to corrupt its address space and
|
impossible for a program written in Java to corrupt its address space and
|
||||||
|
|
@ -236,7 +236,7 @@ you desire in your front-end, on the language-specific AST.
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Tips and Tricks
|
Tips and Tricks
|
||||||
==============
|
===============
|
||||||
|
|
||||||
There is a variety of useful tips and tricks that you come to
|
There is a variety of useful tips and tricks that you come to
|
||||||
know after working on/with LLVM that aren't obvious at first glance.
|
know after working on/with LLVM that aren't obvious at first glance.
|
||||||
|
|
|
||||||
10
docs/source/doc/llpython/articles.rst
Normal file
10
docs/source/doc/llpython/articles.rst
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
LLPython Articles
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
intro_llpython.rst
|
||||||
|
nobitey_dev.rst
|
||||||
|
ctmp_in_llpython.rst
|
||||||
5
docs/source/doc/llpython/ctmp_in_llpython.rst
Normal file
5
docs/source/doc/llpython/ctmp_in_llpython.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
Compile-time Metaprogramming in LLPython
|
||||||
|
========================================
|
||||||
|
|
||||||
|
In this article, we discuss how LLPython supports compile-time
|
||||||
|
metaprogramming.
|
||||||
9
docs/source/doc/llpython/index.rst
Normal file
9
docs/source/doc/llpython/index.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
LLPython Documentation
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
articles.rst
|
||||||
|
reference.rst
|
||||||
46
docs/source/doc/llpython/intro_llpython.rst
Normal file
46
docs/source/doc/llpython/intro_llpython.rst
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
====================
|
||||||
|
Introducing LLPython
|
||||||
|
====================
|
||||||
|
|
||||||
|
In this article, we introduce the llpython package. The primary goal
|
||||||
|
of the llpython package is to provide a Python dialect/subset that
|
||||||
|
maps directly to LLVM code. LLPython differs from its originating
|
||||||
|
LLVM translator, Numba, in the following aspects:
|
||||||
|
|
||||||
|
* LLPython code is not intended to work in Python if not translated
|
||||||
|
and wrapped.
|
||||||
|
* The LLPython translator only uses LLVM types.
|
||||||
|
* LLPython is explicitly typed, and does not support type inference.
|
||||||
|
LLPython does not support implicit casts, all casts must be explicit.
|
||||||
|
* LLPython supports code that directly calls the C API, the Python C
|
||||||
|
API, and the llvm.core.Builder methods.
|
||||||
|
|
||||||
|
Additionally, we designed the sub-package to have the following
|
||||||
|
engineering properties:
|
||||||
|
|
||||||
|
* Usable from Python 2.7, and 3.X. At the time of writing, we plan
|
||||||
|
to support Python 2.6.
|
||||||
|
* Clean from Numba dependencies (other than llvmpy), and can be used
|
||||||
|
as a standalone code generator without a full Numba installation.
|
||||||
|
* Provides a series of Python bytecode passes that can be easily
|
||||||
|
used by other projects.
|
||||||
|
|
||||||
|
|
||||||
|
LLPython Origins
|
||||||
|
================
|
||||||
|
|
||||||
|
We developed LLPython with the initial goal of simplifying writing
|
||||||
|
LLVM code.
|
||||||
|
|
||||||
|
|
||||||
|
LLPython Internals
|
||||||
|
==================
|
||||||
|
|
||||||
|
In this section, we describe the various passes performed by the
|
||||||
|
LLPython translator.
|
||||||
|
|
||||||
|
|
||||||
|
Conclusions
|
||||||
|
===========
|
||||||
|
|
||||||
|
LLPython is neat.
|
||||||
6
docs/source/doc/llpython/llpython.byte_control.rst
Normal file
6
docs/source/doc/llpython/llpython.byte_control.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
=====================
|
||||||
|
llpython.byte_control
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. automodule:: llpython.byte_control
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.byte_flow.rst
Normal file
6
docs/source/doc/llpython/llpython.byte_flow.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
==================
|
||||||
|
llpython.byte_flow
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. automodule:: llpython.byte_flow
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.byte_translator.rst
Normal file
6
docs/source/doc/llpython/llpython.byte_translator.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
========================
|
||||||
|
llpython.byte_translator
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. automodule:: llpython.byte_translator
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.bytecode_visitor.rst
Normal file
6
docs/source/doc/llpython/llpython.bytecode_visitor.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
=========================
|
||||||
|
llpython.bytecode_visitor
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: llpython.bytecode_visitor
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.bytetype.rst
Normal file
6
docs/source/doc/llpython/llpython.bytetype.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
=================
|
||||||
|
llpython.bytetype
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. automodule:: llpython.bytetype
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.control_flow.rst
Normal file
6
docs/source/doc/llpython/llpython.control_flow.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
=====================
|
||||||
|
llpython.control_flow
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. automodule:: llpython.control_flow
|
||||||
|
:members:
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
=============================
|
||||||
|
llpython.gen_bytecode_visitor
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. automodule:: llpython.gen_bytecode_visitor
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.nobitey.rst
Normal file
6
docs/source/doc/llpython/llpython.nobitey.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
================
|
||||||
|
llpython.nobitey
|
||||||
|
================
|
||||||
|
|
||||||
|
.. automodule:: llpython.nobitey
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.opcode_util.rst
Normal file
6
docs/source/doc/llpython/llpython.opcode_util.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
====================
|
||||||
|
llpython.opcode_util
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. automodule:: llpython.opcode_util
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.phi_injector.rst
Normal file
6
docs/source/doc/llpython/llpython.phi_injector.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
=====================
|
||||||
|
llpython.phi_injector
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. automodule:: llpython.phi_injector
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.pyaddfunc.rst
Normal file
6
docs/source/doc/llpython/llpython.pyaddfunc.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
==================
|
||||||
|
llpython.pyaddfunc
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. automodule:: llpython.pyaddfunc
|
||||||
|
:members:
|
||||||
6
docs/source/doc/llpython/llpython.rst
Normal file
6
docs/source/doc/llpython/llpython.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
========
|
||||||
|
llpython
|
||||||
|
========
|
||||||
|
|
||||||
|
.. automodule:: llpython
|
||||||
|
:members:
|
||||||
5
docs/source/doc/llpython/nobitey_dev.rst
Normal file
5
docs/source/doc/llpython/nobitey_dev.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
nobitey: Using ctypes and llvmpy to Bypass ctypes
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
In this article, we show how nobitey uses llvmpy to eliminate the
|
||||||
|
ctypes call overhead.
|
||||||
21
docs/source/doc/llpython/reference.rst
Normal file
21
docs/source/doc/llpython/reference.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
LLPython Module Reference
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
llpython.rst
|
||||||
|
llpython.bytecode_visitor.rst
|
||||||
|
llpython.byte_control.rst
|
||||||
|
llpython.byte_flow.rst
|
||||||
|
llpython.byte_translator.rst
|
||||||
|
llpython.bytetype.rst
|
||||||
|
llpython.control_flow.rst
|
||||||
|
llpython.gen_bytecode_visitor.rst
|
||||||
|
llpython.nobitey.rst
|
||||||
|
llpython.opcode_util.rst
|
||||||
|
llpython.phi_injector.rst
|
||||||
|
llpython.pyaddfunc.rst
|
||||||
|
|
@ -56,3 +56,10 @@ Add an attribute ``attr`` to the argument, from the set listed above.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Remove the attribute ``attr`` of the argument.
|
Remove the attribute ``attr`` of the argument.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Argument
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -28,3 +28,10 @@ A ``Type`` object representing the type of the element of the array.
|
||||||
[read-only]
|
[read-only]
|
||||||
|
|
||||||
The number of elements in the array.
|
The number of elements in the array.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.ArrayType
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,10 @@ The parent function of this basicblock.
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A list of instructions in this basicblock.
|
A list of instructions in this basicblock.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.BasicBlock
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -409,3 +409,10 @@ positioned.
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
Deprecated. Same as ``basic_block``
|
Deprecated. Same as ``basic_block``
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Builder
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
| title: Constant (llvm.core) |
|
| title: Constant (llvm.core) |
|
||||||
+-------------------------------+
|
+-------------------------------+
|
||||||
|
|
||||||
|
llvm.core.Constant
|
||||||
|
==================
|
||||||
|
|
||||||
``Constant``-s represents constants that appear within the code. The
|
``Constant``-s represents constants that appear within the code. The
|
||||||
values of such objects are known at creation time. Constants can be
|
values of such objects are known at creation time. Constants can be
|
||||||
created from Python constants. A constant expression is also a constant
|
created from Python constants. A constant expression is also a constant
|
||||||
|
|
@ -23,331 +26,338 @@ some examples:
|
||||||
|
|
||||||
tr = Type.float()
|
tr = Type.float()
|
||||||
|
|
||||||
r1 = Constant.real(tr, "3.141592") # create from a string r2 =
|
r1 = Constant.real(tr, "3.141592") # create from a string
|
||||||
Constant.real(tr, 1.61803399) # create from a Python float {%
|
r2 = Constant.real(tr, 1.61803399) # create from a Python float
|
||||||
endhighlight %}
|
|
||||||
|
|
||||||
# llvm.core.Constant
|
|
||||||
- This will become a table of contents (this text will be scraped).
|
|
||||||
{:toc}
|
|
||||||
|
|
||||||
Static factory methods
|
# llvm.core.Constant
|
||||||
----------------------
|
- This will become a table of contents (this text will be scraped).
|
||||||
|
{:toc}
|
||||||
|
|
||||||
``null(ty)``
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
A null value (all zeros) of type ``ty``
|
Static factory methods
|
||||||
|
----------------------
|
||||||
|
|
||||||
``all_ones(ty)``
|
``null(ty)``
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
All 1's value of type ``ty``
|
A null value (all zeros) of type ``ty``
|
||||||
|
|
||||||
``undef(ty)``
|
``all_ones(ty)``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
An undefined value of type ``ty``
|
All 1's value of type ``ty``
|
||||||
|
|
||||||
``int(ty, value)``
|
``undef(ty)``
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
Integer of type ``ty``, with value ``value`` (a Python int or long)
|
An undefined value of type ``ty``
|
||||||
|
|
||||||
``int_signextend(ty, value)``
|
``int(ty, value)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Integer of signed type ``ty`` (use for signed types)
|
Integer of type ``ty``, with value ``value`` (a Python int or long)
|
||||||
|
|
||||||
``real(ty, value)``
|
``int_signextend(ty, value)``
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Floating point value of type ``ty``, with value ``value`` (a Python
|
Integer of signed type ``ty`` (use for signed types)
|
||||||
float)
|
|
||||||
|
|
||||||
``stringz(value)``
|
``real(ty, value)``
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A null-terminated string. ``value`` is a Python string
|
Floating point value of type ``ty``, with value ``value`` (a Python
|
||||||
|
float)
|
||||||
|
|
||||||
``string(value)``
|
``stringz(value)``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
As ``string(ty)``, but not null terminated
|
A null-terminated string. ``value`` is a Python string
|
||||||
|
|
||||||
``array(ty, consts)``
|
``string(value)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Array of type ``ty``, initialized with ``consts`` (an iterable yielding
|
As ``string(ty)``, but not null terminated
|
||||||
``Constant`` objects of the appropriate type)
|
|
||||||
|
|
||||||
``struct(ty, consts)``
|
``array(ty, consts)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Struct (unpacked) of type ``ty``, initialized with ``consts`` (an
|
Array of type ``ty``, initialized with ``consts`` (an iterable yielding
|
||||||
iterable yielding ``Constant`` objects of the appropriate type)
|
``Constant`` objects of the appropriate type)
|
||||||
|
|
||||||
``packed_struct(ty, consts)``
|
``struct(ty, consts)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
As ``struct(ty, consts)`` but packed
|
Struct (unpacked) of type ``ty``, initialized with ``consts`` (an
|
||||||
|
iterable yielding ``Constant`` objects of the appropriate type)
|
||||||
|
|
||||||
``vector(consts)``
|
``packed_struct(ty, consts)``
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Vector, initialized with ``consts`` (an iterable yielding ``Constant``
|
As ``struct(ty, consts)`` but packed
|
||||||
objects of the appropriate type)
|
|
||||||
|
|
||||||
``sizeof(ty)``
|
``vector(consts)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Constant value representing the sizeof the type ``ty``
|
Vector, initialized with ``consts`` (an iterable yielding ``Constant``
|
||||||
|
objects of the appropriate type)
|
||||||
|
|
||||||
Methods
|
``sizeof(ty)``
|
||||||
-------
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The following operations on constants are supported. For more details on
|
Constant value representing the sizeof the type ``ty``
|
||||||
any operation, consult the `Constant
|
|
||||||
Expressions <http://www.llvm.org/docs/LangRef.html#constantexprs>`_
|
|
||||||
section of the LLVM Language Reference.
|
|
||||||
|
|
||||||
``k.neg()``
|
Methods
|
||||||
~~~~~~~~~~~
|
-------
|
||||||
|
|
||||||
negation, same as ``0 - k``
|
The following operations on constants are supported. For more details on
|
||||||
|
any operation, consult the `Constant
|
||||||
|
Expressions <http://www.llvm.org/docs/LangRef.html#constantexprs>`_
|
||||||
|
section of the LLVM Language Reference.
|
||||||
|
|
||||||
``k.not_()``
|
``k.neg()``
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
1's complement of ``k``. Note trailing underscore.
|
negation, same as ``0 - k``
|
||||||
|
|
||||||
``k.add(k2)``
|
``k.not_()``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
``k + k2``, where ``k`` and ``k2`` are integers.
|
1's complement of ``k``. Note trailing underscore.
|
||||||
|
|
||||||
``k.fadd(k2)``
|
``k.add(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
``k + k2``, where ``k`` and ``k2`` are floating-point.
|
``k + k2``, where ``k`` and ``k2`` are integers.
|
||||||
|
|
||||||
``k.sub(k2)``
|
``k.fadd(k2)``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
``k - k2``, where ``k`` and ``k2`` are integers.
|
``k + k2``, where ``k`` and ``k2`` are floating-point.
|
||||||
|
|
||||||
``k.fsub(k2)``
|
``k.sub(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
``k - k2``, where ``k`` and ``k2`` are floating-point.
|
``k - k2``, where ``k`` and ``k2`` are integers.
|
||||||
|
|
||||||
``k.mul(k2)``
|
``k.fsub(k2)``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
``k * k2``, where ``k`` and ``k2`` are integers.
|
``k - k2``, where ``k`` and ``k2`` are floating-point.
|
||||||
|
|
||||||
``k.fmul(k2)``
|
``k.mul(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
``k * k2``, where ``k`` and ``k2`` are floating-point.
|
``k * k2``, where ``k`` and ``k2`` are integers.
|
||||||
|
|
||||||
``k.udiv(k2)``
|
``k.fmul(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Quotient of unsigned division of ``k`` with ``k2``
|
``k * k2``, where ``k`` and ``k2`` are floating-point.
|
||||||
|
|
||||||
``k.sdiv(k2)``
|
``k.udiv(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Quotient of signed division of ``k`` with ``k2``
|
Quotient of unsigned division of ``k`` with ``k2``
|
||||||
|
|
||||||
``k.fdiv(k2)``
|
``k.sdiv(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Quotient of floating point division of ``k`` with ``k2``
|
Quotient of signed division of ``k`` with ``k2``
|
||||||
|
|
||||||
``k.urem(k2)``
|
``k.fdiv(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Reminder of unsigned division of ``k`` with ``k2``
|
Quotient of floating point division of ``k`` with ``k2``
|
||||||
|
|
||||||
``k.srem(k2)``
|
``k.urem(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Reminder of signed division of ``k`` with ``k2``
|
Reminder of unsigned division of ``k`` with ``k2``
|
||||||
|
|
||||||
``k.frem(k2)``
|
``k.srem(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Reminder of floating point division of ``k`` with ``k2``
|
Reminder of signed division of ``k`` with ``k2``
|
||||||
|
|
||||||
``k.and_(k2)``
|
``k.frem(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Bitwise and of ``k`` and ``k2``. Note trailing underscore.
|
Reminder of floating point division of ``k`` with ``k2``
|
||||||
|
|
||||||
``k.or_(k2)``
|
``k.and_(k2)``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Bitwise or of ``k`` and ``k2``. Note trailing underscore.
|
Bitwise and of ``k`` and ``k2``. Note trailing underscore.
|
||||||
|
|
||||||
``k.xor(k2)``
|
``k.or_(k2)``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
Bitwise exclusive-or of ``k`` and ``k2``.
|
Bitwise or of ``k`` and ``k2``. Note trailing underscore.
|
||||||
|
|
||||||
``k.icmp(icmp, k2)``
|
``k.xor(k2)``
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
Compare ``k`` with ``k2`` using the predicate ``icmp``. See
|
Bitwise exclusive-or of ``k`` and ``k2``.
|
||||||
`here <comparision.html#icmp>`_ for list of predicates for integer
|
|
||||||
operands.
|
|
||||||
|
|
||||||
``k.fcmp(fcmp, k2)``
|
``k.icmp(icmp, k2)``
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Compare ``k`` with ``k2`` using the predicate ``fcmp``. See
|
Compare ``k`` with ``k2`` using the predicate ``icmp``. See
|
||||||
`here <comparision.html#fcmp>`_ for list of predicates for real
|
`here <comparision.html#icmp>`_ for list of predicates for integer
|
||||||
operands.
|
operands.
|
||||||
|
|
||||||
``k.shl(k2)``
|
``k.fcmp(fcmp, k2)``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Shift ``k`` left by ``k2`` bits.
|
Compare ``k`` with ``k2`` using the predicate ``fcmp``. See
|
||||||
|
`here <comparision.html#fcmp>`_ for list of predicates for real
|
||||||
|
operands.
|
||||||
|
|
||||||
``k.lshr(k2)``
|
``k.shl(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
Shift ``k`` logically right by ``k2`` bits (new bits are 0s).
|
Shift ``k`` left by ``k2`` bits.
|
||||||
|
|
||||||
``k.ashr(k2)``
|
``k.lshr(k2)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Shift ``k`` arithmetically right by ``k2`` bits (new bits are same as
|
Shift ``k`` logically right by ``k2`` bits (new bits are 0s).
|
||||||
previous sign bit).
|
|
||||||
|
|
||||||
``k.gep(indices)``
|
``k.ashr(k2)``
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
GEP, see `LLVM docs <http://www.llvm.org/docs/GetElementPtr.html>`_.
|
Shift ``k`` arithmetically right by ``k2`` bits (new bits are same as
|
||||||
|
previous sign bit).
|
||||||
|
|
||||||
``k.trunc(ty)``
|
``k.gep(indices)``
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Truncate ``k`` to a type ``ty`` of lower bitwidth.
|
GEP, see `LLVM docs <http://www.llvm.org/docs/GetElementPtr.html>`_.
|
||||||
|
|
||||||
``k.sext(ty)``
|
``k.trunc(ty)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Sign extend ``k`` to a type ``ty`` of higher bitwidth, while extending
|
Truncate ``k`` to a type ``ty`` of lower bitwidth.
|
||||||
the sign bit.
|
|
||||||
|
|
||||||
``k.zext(ty)``
|
``k.sext(ty)``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Sign extend ``k`` to a type ``ty`` of higher bitwidth, all new bits are
|
Sign extend ``k`` to a type ``ty`` of higher bitwidth, while extending
|
||||||
0s.
|
the sign bit.
|
||||||
|
|
||||||
``k.fptrunc(ty)``
|
``k.zext(ty)``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Truncate floating point constant ``k`` to floating point type ``ty`` of
|
Sign extend ``k`` to a type ``ty`` of higher bitwidth, all new bits are
|
||||||
lower size than k's.
|
0s.
|
||||||
|
|
||||||
``k.fpext(ty)``
|
``k.fptrunc(ty)``
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Extend floating point constant ``k`` to floating point type ``ty`` of
|
Truncate floating point constant ``k`` to floating point type ``ty`` of
|
||||||
higher size than k's.
|
lower size than k's.
|
||||||
|
|
||||||
``k.uitofp(ty)``
|
``k.fpext(ty)``
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert an unsigned integer constant ``k`` to floating point constant of
|
Extend floating point constant ``k`` to floating point type ``ty`` of
|
||||||
type ``ty``.
|
higher size than k's.
|
||||||
|
|
||||||
``k.sitofp(ty)``
|
``k.uitofp(ty)``
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert a signed integer constant ``k`` to floating point constant of
|
Convert an unsigned integer constant ``k`` to floating point constant of
|
||||||
type ``ty``.
|
type ``ty``.
|
||||||
|
|
||||||
``k.fptoui(ty)``
|
``k.sitofp(ty)``
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert a floating point constant ``k`` to an unsigned integer constant
|
Convert a signed integer constant ``k`` to floating point constant of
|
||||||
of type ``ty``.
|
type ``ty``.
|
||||||
|
|
||||||
``k.fptosi(ty)``
|
``k.fptoui(ty)``
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert a floating point constant ``k`` to a signed integer constant of
|
Convert a floating point constant ``k`` to an unsigned integer constant
|
||||||
type ``ty``.
|
of type ``ty``.
|
||||||
|
|
||||||
``k.ptrtoint(ty)``
|
``k.fptosi(ty)``
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert a pointer constant ``k`` to an integer constant of type ``ty``.
|
Convert a floating point constant ``k`` to a signed integer constant of
|
||||||
|
type ``ty``.
|
||||||
|
|
||||||
``k.inttoptr(ty)``
|
``k.ptrtoint(ty)``
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert an integer constant ``k`` to a pointer constant of type ``ty``.
|
Convert a pointer constant ``k`` to an integer constant of type ``ty``.
|
||||||
|
|
||||||
``k.bitcast(ty)``
|
``k.inttoptr(ty)``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Convert ``k`` to a (equal-width) constant of type ``ty``.
|
Convert an integer constant ``k`` to a pointer constant of type ``ty``.
|
||||||
|
|
||||||
``k.select(cond,k2,k3)``
|
``k.bitcast(ty)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Replace value with ``k2`` if the 1-bit integer constant ``cond`` is 1,
|
Convert ``k`` to a (equal-width) constant of type ``ty``.
|
||||||
else with ``k3``.
|
|
||||||
|
|
||||||
``k.extract_element(idx)``
|
``k.select(cond,k2,k3)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Extract value at ``idx`` (integer constant) from a vector constant
|
Replace value with ``k2`` if the 1-bit integer constant ``cond`` is 1,
|
||||||
``k``.
|
else with ``k3``.
|
||||||
|
|
||||||
``k.insert_element(k2,idx)``
|
``k.extract_element(idx)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Insert value ``k2`` (scalar constant) at index ``idx`` (integer
|
Extract value at ``idx`` (integer constant) from a vector constant
|
||||||
constant) of vector constant ``k``.
|
``k``.
|
||||||
|
|
||||||
``k.shuffle_vector(k2,mask)``
|
``k.insert_element(k2,idx)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Shuffle vector constant ``k`` based on vector constants ``k2`` and
|
Insert value ``k2`` (scalar constant) at index ``idx`` (integer
|
||||||
``mask``.
|
constant) of vector constant ``k``.
|
||||||
|
|
||||||
--------------
|
``k.shuffle_vector(k2,mask)``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
# Other Constant Classes
|
Shuffle vector constant ``k`` based on vector constants ``k2`` and
|
||||||
The following subclasses of ``Constant`` do not provide additional
|
``mask``.
|
||||||
methods, **they serve only to provide richer type information.**
|
|
||||||
|
|
||||||
Subclass \| LLVM C++ Class \| Remarks \|
|
--------------
|
||||||
---------\|----------------\|---------\| ``ConstantExpr`` \|
|
|
||||||
``llvmConstantExpr`` \| A constant expression \|
|
|
||||||
``ConstantAggregateZero``\ \| ``llvmConstantAggregateZero``\ \| All-zero
|
|
||||||
constant \| ``ConstantInt``\ \| ``llvmConstantInt``\ \| An integer
|
|
||||||
constant \| ``ConstantFP``\ \| ``llvmConstantFP``\ \| A floating-point
|
|
||||||
constant \| ``ConstantArray``\ \| ``llvmConstantArray``\ \| An array
|
|
||||||
constant \| ``ConstantStruct``\ \| ``llvmConstantStruct``\ \| A
|
|
||||||
structure constant \| ``ConstantVector``\ \| ``llvmConstantVector``\ \|
|
|
||||||
A vector constant \| ``ConstantPointerNull``\ \|
|
|
||||||
``llvmConstantPointerNull``\ \| All-zero pointer constant \|
|
|
||||||
``UndefValue``\ \| ``llvmUndefValue``\ \| corresponds to ``undef`` of
|
|
||||||
LLVM IR \|
|
|
||||||
|
|
||||||
These types are helpful in ``isinstance`` checks, like so:
|
# Other Constant Classes
|
||||||
|
The following subclasses of ``Constant`` do not provide additional
|
||||||
|
methods, **they serve only to provide richer type information.**
|
||||||
|
|
||||||
{% highlight python %} ti = Type.int(32) k1 = Constant.int(ti, 42) #
|
Subclass \| LLVM C++ Class \| Remarks \|
|
||||||
int32_t k1 = 42; k2 = Constant.array(ti, [k1, k1]) # int32_t k2[] = {
|
---------\|----------------\|---------\| ``ConstantExpr`` \|
|
||||||
k1, k1 };
|
``llvmConstantExpr`` \| A constant expression \|
|
||||||
|
``ConstantAggregateZero``\ \| ``llvmConstantAggregateZero``\ \| All-zero
|
||||||
|
constant \| ``ConstantInt``\ \| ``llvmConstantInt``\ \| An integer
|
||||||
|
constant \| ``ConstantFP``\ \| ``llvmConstantFP``\ \| A floating-point
|
||||||
|
constant \| ``ConstantArray``\ \| ``llvmConstantArray``\ \| An array
|
||||||
|
constant \| ``ConstantStruct``\ \| ``llvmConstantStruct``\ \| A
|
||||||
|
structure constant \| ``ConstantVector``\ \| ``llvmConstantVector``\ \|
|
||||||
|
A vector constant \| ``ConstantPointerNull``\ \|
|
||||||
|
``llvmConstantPointerNull``\ \| All-zero pointer constant \|
|
||||||
|
``UndefValue``\ \| ``llvmUndefValue``\ \| corresponds to ``undef`` of
|
||||||
|
LLVM IR \|
|
||||||
|
|
||||||
assert isinstance(k1, ConstantInt) assert isinstance(k2, ConstantArray)
|
These types are helpful in ``isinstance`` checks, like so:
|
||||||
|
|
||||||
|
{% highlight python %} ti = Type.int(32) k1 = Constant.int(ti, 42) #
|
||||||
|
int32_t k1 = 42; k2 = Constant.array(ti, [k1, k1]) # int32_t k2[] = {
|
||||||
|
k1, k1 };
|
||||||
|
|
||||||
|
assert isinstance(k1, ConstantInt) assert isinstance(k2, ConstantArray)
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Constant
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -149,3 +149,10 @@ function bodies.
|
||||||
|
|
||||||
Verifies the function. See `LLVM
|
Verifies the function. See `LLVM
|
||||||
docs <http://llvm.org/docs/Passes.html#verify>`_.
|
docs <http://llvm.org/docs/Passes.html#verify>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Function
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,10 @@ the function. Used like this:
|
||||||
Type.int(), Type.int() ] ) for arg in func_type.args: assert arg.kind
|
Type.int(), Type.int() ] ) for arg in func_type.args: assert arg.kind
|
||||||
== TYPE_INTEGER assert arg == Type.int() assert func_type.arg_count
|
== TYPE_INTEGER assert arg == Type.int() assert func_type.arg_count
|
||||||
== len(func_type.args)
|
== len(func_type.args)
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.FunctionType
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -95,3 +95,9 @@ A power-of-2 integer indicating the boundary to align to.
|
||||||
|
|
||||||
The module object to which this global belongs to.
|
The module object to which this global belongs to.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.GlobalValue
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
| title: GlobalVariable (llvm.core) |
|
| title: GlobalVariable (llvm.core) |
|
||||||
+-------------------------------------+
|
+-------------------------------------+
|
||||||
|
|
||||||
|
llvm.core.GlobalVariable
|
||||||
|
========================
|
||||||
|
|
||||||
Global variables (``llvm.core.GlobalVariable``) are subclasses of
|
Global variables (``llvm.core.GlobalVariable``) are subclasses of
|
||||||
`llvm.core.GlobalValue <llvm.core.GlobalValue.html>`_ and represent
|
`llvm.core.GlobalValue <llvm.core.GlobalValue.html>`_ and represent
|
||||||
module-level variables. These can have optional initializers and can be
|
module-level variables. These can have optional initializers and can be
|
||||||
|
|
@ -37,3 +40,10 @@ class, or by using the static method ``GlobalVariable.new``.
|
||||||
# list all global variables in a module
|
# list all global variables in a module
|
||||||
for gv in module_obj.global_variables: print gv.name, "of type",
|
for gv in module_obj.global_variables: print gv.name, "of type",
|
||||||
gv.type
|
gv.type
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.GlobalVariable
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -240,3 +240,10 @@ Properties
|
||||||
|
|
||||||
The predicate of the compare instruction, one of the ``ICMP_*`` or
|
The predicate of the compare instruction, one of the ``ICMP_*`` or
|
||||||
``FCMP_*`` constants.
|
``FCMP_*`` constants.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Instruction
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,10 @@ Properties
|
||||||
[read-only]
|
[read-only]
|
||||||
|
|
||||||
The width of the integer type, in number of bits.
|
The width of the integer type, in number of bits.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.IntegerType
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,26 @@
|
||||||
| title: Module (llvm.core) |
|
| title: Module (llvm.core) |
|
||||||
+-----------------------------+
|
+-----------------------------+
|
||||||
|
|
||||||
|
llvm.core.Module
|
||||||
|
================
|
||||||
|
|
||||||
Modules are top-level container objects. You need to create a module
|
Modules are top-level container objects. You need to create a module
|
||||||
object first, before you can add global variables, aliases or functions.
|
object first, before you can add global variables, aliases or functions.
|
||||||
Modules are created using the static method ``Module.new``:
|
Modules are created using the static method ``Module.new``:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from llvm import \* from llvm.core import \*
|
from llvm import *
|
||||||
|
from llvm.core import *
|
||||||
|
|
||||||
# create a module
|
# create a module
|
||||||
my_module = Module.new('my_module')
|
my_module = Module.new('my_module')
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Module
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,10 @@ The address space of the pointer.
|
||||||
|
|
||||||
A `Type <llvm.core.Type.html>`_ object representing the type of the
|
A `Type <llvm.core.Type.html>`_ object representing the type of the
|
||||||
value pointed to.
|
value pointed to.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.PointerType
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
llvm.core.StructType
|
||||||
|
====================
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.StructType
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
@ -126,3 +126,10 @@ Example:
|
||||||
|
|
||||||
assert Type.int().kind == TYPE_INTEGER assert
|
assert Type.int().kind == TYPE_INTEGER assert
|
||||||
Type.void().kind == TYPE_VOID
|
Type.void().kind == TYPE_VOID
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Type
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,10 @@ The list of operands (values, of type
|
||||||
|
|
||||||
The number of operands that this value referes to. Same as
|
The number of operands that this value referes to. Same as
|
||||||
``len(uses.operands)`` but faster if you just want the count.
|
``len(uses.operands)`` but faster if you just want the count.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.User
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -63,3 +63,10 @@ representation.
|
||||||
``Value`` objects can be compared for equality. Internally, this
|
``Value`` objects can be compared for equality. Internally, this
|
||||||
converts both arguments into their LLVM assembly representations and
|
converts both arguments into their LLVM assembly representations and
|
||||||
compares the resultant strings.
|
compares the resultant strings.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.Value
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,10 @@ element of the vector.
|
||||||
[read-only]
|
[read-only]
|
||||||
|
|
||||||
The number of elements in the vector.
|
The number of elements in the vector.
|
||||||
|
|
||||||
|
|
||||||
|
Automatically Generated Documentation
|
||||||
|
-------------------------------------
|
||||||
|
.. autoclass:: llvm.core.VectorType
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
|
||||||
12
docs/source/doc/llvm_cbuilder.rst
Normal file
12
docs/source/doc/llvm_cbuilder.rst
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
llvm_cbuilder
|
||||||
|
=============
|
||||||
|
|
||||||
|
llvm_cbuilder is a set of Python-contexts you can use to write C-like
|
||||||
|
constructs in Python which generates llvmpy code directly.
|
||||||
|
|
||||||
|
Similar to llpython it allows you to build llvm IR without using the
|
||||||
|
llvmpy interface directly.
|
||||||
|
|
||||||
|
Look in the llvm_cbuilder tests directory for examples of use.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,6 +28,7 @@ follow these steps:
|
||||||
- add a *basic block* to the function
|
- add a *basic block* to the function
|
||||||
- using a helper object called an *instruction builder*, add two
|
- using a helper object called an *instruction builder*, add two
|
||||||
instructions into the basic block:
|
instructions into the basic block:
|
||||||
|
|
||||||
- an instruction to add the two
|
- an instruction to add the two
|
||||||
arguments and store the result into a temporary variable
|
arguments and store the result into a temporary variable
|
||||||
- a return
|
- a return
|
||||||
|
|
|
||||||
9
docs/source/doc/llvmcore.rst
Normal file
9
docs/source/doc/llvmcore.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
********************************
|
||||||
|
llvm.core
|
||||||
|
********************************
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
llvm.core.*
|
||||||
9
docs/source/doc/llvmee.rst
Normal file
9
docs/source/doc/llvmee.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
********************************
|
||||||
|
llvm.ee
|
||||||
|
********************************
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
llvm.ee.*
|
||||||
9
docs/source/doc/llvmpasses.rst
Normal file
9
docs/source/doc/llvmpasses.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
********************************
|
||||||
|
llvm.passes
|
||||||
|
********************************
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
llvm.passes.*
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
You can adapt this file completely to your liking, but it should at least
|
You can adapt this file completely to your liking, but it should at least
|
||||||
contain the root `toctree` directive.
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
Documentation for llvmpy
|
llvmpy
|
||||||
=================
|
======
|
||||||
|
|
||||||
Contents:
|
Contents:
|
||||||
|
|
||||||
|
|
@ -13,11 +13,16 @@ Contents:
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
doc/userguide.rst
|
doc/userguide.rst
|
||||||
|
doc/llvm_cbuilder.rst
|
||||||
|
doc/llpython/index.rst
|
||||||
doc/examples.rst
|
doc/examples.rst
|
||||||
|
doc/llvmcore.rst
|
||||||
|
doc/llvmee.rst
|
||||||
|
doc/llvmpasses.rst
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
------------------
|
||||||
|
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
|
|
|
||||||
152
example/vector_instr.py
Normal file
152
example/vector_instr.py
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
'''
|
||||||
|
This example shows:
|
||||||
|
1) how to use vector instructions
|
||||||
|
2) how to take advantage of LLVM loop vectorization to transform scalar
|
||||||
|
operations to vector operations
|
||||||
|
'''
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import llvm.core as lc
|
||||||
|
import llvm.ee as le
|
||||||
|
import llvm.passes as lp
|
||||||
|
from ctypes import CFUNCTYPE, POINTER, c_int, c_float
|
||||||
|
|
||||||
|
def build_manual_vector():
|
||||||
|
mod = lc.Module.new('manual.vector')
|
||||||
|
intty = lc.Type.int(32)
|
||||||
|
vecty = lc.Type.vector(lc.Type.float(), 4)
|
||||||
|
aryty = lc.Type.pointer(lc.Type.float())
|
||||||
|
fnty = lc.Type.function(lc.Type.void(), [aryty, aryty, aryty, intty])
|
||||||
|
fn = mod.add_function(fnty, name='vector_add')
|
||||||
|
bbentry = fn.append_basic_block('entry')
|
||||||
|
bbloopcond = fn.append_basic_block('loop.cond')
|
||||||
|
bbloopbody = fn.append_basic_block('loop.body')
|
||||||
|
bbexit = fn.append_basic_block('exit')
|
||||||
|
builder = lc.Builder.new(bbentry)
|
||||||
|
|
||||||
|
# populate function body
|
||||||
|
in1, in2, out, size = fn.args
|
||||||
|
ZERO = lc.Constant.null(intty)
|
||||||
|
loopi_ptr = builder.alloca(intty)
|
||||||
|
builder.store(ZERO, loopi_ptr)
|
||||||
|
|
||||||
|
builder.branch(bbloopcond)
|
||||||
|
builder.position_at_end(bbloopcond)
|
||||||
|
|
||||||
|
loopi = builder.load(loopi_ptr)
|
||||||
|
loopcond = builder.icmp(lc.ICMP_ULT, loopi, size)
|
||||||
|
|
||||||
|
builder.cbranch(loopcond, bbloopbody, bbexit)
|
||||||
|
builder.position_at_end(bbloopbody)
|
||||||
|
|
||||||
|
vecaryty = lc.Type.pointer(vecty)
|
||||||
|
in1asvec = builder.bitcast(builder.gep(in1, [loopi]), vecaryty)
|
||||||
|
in2asvec = builder.bitcast(builder.gep(in2, [loopi]), vecaryty)
|
||||||
|
outasvec = builder.bitcast(builder.gep(out, [loopi]), vecaryty)
|
||||||
|
|
||||||
|
vec1 = builder.load(in1asvec)
|
||||||
|
vec2 = builder.load(in2asvec)
|
||||||
|
|
||||||
|
vecout = builder.fadd(vec1, vec2)
|
||||||
|
|
||||||
|
builder.store(vecout, outasvec)
|
||||||
|
|
||||||
|
next = builder.add(loopi, lc.Constant.int(intty, 4))
|
||||||
|
builder.store(next, loopi_ptr)
|
||||||
|
|
||||||
|
builder.branch(bbloopcond)
|
||||||
|
builder.position_at_end(bbexit)
|
||||||
|
|
||||||
|
builder.ret_void()
|
||||||
|
|
||||||
|
return mod, fn
|
||||||
|
|
||||||
|
|
||||||
|
def build_auto_vector():
|
||||||
|
mod = lc.Module.new('auto.vector')
|
||||||
|
# Loop vectorize is sensitive to the size of the index size(!?)
|
||||||
|
intty = lc.Type.int(tuple.__itemsize__ * 8)
|
||||||
|
aryty = lc.Type.pointer(lc.Type.float())
|
||||||
|
fnty = lc.Type.function(lc.Type.void(), [aryty, aryty, aryty, intty])
|
||||||
|
fn = mod.add_function(fnty, name='vector_add')
|
||||||
|
bbentry = fn.append_basic_block('entry')
|
||||||
|
bbloopcond = fn.append_basic_block('loop.cond')
|
||||||
|
bbloopbody = fn.append_basic_block('loop.body')
|
||||||
|
bbexit = fn.append_basic_block('exit')
|
||||||
|
builder = lc.Builder.new(bbentry)
|
||||||
|
|
||||||
|
# populate function body
|
||||||
|
in1, in2, out, size = fn.args
|
||||||
|
in1.add_attribute(lc.ATTR_NO_ALIAS)
|
||||||
|
in2.add_attribute(lc.ATTR_NO_ALIAS)
|
||||||
|
out.add_attribute(lc.ATTR_NO_ALIAS)
|
||||||
|
ZERO = lc.Constant.null(intty)
|
||||||
|
loopi_ptr = builder.alloca(intty)
|
||||||
|
builder.store(ZERO, loopi_ptr)
|
||||||
|
|
||||||
|
builder.branch(bbloopcond)
|
||||||
|
builder.position_at_end(bbloopcond)
|
||||||
|
|
||||||
|
loopi = builder.load(loopi_ptr)
|
||||||
|
loopcond = builder.icmp(lc.ICMP_ULT, loopi, size)
|
||||||
|
|
||||||
|
builder.cbranch(loopcond, bbloopbody, bbexit)
|
||||||
|
builder.position_at_end(bbloopbody)
|
||||||
|
|
||||||
|
in1elem = builder.load(builder.gep(in1, [loopi]))
|
||||||
|
in2elem = builder.load(builder.gep(in2, [loopi]))
|
||||||
|
|
||||||
|
outelem = builder.fadd(in1elem, in2elem)
|
||||||
|
|
||||||
|
builder.store(outelem, builder.gep(out, [loopi]))
|
||||||
|
|
||||||
|
next = builder.add(loopi, lc.Constant.int(intty, 1))
|
||||||
|
builder.store(next, loopi_ptr)
|
||||||
|
|
||||||
|
builder.branch(bbloopcond)
|
||||||
|
builder.position_at_end(bbexit)
|
||||||
|
|
||||||
|
builder.ret_void()
|
||||||
|
|
||||||
|
return mod, fn
|
||||||
|
|
||||||
|
def example(title, module_builder, opt):
|
||||||
|
print(title.center(80, '='))
|
||||||
|
mod, fn = module_builder()
|
||||||
|
|
||||||
|
eb = le.EngineBuilder.new(mod).opt(3)
|
||||||
|
if opt:
|
||||||
|
print('opt')
|
||||||
|
tm = eb.select_target()
|
||||||
|
pms = lp.build_pass_managers(mod=mod, tm=tm, opt=3, loop_vectorize=True,
|
||||||
|
fpm=False)
|
||||||
|
pms.pm.run(mod)
|
||||||
|
|
||||||
|
print(mod)
|
||||||
|
print(mod.to_native_assembly())
|
||||||
|
|
||||||
|
engine = eb.create()
|
||||||
|
ptr = engine.get_pointer_to_function(fn)
|
||||||
|
|
||||||
|
callable = CFUNCTYPE(None, POINTER(c_float), POINTER(c_float),
|
||||||
|
POINTER(c_float), c_int)(ptr)
|
||||||
|
|
||||||
|
N = 20
|
||||||
|
in1 = (c_float * N)(*range(N))
|
||||||
|
in2 = (c_float * N)(*range(N))
|
||||||
|
out = (c_float * N)()
|
||||||
|
|
||||||
|
print('in1: ', list(in1))
|
||||||
|
print('in1: ', list(in2))
|
||||||
|
|
||||||
|
callable(in1, in2, out, N)
|
||||||
|
|
||||||
|
print('out', list(out))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
example('manual vector function', build_manual_vector, False)
|
||||||
|
example('auto vector function', build_auto_vector, True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
0
llpython/__init__.py
Normal file
0
llpython/__init__.py
Normal file
125
llpython/byte_control.py
Normal file
125
llpython/byte_control.py
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import opcode
|
||||||
|
from . import opcode_util
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
from .bytecode_visitor import BasicBlockVisitor, BenignBytecodeVisitorMixin
|
||||||
|
from .control_flow import ControlFlowGraph
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
||||||
|
'''Visitor responsible for traversing a bytecode basic block map and
|
||||||
|
building a control flow graph (CFG).
|
||||||
|
|
||||||
|
The primary purpose of this transformation is to create a CFG,
|
||||||
|
which is used by later transformers for dataflow analysis.
|
||||||
|
'''
|
||||||
|
def visit (self, flow, nargs = 0, *args, **kws):
|
||||||
|
'''Given a bytecode flow, and an optional number of arguments,
|
||||||
|
return a :py:class:`llpython.control_flow.ControlFlowGraph`
|
||||||
|
instance describing the full control flow of the bytecode
|
||||||
|
flow.'''
|
||||||
|
self.nargs = nargs
|
||||||
|
ret_val = super(ControlFlowBuilder, self).visit(flow, *args, **kws)
|
||||||
|
del self.nargs
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_blocks (self, blocks):
|
||||||
|
super(ControlFlowBuilder, self).enter_blocks(blocks)
|
||||||
|
self.blocks = blocks
|
||||||
|
self.block_list = list(blocks.keys())
|
||||||
|
self.block_list.sort()
|
||||||
|
self.cfg = ControlFlowGraph()
|
||||||
|
self.loop_stack = []
|
||||||
|
for block in self.block_list:
|
||||||
|
self.cfg.add_block(block, blocks[block])
|
||||||
|
|
||||||
|
def exit_blocks (self, blocks):
|
||||||
|
super(ControlFlowBuilder, self).exit_blocks(blocks)
|
||||||
|
assert self.blocks == blocks
|
||||||
|
self.cfg.compute_dataflow()
|
||||||
|
self.cfg.update_for_ssa()
|
||||||
|
ret_val = self.cfg
|
||||||
|
del self.loop_stack
|
||||||
|
del self.cfg
|
||||||
|
del self.block_list
|
||||||
|
del self.blocks
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
self.block = block
|
||||||
|
assert block in self.cfg.blocks
|
||||||
|
if block == 0:
|
||||||
|
for local_index in range(self.nargs):
|
||||||
|
self.op_STORE_FAST(0, opcode.opmap['STORE_FAST'], local_index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_next_block (self, block):
|
||||||
|
return self.block_list[self.block_list.index(block) + 1]
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
assert block == self.block
|
||||||
|
del self.block
|
||||||
|
i, op, arg = self.blocks[block][-1]
|
||||||
|
opname = opcode.opname[op]
|
||||||
|
if op in opcode.hasjabs:
|
||||||
|
self.cfg.add_edge(block, arg)
|
||||||
|
elif op in opcode.hasjrel:
|
||||||
|
self.cfg.add_edge(block, i + arg + 3)
|
||||||
|
elif opname == 'BREAK_LOOP':
|
||||||
|
loop_i, _, loop_arg = self.loop_stack[-1]
|
||||||
|
self.cfg.add_edge(block, loop_i + loop_arg + 3)
|
||||||
|
elif opname != 'RETURN_VALUE':
|
||||||
|
self.cfg.add_edge(block, self._get_next_block(block))
|
||||||
|
if op in opcode_util.hascbranch:
|
||||||
|
self.cfg.add_edge(block, self._get_next_block(block))
|
||||||
|
|
||||||
|
def op_LOAD_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
self.cfg.blocks_reads[self.block].add(arg)
|
||||||
|
return super(ControlFlowBuilder, self).op_LOAD_FAST(i, op, arg, *args,
|
||||||
|
**kws)
|
||||||
|
|
||||||
|
def op_STORE_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
self.cfg.writes_local(self.block, i, arg)
|
||||||
|
return super(ControlFlowBuilder, self).op_STORE_FAST(i, op, arg, *args,
|
||||||
|
**kws)
|
||||||
|
|
||||||
|
def op_SETUP_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
self.loop_stack.append((i, op, arg))
|
||||||
|
return super(ControlFlowBuilder, self).op_SETUP_LOOP(i, op, arg, *args,
|
||||||
|
**kws)
|
||||||
|
|
||||||
|
def op_POP_BLOCK (self, i, op, arg, *args, **kws):
|
||||||
|
self.loop_stack.pop()
|
||||||
|
return super(ControlFlowBuilder, self).op_POP_BLOCK(i, op, arg, *args,
|
||||||
|
**kws)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_cfg (func):
|
||||||
|
'''Given a Python function, create a bytecode flow, visit the flow
|
||||||
|
object, and return a control flow graph.'''
|
||||||
|
co_obj = opcode_util.get_code_object(func)
|
||||||
|
return ControlFlowBuilder().visit(opcode_util.build_basic_blocks(co_obj),
|
||||||
|
co_obj.co_argcount)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Main (self-test) routine
|
||||||
|
|
||||||
|
def main (*args, **kws):
|
||||||
|
from tests import llfuncs
|
||||||
|
if not args:
|
||||||
|
args = ('doslice',)
|
||||||
|
for arg in args:
|
||||||
|
build_cfg(getattr(llfuncs, arg)).pprint()
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of byte_control.py
|
||||||
266
llpython/byte_flow.py
Normal file
266
llpython/byte_flow.py
Normal file
|
|
@ -0,0 +1,266 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import dis
|
||||||
|
import opcode
|
||||||
|
|
||||||
|
from .bytecode_visitor import BasicBlockVisitor
|
||||||
|
from . import opcode_util
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeFlowBuilder (BasicBlockVisitor):
|
||||||
|
'''Transforms a CFG into a bytecode "flow tree".
|
||||||
|
|
||||||
|
The flow tree is a Python dictionary, described loosely by the
|
||||||
|
following set of productions:
|
||||||
|
|
||||||
|
* `flow_tree` ``:=`` ``{`` `blocks` ``*`` ``}``
|
||||||
|
* `blocks` ``:=`` `block_index` ``:`` ``[`` `bytecode_tree` ``*`` ``]``
|
||||||
|
* `bytecode_tree` ``:=`` ``(`` `opcode_index` ``,`` `opcode` ``,``
|
||||||
|
`opname` ``,`` `arg` ``,`` ``[`` `bytecode_tree` ``*`` ``]`` ``)``
|
||||||
|
|
||||||
|
The primary purpose of this transformation is to simulate the
|
||||||
|
value stack, removing it and any stack-specific opcodes.'''
|
||||||
|
|
||||||
|
def __init__ (self, *args, **kws):
|
||||||
|
super(BytecodeFlowBuilder, self).__init__(*args, **kws)
|
||||||
|
om_items = opcode_util.OPCODE_MAP.items()
|
||||||
|
self.opmap = dict((opcode.opmap[opname], (opname, pops, pushes, stmt))
|
||||||
|
for opname, (pops, pushes, stmt) in om_items
|
||||||
|
if opname in opcode.opmap)
|
||||||
|
|
||||||
|
def _visit_op (self, i, op, arg, opname, pops, pushes, appends):
|
||||||
|
assert pops is not None, ('%s not well defined in opcode_util.'
|
||||||
|
'OPCODE_MAP' % opname)
|
||||||
|
if pops:
|
||||||
|
if pops < 0:
|
||||||
|
pops = arg - pops - 1
|
||||||
|
assert pops <= len(self.stack), ("Stack underflow at instruction "
|
||||||
|
"%d (%s)!" % (i, opname))
|
||||||
|
stk_args = self.stack[-pops:]
|
||||||
|
del self.stack[-pops:]
|
||||||
|
else:
|
||||||
|
stk_args = []
|
||||||
|
ret_val = (i, op, opname, arg, stk_args)
|
||||||
|
if pushes:
|
||||||
|
self.stack.append(ret_val)
|
||||||
|
if appends:
|
||||||
|
self.block.append(ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def _op (self, i, op, arg):
|
||||||
|
opname, pops, pushes, appends = self.opmap[op]
|
||||||
|
return self._visit_op(i, op, arg, opname, pops, pushes, appends)
|
||||||
|
|
||||||
|
def visit_cfg (self, cfg):
|
||||||
|
self.cfg = cfg
|
||||||
|
ret_val = self.visit(cfg.blocks)
|
||||||
|
del self.cfg
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_blocks (self, blocks):
|
||||||
|
labels = list(blocks.keys())
|
||||||
|
labels.sort()
|
||||||
|
self.blocks = dict((index, [])
|
||||||
|
for index in labels)
|
||||||
|
self.loop_stack = []
|
||||||
|
self.stacks = {}
|
||||||
|
|
||||||
|
def exit_blocks (self, blocks):
|
||||||
|
ret_val = self.blocks
|
||||||
|
del self.stacks
|
||||||
|
del self.loop_stack
|
||||||
|
del self.blocks
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
self.block_no = block
|
||||||
|
self.block = self.blocks[block]
|
||||||
|
in_blocks = self.cfg.blocks_in[block]
|
||||||
|
if len(in_blocks) == 0:
|
||||||
|
self.stack = []
|
||||||
|
else:
|
||||||
|
pred_stack = None
|
||||||
|
for pred in in_blocks:
|
||||||
|
if pred in self.stacks:
|
||||||
|
pred_stack = self.stacks[pred]
|
||||||
|
break
|
||||||
|
if pred_stack is not None:
|
||||||
|
self.stack = pred_stack[:]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
assert self.block_no == block
|
||||||
|
self.stacks[block] = self.stack
|
||||||
|
del self.stack
|
||||||
|
del self.block
|
||||||
|
del self.block_no
|
||||||
|
|
||||||
|
op_BINARY_ADD = _op
|
||||||
|
op_BINARY_AND = _op
|
||||||
|
op_BINARY_DIVIDE = _op
|
||||||
|
op_BINARY_FLOOR_DIVIDE = _op
|
||||||
|
op_BINARY_LSHIFT = _op
|
||||||
|
op_BINARY_MODULO = _op
|
||||||
|
op_BINARY_MULTIPLY = _op
|
||||||
|
op_BINARY_OR = _op
|
||||||
|
op_BINARY_POWER = _op
|
||||||
|
op_BINARY_RSHIFT = _op
|
||||||
|
op_BINARY_SUBSCR = _op
|
||||||
|
op_BINARY_SUBTRACT = _op
|
||||||
|
op_BINARY_TRUE_DIVIDE = _op
|
||||||
|
op_BINARY_XOR = _op
|
||||||
|
|
||||||
|
def op_BREAK_LOOP (self, i, op, arg):
|
||||||
|
loop_i, _, loop_arg = self.loop_stack[-1]
|
||||||
|
assert arg is None
|
||||||
|
return self._op(i, op, loop_i + loop_arg + 3)
|
||||||
|
|
||||||
|
#op_BUILD_CLASS = _op
|
||||||
|
op_BUILD_LIST = _op
|
||||||
|
op_BUILD_MAP = _op
|
||||||
|
op_BUILD_SLICE = _op
|
||||||
|
op_BUILD_TUPLE = _op
|
||||||
|
op_CALL_FUNCTION = _op
|
||||||
|
op_CALL_FUNCTION_KW = _op
|
||||||
|
op_CALL_FUNCTION_VAR = _op
|
||||||
|
op_CALL_FUNCTION_VAR_KW = _op
|
||||||
|
op_COMPARE_OP = _op
|
||||||
|
#op_CONTINUE_LOOP = _op
|
||||||
|
op_DELETE_ATTR = _op
|
||||||
|
op_DELETE_FAST = _op
|
||||||
|
op_DELETE_GLOBAL = _op
|
||||||
|
op_DELETE_NAME = _op
|
||||||
|
op_DELETE_SLICE = _op
|
||||||
|
op_DELETE_SUBSCR = _op
|
||||||
|
|
||||||
|
def op_DUP_TOP (self, i, op, arg):
|
||||||
|
self.stack.append(self.stack[-1])
|
||||||
|
|
||||||
|
def op_DUP_TOPX (self, i, op, arg):
|
||||||
|
self.stack += self.stack[-arg:]
|
||||||
|
|
||||||
|
#op_END_FINALLY = _op
|
||||||
|
op_EXEC_STMT = _op
|
||||||
|
#op_EXTENDED_ARG = _op
|
||||||
|
op_FOR_ITER = _op
|
||||||
|
op_GET_ITER = _op
|
||||||
|
op_IMPORT_FROM = _op
|
||||||
|
op_IMPORT_NAME = _op
|
||||||
|
op_IMPORT_STAR = _op
|
||||||
|
op_INPLACE_ADD = _op
|
||||||
|
op_INPLACE_AND = _op
|
||||||
|
op_INPLACE_DIVIDE = _op
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = _op
|
||||||
|
op_INPLACE_LSHIFT = _op
|
||||||
|
op_INPLACE_MODULO = _op
|
||||||
|
op_INPLACE_MULTIPLY = _op
|
||||||
|
op_INPLACE_OR = _op
|
||||||
|
op_INPLACE_POWER = _op
|
||||||
|
op_INPLACE_RSHIFT = _op
|
||||||
|
op_INPLACE_SUBTRACT = _op
|
||||||
|
op_INPLACE_TRUE_DIVIDE = _op
|
||||||
|
op_INPLACE_XOR = _op
|
||||||
|
op_JUMP_ABSOLUTE = _op
|
||||||
|
op_JUMP_FORWARD = _op
|
||||||
|
|
||||||
|
def op_JUMP_IF_FALSE (self, i, op, arg):
|
||||||
|
opname, _, _, _ = self.opmap[op]
|
||||||
|
ret_val = (i, op, opname, arg, [self.stack[-1]])
|
||||||
|
self.block.append(ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
op_JUMP_IF_TRUE = op_JUMP_IF_FALSE
|
||||||
|
op_LIST_APPEND = _op
|
||||||
|
op_LOAD_ATTR = _op
|
||||||
|
op_LOAD_CLOSURE = _op
|
||||||
|
op_LOAD_CONST = _op
|
||||||
|
op_LOAD_DEREF = _op
|
||||||
|
op_LOAD_FAST = _op
|
||||||
|
op_LOAD_GLOBAL = _op
|
||||||
|
op_LOAD_LOCALS = _op
|
||||||
|
op_LOAD_NAME = _op
|
||||||
|
op_MAKE_CLOSURE = _op
|
||||||
|
op_MAKE_FUNCTION = _op
|
||||||
|
op_NOP = _op
|
||||||
|
|
||||||
|
def op_POP_BLOCK (self, i, op, arg):
|
||||||
|
self.loop_stack.pop()
|
||||||
|
return self._op(i, op, arg)
|
||||||
|
|
||||||
|
op_POP_JUMP_IF_FALSE = _op
|
||||||
|
op_POP_JUMP_IF_TRUE = _op
|
||||||
|
op_POP_TOP = _op
|
||||||
|
op_PRINT_EXPR = _op
|
||||||
|
op_PRINT_ITEM = _op
|
||||||
|
op_PRINT_ITEM_TO = _op
|
||||||
|
op_PRINT_NEWLINE = _op
|
||||||
|
op_PRINT_NEWLINE_TO = _op
|
||||||
|
op_RAISE_VARARGS = _op
|
||||||
|
op_RETURN_VALUE = _op
|
||||||
|
|
||||||
|
def op_ROT_FOUR (self, i, op, arg):
|
||||||
|
self.stack[-4:] = (self.stack[-1], self.stack[-4], self.stack[-3],
|
||||||
|
self.stack[-2])
|
||||||
|
|
||||||
|
def op_ROT_THREE (self, i, op, arg):
|
||||||
|
self.stack[-3:] = (self.stack[-1], self.stack[-3], self.stack[-2])
|
||||||
|
|
||||||
|
def op_ROT_TWO (self, i, op, arg):
|
||||||
|
self.stack[-2:] = (self.stack[-1], self.stack[-2])
|
||||||
|
|
||||||
|
#op_SETUP_EXCEPT = _op
|
||||||
|
#op_SETUP_FINALLY = _op
|
||||||
|
|
||||||
|
def op_SETUP_LOOP (self, i, op, arg):
|
||||||
|
self.loop_stack.append((i, op, arg))
|
||||||
|
self.block.append((i, op, self.opnames[op], arg, []))
|
||||||
|
|
||||||
|
op_SLICE = _op
|
||||||
|
#op_STOP_CODE = _op
|
||||||
|
op_STORE_ATTR = _op
|
||||||
|
op_STORE_DEREF = _op
|
||||||
|
op_STORE_FAST = _op
|
||||||
|
op_STORE_GLOBAL = _op
|
||||||
|
op_STORE_MAP = _op
|
||||||
|
op_STORE_NAME = _op
|
||||||
|
op_STORE_SLICE = _op
|
||||||
|
op_STORE_SUBSCR = _op
|
||||||
|
op_UNARY_CONVERT = _op
|
||||||
|
op_UNARY_INVERT = _op
|
||||||
|
op_UNARY_NEGATIVE = _op
|
||||||
|
op_UNARY_NOT = _op
|
||||||
|
op_UNARY_POSITIVE = _op
|
||||||
|
op_UNPACK_SEQUENCE = _op
|
||||||
|
#op_WITH_CLEANUP = _op
|
||||||
|
op_YIELD_VALUE = _op
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_flow (func):
|
||||||
|
'''Given a Python function, return a bytecode flow tree for that
|
||||||
|
function.'''
|
||||||
|
import byte_control
|
||||||
|
cfg = byte_control.build_cfg(func)
|
||||||
|
return BytecodeFlowBuilder().visit_cfg(cfg)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Main (self-test) routine
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
import pprint
|
||||||
|
from tests import llfuncs
|
||||||
|
if not args:
|
||||||
|
args = ('doslice',)
|
||||||
|
for arg in args:
|
||||||
|
pprint.pprint(build_flow(getattr(llfuncs, arg)))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of byte_flow.py
|
||||||
612
llpython/byte_translator.py
Normal file
612
llpython/byte_translator.py
Normal file
|
|
@ -0,0 +1,612 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
'''Defines a bytecode based LLVM translator for llpython code.
|
||||||
|
'''
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module imports
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import opcode
|
||||||
|
import types
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
|
||||||
|
from . import opcode_util
|
||||||
|
from . import bytetype
|
||||||
|
from .bytecode_visitor import BytecodeFlowVisitor
|
||||||
|
from .byte_flow import BytecodeFlowBuilder
|
||||||
|
from .byte_control import ControlFlowBuilder
|
||||||
|
from .phi_injector import PhiInjector, synthetic_opname
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module data
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# XXX Stolen from numba.translate:
|
||||||
|
|
||||||
|
_compare_mapping_float = {'>':lc.FCMP_OGT,
|
||||||
|
'<':lc.FCMP_OLT,
|
||||||
|
'==':lc.FCMP_OEQ,
|
||||||
|
'>=':lc.FCMP_OGE,
|
||||||
|
'<=':lc.FCMP_OLE,
|
||||||
|
'!=':lc.FCMP_ONE}
|
||||||
|
|
||||||
|
_compare_mapping_sint = {'>':lc.ICMP_SGT,
|
||||||
|
'<':lc.ICMP_SLT,
|
||||||
|
'==':lc.ICMP_EQ,
|
||||||
|
'>=':lc.ICMP_SGE,
|
||||||
|
'<=':lc.ICMP_SLE,
|
||||||
|
'!=':lc.ICMP_NE}
|
||||||
|
|
||||||
|
# XXX Stolen from numba.llvm_types:
|
||||||
|
|
||||||
|
class LLVMCaster (object):
|
||||||
|
def build_pointer_cast(_, builder, lval1, lty2):
|
||||||
|
return builder.bitcast(lval1, lty2)
|
||||||
|
|
||||||
|
def build_int_cast(_, builder, lval1, lty2, unsigned = False):
|
||||||
|
width1 = lval1.type.width
|
||||||
|
width2 = lty2.width
|
||||||
|
ret_val = lval1
|
||||||
|
if width2 > width1:
|
||||||
|
if unsigned:
|
||||||
|
ret_val = builder.zext(lval1, lty2)
|
||||||
|
else:
|
||||||
|
ret_val = builder.sext(lval1, lty2)
|
||||||
|
elif width2 < width1:
|
||||||
|
ret_val = builder.trunc(lval1, lty2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def build_float_ext(_, builder, lval1, lty2):
|
||||||
|
return builder.fpext(lval1, lty2)
|
||||||
|
|
||||||
|
def build_float_trunc(_, builder, lval1, lty2):
|
||||||
|
return builder.fptrunc(lval1, lty2)
|
||||||
|
|
||||||
|
def build_int_to_float_cast(_, builder, lval1, lty2, unsigned = False):
|
||||||
|
ret_val = None
|
||||||
|
if unsigned:
|
||||||
|
ret_val = builder.uitofp(lval1, lty2)
|
||||||
|
else:
|
||||||
|
ret_val = builder.sitofp(lval1, lty2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def build_int_to_ptr_cast(_, builder, lval1, lty2):
|
||||||
|
return builder.inttoptr(lval1, lty2)
|
||||||
|
|
||||||
|
def build_float_to_int_cast(_, builder, lval1, lty2, unsigned = False):
|
||||||
|
ret_val = None
|
||||||
|
if unsigned:
|
||||||
|
ret_val = builder.fptoui(lval1, lty2)
|
||||||
|
else:
|
||||||
|
ret_val = builder.fptosi(lval1, lty2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
CAST_MAP = {
|
||||||
|
lc.TYPE_POINTER : build_pointer_cast,
|
||||||
|
lc.TYPE_INTEGER: build_int_cast,
|
||||||
|
(lc.TYPE_FLOAT, lc.TYPE_DOUBLE) : build_float_ext,
|
||||||
|
(lc.TYPE_DOUBLE, lc.TYPE_FLOAT) : build_float_trunc,
|
||||||
|
(lc.TYPE_INTEGER, lc.TYPE_FLOAT) : build_int_to_float_cast,
|
||||||
|
(lc.TYPE_INTEGER, lc.TYPE_DOUBLE) : build_int_to_float_cast,
|
||||||
|
(lc.TYPE_INTEGER, lc.TYPE_POINTER) : build_int_to_ptr_cast,
|
||||||
|
(lc.TYPE_FLOAT, lc.TYPE_INTEGER) : build_float_to_int_cast,
|
||||||
|
(lc.TYPE_DOUBLE, lc.TYPE_INTEGER) : build_float_to_int_cast,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build_cast(cls, builder, lval1, lty2, *args, **kws):
|
||||||
|
ret_val = lval1
|
||||||
|
lty1 = lval1.type
|
||||||
|
lkind1 = lty1.kind
|
||||||
|
lkind2 = lty2.kind
|
||||||
|
|
||||||
|
if lkind1 == lkind2:
|
||||||
|
|
||||||
|
if lkind1 in cls.CAST_MAP:
|
||||||
|
ret_val = cls.CAST_MAP[lkind1](cls, builder, lval1, lty2,
|
||||||
|
*args, **kws)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(lkind1)
|
||||||
|
else:
|
||||||
|
map_index = (lkind1, lkind2)
|
||||||
|
if map_index in cls.CAST_MAP:
|
||||||
|
ret_val = cls.CAST_MAP[map_index](cls, builder, lval1, lty2,
|
||||||
|
*args, **kws)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(lkind1, lkind2)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Class definitions
|
||||||
|
|
||||||
|
class LLVMTranslator (BytecodeFlowVisitor):
|
||||||
|
'''Transformer responsible for visiting a set of bytecode flow
|
||||||
|
trees, emitting LLVM code.
|
||||||
|
|
||||||
|
Unlike other translators in :py:mod:`llpython`, this
|
||||||
|
incorporates the full transformation chain, starting with
|
||||||
|
:py:class:`llpython.byte_flow.BytecodeFlowBuilder`, then
|
||||||
|
:py:class:`llpython.byte_control.ControlFlowBuilder`, and
|
||||||
|
then :py:class:`llpython.phi_injector.PhiInjector`.'''
|
||||||
|
|
||||||
|
def __init__ (self, llvm_module = None, *args, **kws):
|
||||||
|
'''Constructor for LLVMTranslator.'''
|
||||||
|
super(LLVMTranslator, self).__init__(*args, **kws)
|
||||||
|
if llvm_module is None:
|
||||||
|
llvm_module = lc.Module.new('Translated_Module_%d' % (id(self),))
|
||||||
|
self.llvm_module = llvm_module
|
||||||
|
self.bytecode_flow_builder = BytecodeFlowBuilder()
|
||||||
|
self.control_flow_builder = ControlFlowBuilder()
|
||||||
|
self.phi_injector = PhiInjector()
|
||||||
|
|
||||||
|
def translate (self, function, llvm_type = None, llvm_function = None,
|
||||||
|
env = None):
|
||||||
|
'''Translate a function to the given LLVM function type.
|
||||||
|
|
||||||
|
If no type is given, then assume the function is of LLVM type
|
||||||
|
"void ()".
|
||||||
|
|
||||||
|
The optional env parameter allows extension of the global
|
||||||
|
environment.'''
|
||||||
|
if llvm_type is None:
|
||||||
|
if llvm_function is None:
|
||||||
|
llvm_type = lc.Type.function(bytetype.lvoid, ())
|
||||||
|
else:
|
||||||
|
llvm_type = llvm_function.type.pointee
|
||||||
|
if env is None:
|
||||||
|
env = {}
|
||||||
|
else:
|
||||||
|
env = env.copy()
|
||||||
|
env.update((name, method)
|
||||||
|
for name, method in lc.Builder.__dict__.items()
|
||||||
|
if not name.startswith('_'))
|
||||||
|
env.update((name, value)
|
||||||
|
for name, value in bytetype.__dict__.items()
|
||||||
|
if not name.startswith('_'))
|
||||||
|
self.loop_stack = []
|
||||||
|
self.llvm_type = llvm_type
|
||||||
|
self.target_function_name = env.get('target_function_name',
|
||||||
|
function.__name__)
|
||||||
|
self.function = function
|
||||||
|
self.code_obj = opcode_util.get_code_object(function)
|
||||||
|
func_globals = getattr(function, 'func_globals',
|
||||||
|
getattr(function, '__globals__', {})).copy()
|
||||||
|
func_globals.update(env)
|
||||||
|
self.globals = func_globals
|
||||||
|
nargs = self.code_obj.co_argcount
|
||||||
|
self.cfg = self.control_flow_builder.visit(
|
||||||
|
opcode_util.build_basic_blocks(self.code_obj), nargs)
|
||||||
|
self.cfg.blocks = self.bytecode_flow_builder.visit_cfg(self.cfg)
|
||||||
|
self.llvm_function = llvm_function
|
||||||
|
flow = self.phi_injector.visit_cfg(self.cfg, nargs)
|
||||||
|
ret_val = self.visit(flow)
|
||||||
|
del self.cfg
|
||||||
|
del self.globals
|
||||||
|
del self.code_obj
|
||||||
|
del self.target_function_name
|
||||||
|
del self.function
|
||||||
|
del self.llvm_type
|
||||||
|
del self.loop_stack
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_flow_object (self, flow):
|
||||||
|
super(LLVMTranslator, self).enter_flow_object(flow)
|
||||||
|
if self.llvm_function is None:
|
||||||
|
self.llvm_function = self.llvm_module.add_function(
|
||||||
|
self.llvm_type, self.target_function_name)
|
||||||
|
self.llvm_blocks = {}
|
||||||
|
self.llvm_definitions = {}
|
||||||
|
self.pending_phis = {}
|
||||||
|
for block in self.block_list:
|
||||||
|
if 0 in self.cfg.blocks_reaching[block]:
|
||||||
|
bb = self.llvm_function.append_basic_block(
|
||||||
|
'BLOCK_%d' % (block,))
|
||||||
|
self.llvm_blocks[block] = bb
|
||||||
|
|
||||||
|
def exit_flow_object (self, flow):
|
||||||
|
super(LLVMTranslator, self).exit_flow_object(flow)
|
||||||
|
ret_val = self.llvm_function
|
||||||
|
del self.pending_phis
|
||||||
|
del self.llvm_definitions
|
||||||
|
del self.llvm_blocks
|
||||||
|
if __debug__ and logger.getEffectiveLevel() < logging.DEBUG:
|
||||||
|
logger.debug(str(ret_val))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
ret_val = False
|
||||||
|
if block in self.llvm_blocks:
|
||||||
|
self.llvm_block = self.llvm_blocks[block]
|
||||||
|
self.builder = lc.Builder.new(self.llvm_block)
|
||||||
|
ret_val = True
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
bb_instrs = self.llvm_block.instructions
|
||||||
|
if ((len(bb_instrs) == 0) or
|
||||||
|
(not bb_instrs[-1].is_terminator)):
|
||||||
|
out_blocks = list(self.cfg.blocks_out[block])
|
||||||
|
assert len(out_blocks) == 1
|
||||||
|
self.builder.branch(self.llvm_blocks[out_blocks[0]])
|
||||||
|
del self.llvm_block
|
||||||
|
del self.builder
|
||||||
|
|
||||||
|
def visit_synthetic_op (self, i, op, arg, *args, **kws):
|
||||||
|
method = getattr(self, 'op_%s' % (synthetic_opname[op],))
|
||||||
|
return method(i, op, arg, *args, **kws)
|
||||||
|
|
||||||
|
def op_REF_ARG (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.llvm_function.args[arg]]
|
||||||
|
|
||||||
|
def op_BUILD_PHI (self, i, op, arg, *args, **kws):
|
||||||
|
phi_type = None
|
||||||
|
incoming = []
|
||||||
|
pending = []
|
||||||
|
for child_arg in arg:
|
||||||
|
child_block, _, child_opname, child_arg, _ = child_arg
|
||||||
|
assert child_opname == 'REF_DEF'
|
||||||
|
if child_arg in self.llvm_definitions:
|
||||||
|
child_def = self.llvm_definitions[child_arg]
|
||||||
|
if phi_type is None:
|
||||||
|
phi_type = child_def.type
|
||||||
|
incoming.append((child_block, child_def))
|
||||||
|
else:
|
||||||
|
pending.append((child_arg, child_block))
|
||||||
|
phi = self.builder.phi(phi_type)
|
||||||
|
for block_index, defn in incoming:
|
||||||
|
phi.add_incoming(defn, self.llvm_blocks[block_index])
|
||||||
|
for defn_index, block_index in pending:
|
||||||
|
if defn_index not in self.pending_phis:
|
||||||
|
self.pending_phis[defn_index] = []
|
||||||
|
self.pending_phis[defn_index].append((phi, block_index))
|
||||||
|
return [phi]
|
||||||
|
|
||||||
|
def op_DEFINITION (self, i, op, def_index, *args, **kws):
|
||||||
|
assert len(args) == 1
|
||||||
|
arg = args[0]
|
||||||
|
if def_index in self.pending_phis:
|
||||||
|
for phi, block_index in self.pending_phis[def_index]:
|
||||||
|
phi.add_incoming(arg, self.llvm_blocks[block_index])
|
||||||
|
self.llvm_definitions[def_index] = arg
|
||||||
|
return args
|
||||||
|
|
||||||
|
def op_REF_DEF (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.llvm_definitions[arg]]
|
||||||
|
|
||||||
|
def op_BINARY_ADD (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.add(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fadd(arg1, arg2)]
|
||||||
|
elif arg1.type.kind == lc.TYPE_POINTER:
|
||||||
|
ret_val = [self.builder.gep(arg1, [arg2])]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_ADD for %r" %
|
||||||
|
(args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_AND (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.and_(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_DIVIDE (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.sdiv(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fdiv(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_DIVIDE for %r"
|
||||||
|
% (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_FLOOR_DIVIDE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_FLOOR_DIVIDE")
|
||||||
|
|
||||||
|
def op_BINARY_LSHIFT (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.shl(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_MODULO (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.srem(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.frem(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_MODULO for %r"
|
||||||
|
% (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_MULTIPLY (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.mul(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fmul(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_MULTIPLY for "
|
||||||
|
"%r" % (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_BINARY_OR (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.or_(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_POWER (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_POWER")
|
||||||
|
|
||||||
|
def op_BINARY_RSHIFT (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.lshr(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BINARY_SUBSCR (self, i, op, arg, *args, **kws):
|
||||||
|
arr_val = args[0]
|
||||||
|
index_vals = args[1:]
|
||||||
|
ret_val = gep_result = self.builder.gep(arr_val, index_vals)
|
||||||
|
if (gep_result.type.kind == lc.TYPE_POINTER and
|
||||||
|
gep_result.type.pointee.kind != lc.TYPE_POINTER):
|
||||||
|
ret_val = self.builder.load(gep_result)
|
||||||
|
return [ret_val]
|
||||||
|
|
||||||
|
def op_BINARY_SUBTRACT (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
if arg1.type.kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = [self.builder.sub(arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fsub(arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BINARY_SUBTRACT for "
|
||||||
|
"%r" % (args,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
op_BINARY_TRUE_DIVIDE = op_BINARY_DIVIDE
|
||||||
|
|
||||||
|
def op_BINARY_XOR (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.xor(args[0], args[1])]
|
||||||
|
|
||||||
|
def op_BREAK_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.branch(self.llvm_blocks[arg])]
|
||||||
|
|
||||||
|
def op_BUILD_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_BUILD_SLICE")
|
||||||
|
|
||||||
|
def op_BUILD_TUPLE (self, i, op, arg, *args, **kws):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION (self, i, op, arg, *args, **kws):
|
||||||
|
fn = args[0]
|
||||||
|
args = args[1:]
|
||||||
|
fn_name = getattr(fn, '__name__', None)
|
||||||
|
if isinstance(fn, (types.FunctionType, types.MethodType)):
|
||||||
|
ret_val = [fn(self.builder, *args)]
|
||||||
|
elif isinstance(fn, lc.Value):
|
||||||
|
ret_val = [self.builder.call(fn, args)]
|
||||||
|
elif isinstance(fn, lc.Type):
|
||||||
|
if isinstance(fn, lc.FunctionType):
|
||||||
|
ret_val = [self.builder.call(
|
||||||
|
self.llvm_module.get_or_insert_function(fn, fn_name),
|
||||||
|
args)]
|
||||||
|
else:
|
||||||
|
assert len(args) == 1
|
||||||
|
ret_val = [LLVMCaster.build_cast(self.builder, args[0], fn)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Don't know how to call %s() (%r @ %d)!"
|
||||||
|
% (fn_name, fn, i))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION_KW (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_CALL_FUNCTION_KW")
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION_VAR (self, i, op, arg, *args, **kws):
|
||||||
|
args = list(args)
|
||||||
|
var_args = list(args.pop())
|
||||||
|
args.extend(var_args)
|
||||||
|
return self.op_CALL_FUNCTION(i, op, arg, *args, **kws)
|
||||||
|
|
||||||
|
def op_CALL_FUNCTION_VAR_KW (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_CALL_FUNCTION_VAR_KW")
|
||||||
|
|
||||||
|
def op_COMPARE_OP (self, i, op, arg, *args, **kws):
|
||||||
|
arg1, arg2 = args
|
||||||
|
cmp_kind = opcode.cmp_op[arg]
|
||||||
|
if isinstance(arg1.type, lc.IntegerType):
|
||||||
|
ret_val = [self.builder.icmp(_compare_mapping_sint[cmp_kind],
|
||||||
|
arg1, arg2)]
|
||||||
|
elif arg1.type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
ret_val = [self.builder.fcmp(_compare_mapping_float[cmp_kind],
|
||||||
|
arg1, arg2)]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('Comparison of type %r' % (arg1.type,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_CONTINUE_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_CONTINUE_LOOP")
|
||||||
|
|
||||||
|
def op_DELETE_ATTR (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_DELETE_ATTR")
|
||||||
|
|
||||||
|
def op_DELETE_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_DELETE_SLICE")
|
||||||
|
|
||||||
|
def op_FOR_ITER (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_FOR_ITER")
|
||||||
|
|
||||||
|
def op_GET_ITER (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_GET_ITER")
|
||||||
|
|
||||||
|
op_INPLACE_ADD = op_BINARY_ADD
|
||||||
|
op_INPLACE_AND = op_BINARY_AND
|
||||||
|
op_INPLACE_DIVIDE = op_BINARY_DIVIDE
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = op_BINARY_FLOOR_DIVIDE
|
||||||
|
op_INPLACE_LSHIFT = op_BINARY_LSHIFT
|
||||||
|
op_INPLACE_MODULO = op_BINARY_MODULO
|
||||||
|
op_INPLACE_MULTIPLY = op_BINARY_MULTIPLY
|
||||||
|
op_INPLACE_OR = op_BINARY_OR
|
||||||
|
op_INPLACE_POWER = op_BINARY_POWER
|
||||||
|
op_INPLACE_RSHIFT = op_BINARY_RSHIFT
|
||||||
|
op_INPLACE_SUBTRACT = op_BINARY_SUBTRACT
|
||||||
|
op_INPLACE_TRUE_DIVIDE = op_BINARY_TRUE_DIVIDE
|
||||||
|
op_INPLACE_XOR = op_BINARY_XOR
|
||||||
|
|
||||||
|
def op_JUMP_ABSOLUTE (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.branch(self.llvm_blocks[arg])]
|
||||||
|
|
||||||
|
def op_JUMP_FORWARD (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.branch(self.llvm_blocks[i + arg + 3])]
|
||||||
|
|
||||||
|
def op_JUMP_IF_FALSE (self, i, op, arg, *args, **kws):
|
||||||
|
cond = args[0]
|
||||||
|
block_false = self.llvm_blocks[i + 3 + arg]
|
||||||
|
block_true = self.llvm_blocks[i + 3]
|
||||||
|
return [self.builder.cbranch(cond, block_true, block_false)]
|
||||||
|
# raise NotImplementedError("LLVMTranslator.op_JUMP_IF_FALSE")
|
||||||
|
|
||||||
|
def op_JUMP_IF_FALSE_OR_POP (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_FALSE_OR_POP")
|
||||||
|
|
||||||
|
def op_JUMP_IF_TRUE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_TRUE")
|
||||||
|
|
||||||
|
def op_JUMP_IF_TRUE_OR_POP (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_JUMP_IF_TRUE_OR_POP")
|
||||||
|
|
||||||
|
def op_LOAD_ATTR (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_LOAD_ATTR")
|
||||||
|
|
||||||
|
def op_LOAD_CONST (self, i, op, arg, *args, **kws):
|
||||||
|
py_val = self.code_obj.co_consts[arg]
|
||||||
|
if isinstance(py_val, int):
|
||||||
|
ret_val = [lc.Constant.int(bytetype.lc_int, py_val)]
|
||||||
|
elif isinstance(py_val, float):
|
||||||
|
ret_val = [lc.Constant.double(py_val)]
|
||||||
|
elif py_val == None:
|
||||||
|
ret_val = [None]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('Constant converstion for %r' %
|
||||||
|
(py_val,))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_LOAD_DEREF (self, i, op, arg, *args, **kws):
|
||||||
|
name = self.code_obj.co_freevars[arg]
|
||||||
|
ret_val = self.globals[name]
|
||||||
|
if isinstance(ret_val, lc.Type) and not hasattr(ret_val, '__name__'):
|
||||||
|
ret_val.__name__ = name
|
||||||
|
return [ret_val]
|
||||||
|
|
||||||
|
def op_LOAD_GLOBAL (self, i, op, arg, *args, **kws):
|
||||||
|
name = self.code_obj.co_names[arg]
|
||||||
|
ret_val = self.globals[name]
|
||||||
|
if isinstance(ret_val, lc.Type) and not hasattr(ret_val, '__name__'):
|
||||||
|
ret_val.__name__ = name
|
||||||
|
return [ret_val]
|
||||||
|
|
||||||
|
def op_POP_BLOCK (self, i, op, arg, *args, **kws):
|
||||||
|
self.loop_stack.pop()
|
||||||
|
return [self.builder.branch(self.llvm_blocks[i + 1])]
|
||||||
|
|
||||||
|
def op_POP_JUMP_IF_FALSE (self, i, op, arg, *args, **kws):
|
||||||
|
return [self.builder.cbranch(args[0], self.llvm_blocks[i + 3],
|
||||||
|
self.llvm_blocks[arg])]
|
||||||
|
|
||||||
|
def op_POP_JUMP_IF_TRUE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_POP_JUMP_IF_TRUE")
|
||||||
|
|
||||||
|
def op_POP_TOP (self, i, op, arg, *args, **kws):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def op_RETURN_VALUE (self, i, op, arg, *args, **kws):
|
||||||
|
if args[0] is None:
|
||||||
|
ret_val = [self.builder.ret_void()]
|
||||||
|
else:
|
||||||
|
ret_val = [self.builder.ret(args[0])]
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def op_SETUP_LOOP (self, i, op, arg, *args, **kws):
|
||||||
|
self.loop_stack.append((i, arg))
|
||||||
|
return [self.builder.branch(self.llvm_blocks[i + 3])]
|
||||||
|
|
||||||
|
def op_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_SLICE")
|
||||||
|
|
||||||
|
def op_STORE_ATTR (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_STORE_ATTR")
|
||||||
|
|
||||||
|
def op_STORE_SLICE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_STORE_SLICE")
|
||||||
|
|
||||||
|
def op_STORE_SUBSCR (self, i, op, arg, *args, **kws):
|
||||||
|
store_val, arr_val, index_val = args
|
||||||
|
dest_addr = self.builder.gep(arr_val, [index_val])
|
||||||
|
return [self.builder.store(store_val, dest_addr)]
|
||||||
|
|
||||||
|
def op_UNARY_CONVERT (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_CONVERT")
|
||||||
|
|
||||||
|
def op_UNARY_INVERT (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_INVERT")
|
||||||
|
|
||||||
|
def op_UNARY_NEGATIVE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_NEGATIVE")
|
||||||
|
|
||||||
|
def op_UNARY_NOT (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_NOT")
|
||||||
|
|
||||||
|
def op_UNARY_POSITIVE (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("LLVMTranslator.op_UNARY_POSITIVE")
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def translate_function (func, lltype, llvm_module = None, **kws):
|
||||||
|
'''Given a function and an LLVM function type, emit LLVM code for
|
||||||
|
that function using a new LLVMTranslator instance.'''
|
||||||
|
translator = LLVMTranslator(llvm_module)
|
||||||
|
ret_val = translator.translate(func, lltype, env = kws)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def translate_into_function (py_function, llvm_function, **kws):
|
||||||
|
translator = LLVMTranslator(llvm_function.module)
|
||||||
|
ret_val = translator.translate(py_function, llvm_function = llvm_function,
|
||||||
|
env = kws)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def llpython (lltype, llvm_module = None, **kws):
|
||||||
|
'''Decorator version of translate_function().'''
|
||||||
|
def _llpython (func):
|
||||||
|
return translate_function(func, lltype, llvm_module, **kws)
|
||||||
|
return _llpython
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def llpython_into (llvm_function, **kws):
|
||||||
|
def _llpython_into (func):
|
||||||
|
return translate_into_function(llvm_function, func, **kws)
|
||||||
|
return _llpython_into
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Main (self-test) routine
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
from tests import llfuncs, llfunctys
|
||||||
|
if not args:
|
||||||
|
args = ('doslice',)
|
||||||
|
elif 'all' in args:
|
||||||
|
args = [llfunc
|
||||||
|
for llfunc in dir(llfuncs) if not llfunc.startswith('_')]
|
||||||
|
llvm_module = lc.Module.new('test_module')
|
||||||
|
for arg in args:
|
||||||
|
translate_function(getattr(llfuncs, arg), getattr(llfunctys, arg),
|
||||||
|
llvm_module)
|
||||||
|
print(llvm_module)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of byte_translator.py
|
||||||
360
llpython/bytecode_visitor.py
Normal file
360
llpython/bytecode_visitor.py
Normal file
|
|
@ -0,0 +1,360 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
import opcode
|
||||||
|
from .opcode_util import itercode
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeVisitor (object):
|
||||||
|
opnames = [name.split('+')[0] for name in opcode.opname]
|
||||||
|
|
||||||
|
def visit_op (self, i, op, arg, *args, **kws):
|
||||||
|
if op < 0:
|
||||||
|
ret_val = self.visit_synthetic_op(i, op, arg, *args, **kws)
|
||||||
|
else:
|
||||||
|
method = getattr(self, 'op_' + self.opnames[op])
|
||||||
|
ret_val = method(i, op, arg, *args, **kws)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def visit_synthetic_op (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError(
|
||||||
|
'BytecodeVisitor.visit_synthetic_op() must be overloaded if using '
|
||||||
|
'synthetic opcodes.')
|
||||||
|
|
||||||
|
def _not_implemented (self, i, op, arg, *args, **kws):
|
||||||
|
raise NotImplementedError("BytecodeVisitor.op_%s (@ bytecode index %d)"
|
||||||
|
% (self.opnames[op], i))
|
||||||
|
|
||||||
|
op_BINARY_ADD = _not_implemented
|
||||||
|
op_BINARY_AND = _not_implemented
|
||||||
|
op_BINARY_DIVIDE = _not_implemented
|
||||||
|
op_BINARY_FLOOR_DIVIDE = _not_implemented
|
||||||
|
op_BINARY_LSHIFT = _not_implemented
|
||||||
|
op_BINARY_MODULO = _not_implemented
|
||||||
|
op_BINARY_MULTIPLY = _not_implemented
|
||||||
|
op_BINARY_OR = _not_implemented
|
||||||
|
op_BINARY_POWER = _not_implemented
|
||||||
|
op_BINARY_RSHIFT = _not_implemented
|
||||||
|
op_BINARY_SUBSCR = _not_implemented
|
||||||
|
op_BINARY_SUBTRACT = _not_implemented
|
||||||
|
op_BINARY_TRUE_DIVIDE = _not_implemented
|
||||||
|
op_BINARY_XOR = _not_implemented
|
||||||
|
op_BREAK_LOOP = _not_implemented
|
||||||
|
op_BUILD_CLASS = _not_implemented
|
||||||
|
op_BUILD_LIST = _not_implemented
|
||||||
|
op_BUILD_MAP = _not_implemented
|
||||||
|
op_BUILD_SET = _not_implemented
|
||||||
|
op_BUILD_SLICE = _not_implemented
|
||||||
|
op_BUILD_TUPLE = _not_implemented
|
||||||
|
op_CALL_FUNCTION = _not_implemented
|
||||||
|
op_CALL_FUNCTION_KW = _not_implemented
|
||||||
|
op_CALL_FUNCTION_VAR = _not_implemented
|
||||||
|
op_CALL_FUNCTION_VAR_KW = _not_implemented
|
||||||
|
op_COMPARE_OP = _not_implemented
|
||||||
|
op_CONTINUE_LOOP = _not_implemented
|
||||||
|
op_DELETE_ATTR = _not_implemented
|
||||||
|
op_DELETE_DEREF = _not_implemented
|
||||||
|
op_DELETE_FAST = _not_implemented
|
||||||
|
op_DELETE_GLOBAL = _not_implemented
|
||||||
|
op_DELETE_NAME = _not_implemented
|
||||||
|
op_DELETE_SLICE = _not_implemented
|
||||||
|
op_DELETE_SUBSCR = _not_implemented
|
||||||
|
op_DUP_TOP = _not_implemented
|
||||||
|
op_DUP_TOPX = _not_implemented
|
||||||
|
op_DUP_TOP_TWO = _not_implemented
|
||||||
|
op_END_FINALLY = _not_implemented
|
||||||
|
op_EXEC_STMT = _not_implemented
|
||||||
|
op_EXTENDED_ARG = _not_implemented
|
||||||
|
op_FOR_ITER = _not_implemented
|
||||||
|
op_GET_ITER = _not_implemented
|
||||||
|
op_IMPORT_FROM = _not_implemented
|
||||||
|
op_IMPORT_NAME = _not_implemented
|
||||||
|
op_IMPORT_STAR = _not_implemented
|
||||||
|
op_INPLACE_ADD = _not_implemented
|
||||||
|
op_INPLACE_AND = _not_implemented
|
||||||
|
op_INPLACE_DIVIDE = _not_implemented
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = _not_implemented
|
||||||
|
op_INPLACE_LSHIFT = _not_implemented
|
||||||
|
op_INPLACE_MODULO = _not_implemented
|
||||||
|
op_INPLACE_MULTIPLY = _not_implemented
|
||||||
|
op_INPLACE_OR = _not_implemented
|
||||||
|
op_INPLACE_POWER = _not_implemented
|
||||||
|
op_INPLACE_RSHIFT = _not_implemented
|
||||||
|
op_INPLACE_SUBTRACT = _not_implemented
|
||||||
|
op_INPLACE_TRUE_DIVIDE = _not_implemented
|
||||||
|
op_INPLACE_XOR = _not_implemented
|
||||||
|
op_JUMP_ABSOLUTE = _not_implemented
|
||||||
|
op_JUMP_FORWARD = _not_implemented
|
||||||
|
op_JUMP_IF_FALSE = _not_implemented
|
||||||
|
op_JUMP_IF_FALSE_OR_POP = _not_implemented
|
||||||
|
op_JUMP_IF_TRUE = _not_implemented
|
||||||
|
op_JUMP_IF_TRUE_OR_POP = _not_implemented
|
||||||
|
op_LIST_APPEND = _not_implemented
|
||||||
|
op_LOAD_ATTR = _not_implemented
|
||||||
|
op_LOAD_BUILD_CLASS = _not_implemented
|
||||||
|
op_LOAD_CLOSURE = _not_implemented
|
||||||
|
op_LOAD_CONST = _not_implemented
|
||||||
|
op_LOAD_DEREF = _not_implemented
|
||||||
|
op_LOAD_FAST = _not_implemented
|
||||||
|
op_LOAD_GLOBAL = _not_implemented
|
||||||
|
op_LOAD_LOCALS = _not_implemented
|
||||||
|
op_LOAD_NAME = _not_implemented
|
||||||
|
op_MAKE_CLOSURE = _not_implemented
|
||||||
|
op_MAKE_FUNCTION = _not_implemented
|
||||||
|
op_MAP_ADD = _not_implemented
|
||||||
|
op_NOP = _not_implemented
|
||||||
|
op_POP_BLOCK = _not_implemented
|
||||||
|
op_POP_EXCEPT = _not_implemented
|
||||||
|
op_POP_JUMP_IF_FALSE = _not_implemented
|
||||||
|
op_POP_JUMP_IF_TRUE = _not_implemented
|
||||||
|
op_POP_TOP = _not_implemented
|
||||||
|
op_PRINT_EXPR = _not_implemented
|
||||||
|
op_PRINT_ITEM = _not_implemented
|
||||||
|
op_PRINT_ITEM_TO = _not_implemented
|
||||||
|
op_PRINT_NEWLINE = _not_implemented
|
||||||
|
op_PRINT_NEWLINE_TO = _not_implemented
|
||||||
|
op_RAISE_VARARGS = _not_implemented
|
||||||
|
op_RETURN_VALUE = _not_implemented
|
||||||
|
op_ROT_FOUR = _not_implemented
|
||||||
|
op_ROT_THREE = _not_implemented
|
||||||
|
op_ROT_TWO = _not_implemented
|
||||||
|
op_SETUP_EXCEPT = _not_implemented
|
||||||
|
op_SETUP_FINALLY = _not_implemented
|
||||||
|
op_SETUP_LOOP = _not_implemented
|
||||||
|
op_SETUP_WITH = _not_implemented
|
||||||
|
op_SET_ADD = _not_implemented
|
||||||
|
op_SLICE = _not_implemented
|
||||||
|
op_STOP_CODE = _not_implemented
|
||||||
|
op_STORE_ATTR = _not_implemented
|
||||||
|
op_STORE_DEREF = _not_implemented
|
||||||
|
op_STORE_FAST = _not_implemented
|
||||||
|
op_STORE_GLOBAL = _not_implemented
|
||||||
|
op_STORE_LOCALS = _not_implemented
|
||||||
|
op_STORE_MAP = _not_implemented
|
||||||
|
op_STORE_NAME = _not_implemented
|
||||||
|
op_STORE_SLICE = _not_implemented
|
||||||
|
op_STORE_SUBSCR = _not_implemented
|
||||||
|
op_UNARY_CONVERT = _not_implemented
|
||||||
|
op_UNARY_INVERT = _not_implemented
|
||||||
|
op_UNARY_NEGATIVE = _not_implemented
|
||||||
|
op_UNARY_NOT = _not_implemented
|
||||||
|
op_UNARY_POSITIVE = _not_implemented
|
||||||
|
op_UNPACK_EX = _not_implemented
|
||||||
|
op_UNPACK_SEQUENCE = _not_implemented
|
||||||
|
op_WITH_CLEANUP = _not_implemented
|
||||||
|
op_YIELD_VALUE = _not_implemented
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeIterVisitor (BytecodeVisitor):
|
||||||
|
def visit (self, co_obj):
|
||||||
|
self.enter_code_object(co_obj)
|
||||||
|
for i, op, arg in itercode(co_obj.co_code):
|
||||||
|
self.visit_op(i, op, arg)
|
||||||
|
return self.exit_code_object(co_obj)
|
||||||
|
|
||||||
|
def enter_code_object (self, co_obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exit_code_object (self, co_obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BasicBlockVisitor (BytecodeVisitor):
|
||||||
|
def visit (self, blocks):
|
||||||
|
self.enter_blocks(blocks)
|
||||||
|
block_indices = list(blocks.keys())
|
||||||
|
block_indices.sort()
|
||||||
|
for block_index in block_indices:
|
||||||
|
self.enter_block(block_index)
|
||||||
|
for i, op, arg in blocks[block_index]:
|
||||||
|
self.visit_op(i, op, arg)
|
||||||
|
self.exit_block(block_index)
|
||||||
|
return self.exit_blocks(blocks)
|
||||||
|
|
||||||
|
def enter_blocks (self, blocks):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exit_blocks (self, blocks):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def enter_block (self, block_index):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exit_block (self, block_index):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BytecodeFlowVisitor (BytecodeVisitor):
|
||||||
|
def visit (self, flow):
|
||||||
|
self.block_list = list(flow.keys())
|
||||||
|
self.block_list.sort()
|
||||||
|
self.enter_flow_object(flow)
|
||||||
|
for block in self.block_list:
|
||||||
|
prelude = self.enter_block(block)
|
||||||
|
prelude_isa_list = isinstance(prelude, list)
|
||||||
|
if prelude or prelude_isa_list:
|
||||||
|
if not prelude_isa_list:
|
||||||
|
prelude = []
|
||||||
|
new_stmts = list(self.visit_op(i, op, arg, *args)
|
||||||
|
for i, op, _, arg, args in flow[block])
|
||||||
|
self.new_flow[block] = list(itertools.chain(
|
||||||
|
prelude, *new_stmts))
|
||||||
|
self.exit_block(block)
|
||||||
|
del self.block_list
|
||||||
|
return self.exit_flow_object(flow)
|
||||||
|
|
||||||
|
def visit_op (self, i, op, arg, *args, **kws):
|
||||||
|
new_args = []
|
||||||
|
for child_i, child_op, _, child_arg, child_args in args:
|
||||||
|
new_args.extend(self.visit_op(child_i, child_op, child_arg,
|
||||||
|
*child_args))
|
||||||
|
ret_val = super(BytecodeFlowVisitor, self).visit_op(i, op, arg,
|
||||||
|
*new_args)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_flow_object (self, flow):
|
||||||
|
self.new_flow = {}
|
||||||
|
|
||||||
|
def exit_flow_object (self, flow):
|
||||||
|
ret_val = self.new_flow
|
||||||
|
del self.new_flow
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class BenignBytecodeVisitorMixin (object):
|
||||||
|
def _do_nothing (self, i, op, arg, *args, **kws):
|
||||||
|
return [(i, op, self.opnames[op], arg, args)]
|
||||||
|
|
||||||
|
op_BINARY_ADD = _do_nothing
|
||||||
|
op_BINARY_AND = _do_nothing
|
||||||
|
op_BINARY_DIVIDE = _do_nothing
|
||||||
|
op_BINARY_FLOOR_DIVIDE = _do_nothing
|
||||||
|
op_BINARY_LSHIFT = _do_nothing
|
||||||
|
op_BINARY_MODULO = _do_nothing
|
||||||
|
op_BINARY_MULTIPLY = _do_nothing
|
||||||
|
op_BINARY_OR = _do_nothing
|
||||||
|
op_BINARY_POWER = _do_nothing
|
||||||
|
op_BINARY_RSHIFT = _do_nothing
|
||||||
|
op_BINARY_SUBSCR = _do_nothing
|
||||||
|
op_BINARY_SUBTRACT = _do_nothing
|
||||||
|
op_BINARY_TRUE_DIVIDE = _do_nothing
|
||||||
|
op_BINARY_XOR = _do_nothing
|
||||||
|
op_BREAK_LOOP = _do_nothing
|
||||||
|
op_BUILD_CLASS = _do_nothing
|
||||||
|
op_BUILD_LIST = _do_nothing
|
||||||
|
op_BUILD_MAP = _do_nothing
|
||||||
|
op_BUILD_SET = _do_nothing
|
||||||
|
op_BUILD_SLICE = _do_nothing
|
||||||
|
op_BUILD_TUPLE = _do_nothing
|
||||||
|
op_CALL_FUNCTION = _do_nothing
|
||||||
|
op_CALL_FUNCTION_KW = _do_nothing
|
||||||
|
op_CALL_FUNCTION_VAR = _do_nothing
|
||||||
|
op_CALL_FUNCTION_VAR_KW = _do_nothing
|
||||||
|
op_COMPARE_OP = _do_nothing
|
||||||
|
op_CONTINUE_LOOP = _do_nothing
|
||||||
|
op_DELETE_ATTR = _do_nothing
|
||||||
|
op_DELETE_DEREF = _do_nothing
|
||||||
|
op_DELETE_FAST = _do_nothing
|
||||||
|
op_DELETE_GLOBAL = _do_nothing
|
||||||
|
op_DELETE_NAME = _do_nothing
|
||||||
|
op_DELETE_SLICE = _do_nothing
|
||||||
|
op_DELETE_SUBSCR = _do_nothing
|
||||||
|
op_DUP_TOP = _do_nothing
|
||||||
|
op_DUP_TOPX = _do_nothing
|
||||||
|
op_DUP_TOP_TWO = _do_nothing
|
||||||
|
op_END_FINALLY = _do_nothing
|
||||||
|
op_EXEC_STMT = _do_nothing
|
||||||
|
op_EXTENDED_ARG = _do_nothing
|
||||||
|
op_FOR_ITER = _do_nothing
|
||||||
|
op_GET_ITER = _do_nothing
|
||||||
|
op_IMPORT_FROM = _do_nothing
|
||||||
|
op_IMPORT_NAME = _do_nothing
|
||||||
|
op_IMPORT_STAR = _do_nothing
|
||||||
|
op_INPLACE_ADD = _do_nothing
|
||||||
|
op_INPLACE_AND = _do_nothing
|
||||||
|
op_INPLACE_DIVIDE = _do_nothing
|
||||||
|
op_INPLACE_FLOOR_DIVIDE = _do_nothing
|
||||||
|
op_INPLACE_LSHIFT = _do_nothing
|
||||||
|
op_INPLACE_MODULO = _do_nothing
|
||||||
|
op_INPLACE_MULTIPLY = _do_nothing
|
||||||
|
op_INPLACE_OR = _do_nothing
|
||||||
|
op_INPLACE_POWER = _do_nothing
|
||||||
|
op_INPLACE_RSHIFT = _do_nothing
|
||||||
|
op_INPLACE_SUBTRACT = _do_nothing
|
||||||
|
op_INPLACE_TRUE_DIVIDE = _do_nothing
|
||||||
|
op_INPLACE_XOR = _do_nothing
|
||||||
|
op_JUMP_ABSOLUTE = _do_nothing
|
||||||
|
op_JUMP_FORWARD = _do_nothing
|
||||||
|
op_JUMP_IF_FALSE = _do_nothing
|
||||||
|
op_JUMP_IF_FALSE_OR_POP = _do_nothing
|
||||||
|
op_JUMP_IF_TRUE = _do_nothing
|
||||||
|
op_JUMP_IF_TRUE_OR_POP = _do_nothing
|
||||||
|
op_LIST_APPEND = _do_nothing
|
||||||
|
op_LOAD_ATTR = _do_nothing
|
||||||
|
op_LOAD_BUILD_CLASS = _do_nothing
|
||||||
|
op_LOAD_CLOSURE = _do_nothing
|
||||||
|
op_LOAD_CONST = _do_nothing
|
||||||
|
op_LOAD_DEREF = _do_nothing
|
||||||
|
op_LOAD_FAST = _do_nothing
|
||||||
|
op_LOAD_GLOBAL = _do_nothing
|
||||||
|
op_LOAD_LOCALS = _do_nothing
|
||||||
|
op_LOAD_NAME = _do_nothing
|
||||||
|
op_MAKE_CLOSURE = _do_nothing
|
||||||
|
op_MAKE_FUNCTION = _do_nothing
|
||||||
|
op_MAP_ADD = _do_nothing
|
||||||
|
op_NOP = _do_nothing
|
||||||
|
op_POP_BLOCK = _do_nothing
|
||||||
|
op_POP_EXCEPT = _do_nothing
|
||||||
|
op_POP_JUMP_IF_FALSE = _do_nothing
|
||||||
|
op_POP_JUMP_IF_TRUE = _do_nothing
|
||||||
|
op_POP_TOP = _do_nothing
|
||||||
|
op_PRINT_EXPR = _do_nothing
|
||||||
|
op_PRINT_ITEM = _do_nothing
|
||||||
|
op_PRINT_ITEM_TO = _do_nothing
|
||||||
|
op_PRINT_NEWLINE = _do_nothing
|
||||||
|
op_PRINT_NEWLINE_TO = _do_nothing
|
||||||
|
op_RAISE_VARARGS = _do_nothing
|
||||||
|
op_RETURN_VALUE = _do_nothing
|
||||||
|
op_ROT_FOUR = _do_nothing
|
||||||
|
op_ROT_THREE = _do_nothing
|
||||||
|
op_ROT_TWO = _do_nothing
|
||||||
|
op_SETUP_EXCEPT = _do_nothing
|
||||||
|
op_SETUP_FINALLY = _do_nothing
|
||||||
|
op_SETUP_LOOP = _do_nothing
|
||||||
|
op_SETUP_WITH = _do_nothing
|
||||||
|
op_SET_ADD = _do_nothing
|
||||||
|
op_SLICE = _do_nothing
|
||||||
|
op_STOP_CODE = _do_nothing
|
||||||
|
op_STORE_ATTR = _do_nothing
|
||||||
|
op_STORE_DEREF = _do_nothing
|
||||||
|
op_STORE_FAST = _do_nothing
|
||||||
|
op_STORE_GLOBAL = _do_nothing
|
||||||
|
op_STORE_LOCALS = _do_nothing
|
||||||
|
op_STORE_MAP = _do_nothing
|
||||||
|
op_STORE_NAME = _do_nothing
|
||||||
|
op_STORE_SLICE = _do_nothing
|
||||||
|
op_STORE_SUBSCR = _do_nothing
|
||||||
|
op_UNARY_CONVERT = _do_nothing
|
||||||
|
op_UNARY_INVERT = _do_nothing
|
||||||
|
op_UNARY_NEGATIVE = _do_nothing
|
||||||
|
op_UNARY_NOT = _do_nothing
|
||||||
|
op_UNARY_POSITIVE = _do_nothing
|
||||||
|
op_UNPACK_EX = _do_nothing
|
||||||
|
op_UNPACK_SEQUENCE = _do_nothing
|
||||||
|
op_WITH_CLEANUP = _do_nothing
|
||||||
|
op_YIELD_VALUE = _do_nothing
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of bytecode_visitor.py
|
||||||
42
llpython/bytetype.py
Normal file
42
llpython/bytetype.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
lvoid = lc.Type.void()
|
||||||
|
li1 = lc.Type.int(1)
|
||||||
|
li8 = lc.Type.int(8)
|
||||||
|
li16 = lc.Type.int(16)
|
||||||
|
li32 = lc.Type.int(32)
|
||||||
|
li64 = lc.Type.int(64)
|
||||||
|
liptr = lc.Type.int(ctypes.sizeof(ctypes.c_void_p) * 8)
|
||||||
|
lc_size_t = lc.Type.int(ctypes.sizeof(
|
||||||
|
getattr(ctypes, 'c_ssize_t', getattr(ctypes, 'c_size_t'))) * 8)
|
||||||
|
lfloat = lc.Type.float()
|
||||||
|
ldouble = lc.Type.double()
|
||||||
|
li8_ptr = lc.Type.pointer(li8)
|
||||||
|
|
||||||
|
lc_int = lc.Type.int(ctypes.sizeof(ctypes.c_int) * 8)
|
||||||
|
lc_long = lc.Type.int(ctypes.sizeof(ctypes.c_long) * 8)
|
||||||
|
|
||||||
|
l_pyobject_head = [lc_size_t, lc.Type.pointer(li32)]
|
||||||
|
l_pyobject_head_struct = lc.Type.struct(l_pyobject_head)
|
||||||
|
l_pyobj_p = l_pyobject_head_struct_p = lc.Type.pointer(l_pyobject_head_struct)
|
||||||
|
l_pyfunc = lc.Type.function(l_pyobj_p, (l_pyobj_p, l_pyobj_p))
|
||||||
|
|
||||||
|
strlen = lc.Type.function(lc_size_t, (li8_ptr,))
|
||||||
|
strncpy = lc.Type.function(li8_ptr, (li8_ptr, li8_ptr, lc_size_t))
|
||||||
|
strndup = lc.Type.function(li8_ptr, (li8_ptr, lc_size_t))
|
||||||
|
malloc = lc.Type.function(li8_ptr, (lc_size_t,))
|
||||||
|
free = lc.Type.function(lvoid, (li8_ptr,))
|
||||||
|
|
||||||
|
Py_BuildValue = lc.Type.function(l_pyobj_p, [li8_ptr], True)
|
||||||
|
PyArg_ParseTuple = lc.Type.function(lc_int, [l_pyobj_p, li8_ptr], True)
|
||||||
|
PyEval_SaveThread = lc.Type.function(li8_ptr, [])
|
||||||
|
PyEval_RestoreThread = lc.Type.function(lc.Type.void(), [li8_ptr])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of bytetype.py
|
||||||
217
llpython/control_flow.py
Normal file
217
llpython/control_flow.py
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class ControlFlowGraph (object):
|
||||||
|
def __init__ (self):
|
||||||
|
self.blocks = {}
|
||||||
|
self.blocks_in = {}
|
||||||
|
self.blocks_out = {}
|
||||||
|
self.blocks_reads = {}
|
||||||
|
self.blocks_writes = {}
|
||||||
|
self.blocks_writer = {}
|
||||||
|
self.blocks_dom = {}
|
||||||
|
self.blocks_reaching = {}
|
||||||
|
|
||||||
|
def add_block (self, key, value = None):
|
||||||
|
self.blocks[key] = value
|
||||||
|
if key not in self.blocks_in:
|
||||||
|
self.blocks_in[key] = set()
|
||||||
|
self.blocks_out[key] = set()
|
||||||
|
self.blocks_reads[key] = set()
|
||||||
|
self.blocks_writes[key] = set()
|
||||||
|
self.blocks_writer[key] = {}
|
||||||
|
|
||||||
|
def add_edge (self, from_block, to_block):
|
||||||
|
self.blocks_out[from_block].add(to_block)
|
||||||
|
self.blocks_in[to_block].add(from_block)
|
||||||
|
|
||||||
|
def unlink_unreachables (self):
|
||||||
|
changed = True
|
||||||
|
next_blocks = self.blocks.keys()
|
||||||
|
next_blocks.remove(0)
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
blocks = next_blocks
|
||||||
|
next_blocks = blocks[:]
|
||||||
|
for block in blocks:
|
||||||
|
if len(self.blocks_in[block]) == 0:
|
||||||
|
blocks_out = self.blocks_out[block]
|
||||||
|
for out_edge in blocks_out:
|
||||||
|
self.blocks_in[out_edge].discard(block)
|
||||||
|
blocks_out.clear()
|
||||||
|
next_blocks.remove(block)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
def compute_dataflow (self):
|
||||||
|
'''Compute the dominator and reaching dataflow relationships
|
||||||
|
in the CFG.'''
|
||||||
|
blocks = set(self.blocks.keys())
|
||||||
|
nonentry_blocks = blocks.copy()
|
||||||
|
for block in blocks:
|
||||||
|
self.blocks_dom[block] = blocks
|
||||||
|
self.blocks_reaching[block] = set((block,))
|
||||||
|
if len(self.blocks_in[block]) == 0:
|
||||||
|
self.blocks_dom[block] = set((block,))
|
||||||
|
nonentry_blocks.remove(block)
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
for block in nonentry_blocks:
|
||||||
|
olddom = self.blocks_dom[block]
|
||||||
|
newdom = set.intersection(*[self.blocks_dom[pred]
|
||||||
|
for pred in self.blocks_in[block]])
|
||||||
|
newdom.add(block)
|
||||||
|
if newdom != olddom:
|
||||||
|
changed = True
|
||||||
|
self.blocks_dom[block] = newdom
|
||||||
|
oldreaching = self.blocks_reaching[block]
|
||||||
|
newreaching = set.union(
|
||||||
|
*[self.blocks_reaching[pred]
|
||||||
|
for pred in self.blocks_in[block]])
|
||||||
|
newreaching.add(block)
|
||||||
|
if newreaching != oldreaching:
|
||||||
|
changed = True
|
||||||
|
self.blocks_reaching[block] = newreaching
|
||||||
|
return self.blocks_dom, self.blocks_reaching
|
||||||
|
|
||||||
|
def update_for_ssa (self):
|
||||||
|
'''Modify the blocks_writes map to reflect phi nodes inserted
|
||||||
|
for static single assignment representations.'''
|
||||||
|
joins = [block for block in self.blocks.keys()
|
||||||
|
if len(self.blocks_in[block]) > 1]
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
for block in joins:
|
||||||
|
phis_needed = self.phi_needed(block)
|
||||||
|
for affected_local in phis_needed:
|
||||||
|
if affected_local not in self.blocks_writes[block]:
|
||||||
|
changed = True
|
||||||
|
# NOTE: For this to work, we assume that basic
|
||||||
|
# blocks are indexed by their instruction
|
||||||
|
# index in the VM bytecode.
|
||||||
|
self.writes_local(block, block, affected_local)
|
||||||
|
if changed:
|
||||||
|
# Any modifications have invalidated the reaching
|
||||||
|
# definitions, so delete any memoized results.
|
||||||
|
if hasattr(self, 'reaching_definitions'):
|
||||||
|
del self.reaching_definitions
|
||||||
|
|
||||||
|
def idom (self, block):
|
||||||
|
'''Compute the immediate dominator (idom) of the given block
|
||||||
|
key. Returns None if the block has no in edges.
|
||||||
|
|
||||||
|
Note that in the case where there are multiple immediate
|
||||||
|
dominators (a join after a non-loop branch), this returns one
|
||||||
|
of the predecessors, but is not guaranteed to reliably select
|
||||||
|
one over the others (depends on the ordering of the set type
|
||||||
|
iterator).'''
|
||||||
|
preds = self.blocks_in[block]
|
||||||
|
npreds = len(preds)
|
||||||
|
if npreds == 0:
|
||||||
|
ret_val = None
|
||||||
|
elif npreds == 1:
|
||||||
|
ret_val = tuple(preds)[0]
|
||||||
|
else:
|
||||||
|
ret_val = [pred for pred in preds
|
||||||
|
if block not in self.blocks_dom[pred]][0]
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def block_writes_to_writer_map (self, block):
|
||||||
|
ret_val = {}
|
||||||
|
for local in self.blocks_writes[block]:
|
||||||
|
ret_val[local] = block
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def get_reaching_definitions (self, block):
|
||||||
|
'''Return a nested map for the given block
|
||||||
|
s.t. ret_val[pred][local] equals the block key for the
|
||||||
|
definition of local that reaches the argument block via that
|
||||||
|
predecessor.
|
||||||
|
|
||||||
|
Useful for actually populating phi nodes, once you know you
|
||||||
|
need them.'''
|
||||||
|
has_memoized = hasattr(self, 'reaching_definitions')
|
||||||
|
if has_memoized and block in self.reaching_definitions:
|
||||||
|
ret_val = self.reaching_definitions[block]
|
||||||
|
else:
|
||||||
|
preds = self.blocks_in[block]
|
||||||
|
ret_val = {}
|
||||||
|
for pred in preds:
|
||||||
|
ret_val[pred] = self.block_writes_to_writer_map(pred)
|
||||||
|
crnt = self.idom(pred)
|
||||||
|
while crnt != None:
|
||||||
|
crnt_writer_map = self.block_writes_to_writer_map(crnt)
|
||||||
|
# This order of update favors the first definitions
|
||||||
|
# encountered in the traversal since the traversal
|
||||||
|
# visits blocks in reverse execution order.
|
||||||
|
crnt_writer_map.update(ret_val[pred])
|
||||||
|
ret_val[pred] = crnt_writer_map
|
||||||
|
crnt = self.idom(crnt)
|
||||||
|
if not has_memoized:
|
||||||
|
self.reaching_definitions = {}
|
||||||
|
self.reaching_definitions[block] = ret_val
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def nreaches (self, block):
|
||||||
|
'''For each local, find the number of unique reaching
|
||||||
|
definitions the current block has.'''
|
||||||
|
reaching_definitions = self.get_reaching_definitions(block)
|
||||||
|
definition_map = {}
|
||||||
|
for pred in self.blocks_in[block]:
|
||||||
|
reaching_from_pred = reaching_definitions[pred]
|
||||||
|
for local in reaching_from_pred.keys():
|
||||||
|
if local not in definition_map:
|
||||||
|
definition_map[local] = set()
|
||||||
|
definition_map[local].add(reaching_from_pred[local])
|
||||||
|
ret_val = {}
|
||||||
|
for local in definition_map.keys():
|
||||||
|
ret_val[local] = len(definition_map[local])
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def writes_local (self, block, write_instr_index, local_index):
|
||||||
|
self.blocks_writes[block].add(local_index)
|
||||||
|
block_writers = self.blocks_writer[block]
|
||||||
|
old_index = block_writers.get(local_index, -1)
|
||||||
|
# This checks for a corner case that would impact
|
||||||
|
# numba.translate.Translate.build_phi_nodes().
|
||||||
|
assert old_index != write_instr_index, (
|
||||||
|
"Found corner case for STORE_FAST at a CFG join!")
|
||||||
|
block_writers[local_index] = max(write_instr_index, old_index)
|
||||||
|
|
||||||
|
def phi_needed (self, join):
|
||||||
|
'''Return the set of locals that will require a phi node to be
|
||||||
|
generated at the given join.'''
|
||||||
|
nreaches = self.nreaches(join)
|
||||||
|
return set([local for local in nreaches.keys()
|
||||||
|
if nreaches[local] > 1])
|
||||||
|
|
||||||
|
def pprint (self, *args, **kws):
|
||||||
|
pprint.pprint(self.__dict__, *args, **kws)
|
||||||
|
|
||||||
|
def pformat (self, *args, **kws):
|
||||||
|
return pprint.pformat(self.__dict__, *args, **kws)
|
||||||
|
|
||||||
|
def to_dot (self, graph_name = None):
|
||||||
|
'''Return a dot (digraph visualizer in Graphviz) graph
|
||||||
|
description as a string.'''
|
||||||
|
if graph_name is None:
|
||||||
|
graph_name = 'CFG_%d' % id(self)
|
||||||
|
lines_out = []
|
||||||
|
for block_index in self.blocks:
|
||||||
|
lines_out.append(
|
||||||
|
'BLOCK_%r [shape=box, label="BLOCK_%r\\nr: %r, w: %r"];' %
|
||||||
|
(block_index, block_index,
|
||||||
|
tuple(self.blocks_reads[block_index]),
|
||||||
|
tuple(self.blocks_writes[block_index])))
|
||||||
|
for block_index in self.blocks:
|
||||||
|
for out_edge in self.blocks_out[block_index]:
|
||||||
|
lines_out.append('BLOCK_%r -> BLOCK_%r;' %
|
||||||
|
(block_index, out_edge))
|
||||||
|
return 'digraph %s {\n%s\n}\n' % (graph_name, '\n'.join(lines_out))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of control_flow.py
|
||||||
26
llpython/gen_bytecode_visitor.py
Normal file
26
llpython/gen_bytecode_visitor.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from . import opcode_util
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def generate_bytecode_visitor (classname = 'BytecodeVisitor',
|
||||||
|
baseclass = 'object'):
|
||||||
|
opnames = list(set((opname.split('+')[0]
|
||||||
|
for opname in opcode_util.OPCODE_MAP.keys())))
|
||||||
|
opnames.sort()
|
||||||
|
return 'class %s (%s):\n%s\n' % (
|
||||||
|
classname, baseclass,
|
||||||
|
'\n\n'.join((' def op_%s (self, i, op, arg):\n'
|
||||||
|
' raise NotImplementedError("%s.op_%s")' %
|
||||||
|
(opname, classname, opname)
|
||||||
|
for opname in opnames)))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
print(generate_bytecode_visitor(*sys.argv[1:]))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of gen_bytecode_visitor.py
|
||||||
367
llpython/nobitey.py
Normal file
367
llpython/nobitey.py
Normal file
|
|
@ -0,0 +1,367 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
import imp
|
||||||
|
import io
|
||||||
|
import types
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
import llvm.ee as le
|
||||||
|
|
||||||
|
from . import bytetype, byte_translator
|
||||||
|
from .pyaddfunc import pyaddfunc
|
||||||
|
|
||||||
|
LLVM_TO_INT_PARSE_STR_MAP = {
|
||||||
|
8 : 'b',
|
||||||
|
16 : 'h',
|
||||||
|
32 : 'i', # Note that on 32-bit systems sizeof(int) == sizeof(long)
|
||||||
|
64 : 'L', # Seeing sizeof(long long) == 8 on both 32 and 64-bit platforms
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVM_TO_PARSE_STR_MAP = {
|
||||||
|
lc.TYPE_FLOAT : 'f',
|
||||||
|
lc.TYPE_DOUBLE : 'd',
|
||||||
|
}
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
# XXX Stolen from numba.translate
|
||||||
|
|
||||||
|
def get_string_constant (module, const_str):
|
||||||
|
const_name = "__STR_%x" % (hash(const_str),)
|
||||||
|
try:
|
||||||
|
ret_val = module.get_global_variable_named(const_name)
|
||||||
|
except:
|
||||||
|
lconst_str = lc.Constant.stringz(const_str)
|
||||||
|
ret_val = module.add_global_variable(lconst_str.type, const_name)
|
||||||
|
ret_val.initializer = lconst_str
|
||||||
|
ret_val.linkage = lc.LINKAGE_INTERNAL
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class NoBitey (object):
|
||||||
|
def __init__ (self, target_module = None, type_annotations = None):
|
||||||
|
if target_module is None:
|
||||||
|
target_module = lc.Module.new('NoBitey_%d' % id(self))
|
||||||
|
if type_annotations is None:
|
||||||
|
type_annotations = {}
|
||||||
|
self.target_module = target_module
|
||||||
|
self.type_aliases = type_annotations # Reserved for future use.
|
||||||
|
|
||||||
|
def _build_parse_string (self, llvm_type):
|
||||||
|
kind = llvm_type.kind
|
||||||
|
if kind == lc.TYPE_INTEGER:
|
||||||
|
ret_val = LLVM_TO_INT_PARSE_STR_MAP[llvm_type.width]
|
||||||
|
elif kind in LLVM_TO_PARSE_STR_MAP:
|
||||||
|
ret_val = LLVM_TO_PARSE_STR_MAP[kind]
|
||||||
|
else:
|
||||||
|
raise TypeError('Unsupported LLVM type: %s' % str(llvm_type))
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def build_parse_string (self, llvm_tys):
|
||||||
|
"""Given a set of LLVM types, return a string for parsing
|
||||||
|
them via PyArg_ParseTuple."""
|
||||||
|
return ''.join((self._build_parse_string(ty)
|
||||||
|
for ty in llvm_tys))
|
||||||
|
|
||||||
|
def handle_abi_casts (self, builder, result):
|
||||||
|
if result.type.kind == lc.TYPE_FLOAT:
|
||||||
|
# NOTE: The C ABI apparently casts floats to doubles when
|
||||||
|
# an argument must be pushed on the stack, as is the case
|
||||||
|
# when calling a variable argument function.
|
||||||
|
# XXX Is there documentation on this where I can find all
|
||||||
|
# coercion rules? Do we still need some libffi
|
||||||
|
# integration?
|
||||||
|
result = builder.fpext(result, bytetype.ldouble)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def build_wrapper_function (self, llvm_function, engine = None):
|
||||||
|
arg_types = llvm_function.type.pointee.args
|
||||||
|
return_type = llvm_function.type.pointee.return_type
|
||||||
|
li32_0 = lc.Constant.int(bytetype.li32, 0)
|
||||||
|
def get_llvm_function (builder):
|
||||||
|
if self.target_module != llvm_function.module:
|
||||||
|
llvm_function_ptr = self.target_module.add_global_variable(
|
||||||
|
llvm_function.type, llvm_function.name)
|
||||||
|
llvm_function_ptr.initializer = lc.Constant.inttoptr(
|
||||||
|
lc.Constant.int(
|
||||||
|
bytetype.liptr,
|
||||||
|
engine.get_pointer_to_function(llvm_function)),
|
||||||
|
llvm_function.type)
|
||||||
|
llvm_function_ptr.linkage = lc.LINKAGE_INTERNAL
|
||||||
|
ret_val = builder.load(llvm_function_ptr)
|
||||||
|
else:
|
||||||
|
ret_val = llvm_function
|
||||||
|
return ret_val
|
||||||
|
def build_parse_args (builder):
|
||||||
|
return [builder.alloca(arg_type) for arg_type in arg_types]
|
||||||
|
def build_parse_string (builder):
|
||||||
|
parse_str = get_string_constant(
|
||||||
|
self.target_module, self.build_parse_string(arg_types))
|
||||||
|
return builder.gep(parse_str, (li32_0, li32_0))
|
||||||
|
def load_target_args (builder, args):
|
||||||
|
return [builder.load(arg) for arg in args]
|
||||||
|
def build_build_string (builder):
|
||||||
|
build_str = get_string_constant(
|
||||||
|
self.target_module, self._build_parse_string(return_type))
|
||||||
|
return builder.gep(build_str, (li32_0, li32_0))
|
||||||
|
handle_abi_casts = self.handle_abi_casts
|
||||||
|
target_function_name = llvm_function.name + "_wrapper"
|
||||||
|
# __________________________________________________
|
||||||
|
@byte_translator.llpython(bytetype.l_pyfunc, self.target_module,
|
||||||
|
**locals())
|
||||||
|
def _wrapper (self, args):
|
||||||
|
ret_val = l_pyobj_p(0)
|
||||||
|
parse_args = build_parse_args()
|
||||||
|
parse_result = PyArg_ParseTuple(args, build_parse_string(),
|
||||||
|
*parse_args)
|
||||||
|
if parse_result != li32(0):
|
||||||
|
thread_state = PyEval_SaveThread()
|
||||||
|
target_args = load_target_args(parse_args)
|
||||||
|
llresult = handle_abi_casts(get_llvm_function()(*target_args))
|
||||||
|
PyEval_RestoreThread(thread_state)
|
||||||
|
ret_val = Py_BuildValue(build_build_string(), llresult)
|
||||||
|
return ret_val
|
||||||
|
# __________________________________________________
|
||||||
|
return _wrapper
|
||||||
|
|
||||||
|
def wrap_llvm_module (self, llvm_module, engine = None, py_module = None):
|
||||||
|
'''
|
||||||
|
Shamefully adapted from bitey.bind.wrap_llvm_module().
|
||||||
|
'''
|
||||||
|
functions = [func for func in llvm_module.functions
|
||||||
|
if not func.name.startswith("_")
|
||||||
|
and not func.is_declaration
|
||||||
|
and func.linkage == lc.LINKAGE_EXTERNAL]
|
||||||
|
if engine is None:
|
||||||
|
engine = le.ExecutionEngine.new(llvm_module)
|
||||||
|
wrappers = [self.build_wrapper_function(func, engine)
|
||||||
|
for func in functions]
|
||||||
|
if __debug__: print(self.target_module)
|
||||||
|
if self.target_module != llvm_module:
|
||||||
|
engine.add_module(self.target_module)
|
||||||
|
py_wrappers = [pyaddfunc(wrapper.name,
|
||||||
|
engine.get_pointer_to_function(wrapper))
|
||||||
|
for wrapper in wrappers]
|
||||||
|
if py_module:
|
||||||
|
for py_wrapper in py_wrappers:
|
||||||
|
setattr(py_module, py_wrapper.__name__[:-8], py_wrapper)
|
||||||
|
setattr(py_module, '_llvm_module', llvm_module)
|
||||||
|
setattr(py_module, '_llvm_engine', engine)
|
||||||
|
if self.target_module != llvm_module:
|
||||||
|
setattr(py_module, '_llvm_wrappers', self.target_module)
|
||||||
|
return engine, py_wrappers
|
||||||
|
|
||||||
|
def wrap_llvm_module_in_python (self, llvm_module, py_module = None):
|
||||||
|
'''
|
||||||
|
Mildly reworked and abstracted bitey.bind.wrap_llvm_bitcode().
|
||||||
|
Abstracted to accept any existing LLVM Module object, and
|
||||||
|
return a Python wrapper module (even if one wasn't originally
|
||||||
|
specified).
|
||||||
|
'''
|
||||||
|
if py_module is None:
|
||||||
|
py_module = types.ModuleType(str(llvm_module.id))
|
||||||
|
engine = le.ExecutionEngine.new(llvm_module)
|
||||||
|
self.wrap_llvm_module(llvm_module, engine, py_module)
|
||||||
|
return py_module
|
||||||
|
|
||||||
|
def wrap_llvm_bitcode (self, bitcode, py_module = None):
|
||||||
|
'''
|
||||||
|
Intended to be drop-in replacement of
|
||||||
|
bitey.bind.wrap_llvm_bitcode().
|
||||||
|
'''
|
||||||
|
return self.wrap_llvm_module_in_python(
|
||||||
|
lc.Module.from_bitcode(io.BytesIO(bitcode)), py_module)
|
||||||
|
|
||||||
|
def wrap_llvm_assembly (self, llvm_asm, py_module = None):
|
||||||
|
return self.wrap_llvm_module_in_python(
|
||||||
|
lc.Module.from_assembly(io.BytesIO(llvm_asm)), py_module)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class NoBiteyLoader(object):
|
||||||
|
"""
|
||||||
|
Load LLVM compiled bitcode and autogenerate a ctypes binding.
|
||||||
|
|
||||||
|
Initially copied and adapted from bitey.loader module.
|
||||||
|
"""
|
||||||
|
def __init__(self, pkg, name, source, preload, postload):
|
||||||
|
self.package = pkg
|
||||||
|
self.name = name
|
||||||
|
self.fullname = '.'.join((pkg,name))
|
||||||
|
self.source = source
|
||||||
|
self.preload = preload
|
||||||
|
self.postload = postload
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _check_magic(cls, filename):
|
||||||
|
if os.path.exists(filename):
|
||||||
|
magic = open(filename,"rb").read(4)
|
||||||
|
if magic == b'\xde\xc0\x17\x0b':
|
||||||
|
return True
|
||||||
|
elif magic[:2] == b'\x42\x43':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build_module(cls, fullname, source_path, source_data, preload=None,
|
||||||
|
postload=None):
|
||||||
|
name = fullname.split(".")[-1]
|
||||||
|
mod = imp.new_module(name)
|
||||||
|
if preload:
|
||||||
|
exec(preload, mod.__dict__, mod.__dict__)
|
||||||
|
type_annotations = getattr(mod, '_type_annotations', None)
|
||||||
|
nb = NoBitey(type_annotations = type_annotations)
|
||||||
|
if source_path.endswith(('.o', '.bc')):
|
||||||
|
nb.wrap_llvm_bitcode(source_data, mod)
|
||||||
|
elif source_path.endswith('.s'):
|
||||||
|
nb.wrap_llvm_assembly(source_data, mod)
|
||||||
|
if postload:
|
||||||
|
exec(postload, mod.__dict__, mod.__dict__)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find_module(cls, fullname, paths = None):
|
||||||
|
if paths is None:
|
||||||
|
paths = sys.path
|
||||||
|
names = fullname.split('.')
|
||||||
|
modname = names[-1]
|
||||||
|
source_paths = None
|
||||||
|
for f in paths:
|
||||||
|
path = os.path.join(os.path.realpath(f), modname)
|
||||||
|
source = path + '.o'
|
||||||
|
if cls._check_magic(source):
|
||||||
|
source_paths = path, source
|
||||||
|
break
|
||||||
|
source = path + '.bc'
|
||||||
|
if os.path.exists(source):
|
||||||
|
source_paths = path, source
|
||||||
|
break
|
||||||
|
source = path + '.s'
|
||||||
|
if os.path.exists(source):
|
||||||
|
source_paths = path, source
|
||||||
|
break
|
||||||
|
if source_paths:
|
||||||
|
path, source = source_paths
|
||||||
|
return cls('.'.join(names[:-1]), modname, source,
|
||||||
|
path + ".pre.py", path + ".post.py")
|
||||||
|
|
||||||
|
def get_code(self, module):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_data(self, module):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_filename(self, name):
|
||||||
|
return self.source
|
||||||
|
|
||||||
|
def get_source(self, name):
|
||||||
|
with open(self.source, 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def is_package(self, *args, **kw):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def load_module(self, fullname):
|
||||||
|
if fullname in sys.modules:
|
||||||
|
return sys.modules[fullname]
|
||||||
|
|
||||||
|
preload = None
|
||||||
|
postload = None
|
||||||
|
|
||||||
|
# Get the preload file (if any)
|
||||||
|
if os.path.exists(self.preload):
|
||||||
|
with open(self.preload) as f:
|
||||||
|
preload = f.read()
|
||||||
|
|
||||||
|
# Get the source
|
||||||
|
with open(self.source, 'rb') as f:
|
||||||
|
source_data = f.read()
|
||||||
|
|
||||||
|
# Get the postload file (if any)
|
||||||
|
if os.path.exists(self.postload):
|
||||||
|
with open(self.postload) as f:
|
||||||
|
postload = f.read()
|
||||||
|
|
||||||
|
mod = self.build_module(fullname, self.get_filename(None), source_data,
|
||||||
|
preload, postload)
|
||||||
|
sys.modules[fullname] = mod
|
||||||
|
mod.__loader__ = self
|
||||||
|
mod.__file__ = self.source
|
||||||
|
return mod
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls):
|
||||||
|
if cls not in sys.meta_path:
|
||||||
|
sys.meta_path.append(cls)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove(cls):
|
||||||
|
sys.meta_path.remove(cls)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def _mk_add_42 (llvm_module, at_type = bytetype.lc_long):
|
||||||
|
f = llvm_module.add_function(
|
||||||
|
lc.Type.function(at_type, [at_type]), 'add_42_%s' % str(at_type))
|
||||||
|
block = f.append_basic_block('entry')
|
||||||
|
builder = lc.Builder.new(block)
|
||||||
|
if at_type.kind == lc.TYPE_INTEGER:
|
||||||
|
const_42 = lc.Constant.int(at_type, 42)
|
||||||
|
add = builder.add
|
||||||
|
elif at_type.kind in (lc.TYPE_FLOAT, lc.TYPE_DOUBLE):
|
||||||
|
const_42 = lc.Constant.real(at_type, 42.)
|
||||||
|
add = builder.fadd
|
||||||
|
else:
|
||||||
|
raise TypeError('Unsupported type: %s' % str(at_type))
|
||||||
|
builder.ret(add(f.args[0], const_42))
|
||||||
|
return f
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_test_module ():
|
||||||
|
llvm_module = lc.Module.new('nobitey_test')
|
||||||
|
for ty in (bytetype.li32, bytetype.li64, bytetype.lfloat,
|
||||||
|
bytetype.ldouble):
|
||||||
|
fn = _mk_add_42(llvm_module, ty)
|
||||||
|
return llvm_module
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def test_wrap_module (arg = None):
|
||||||
|
# Build up a module.
|
||||||
|
m = build_test_module()
|
||||||
|
if arg and arg.lower() == 'separated':
|
||||||
|
wrap_module = NoBitey().wrap_llvm_module_in_python(m)
|
||||||
|
else:
|
||||||
|
wrap_module = NoBitey(m).wrap_llvm_module_in_python(m)
|
||||||
|
# Now try running the generated wrappers.
|
||||||
|
for py_wf_name in ('add_42_i32', 'add_42_i64', 'add_42_float',
|
||||||
|
'add_42_double'):
|
||||||
|
py_wf = getattr(wrap_module, py_wf_name)
|
||||||
|
for i in range(42):
|
||||||
|
result = py_wf(i)
|
||||||
|
expected = i + 42
|
||||||
|
assert result == expected, "%r != %r in %r" % (
|
||||||
|
result, expected, py_wf)
|
||||||
|
return wrap_module
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
if args:
|
||||||
|
for arg in args:
|
||||||
|
test_wrap_module(arg)
|
||||||
|
else:
|
||||||
|
test_wrap_module()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of nobitey.py
|
||||||
225
llpython/opcode_util.py
Normal file
225
llpython/opcode_util.py
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import dis
|
||||||
|
import opcode
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module data
|
||||||
|
|
||||||
|
hasjump = opcode.hasjrel + opcode.hasjabs
|
||||||
|
hascbranch = [op for op in hasjump
|
||||||
|
if 'IF' in opcode.opname[op]
|
||||||
|
or opcode.opname[op] in ('FOR_ITER', 'SETUP_LOOP')]
|
||||||
|
|
||||||
|
# Since the actual opcode value may change, manage opcode abstraction
|
||||||
|
# data by opcode name.
|
||||||
|
|
||||||
|
OPCODE_MAP = {
|
||||||
|
'BINARY_ADD': (2, 1, None),
|
||||||
|
'BINARY_AND': (2, 1, None),
|
||||||
|
'BINARY_DIVIDE': (2, 1, None),
|
||||||
|
'BINARY_FLOOR_DIVIDE': (2, 1, None),
|
||||||
|
'BINARY_LSHIFT': (2, 1, None),
|
||||||
|
'BINARY_MODULO': (2, 1, None),
|
||||||
|
'BINARY_MULTIPLY': (2, 1, None),
|
||||||
|
'BINARY_OR': (2, 1, None),
|
||||||
|
'BINARY_POWER': (2, 1, None),
|
||||||
|
'BINARY_RSHIFT': (2, 1, None),
|
||||||
|
'BINARY_SUBSCR': (2, 1, None),
|
||||||
|
'BINARY_SUBTRACT': (2, 1, None),
|
||||||
|
'BINARY_TRUE_DIVIDE': (2, 1, None),
|
||||||
|
'BINARY_XOR': (2, 1, None),
|
||||||
|
'BREAK_LOOP': (0, None, 1),
|
||||||
|
'BUILD_CLASS': (None, None, None),
|
||||||
|
'BUILD_LIST': (-1, 1, None),
|
||||||
|
'BUILD_MAP': (None, None, None),
|
||||||
|
'BUILD_SET': (None, None, None),
|
||||||
|
'BUILD_SLICE': (None, None, None),
|
||||||
|
'BUILD_TUPLE': (-1, 1, None),
|
||||||
|
'CALL_FUNCTION': (-2, 1, None),
|
||||||
|
'CALL_FUNCTION_KW': (-3, 1, None),
|
||||||
|
'CALL_FUNCTION_VAR': (-3, 1, None),
|
||||||
|
'CALL_FUNCTION_VAR_KW': (-4, 1, None),
|
||||||
|
'COMPARE_OP': (2, 1, None),
|
||||||
|
'CONTINUE_LOOP': (None, None, None),
|
||||||
|
'DELETE_ATTR': (1, None, 1),
|
||||||
|
'DELETE_DEREF': (None, None, None),
|
||||||
|
'DELETE_FAST': (0, None, 1),
|
||||||
|
'DELETE_GLOBAL': (0, None, 1),
|
||||||
|
'DELETE_NAME': (0, None, 1),
|
||||||
|
'DELETE_SLICE+0': (1, None, 1),
|
||||||
|
'DELETE_SLICE+1': (2, None, 1),
|
||||||
|
'DELETE_SLICE+2': (2, None, 1),
|
||||||
|
'DELETE_SLICE+3': (3, None, 1),
|
||||||
|
'DELETE_SUBSCR': (2, None, 1),
|
||||||
|
'DUP_TOP': (None, None, None),
|
||||||
|
'DUP_TOPX': (None, None, None),
|
||||||
|
'DUP_TOP_TWO': (None, None, None),
|
||||||
|
'END_FINALLY': (None, None, None),
|
||||||
|
'EXEC_STMT': (None, None, None),
|
||||||
|
'EXTENDED_ARG': (None, None, None),
|
||||||
|
'FOR_ITER': (1, 1, 1),
|
||||||
|
'GET_ITER': (1, 1, None),
|
||||||
|
'IMPORT_FROM': (None, None, None),
|
||||||
|
'IMPORT_NAME': (None, None, None),
|
||||||
|
'IMPORT_STAR': (1, None, 1),
|
||||||
|
'INPLACE_ADD': (2, 1, None),
|
||||||
|
'INPLACE_AND': (2, 1, None),
|
||||||
|
'INPLACE_DIVIDE': (2, 1, None),
|
||||||
|
'INPLACE_FLOOR_DIVIDE': (2, 1, None),
|
||||||
|
'INPLACE_LSHIFT': (2, 1, None),
|
||||||
|
'INPLACE_MODULO': (2, 1, None),
|
||||||
|
'INPLACE_MULTIPLY': (2, 1, None),
|
||||||
|
'INPLACE_OR': (2, 1, None),
|
||||||
|
'INPLACE_POWER': (2, 1, None),
|
||||||
|
'INPLACE_RSHIFT': (2, 1, None),
|
||||||
|
'INPLACE_SUBTRACT': (2, 1, None),
|
||||||
|
'INPLACE_TRUE_DIVIDE': (2, 1, None),
|
||||||
|
'INPLACE_XOR': (2, 1, None),
|
||||||
|
'JUMP_ABSOLUTE': (0, None, 1),
|
||||||
|
'JUMP_FORWARD': (0, None, 1),
|
||||||
|
'JUMP_IF_FALSE': (1, 1, 1),
|
||||||
|
'JUMP_IF_FALSE_OR_POP': (None, None, None),
|
||||||
|
'JUMP_IF_TRUE': (1, 1, 1),
|
||||||
|
'JUMP_IF_TRUE_OR_POP': (None, None, None),
|
||||||
|
'LIST_APPEND': (2, 0, 1),
|
||||||
|
'LOAD_ATTR': (1, 1, None),
|
||||||
|
'LOAD_BUILD_CLASS': (None, None, None),
|
||||||
|
'LOAD_CLOSURE': (None, None, None),
|
||||||
|
'LOAD_CONST': (0, 1, None),
|
||||||
|
'LOAD_DEREF': (0, 1, None),
|
||||||
|
'LOAD_FAST': (0, 1, None),
|
||||||
|
'LOAD_GLOBAL': (0, 1, None),
|
||||||
|
'LOAD_LOCALS': (None, None, None),
|
||||||
|
'LOAD_NAME': (0, 1, None),
|
||||||
|
'MAKE_CLOSURE': (None, None, None),
|
||||||
|
'MAKE_FUNCTION': (-2, 1, None),
|
||||||
|
'MAP_ADD': (None, None, None),
|
||||||
|
'NOP': (0, None, None),
|
||||||
|
'POP_BLOCK': (0, None, 1),
|
||||||
|
'POP_EXCEPT': (None, None, None),
|
||||||
|
'POP_JUMP_IF_FALSE': (1, None, 1),
|
||||||
|
'POP_JUMP_IF_TRUE': (1, None, 1),
|
||||||
|
'POP_TOP': (1, None, 1),
|
||||||
|
'PRINT_EXPR': (1, None, 1),
|
||||||
|
'PRINT_ITEM': (1, None, 1),
|
||||||
|
'PRINT_ITEM_TO': (2, None, 1),
|
||||||
|
'PRINT_NEWLINE': (0, None, 1),
|
||||||
|
'PRINT_NEWLINE_TO': (1, None, 1),
|
||||||
|
'RAISE_VARARGS': (None, None, None),
|
||||||
|
'RETURN_VALUE': (1, None, 1),
|
||||||
|
'ROT_FOUR': (None, None, None),
|
||||||
|
'ROT_THREE': (None, None, None),
|
||||||
|
'ROT_TWO': (None, None, None),
|
||||||
|
'SETUP_EXCEPT': (None, None, None),
|
||||||
|
'SETUP_FINALLY': (None, None, None),
|
||||||
|
'SETUP_LOOP': (None, None, None),
|
||||||
|
'SETUP_WITH': (None, None, None),
|
||||||
|
'SET_ADD': (None, None, None),
|
||||||
|
'SLICE+0': (1, 1, None),
|
||||||
|
'SLICE+1': (2, 1, None),
|
||||||
|
'SLICE+2': (2, 1, None),
|
||||||
|
'SLICE+3': (3, 1, None),
|
||||||
|
'STOP_CODE': (None, None, None),
|
||||||
|
'STORE_ATTR': (2, None, 1),
|
||||||
|
'STORE_DEREF': (1, 0, 1),
|
||||||
|
'STORE_FAST': (1, None, 1),
|
||||||
|
'STORE_GLOBAL': (1, None, 1),
|
||||||
|
'STORE_LOCALS': (None, None, None),
|
||||||
|
'STORE_MAP': (1, None, 1),
|
||||||
|
'STORE_NAME': (1, None, 1),
|
||||||
|
'STORE_SLICE+0': (1, None, 1),
|
||||||
|
'STORE_SLICE+1': (2, None, 1),
|
||||||
|
'STORE_SLICE+2': (2, None, 1),
|
||||||
|
'STORE_SLICE+3': (3, None, 1),
|
||||||
|
'STORE_SUBSCR': (3, None, 1),
|
||||||
|
'UNARY_CONVERT': (1, 1, None),
|
||||||
|
'UNARY_INVERT': (1, 1, None),
|
||||||
|
'UNARY_NEGATIVE': (1, 1, None),
|
||||||
|
'UNARY_NOT': (1, 1, None),
|
||||||
|
'UNARY_POSITIVE': (1, 1, None),
|
||||||
|
'UNPACK_EX': (None, None, None),
|
||||||
|
'UNPACK_SEQUENCE': (None, None, None),
|
||||||
|
'WITH_CLEANUP': (None, None, None),
|
||||||
|
'YIELD_VALUE': (1, None, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Module functions
|
||||||
|
|
||||||
|
def itercode(code, start = 0):
|
||||||
|
"""Return a generator of byte-offset, opcode, and argument
|
||||||
|
from a byte-code-string
|
||||||
|
"""
|
||||||
|
i = 0
|
||||||
|
extended_arg = 0
|
||||||
|
if isinstance(code[0], str):
|
||||||
|
code = [ord(c) for c in code]
|
||||||
|
n = len(code)
|
||||||
|
while i < n:
|
||||||
|
op = code[i]
|
||||||
|
num = i + start
|
||||||
|
i = i + 1
|
||||||
|
oparg = None
|
||||||
|
if op >= opcode.HAVE_ARGUMENT:
|
||||||
|
oparg = code[i] + (code[i + 1] * 256) + extended_arg
|
||||||
|
extended_arg = 0
|
||||||
|
i = i + 2
|
||||||
|
if op == opcode.EXTENDED_ARG:
|
||||||
|
extended_arg = oparg * 65536
|
||||||
|
|
||||||
|
delta = yield num, op, oparg
|
||||||
|
if delta is not None:
|
||||||
|
abs_rel, dst = delta
|
||||||
|
assert abs_rel == 'abs' or abs_rel == 'rel'
|
||||||
|
i = dst if abs_rel == 'abs' else i + dst
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def extendlabels(code, labels = None):
|
||||||
|
"""Extend the set of jump target labels to account for the
|
||||||
|
passthrough targets of conditional branches.
|
||||||
|
|
||||||
|
This allows us to create a control flow graph where there is at
|
||||||
|
most one branch per basic block.
|
||||||
|
"""
|
||||||
|
if labels is None:
|
||||||
|
labels = []
|
||||||
|
if isinstance(code[0], str):
|
||||||
|
code = [ord(c) for c in code]
|
||||||
|
n = len(code)
|
||||||
|
i = 0
|
||||||
|
while i < n:
|
||||||
|
op = code[i]
|
||||||
|
i += 1
|
||||||
|
if op >= dis.HAVE_ARGUMENT:
|
||||||
|
i += 2
|
||||||
|
label = -1
|
||||||
|
if op in hasjump:
|
||||||
|
label = i
|
||||||
|
if label >= 0:
|
||||||
|
if label not in labels:
|
||||||
|
labels.append(label)
|
||||||
|
elif op == opcode.opmap['BREAK_LOOP']:
|
||||||
|
if i not in labels:
|
||||||
|
labels.append(i)
|
||||||
|
return labels
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def get_code_object (func):
|
||||||
|
return getattr(func, '__code__', getattr(func, 'func_code', None))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def build_basic_blocks (co_obj):
|
||||||
|
co_code = co_obj.co_code
|
||||||
|
labels = extendlabels(co_code, dis.findlabels(co_code))
|
||||||
|
labels.sort()
|
||||||
|
blocks = dict((index, list(itercode(co_code[index:next_index], index)))
|
||||||
|
for index, next_index in zip([0] + labels,
|
||||||
|
labels + [len(co_code)]))
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of opcode_util.py
|
||||||
153
llpython/phi_injector.py
Normal file
153
llpython/phi_injector.py
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
from .bytecode_visitor import BytecodeFlowVisitor, BenignBytecodeVisitorMixin
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
synthetic_opname = []
|
||||||
|
synthetic_opmap = {}
|
||||||
|
|
||||||
|
def def_synth_op (opname):
|
||||||
|
global synthetic_opname, synthetic_opmap
|
||||||
|
ret_val = -(len(synthetic_opname) + 1)
|
||||||
|
synthetic_opname.insert(0, opname)
|
||||||
|
synthetic_opmap[opname] = ret_val
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
REF_ARG = def_synth_op('REF_ARG')
|
||||||
|
BUILD_PHI = def_synth_op('BUILD_PHI')
|
||||||
|
DEFINITION = def_synth_op('DEFINITION')
|
||||||
|
REF_DEF = def_synth_op('REF_DEF')
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
class PhiInjector (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
||||||
|
'''Transformer responsible for modifying a bytecode flow, removing
|
||||||
|
LOAD_FAST and STORE_FAST opcodes, and replacing them with a static
|
||||||
|
single assignment (SSA) representation.
|
||||||
|
|
||||||
|
In order to support SSA, PhiInjector adds the following synthetic
|
||||||
|
opcodes to transformed flows:
|
||||||
|
|
||||||
|
* REF_ARG: Specifically reference an incomming argument value.
|
||||||
|
|
||||||
|
* BUILD_PHI: Build a phi node to disambiguate between several
|
||||||
|
possible definitions at a control flow join.
|
||||||
|
|
||||||
|
* DEFINITION: Unique value definition indexed by the "arg" field
|
||||||
|
in the tuple.
|
||||||
|
|
||||||
|
* REF_DEF: Reference a specific value definition.'''
|
||||||
|
|
||||||
|
def visit_cfg (self, cfg, nargs = 0, *args, **kws):
|
||||||
|
self.cfg = cfg
|
||||||
|
ret_val = self.visit(cfg.blocks, nargs)
|
||||||
|
del self.cfg
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def visit (self, flow, nargs = 0, *args, **kws):
|
||||||
|
self.nargs = nargs
|
||||||
|
self.definitions = []
|
||||||
|
self.phis = []
|
||||||
|
self.prev_blocks = []
|
||||||
|
self.blocks_locals = dict((block, {})
|
||||||
|
for block in self.cfg.blocks.keys())
|
||||||
|
ret_val = super(PhiInjector, self).visit(flow, *args, **kws)
|
||||||
|
for block, _, _, args, _ in self.phis:
|
||||||
|
local = args.pop()
|
||||||
|
reaching_definitions = self.cfg.reaching_definitions[block]
|
||||||
|
for prev in reaching_definitions.keys():
|
||||||
|
if 0 in self.cfg.blocks_reaching[prev]:
|
||||||
|
args.append((prev, REF_DEF, 'REF_DEF',
|
||||||
|
self.blocks_locals[prev][local], ()))
|
||||||
|
args.sort()
|
||||||
|
del self.blocks_locals
|
||||||
|
del self.prev_blocks
|
||||||
|
del self.phis
|
||||||
|
del self.definitions
|
||||||
|
del self.nargs
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def add_definition (self, index, local, arg):
|
||||||
|
definition_index = len(self.definitions)
|
||||||
|
definition = (index, DEFINITION, 'DEFINITION', definition_index,
|
||||||
|
(arg,))
|
||||||
|
self.definitions.append(definition)
|
||||||
|
self.blocks_locals[self.block][local] = definition_index
|
||||||
|
return definition
|
||||||
|
|
||||||
|
def add_phi (self, index, local):
|
||||||
|
ret_val = (index, BUILD_PHI, 'BUILD_PHI', [local], ())
|
||||||
|
self.phis.append(ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def enter_block (self, block):
|
||||||
|
ret_val = False
|
||||||
|
self.block = block
|
||||||
|
if block == 0:
|
||||||
|
if self.nargs > 0:
|
||||||
|
ret_val = [self.add_definition(-1, arg,
|
||||||
|
(-1, REF_ARG, 'REF_ARG', arg,
|
||||||
|
()))
|
||||||
|
for arg in range(self.nargs)]
|
||||||
|
else:
|
||||||
|
ret_val = True
|
||||||
|
elif 0 in self.cfg.blocks_reaching[block]:
|
||||||
|
ret_val = True
|
||||||
|
prev_block_locals = None
|
||||||
|
for pred_block in self.cfg.blocks_in[block]:
|
||||||
|
if pred_block in self.prev_blocks:
|
||||||
|
prev_block_locals = self.blocks_locals[pred_block]
|
||||||
|
break
|
||||||
|
assert prev_block_locals is not None, "Internal translation error"
|
||||||
|
self.blocks_locals[block] = prev_block_locals.copy()
|
||||||
|
phis_needed = self.cfg.phi_needed(block)
|
||||||
|
if phis_needed:
|
||||||
|
ret_val = [self.add_definition(block, local,
|
||||||
|
self.add_phi(block, local))
|
||||||
|
for local in phis_needed]
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def exit_block (self, block):
|
||||||
|
if 0 in self.cfg.blocks_reaching[block]:
|
||||||
|
self.prev_blocks.append(block)
|
||||||
|
del self.block
|
||||||
|
|
||||||
|
def op_STORE_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
assert len(args) == 1
|
||||||
|
return [self.add_definition(i, arg, args[0])]
|
||||||
|
|
||||||
|
def op_LOAD_FAST (self, i, op, arg, *args, **kws):
|
||||||
|
return [(i, REF_DEF, 'REF_DEF', self.blocks_locals[self.block][arg],
|
||||||
|
args)]
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def inject_phis (func):
|
||||||
|
'''Given a Python function, return a bytecode flow object that has
|
||||||
|
been transformed by a fresh PhiInjector instance.'''
|
||||||
|
import byte_control, byte_flow
|
||||||
|
argcount = byte_control.opcode_util.get_code_object(func).co_argcount
|
||||||
|
cfg = byte_control.build_cfg(func)
|
||||||
|
cfg.blocks = byte_flow.BytecodeFlowBuilder().visit_cfg(cfg)
|
||||||
|
return PhiInjector().visit_cfg(cfg, argcount)
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# Main (self-test) routine
|
||||||
|
|
||||||
|
def main (*args):
|
||||||
|
import pprint
|
||||||
|
from tests import llfuncs
|
||||||
|
if not args:
|
||||||
|
args = ('doslice',)
|
||||||
|
for arg in args:
|
||||||
|
pprint.pprint(inject_phis(getattr(llfuncs, arg)))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of phi_injector.py
|
||||||
40
llpython/pyaddfunc.py
Normal file
40
llpython/pyaddfunc.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
class PyMethodDef (ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('ml_name', ctypes.c_char_p),
|
||||||
|
('ml_meth', ctypes.c_void_p),
|
||||||
|
('ml_flags', ctypes.c_int),
|
||||||
|
('ml_doc', ctypes.c_char_p),
|
||||||
|
]
|
||||||
|
|
||||||
|
PyCFunction_NewEx = ctypes.pythonapi.PyCFunction_NewEx
|
||||||
|
PyCFunction_NewEx.argtypes = (ctypes.POINTER(PyMethodDef),
|
||||||
|
ctypes.c_void_p,
|
||||||
|
ctypes.c_void_p)
|
||||||
|
PyCFunction_NewEx.restype = ctypes.py_object
|
||||||
|
|
||||||
|
cache = {} # Unsure if this is necessary to keep the PyMethodDef
|
||||||
|
# structures from being garbage collected. Assuming so...
|
||||||
|
|
||||||
|
def pyaddfunc (func_name, func_ptr, func_doc = None):
|
||||||
|
global cache
|
||||||
|
if bytes != str:
|
||||||
|
func_name = bytes(ord(ch) for ch in func_name)
|
||||||
|
key = (func_name, func_ptr)
|
||||||
|
if key in cache:
|
||||||
|
_, ret_val = cache[key]
|
||||||
|
else:
|
||||||
|
mdef = PyMethodDef(bytes(func_name),
|
||||||
|
func_ptr,
|
||||||
|
1, # == METH_VARARGS (hopefully remains so...)
|
||||||
|
func_doc)
|
||||||
|
ret_val = PyCFunction_NewEx(ctypes.byref(mdef), 0, 0)
|
||||||
|
cache[key] = (mdef, ret_val)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of pyaddfunc.py
|
||||||
0
llpython/tests/__init__.py
Normal file
0
llpython/tests/__init__.py
Normal file
41
llpython/tests/llfuncs.py
Normal file
41
llpython/tests/llfuncs.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
def doslice (in_string, lower, upper):
|
||||||
|
l = strlen(in_string)
|
||||||
|
if lower < lc_size_t(0):
|
||||||
|
lower += l
|
||||||
|
if upper < lc_size_t(0):
|
||||||
|
upper += l
|
||||||
|
temp_len = upper - lower
|
||||||
|
if temp_len < lc_size_t(0):
|
||||||
|
temp_len = lc_size_t(0)
|
||||||
|
ret_val = alloca_array(li8, temp_len + lc_size_t(1))
|
||||||
|
strncpy(ret_val, in_string + lower, temp_len)
|
||||||
|
ret_val[temp_len] = li8(0)
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def ipow (val, exp):
|
||||||
|
ret_val = 1
|
||||||
|
temp = val
|
||||||
|
w = exp
|
||||||
|
while w > 0:
|
||||||
|
if (w & 1) != 0:
|
||||||
|
ret_val *= temp
|
||||||
|
# TODO: Overflow check on ret_val
|
||||||
|
w >>= 1
|
||||||
|
if w == 0: break
|
||||||
|
temp *= temp
|
||||||
|
# TODO: Overflow check on temp
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
def pymod (arg1, arg2):
|
||||||
|
ret_val = arg1 % arg2
|
||||||
|
if ret_val < 0:
|
||||||
|
if arg2 > 0:
|
||||||
|
ret_val += arg2
|
||||||
|
elif arg2 < 0:
|
||||||
|
ret_val += arg2
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of llfuncs.py
|
||||||
19
llpython/tests/llfunctys.py
Normal file
19
llpython/tests/llfunctys.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
import llvm.core as lc
|
||||||
|
|
||||||
|
from llpython import bytetype
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
|
||||||
|
doslice = lc.Type.function(bytetype.li8_ptr, (
|
||||||
|
bytetype.li8_ptr, bytetype.lc_size_t, bytetype.lc_size_t))
|
||||||
|
|
||||||
|
ipow = lc.Type.function(bytetype.li32, (bytetype.li32,
|
||||||
|
bytetype.li32))
|
||||||
|
|
||||||
|
pymod = lc.Type.function(bytetype.li32, (bytetype.li32,
|
||||||
|
bytetype.li32))
|
||||||
|
|
||||||
|
# ______________________________________________________________________
|
||||||
|
# End of llfunctys.py
|
||||||
22
llrtc/Makefile
Normal file
22
llrtc/Makefile
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
all:
|
||||||
|
make -C lib
|
||||||
|
|
||||||
|
ir:
|
||||||
|
make -C lib ir
|
||||||
|
|
||||||
|
test:
|
||||||
|
make -C lib test
|
||||||
|
|
||||||
|
clean-test:
|
||||||
|
make -C lib clean-test
|
||||||
|
|
||||||
|
clean-temp:
|
||||||
|
make -C lib clean-temp
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C lib clean
|
||||||
|
|
||||||
|
install: ir
|
||||||
|
cp llrt_*.ll ../llvm/llrt
|
||||||
|
make -C lib clean-temp
|
||||||
|
|
||||||
25
llrtc/README.md
Normal file
25
llrtc/README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# LLRT: Low Level Runtime
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
The same reason for LLVM compiler-rt. LLVM generates libgcc symbols, such as
|
||||||
|
__divdi3 for 64-bit division on 32-bit platform. They are not also available.
|
||||||
|
We need to ship compiler-rt but it is not Windows ready.
|
||||||
|
This subproject aims to provide a small portable subset of compiler-rt.
|
||||||
|
Start small and add only the things we really needed.
|
||||||
|
Performance is not crucial but should not be terrible.
|
||||||
|
Functionality and usefullness should be more important than performance.
|
||||||
|
|
||||||
|
## Developer Instructions
|
||||||
|
|
||||||
|
LLRT implements some functionalities in compiler-rt in ANSI C.
|
||||||
|
The C files are compiled using clang to produce LLVM IR which are shipped.
|
||||||
|
The IR files are committed in the repository.
|
||||||
|
So, remember to build the IR files commit them after modifying the C files.
|
||||||
|
|
||||||
|
## Build Requirement
|
||||||
|
|
||||||
|
- Make
|
||||||
|
- Clang
|
||||||
|
- Python
|
||||||
|
|
||||||
4
llrtc/lib/.gitignore
vendored
Normal file
4
llrtc/lib/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
*.o
|
||||||
|
*.run
|
||||||
|
*.out
|
||||||
|
*.ll
|
||||||
66
llrtc/lib/Makefile
Normal file
66
llrtc/lib/Makefile
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
OUTPUT = llrt
|
||||||
|
SOURCES = udivmod64.c sdivmod64.c div64.c mod64.c
|
||||||
|
TESTS = test_udivmod64.c test_sdivmod64.c
|
||||||
|
|
||||||
|
CLANG = clang
|
||||||
|
LLVM_LINK = llvm-link
|
||||||
|
CF = -Wall -ansi
|
||||||
|
CF_TEST = $(CF) -ftrapv
|
||||||
|
CF_BUILD = $(CF) -O0 -emit-llvm
|
||||||
|
OUTDIR = ..
|
||||||
|
STRIPPER = ../tools/striptriple.py
|
||||||
|
|
||||||
|
all: ir
|
||||||
|
|
||||||
|
ir: $(OUTDIR)/$(OUTPUT)_x86.ll $(OUTDIR)/$(OUTPUT)_x86_64.ll
|
||||||
|
|
||||||
|
$(OUTDIR)/$(OUTPUT)_x86.ll: $(SOURCES:.c=_x86.bc)
|
||||||
|
$(LLVM_LINK) -S $+ -o $@
|
||||||
|
python $(STRIPPER) $@
|
||||||
|
|
||||||
|
$(OUTDIR)/$(OUTPUT)_x86_64.ll: $(SOURCES:.c=_x86_64.bc)
|
||||||
|
$(LLVM_LINK) -S $+ -o $@
|
||||||
|
python $(STRIPPER) $@
|
||||||
|
|
||||||
|
build-test: $(SOURCES:.c=.o) $(TESTS:.c=.run)
|
||||||
|
|
||||||
|
lib$(OUTPUT).a: $(SOURCES:.c=.o)
|
||||||
|
$(CLANG) -static $+ -o $@
|
||||||
|
|
||||||
|
test: $(TESTS:.c=.run)
|
||||||
|
for src in $+; do \
|
||||||
|
echo "testing $${src}"; \
|
||||||
|
python $${src%.*}.py > $${src%.*}.out; \
|
||||||
|
done;
|
||||||
|
|
||||||
|
clean-test:
|
||||||
|
rm -f *.out
|
||||||
|
rm -f *.o
|
||||||
|
rm -f *.run
|
||||||
|
|
||||||
|
clean-dist: clean-temp
|
||||||
|
rm -f *.ll
|
||||||
|
|
||||||
|
clean-temp:
|
||||||
|
rm -f *.bc
|
||||||
|
rm -f *.o
|
||||||
|
rm -f *.out
|
||||||
|
|
||||||
|
clean: clean-test clean-dist
|
||||||
|
|
||||||
|
%.c: llrt.h
|
||||||
|
|
||||||
|
%_x86.bc: %.c
|
||||||
|
$(CLANG) -m32 $(CF_BUILD) -c $< -o $@
|
||||||
|
|
||||||
|
%_x86_64.bc: %.c
|
||||||
|
$(CLANG) -m64 $(CF_BUILD) -c $< -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CLANG) $(CF_TEST) -c $<
|
||||||
|
|
||||||
|
%.run: %.c
|
||||||
|
$(CLANG) $(CF_TEST) -o $@ $+
|
||||||
|
|
||||||
|
test_udivmod64.run: udivmod64.o
|
||||||
|
test_sdivmod64.run: udivmod64.o sdivmod64.o
|
||||||
11
llrtc/lib/div64.c
Normal file
11
llrtc/lib/div64.c
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "llrt.h"
|
||||||
|
|
||||||
|
uint64_t udiv64(uint64_t dividend, uint64_t divisor)
|
||||||
|
{
|
||||||
|
return udivmod64(dividend, divisor, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t sdiv64(int64_t dividend, int64_t divisor)
|
||||||
|
{
|
||||||
|
return sdivmod64(dividend, divisor, NULL);
|
||||||
|
}
|
||||||
19
llrtc/lib/llrt.h
Normal file
19
llrtc/lib/llrt.h
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef LLRT_H_
|
||||||
|
#define LLRT_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define NULL 0
|
||||||
|
#define BITS_PER_BYTE 8
|
||||||
|
|
||||||
|
uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder);
|
||||||
|
int64_t sdivmod64(int64_t dividend, int64_t divisor, int64_t *remainder);
|
||||||
|
|
||||||
|
uint64_t udiv64(uint64_t dividend, uint64_t divisor);
|
||||||
|
int64_t sdiv64(int64_t dividend, int64_t divisor);
|
||||||
|
|
||||||
|
uint64_t umod64(uint64_t dividend, uint64_t divisor);
|
||||||
|
int64_t smod64(int64_t dividend, int64_t divisor);
|
||||||
|
|
||||||
|
#endif /* LLRT_H_ */
|
||||||
|
|
||||||
15
llrtc/lib/mod64.c
Normal file
15
llrtc/lib/mod64.c
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include "llrt.h"
|
||||||
|
|
||||||
|
uint64_t umod64(uint64_t dividend, uint64_t divisor)
|
||||||
|
{
|
||||||
|
uint64_t rem;
|
||||||
|
udivmod64(dividend, divisor, &rem);
|
||||||
|
return rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t smod64(int64_t dividend, int64_t divisor)
|
||||||
|
{
|
||||||
|
int64_t rem;
|
||||||
|
sdivmod64(dividend, divisor, &rem);
|
||||||
|
return rem;
|
||||||
|
}
|
||||||
40
llrtc/lib/sdivmod64.c
Normal file
40
llrtc/lib/sdivmod64.c
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "llrt.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calls to udivmod64 internally.
|
||||||
|
Note: remainder uses sign of divisor.
|
||||||
|
*/
|
||||||
|
int64_t sdivmod64(int64_t dividend, int64_t divisor, int64_t *remainder)
|
||||||
|
{
|
||||||
|
int signbitidx = BITS_PER_BYTE * sizeof(dividend) - 1;
|
||||||
|
int signed_dividend = dividend < 0;
|
||||||
|
int signed_divisor = divisor < 0;
|
||||||
|
int signed_result = signed_divisor ^ signed_dividend;
|
||||||
|
|
||||||
|
int64_t quotient;
|
||||||
|
uint64_t udvd, udvr, uquotient, uremainder;
|
||||||
|
|
||||||
|
udvd = signed_dividend ? -dividend : dividend;
|
||||||
|
udvr = signed_divisor ? -divisor : divisor;
|
||||||
|
uquotient = udivmod64(udvd, udvr, &uremainder);
|
||||||
|
|
||||||
|
if (signed_result){
|
||||||
|
if (uremainder) {
|
||||||
|
quotient = -(int64_t)uquotient - 1;
|
||||||
|
} else {
|
||||||
|
quotient = -(int64_t)uquotient;
|
||||||
|
}
|
||||||
|
if (remainder) {
|
||||||
|
/* if signed, there could be unsigned overflow
|
||||||
|
causing undefined behavior */
|
||||||
|
*remainder = (uint64_t)dividend - (uint64_t)quotient * (uint64_t)divisor;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quotient = (int64_t)uquotient;
|
||||||
|
if (remainder) {
|
||||||
|
*remainder = signed_divisor ? -uremainder : uremainder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return quotient;
|
||||||
|
}
|
||||||
21
llrtc/lib/test_sdivmod64.c
Normal file
21
llrtc/lib/test_sdivmod64.c
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "llrt.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]){
|
||||||
|
int64_t n, d, q, r;
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("invalid argument: %s dividend divisor", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
sscanf(argv[1], "%lld", &n);
|
||||||
|
sscanf(argv[2], "%lld", &d);
|
||||||
|
|
||||||
|
q = sdivmod64(n, d, &r);
|
||||||
|
|
||||||
|
printf("%lld\n", q);
|
||||||
|
printf("%lld\n", r);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
56
llrtc/lib/test_sdivmod64.py
Normal file
56
llrtc/lib/test_sdivmod64.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
udt = os.path.join('.', 'test_sdivmod64.run')
|
||||||
|
|
||||||
|
def testcase(dividend, divisor):
|
||||||
|
print 'divmod64(%d, %d)' % (dividend, divisor)
|
||||||
|
|
||||||
|
procargs = ('%s %s %s' % (udt, dividend, divisor)).split()
|
||||||
|
result = subprocess.check_output(procargs)
|
||||||
|
gotQ, gotR = map(int, result.splitlines())
|
||||||
|
|
||||||
|
expectQ = dividend // divisor
|
||||||
|
expectR = dividend % divisor
|
||||||
|
|
||||||
|
print 'Q = %d, R = %d' % (gotQ, gotR)
|
||||||
|
|
||||||
|
if expectQ != gotQ:
|
||||||
|
raise ValueError("invalid quotient: got=%d but expect=%d" %
|
||||||
|
(gotQ, expectQ))
|
||||||
|
if expectR != gotR:
|
||||||
|
raise ValueError("invalid remainder: got=%d but expect=%d" %
|
||||||
|
(gotR, expectR))
|
||||||
|
print 'OK'
|
||||||
|
|
||||||
|
def testsequence():
|
||||||
|
subjects = [
|
||||||
|
(0, 1),
|
||||||
|
(0, 0xffffffff),
|
||||||
|
(1, 2),
|
||||||
|
(1, 983219),
|
||||||
|
(2, 2),
|
||||||
|
(3, 2),
|
||||||
|
(1024, 2),
|
||||||
|
(2048, 512),
|
||||||
|
(21321, 512),
|
||||||
|
(9329189, 1031),
|
||||||
|
(0xffffffff, 2),
|
||||||
|
(0xffffffff, 0xffff),
|
||||||
|
(0x1ffffffff, 2),
|
||||||
|
(0x1ffffffff, 0xffff),
|
||||||
|
(0xffff, 0xffffffff),
|
||||||
|
(0x0fffffffffffffff, 0xffff),
|
||||||
|
(0x7fffffffffffffff, 0x7fffffffffffffff),
|
||||||
|
(0x7fffffffffffffff, 0x7ffffffffffffff0),
|
||||||
|
(0x7fffffffffffffff, 87655678587161901),
|
||||||
|
]
|
||||||
|
|
||||||
|
for dvd, dvr in subjects:
|
||||||
|
testcase(dvd, dvr)
|
||||||
|
testcase(dvd, -dvr)
|
||||||
|
testcase(-dvd, dvr)
|
||||||
|
testcase(-dvd, -dvr)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
testsequence()
|
||||||
20
llrtc/lib/test_udivmod64.c
Normal file
20
llrtc/lib/test_udivmod64.c
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "llrt.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]){
|
||||||
|
uint64_t n, d, q, r;
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("invalid argument: %s dividend divisor", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
sscanf(argv[1], "%llu", &n);
|
||||||
|
sscanf(argv[2], "%llu", &d);
|
||||||
|
|
||||||
|
q = udivmod64(n, d, &r);
|
||||||
|
|
||||||
|
printf("%llu\n", q);
|
||||||
|
printf("%llu\n", r);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
53
llrtc/lib/test_udivmod64.py
Normal file
53
llrtc/lib/test_udivmod64.py
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
udt = os.path.join('.', 'test_udivmod64.run')
|
||||||
|
|
||||||
|
def testcase(dividend, divisor):
|
||||||
|
print 'divmod64(%d, %d)' % (dividend, divisor)
|
||||||
|
|
||||||
|
procargs = ('%s %s %s' % (udt, dividend, divisor)).split()
|
||||||
|
result = subprocess.check_output(procargs)
|
||||||
|
gotQ, gotR = map(int, result.splitlines())
|
||||||
|
|
||||||
|
expectQ = dividend // divisor
|
||||||
|
expectR = dividend % divisor
|
||||||
|
|
||||||
|
print 'Q = %d, R = %d' % (gotQ, gotR)
|
||||||
|
|
||||||
|
if expectQ != gotQ:
|
||||||
|
raise ValueError("invalid quotient: got=%d but expect=%d" %
|
||||||
|
(gotQ, expectQ))
|
||||||
|
if expectR != gotR:
|
||||||
|
raise ValueError("invalid remainder: got=%d but expect=%d" %
|
||||||
|
(gotR, expectR))
|
||||||
|
print 'OK'
|
||||||
|
|
||||||
|
def testsequence():
|
||||||
|
subjects = [
|
||||||
|
(0, 1),
|
||||||
|
(0, 0xffffffffffffffff),
|
||||||
|
(1, 2),
|
||||||
|
(1, 983219),
|
||||||
|
(2, 2),
|
||||||
|
(3, 2),
|
||||||
|
(1024, 2),
|
||||||
|
(2048, 512),
|
||||||
|
(21321, 512),
|
||||||
|
(9329189, 1031),
|
||||||
|
(0xffffffff, 2),
|
||||||
|
(0xffffffff, 0xffff),
|
||||||
|
(0x1ffffffff, 2),
|
||||||
|
(0x1ffffffff, 0xffff),
|
||||||
|
(0xffff, 0xffffffff),
|
||||||
|
(0xffffffffffffffff, 0xffff),
|
||||||
|
(0xffffffffffffffff, 0x7fffffffffffffff),
|
||||||
|
(0xffffffffffffffff, 0xfffffffffffffff0),
|
||||||
|
(0xffffffffffffffff, 87655678587161901),
|
||||||
|
]
|
||||||
|
|
||||||
|
for dvd, dvr in subjects:
|
||||||
|
testcase(dvd, dvr)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
testsequence()
|
||||||
84
llrtc/lib/udivmod64.c
Normal file
84
llrtc/lib/udivmod64.c
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
Implements unsigned divmod using for platform missing 64-bit division and/or
|
||||||
|
modulo functions.
|
||||||
|
*/
|
||||||
|
#include "llrt.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
count left zero for 64-bit words
|
||||||
|
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
int clz64(uint64_t x)
|
||||||
|
{
|
||||||
|
const int total_bits = sizeof(x) * BITS_PER_BYTE;
|
||||||
|
int zc = 0;
|
||||||
|
|
||||||
|
while (zc < total_bits && ((x >> (total_bits - zc - 1)) & 1) == 0) {
|
||||||
|
++zc;
|
||||||
|
}
|
||||||
|
return zc;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct div_state_
|
||||||
|
{
|
||||||
|
uint64_t tmp, dvd;
|
||||||
|
} div_state;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Left shift div_state by 1 bit
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
void div_state_lshift(div_state *state)
|
||||||
|
{
|
||||||
|
state->tmp = (state->tmp << 1) | (state->dvd >> 63);
|
||||||
|
state->dvd = state->dvd << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Division of unsigned 64-bit word using 64-bit addition and subtration following
|
||||||
|
the shift-restore division algorithm.
|
||||||
|
For those interested in 32-bit implementation,
|
||||||
|
mapping of 64-bit addition and subtraction to 32-bit should be trivial.
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
- IBM. The PowerPC Compiler Writer's Guide
|
||||||
|
- LLVM compiler-rt
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
- all operands and results are positive
|
||||||
|
- unsigned wrapped around
|
||||||
|
*/
|
||||||
|
uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder)
|
||||||
|
{
|
||||||
|
div_state state = {0, dividend};
|
||||||
|
uint64_t quotient = 0;
|
||||||
|
int i;
|
||||||
|
int skipahead;
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
return 1 / 0; /* intentionally div by zero */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
skipahead to reduce iteration
|
||||||
|
*/
|
||||||
|
skipahead = clz64(dividend);
|
||||||
|
|
||||||
|
for (i = 0; i < skipahead; ++i) {
|
||||||
|
div_state_lshift(&state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
division loop
|
||||||
|
*/
|
||||||
|
for (i = skipahead; i < 64; ++i) {
|
||||||
|
div_state_lshift(&state);
|
||||||
|
if (state.tmp >= divisor) {
|
||||||
|
state.tmp = state.tmp - divisor;
|
||||||
|
quotient |= 1ull << (63 - i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (remainder) *remainder = state.tmp;
|
||||||
|
return quotient;
|
||||||
|
}
|
||||||
15
llrtc/tools/striptriple.py
Normal file
15
llrtc/tools/striptriple.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
buf = []
|
||||||
|
with open(sys.argv[1], 'r') as fin:
|
||||||
|
tripleline = re.compile('^target\s+triple\s+=\s+')
|
||||||
|
for line in fin.readlines():
|
||||||
|
if not tripleline.match(line):
|
||||||
|
buf.append(line)
|
||||||
|
|
||||||
|
with open(sys.argv[1], 'w') as fout:
|
||||||
|
for line in buf:
|
||||||
|
fout.write(line)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,76 +1,88 @@
|
||||||
import sys, os
|
import re
|
||||||
|
import sys
|
||||||
|
from distutils.spawn import find_executable
|
||||||
|
from os.path import abspath, dirname, isfile, join
|
||||||
|
from os import listdir
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
def find_path_of(filename, envvar='PATH'):
|
|
||||||
"""Finds the path from $PATH where the file exists, returns None if not found."""
|
|
||||||
pathlist = os.getenv(envvar).split(os.pathsep)
|
|
||||||
for path in pathlist:
|
|
||||||
if os.path.exists(os.path.join(path, filename)):
|
|
||||||
return os.path.abspath(path)
|
|
||||||
return None
|
|
||||||
|
|
||||||
if sys.argv[1] == '--version':
|
def find_llvm_tblgen():
|
||||||
cmd = 'llvm-tblgen --version'
|
path = find_executable('llvm-tblgen')
|
||||||
# Hardcoded extraction, only tested on llvm 3.1
|
if path is None:
|
||||||
result = os.popen(cmd).read().split('\n')[1].strip().split(' ')[2]
|
sys.exit('Error: could not locate llvm-tblgen')
|
||||||
print result
|
return path
|
||||||
elif sys.argv[1] == '--libs':
|
|
||||||
|
|
||||||
|
def find_llvm_prefix():
|
||||||
|
return abspath(dirname(dirname(find_llvm_tblgen())))
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_file(path):
|
||||||
|
if not isfile(path):
|
||||||
|
sys.exit('Error: no file: %r' % path)
|
||||||
|
|
||||||
|
|
||||||
|
def get_llvm_version():
|
||||||
|
args = [find_llvm_tblgen(), '--version']
|
||||||
|
p = Popen(args, stdout=PIPE, stderr=PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
if stderr:
|
||||||
|
sys.exit("Error: %r stderr is:\n%s" % (args, stderr.decode()))
|
||||||
|
out = stdout.decode().strip()
|
||||||
|
pat = re.compile(r'llvm\s+version\s+(\d+\.\d+\S*)', re.I)
|
||||||
|
m = pat.search(out)
|
||||||
|
if m is None:
|
||||||
|
sys.exit('Error: could not parse version in:' + out)
|
||||||
|
return m.group(1)
|
||||||
|
|
||||||
|
|
||||||
|
def libs_options():
|
||||||
# NOTE: instead of actually looking at the components requested,
|
# NOTE: instead of actually looking at the components requested,
|
||||||
# we just spit out a bunch of libs
|
# we just print out a bunch of libs
|
||||||
for lib in """
|
for lib in """
|
||||||
LLVMAnalysis
|
|
||||||
LLVMAsmParser
|
|
||||||
LLVMAsmPrinter
|
|
||||||
LLVMBitReader
|
|
||||||
LLVMBitWriter
|
|
||||||
LLVMCodeGen
|
|
||||||
LLVMCore
|
|
||||||
LLVMExecutionEngine
|
|
||||||
LLVMInstCombine
|
|
||||||
LLVMInstrumentation
|
|
||||||
LLVMInterpreter
|
|
||||||
LLVMipa
|
|
||||||
LLVMipo
|
|
||||||
LLVMJIT
|
|
||||||
LLVMLinker
|
|
||||||
LLVMMC
|
|
||||||
LLVMMCParser
|
|
||||||
LLVMScalarOpts
|
|
||||||
LLVMSelectionDAG
|
|
||||||
LLVMSupport
|
|
||||||
LLVMTarget
|
|
||||||
LLVMTransformUtils
|
|
||||||
LLVMVectorize
|
|
||||||
LLVMX86AsmParser
|
|
||||||
LLVMX86AsmPrinter
|
|
||||||
LLVMX86CodeGen
|
|
||||||
LLVMX86Desc
|
|
||||||
LLVMX86Info
|
|
||||||
LLVMX86Utils
|
|
||||||
Advapi32
|
Advapi32
|
||||||
Shell32
|
Shell32
|
||||||
""".split():
|
""".split():
|
||||||
print('-l%s' % lib)
|
print('-l%s' % lib)
|
||||||
# Look for the PTX .lib in %LIBPATH%
|
|
||||||
if find_path_of('LLVMPTXCodeGen.lib', 'LIBPATH') != None:
|
bpath = join(find_llvm_prefix(), 'lib')
|
||||||
print('-lLLVMPTXAsmPrinter')
|
for filename in listdir(bpath):
|
||||||
print('-lLLVMPTXCodeGen')
|
filepath = join(bpath, filename)
|
||||||
print('-lLLVMPTXDesc')
|
if isfile(filepath) and filename.endswith('.lib') and filename.startswith('LLVM'):
|
||||||
print('-lLLVMPTXInfo')
|
name = filename.split('.', 1)[0]
|
||||||
elif sys.argv[1] == '--includedir':
|
print('-l%s' % name)
|
||||||
llvmbin = find_path_of('llvm-tblgen.exe')
|
|
||||||
if llvmbin is None:
|
def main():
|
||||||
raise RuntimeError('Could not find LLVM')
|
try:
|
||||||
incdir = os.path.abspath(os.path.join(llvmbin, '../include'))
|
option = sys.argv[1]
|
||||||
if not os.path.exists(os.path.join(incdir, 'llvm/BasicBlock.h')):
|
except IndexError:
|
||||||
raise RuntimeError('Could not find LLVM include dir')
|
sys.exit('Error: option missing')
|
||||||
print incdir
|
|
||||||
elif sys.argv[1] == '--libdir':
|
if option == '--version':
|
||||||
llvmbin = find_path_of('llvm-tblgen.exe')
|
print(get_llvm_version())
|
||||||
if llvmbin is None:
|
|
||||||
raise RuntimeError('Could not find LLVM')
|
elif option == '--targets-built':
|
||||||
libdir = os.path.abspath(os.path.join(llvmbin, '../lib'))
|
print('X86') # just do X86
|
||||||
if not os.path.exists(os.path.join(libdir, 'LLVMCore.lib')):
|
|
||||||
raise RuntimeError('Could not find LLVM lib dir')
|
elif option == '--libs':
|
||||||
print libdir
|
libs_options()
|
||||||
else:
|
|
||||||
raise RuntimeError('Unrecognized llvm-config command %s' % sys.argv[1])
|
elif option == '--includedir':
|
||||||
|
incdir = join(find_llvm_prefix(), 'include')
|
||||||
|
ensure_file(join(incdir, 'llvm' , 'Linker.h'))
|
||||||
|
print(incdir)
|
||||||
|
|
||||||
|
elif option == '--libdir':
|
||||||
|
libdir = join(find_llvm_prefix(), 'lib')
|
||||||
|
ensure_file(join(libdir, 'LLVMCore.lib'))
|
||||||
|
print(libdir)
|
||||||
|
|
||||||
|
elif option in ('--ldflags', '--components'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
sys.exit('Error: Unrecognized llvm-config option %r' % option)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
|
||||||
2117
llvm/2.9_update.diff
2117
llvm/2.9_update.diff
File diff suppressed because it is too large
Load diff
165
llvm/__init__.py
165
llvm/__init__.py
|
|
@ -1,140 +1,47 @@
|
||||||
"""
|
from ._version import get_versions
|
||||||
Common classes related to LLVM.
|
__version__ = get_versions()['version']
|
||||||
"""
|
del get_versions
|
||||||
|
|
||||||
__version__ = '0.8.3'
|
|
||||||
|
|
||||||
|
|
||||||
from weakref import WeakValueDictionary
|
from llvmpy import extra
|
||||||
|
|
||||||
|
version = extra.get_llvm_version()
|
||||||
|
del extra
|
||||||
|
|
||||||
|
class Wrapper(object):
|
||||||
|
__slots__ = '__ptr'
|
||||||
|
|
||||||
|
def __init__(self, ptr):
|
||||||
|
assert ptr
|
||||||
|
self.__ptr = ptr
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _ptr(self):
|
||||||
|
try:
|
||||||
|
return self.__ptr
|
||||||
|
except AttributeError:
|
||||||
|
raise AttributeError("_ptr resource has been removed")
|
||||||
|
|
||||||
|
@_ptr.deleter
|
||||||
|
def _ptr(self):
|
||||||
|
del self.__ptr
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
def _extract_ptrs(objs):
|
||||||
# Exceptions
|
return [(x._ptr if x is not None else None)
|
||||||
#===----------------------------------------------------------------------===
|
for x in objs]
|
||||||
|
|
||||||
class LLVMException(Exception):
|
class LLVMException(Exception):
|
||||||
"""Generic LLVM exception."""
|
|
||||||
|
|
||||||
def __init__(self, msg=""):
|
|
||||||
Exception.__init__(self, msg)
|
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# Ownables
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
class Ownable(object):
|
|
||||||
"""Objects that can be owned.
|
|
||||||
|
|
||||||
Modules and Module Providers can be owned, i.e., the responsibility of
|
|
||||||
destruction of ownable objects can be handed over to other objects. The
|
|
||||||
llvm.Ownable class represents objects that can be so owned. This class
|
|
||||||
is NOT intended for public use.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, ptr, del_fn):
|
|
||||||
self.ptr = ptr
|
|
||||||
self.owner = None
|
|
||||||
self.del_fn = del_fn
|
|
||||||
|
|
||||||
def _own(self, owner):
|
|
||||||
if self.owner:
|
|
||||||
raise LLVMException("object already owned")
|
|
||||||
self.owner = owner
|
|
||||||
|
|
||||||
def _disown(self):
|
|
||||||
if not self.owner:
|
|
||||||
raise LLVMException("not owned")
|
|
||||||
self.owner = None
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
if not self.owner:
|
|
||||||
self.del_fn(self.ptr)
|
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# Dummy owner, will not delete ownee. Be careful.
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
class DummyOwner(object):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test(verbosity=3, run_isolated=True):
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# A metaclass to prevent aliasing. It stores a (weak) reference to objects
|
|
||||||
# constructed based on a PyCObject. If an object is constructed based on a
|
|
||||||
# PyCObject with the same underlying pointer as a previous object, a reference
|
|
||||||
# to the previous object is returned rather than a new one.
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
class _ObjectCache(type):
|
|
||||||
"""A metaclass to prevent aliasing.
|
|
||||||
|
|
||||||
Classes using 'ObjectCache' as a metaclass must have constructors
|
|
||||||
that take a PyCObject as their first argument. When the class is
|
|
||||||
called (to create a new instance of the class), the value of the
|
|
||||||
pointer wrapped by the PyCObj is checked:
|
|
||||||
|
|
||||||
If no previous object has been created based on the same
|
|
||||||
underlying pointer (note that different PyCObject objects can
|
|
||||||
wrap the same pointer), the object will be initialized as
|
|
||||||
usual and returned.
|
|
||||||
|
|
||||||
If a previous has been created based on the same pointer,
|
|
||||||
then a reference to that object will be returned, and no
|
|
||||||
object initialization is performed.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__instances = WeakValueDictionary()
|
|
||||||
|
|
||||||
def __call__(cls, ptr, *args, **kwargs):
|
|
||||||
objid = _core.PyCObjectVoidPtrToPyLong(ptr)
|
|
||||||
key = "%s:%d" % (cls.__name__, objid)
|
|
||||||
obj = _ObjectCache.__instances.get(key)
|
|
||||||
if obj is None:
|
|
||||||
obj = super(_ObjectCache, cls).__call__(ptr, *args, **kwargs)
|
|
||||||
_ObjectCache.__instances[key] = obj
|
|
||||||
return obj
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def forget(obj):
|
|
||||||
objid = _core.PyCObjectVoidPtrToPyLong(obj.ptr)
|
|
||||||
key = "%s:%d" % (type(obj).__name__, objid)
|
|
||||||
if key in _ObjectCache.__instances:
|
|
||||||
del _ObjectCache.__instances[key]
|
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# Cacheables
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
# version 2/3 compatibility help
|
|
||||||
# version 2 metaclass
|
|
||||||
# class Cacheable(object):
|
|
||||||
# __metaclass__ = _ObjectCache # Doing nothing for version 3
|
|
||||||
#
|
|
||||||
# version 3 metaclass
|
|
||||||
# class Cacheable(metaclass=_ObjectCache):
|
|
||||||
#
|
|
||||||
# Reference: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/#using-the-metaclass-in-python-3-x
|
|
||||||
ObjectCache = _ObjectCache('ObjectCache', (object, ), {})
|
|
||||||
|
|
||||||
class Cacheable(ObjectCache):
|
|
||||||
"""Objects that can be cached.
|
|
||||||
|
|
||||||
Objects that wrap a PyCObject are cached to avoid "aliasing", i.e.,
|
|
||||||
two Python objects each containing a PyCObject which internally points
|
|
||||||
to the same C pointer."""
|
|
||||||
|
|
||||||
def forget(self):
|
|
||||||
ObjectCache.forget(self)
|
|
||||||
|
|
||||||
|
|
||||||
def test(verbosity=1):
|
|
||||||
"""test(verbosity=1) -> TextTestResult
|
"""test(verbosity=1) -> TextTestResult
|
||||||
|
|
||||||
Run self-test, and return unittest.runner.TextTestResult object.
|
Run self-test, and return the number of failures + errors
|
||||||
"""
|
"""
|
||||||
from llvm.test_llvmpy import run
|
from llvm.tests import run
|
||||||
|
|
||||||
return run(verbosity=verbosity)
|
result = run(verbosity=verbosity, run_isolated=run_isolated)
|
||||||
|
errct = len(result.failures) + len(result.errors)
|
||||||
|
|
||||||
|
return errct
|
||||||
|
|
|
||||||
2180
llvm/_core.cpp
2180
llvm/_core.cpp
File diff suppressed because it is too large
Load diff
103
llvm/_util.py
103
llvm/_util.py
|
|
@ -1,103 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (c) 2008-10, Mahadevan R All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
|
||||||
# and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# * Neither the name of this software, nor the names of its
|
|
||||||
# contributors may be used to endorse or promote products derived from
|
|
||||||
# this software without specific prior written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
|
|
||||||
"""Utility functions and classes.
|
|
||||||
|
|
||||||
Used only in other modules, not for public use."""
|
|
||||||
|
|
||||||
import llvm
|
|
||||||
import llvm._core as _core # for PyCObjectVoidPtrToPyLong
|
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# A set of helpers to check various things. Raises exceptions on
|
|
||||||
# failures.
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
def check_gen(obj, typ):
|
|
||||||
if not isinstance(obj, typ):
|
|
||||||
typ_str = typ.__name__
|
|
||||||
msg = "argument not an instance of llvm.core.%s" % typ_str
|
|
||||||
raise TypeError(msg)
|
|
||||||
|
|
||||||
def check_is_unowned(ownable):
|
|
||||||
if ownable.owner:
|
|
||||||
raise llvm.LLVMException("object is already owned")
|
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# A set of helpers to unpack a list of Python wrapper objects
|
|
||||||
# into a list of PyCObject wrapped objects, checking types along
|
|
||||||
# the way.
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
def unpack_gen(objlist, check_fn):
|
|
||||||
for obj in objlist: check_fn(obj)
|
|
||||||
return [ obj.ptr for obj in objlist ]
|
|
||||||
|
|
||||||
def unpack_gen_allow_none(objlist, check_fn):
|
|
||||||
for obj in objlist:
|
|
||||||
if obj is not None:
|
|
||||||
check_fn(obj)
|
|
||||||
return [ (obj.ptr if obj is not None else None) for obj in objlist ]
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# Helper to wrap over iterables (LLVMFirstXXX, LLVMNextXXX). This used
|
|
||||||
# to be a generator, but that loses subscriptability of the result, so
|
|
||||||
# we now return a list.
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
|
|
||||||
def wrapiter(first, next, container, wrapper):
|
|
||||||
ret = []
|
|
||||||
ptr = first(container)
|
|
||||||
while ptr:
|
|
||||||
ret.append(wrapper(ptr))
|
|
||||||
ptr = next(ptr)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
# Py2/3 compatibility string check
|
|
||||||
#===----------------------------------------------------------------------===
|
|
||||||
def _isstring_py2(x):
|
|
||||||
return isinstance(x, basestring)
|
|
||||||
|
|
||||||
def _isstring_py3(x):
|
|
||||||
return isinstance(x, str)
|
|
||||||
|
|
||||||
def _isstring_choose():
|
|
||||||
try:
|
|
||||||
basestring
|
|
||||||
return _isstring_py2
|
|
||||||
except:
|
|
||||||
return _isstring_py3
|
|
||||||
|
|
||||||
isstring = _isstring_choose()
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue