mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
847 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b7fa93fcb | |||
| cce9c6d5a1 | |||
| f41b6a3b96 | |||
| e066e67baf | |||
| cacb452aa6 | |||
| 9296332151 | |||
| 1125d42149 | |||
| 7982d26db0 | |||
| f6758d22d8 | |||
| 87cfe6a538 | |||
| c02cc339c5 | |||
| 361ecf9ea4 | |||
| e21ab04fad | |||
| 4e8453167f | |||
| ebb410afd9 | |||
| 012bdfae80 | |||
| f6df276118 | |||
| e0a5fc2555 | |||
| cecb80b8bf | |||
| d899895e32 | |||
| 0d56dee00f | |||
| 1f0bf9ba63 | |||
| 0b0ae19656 | |||
| 9027c50405 | |||
| 4ce54ede15 | |||
| d8b1fac690 | |||
| 1996fad075 | |||
| 7fe996e7ba | |||
| 47ca1c037b | |||
| df0f5ae1da | |||
| 45c8817ddd | |||
| eebd2f648b | |||
| 13698b17f7 | |||
| dd9c2273e4 | |||
| 1adfdf8fa5 | |||
| da362b8a24 | |||
| 7fa13ed0a6 | |||
| eec915170d | |||
| 334a12818d | |||
| 0504e4a201 | |||
| 530ca0838d | |||
| c41a212712 | |||
| 27824e3c80 | |||
| 159317a5fe | |||
| 655075e5e0 | |||
| f9d0ff3bec | |||
| 17176a2cea | |||
| fb73074325 | |||
| 187d9c6aca | |||
| 35f427c8cc | |||
| 26b6d0b06d | |||
| 38252e338f | |||
| e7ba07dd5a | |||
| 0d646d8e95 | |||
| 3177591b31 | |||
| 19db0144ae | |||
| d639e01650 | |||
| c43958972c | |||
| 33489d6344 | |||
| 80077ef276 | |||
| 911ce3a4bc | |||
| 776dabb2c8 | |||
| e11df5c212 | |||
| 656805d91f | |||
| 05ec4e6bd0 | |||
| ae65b37762 | |||
| aa12579864 | |||
| 715b269f78 | |||
| 5f3534fe72 | |||
| a7ff2731b9 | |||
| 0566bbfeb4 | |||
| 70c4e1cf96 | |||
| 9fd0567fb5 | |||
| 481aba6536 | |||
| a700f58581 | |||
| e71e289e8b | |||
| 6cdc718bfb | |||
| 70c94c6d01 | |||
| 762a6f1256 | |||
| 8d4d3c6201 | |||
| 146e2cd376 | |||
| d304e6706b | |||
| c5c6464175 | |||
| c90ff1e1cf | |||
| 0f6dfedcfd | |||
| 69a284bd36 | |||
| 92be61534e | |||
| 675b8e3f3c | |||
| f3d0195d35 | |||
| 398c284480 | |||
| 86f125cf72 | |||
| 5293d3e5f0 | |||
| 7aca814954 | |||
| 1d52e6be29 | |||
| acb2391439 | |||
| 49007891c8 | |||
| 54aec042b7 | |||
| baca8a643e | |||
| b731919f97 | |||
| c759e6fc2d | |||
| a329f56ec2 | |||
| 6e503f5ab9 | |||
| 217449df07 | |||
| a7fc65861d | |||
| f0a271bd21 | |||
| 4d14d3cd54 | |||
| 5c622cfc43 | |||
| 783c6a813a | |||
| 67841207e8 | |||
| d92e16e6dc | |||
| 4daf4fa0a0 | |||
| 0b3a7280fa | |||
| bb5050a50d | |||
| 73b368270c | |||
| 2001c44ed3 | |||
| 666ead3996 | |||
| df888acd5e | |||
| b9bb605257 | |||
| e1281b0128 | |||
| 81dbbea1c8 | |||
| a834359079 | |||
| 29f8d22a2d | |||
| 9e34ca9e5f | |||
| 268140ae40 | |||
| 8906584fb9 | |||
| aaa860f154 | |||
| bce0516394 | |||
| 726d74ad1b | |||
| ad544b5133 | |||
| 580fd490a5 | |||
| c0d505d13a | |||
| 12a41dc791 | |||
| dd3ad57efb | |||
| 89facf834f | |||
| 62594004c6 | |||
| 3dbc4d5210 | |||
| 42e4a75319 | |||
| 2b7f562202 | |||
| 08a51535d4 | |||
| 0ddcf4af96 | |||
| d435fc868b | |||
| 62df1d7490 | |||
| 229746b71e | |||
| 09ec846c9f | |||
| f57cffce2d | |||
| 7b6a9fab42 | |||
| 6bc6de8f82 | |||
| b79bf29c11 | |||
| 65d65c1313 | |||
| 6b46ae261a | |||
| b734b3c7a1 | |||
| 5937f03a07 | |||
| bf9282a3a0 | |||
| cad5b79a2d | |||
| d56c9f5533 | |||
| 3d7f24e26c | |||
| c32490935b | |||
| 2b04006b1e | |||
| 9de757205f | |||
| f866d5cc38 | |||
| c153239682 | |||
| 8e88f3e76a | |||
| a62044d89b | |||
| 5548b5999b | |||
| 8f33721b88 | |||
| 7e9d84777e | |||
| 2072d1bb5c | |||
| 636a151ffe | |||
| e7d02cd51b | |||
| f136eb6adf | |||
| 2688d9d733 | |||
| b5ad81c884 | |||
| c2fb1e776a | |||
| 895c542076 | |||
| e91fae5756 | |||
| f52ecf3054 | |||
| 725864d8dc | |||
| fca2de21b1 | |||
| 107b1c2906 | |||
| 2409bbef69 | |||
| 8b078a9e8f | |||
| 9e24441978 | |||
| 1e6c14b3e4 | |||
| 25f6be1488 | |||
| 9a9106a297 | |||
| a68f7e4ce3 | |||
| 70b3ece6ec | |||
| 86ea216e7d | |||
| 5a92c35536 | |||
| 7513fa2c96 | |||
| bc946f346d | |||
| 6e6666519c | |||
| cecae8084c | |||
| 9720ccacbb | |||
| a5aa777c7b | |||
| aca1ad187d | |||
| 5a41ed6329 | |||
| 91e9c84d85 | |||
| 4af530f238 | |||
| d3863f337d | |||
| 5551ec7a1e | |||
| 91104b2f27 | |||
| 80f38e8449 | |||
| 22893c5180 | |||
| 9e4e182615 | |||
| 5f96d2b784 | |||
| bd1d93b705 | |||
| 03d1c44265 | |||
| dc0346d968 | |||
| dadae95448 | |||
| f5fc2fc072 | |||
| 4ba76a5df9 | |||
| ea1e08f53f | |||
| bba0dc9575 | |||
| 617c7586fe | |||
| 7366d5e185 | |||
| 96e3ecd949 | |||
| 8bbfb1ee68 | |||
| 07cdcc61f7 | |||
| 4ca9b34133 | |||
| d5e4b16911 | |||
| fd05254683 | |||
| c579e7e509 | |||
| d463528a72 | |||
| 49b0086f8f | |||
| d7814ff50b | |||
| 02160aa8d8 | |||
| c199dd03c7 | |||
| 472aecf07a | |||
| 09350e5719 | |||
| 623dd6a815 | |||
| e09bd5abb3 | |||
| e955aa1941 | |||
| edaf13f951 | |||
| 5f6cf665f5 | |||
| c6db89ff05 | |||
| 61d651c947 | |||
| 227a55d891 | |||
| b485e8bfbd | |||
| 9e8739bb47 | |||
| 20a7a6fd54 | |||
| 4695da83b7 | |||
| f094e895c5 | |||
| 55573c6729 | |||
| b8a6a1f62b | |||
| ef912a132d | |||
| fb603a471c | |||
| f439514703 | |||
| 303410d3fc | |||
| aa13b5d83b | |||
| 12d63d9642 | |||
| 24b45555bd | |||
| 80c17c0f32 | |||
| f6e6aca8d9 | |||
| c4d99e5297 | |||
| 697e8c725b | |||
| a87e5cdbf0 | |||
| b56baa7aad | |||
| ee94f92ede | |||
| 0b77e92c50 | |||
| a4ee98e0e1 | |||
| d0808c93f6 | |||
| a0407be7b7 | |||
| e70dfa4dd6 | |||
| f34a0d44ca | |||
| df52b500bf | |||
| c2f437e0b7 | |||
| 2365c7cfec | |||
| a5d28bdcca | |||
| f12ddfe164 | |||
| f9ba99344a | |||
| 1784bcd728 | |||
| a2704f1435 | |||
| edbbca5614 | |||
| 13f21477b1 | |||
| 9d73e6cfe4 | |||
| 9e7667ffae | |||
| b86467f9c5 | |||
| 13a16afc2a | |||
| a107b4d459 | |||
| 5c176f80d5 | |||
| c4392d9472 | |||
| b6d6efde07 | |||
| 8b67282f91 | |||
| c7a23183e8 | |||
| f936125658 | |||
| 3cfaceeb16 | |||
| f0391eac25 | |||
| 05cdde162c | |||
| 25c8d3ec3a | |||
| 7b1e5f6f99 | |||
| 70c52ef1b4 | |||
| 5078ce382d | |||
| 408855da14 | |||
| 58072a5167 | |||
| 5026047cde | |||
| 67aea275c5 | |||
| 99bd4d2c7c | |||
| a4b22bd125 | |||
| 2ed7523001 | |||
| 4aec3d04f9 | |||
| 4f729c323d | |||
| 1f43f32aae | |||
| 6d1cbe6fb9 | |||
| 7cd2890434 | |||
| 13311da7e9 | |||
| 3047d8f321 | |||
| 13ed58950f | |||
| 5271bd8cfa | |||
| 72137d663b | |||
| 740134197e | |||
| 610e7fd16a | |||
| 69a6718b38 | |||
| 7d31d98f0f | |||
| 0a93347e1c | |||
| 411333327e | |||
| 159f7ebbc3 | |||
| 153ff09cc5 | |||
| 57cddf5657 | |||
| 6a35719594 | |||
| 20a46e1b28 | |||
| 926c02b6ff | |||
| f370cd18fc | |||
| a1d7cab080 | |||
| 15a9e2313a | |||
| 58f2533f15 | |||
| 3c2d2a93d3 | |||
| ea36fea5a9 | |||
| 5fdb894760 | |||
| 8fed063820 | |||
| 2f843af286 | |||
| 7afc47d67a | |||
| 0bc7c2a61f | |||
| d434058aef | |||
| 56780c2691 | |||
| 1d99b1ed84 | |||
| e1405640da | |||
| 5996e113ad | |||
| f1e6997018 | |||
| 335a7ad0b7 | |||
| 17deac116f | |||
| b112477e95 | |||
| 2fa24ef752 | |||
| fcefcb191c | |||
| ff7c71c99c | |||
| 7eaad62568 | |||
| 401bd89664 | |||
| 24b1bfcecd | |||
| 27162ebe32 | |||
| b347aea450 | |||
| 1766746c60 | |||
| 07049b87b6 | |||
| f38603530d | |||
| b65447c7d5 | |||
| 0abc909739 | |||
| d984b55311 | |||
| 446fe1f5b3 | |||
| 873412b43a | |||
| 8a5ab5ab12 | |||
| 77cf60ef30 | |||
| a4e2e09457 | |||
| 4262f0ff1f | |||
| 1ceb63d14c | |||
| e1052f921c | |||
| 6dff85c756 | |||
| afae6b3795 | |||
| 2825b4fe74 | |||
| b5c5d95b68 | |||
| 633fe95187 | |||
| 64d7f1e436 | |||
| 9af535bf98 | |||
| 159f0051bb | |||
| 49e22fe678 | |||
| c119721e3b | |||
| 9294141d70 | |||
| bcf58c0840 | |||
| 7a61cb1bc3 | |||
| 11e1b2148f | |||
| a4599ba539 | |||
| 4c64784724 | |||
| 8d6b210c09 | |||
| 79ba6a50c3 | |||
| 2fb5174910 | |||
| b31281a5b6 | |||
| 8754f64f3b | |||
| caf405b01d | |||
| bb65c5ce2b | |||
| 283fed16a5 | |||
| 76bba13963 | |||
| ada11090a2 | |||
| a07287d3c6 | |||
| 1b2e764835 | |||
| 2c93a4085c | |||
| 906757603d | |||
| 5d16811199 | |||
| f2e576decf | |||
| 2d61972475 | |||
| 5c488e3145 | |||
| e45ff9c0e8 | |||
| 39805bca45 | |||
| 97842b367c | |||
| 9246899b30 | |||
| 04ace4de5f | |||
| 87a4676137 | |||
| fd20f63ee3 | |||
| 9dd249a431 | |||
| 777d3f3ea5 | |||
| ea3b5dc689 | |||
| 3dd502a928 | |||
| 48242ba8d6 | |||
| 7ea9eff406 | |||
| 80d94115dc | |||
| 9de76fb42e | |||
| 52c61de19f | |||
| f308d7d28c | |||
| 7729a36782 | |||
| 5404c81ffd | |||
| b52310fed2 | |||
| 046070074d | |||
| c23a5b65ef | |||
| b8e433821a | |||
| dd1a836903 | |||
| 4ed0936c0e | |||
| 7a5d4c9537 | |||
| af6bbaf532 | |||
| 4c7449efe5 | |||
| 995e815b63 | |||
| 109b5298c4 | |||
| a94e03eff2 | |||
| eb3d6261b4 | |||
| 15724848f9 | |||
| 8f40b107d9 | |||
| fa673f0827 | |||
| e3fe6d8f37 | |||
| 5c23f6fb6c | |||
| 5afb63df60 | |||
| 1d2c39c3b9 | |||
| 270a2f51fd | |||
| e87cd23e87 | |||
| db389ba443 | |||
| d1dfa1155c | |||
| cf0b7edc78 | |||
| a32cda7a4c | |||
| b251ce7b33 | |||
| 1abe1203a3 | |||
| 8b360ec911 | |||
| 93654544b3 | |||
| 050d516264 | |||
| 9a8328fcb7 | |||
| 6812a4dc04 | |||
| 8568d4f625 | |||
| bd56636d58 | |||
| 4fe3a379a5 | |||
| fce42fc7fa | |||
| 5d177d1328 | |||
| 9eb4a6268a | |||
| 11a402de95 | |||
| cdd9198888 | |||
| 6055e6eb23 | |||
| 9239c61edb | |||
| 008b373df4 | |||
| c387a09f54 | |||
| 5bb3ca2618 | |||
| a4ea85b303 | |||
| 27d38a9663 | |||
| ee497a296b | |||
| 6b31ee1e93 | |||
| cad6a57077 | |||
| 52233790cc | |||
| 9160f3f16d | |||
| 69d7ed5cda | |||
| 111661658b | |||
| 3000b2d15e | |||
| c89f026532 | |||
| e09b1d06f5 | |||
| 0590ccb260 | |||
| fe0acb1670 | |||
| d58c7d2521 | |||
| 85f5411d60 | |||
| c5de68622e | |||
| 8fb93fbac4 | |||
| ef3b3c0d6a | |||
| 2dc46a851b | |||
| f3afc7bbf3 | |||
| 4858b27120 | |||
| 7f507a7026 | |||
| 83227e68df | |||
| 09a3c6a16b | |||
| f613cb41a3 | |||
| e060e588ab | |||
| 8dfb317d28 | |||
| 9cedd78d9c | |||
| 20a27746b2 | |||
| c78df85015 | |||
| c0d257d682 | |||
| 7af24c7864 | |||
| 3dc1e403ea | |||
| 1deee3ec5a | |||
| 0f9f316db2 | |||
| 0dd521e1d1 | |||
| 1dcdd5f2d2 | |||
| dcd8ee63a8 | |||
| b8bf0ac3f1 | |||
| 73571dd423 | |||
| 8872229a0b | |||
| 7e05efa274 | |||
| b02cea0321 | |||
| 8823a934a3 | |||
| 6ac02cc34c | |||
| e259598b68 | |||
| f159573975 | |||
| 234234af15 | |||
| 3d68804702 | |||
| 38efca1e2d | |||
| 0a75dd82b9 | |||
| fbc748f152 | |||
| 881aba1785 | |||
| aa421bf655 | |||
| 1fb1f08233 | |||
| 0b5d300d94 | |||
| 2fdcabdcce | |||
| d066b5c232 | |||
| 836ab95192 | |||
| cf8f4d65f2 | |||
| 06b19cde2d | |||
| 42ae9a665f | |||
| c55c996c5d | |||
| 6fb90b6fc4 | |||
| 6f31712b7d | |||
| e295770363 | |||
| 486b99961d | |||
| 4a98db4c86 | |||
| c534fa1b6f | |||
| eed78338e6 | |||
| 5141638cbf | |||
| 5d4f795aec | |||
| db8603e378 | |||
| bc098da93f | |||
| e49e93f22c | |||
| 7d4bf09b1a | |||
| 320f21524e | |||
| 4117055611 | |||
| 603fff78d4 | |||
| d4b1467e4c | |||
| 4cf8ad2bee | |||
| 96c60f9692 | |||
| 99ce46669b | |||
| a1b4eafa07 | |||
| ccbe03795a | |||
| 3f69c32a55 | |||
| b176494e44 | |||
| 0243a5b8be | |||
| 1fac41d535 | |||
| 1472fb6032 | |||
| 0fd3d785a3 | |||
| b9c0aa77bf | |||
| 4c28f1b875 | |||
| 0791603c86 | |||
| 095312fa3f | |||
| 7e647a5e81 | |||
| 29c1d7c68d | |||
| ecb7eebcff | |||
| a680bfbb7a | |||
| bf23839931 | |||
| 6b15a72895 | |||
| 08fd49fe60 | |||
| d65e3deccf | |||
| cbfa85be6a | |||
| 6c23f27f73 | |||
| 31a3a7c84b | |||
| a9a225ba26 | |||
| 58d0499e55 | |||
| b24af78327 | |||
| b406422277 | |||
| cfed3f0213 | |||
| d75e14245b | |||
| af8244e868 | |||
| f962e2fd15 | |||
| 5746154cc0 | |||
| e9d9ae8781 | |||
| 52ffd6aa98 | |||
| 644356c220 | |||
| 47a622a903 | |||
| 4417374b53 | |||
| 262291a80a | |||
| cdd267fa10 | |||
| 850a5fdbfb | |||
| 4b2edc9a26 | |||
| f2fef290c9 | |||
| 010cc13e9e | |||
| ac7e370c72 | |||
| 77ec9b6369 | |||
| aa8e0bb509 | |||
| 372d9e0f5e | |||
| d139fa5e0f | |||
| 367fb9d40e | |||
| 98a6ed8059 | |||
| 641a7e5cb9 | |||
| 3fa7d6cc40 | |||
| e497b5c5ba | |||
| 23dc861ddb | |||
| b2ccf12e98 | |||
| 23479c92d3 | |||
| 3458726234 | |||
| a7a69d38a4 | |||
| 4210200070 | |||
| 3883550c44 | |||
| 5ce072b827 | |||
| 4029b838ce | |||
| 3fbab2ede3 | |||
| a29e1d43f1 | |||
| ee663ccb96 | |||
| cd867b72b9 | |||
| 8aa223aed9 | |||
| 3a4e0c94e1 | |||
| 0a0981a475 | |||
| 2f57e4ff6d | |||
| 5e33dccf1f | |||
| f4fdf8dce9 | |||
| 9c3f9537be | |||
| f5b7925047 | |||
| 50401c05e8 | |||
| 7914a729a5 | |||
| 8eedd2b04b | |||
| 60b483bd3b | |||
| 322abf6d22 | |||
| b85cb55aab | |||
| 03d83181ca | |||
| 31ee1637df | |||
| b7bcb256d7 | |||
| 7516c5d69e | |||
| c6ae1c58fc | |||
| 0c2fefa09b | |||
| ea2a6b70b2 | |||
| d054d4d512 | |||
| e7e1b755c9 | |||
| 38cd5b80ec | |||
| 57ff3a5ca0 | |||
| ecf7014f7e | |||
| 7c3a22d289 | |||
| 2c7ea98ccf | |||
| 57b8f7e667 | |||
| db0efc52c7 | |||
| 94b075e423 | |||
| ce5d52870f | |||
| dd6adcacb6 | |||
| 610a20c4d5 | |||
| daf5d0b63a | |||
| 233f5c6af2 | |||
| ca2f25ed48 | |||
| cc1a95a489 | |||
| 431e223803 | |||
| 1b46324456 | |||
| 751ac85488 | |||
| 512fb1dae6 | |||
| 0d20afa85f | |||
| c87bb0e9cc | |||
| 6afceb5621 | |||
| aa053b5fb0 | |||
| 0ed34e3cb0 | |||
| 78a36f7724 | |||
| a743580734 | |||
| e2178ce682 | |||
| b2ef7299b7 | |||
| 14ce531498 | |||
| 21c76c0cb0 | |||
| fbd72a4b2c | |||
| 93e0636c03 | |||
| ef77b645b9 | |||
| 0b939f93bc | |||
| 798085e866 | |||
| 69bf6e3c89 | |||
| 8f8d83bffe | |||
| 998f01608c | |||
| 84279ab2ec | |||
| a888d6245d | |||
| f80efa5b8b | |||
| 29f7ebc63e | |||
| 72e15af1fa | |||
| 6cb604c752 | |||
| cb01d4d63e | |||
| fe21650010 | |||
| ebd4969cde | |||
| 32a7b8cc4e | |||
| b4fa658705 | |||
| ee4e42ba1f | |||
| c6678525a9 | |||
| 8d75b58bdf | |||
| ec97313239 | |||
| e046b23773 | |||
| 0099d47002 | |||
| 5c3ad004ab | |||
| 61be9189f1 | |||
| 1e2791b216 | |||
| d86dd642b3 | |||
| 21b89ef327 | |||
| 39c8711332 | |||
| edc9319e0d | |||
| fcd88356db | |||
| e536676a67 | |||
| 342c438586 | |||
| 6b7ecf1f52 | |||
| bd7ab8ddd5 | |||
| 302e72e84f | |||
| 55ca5287fe | |||
| 11ee0969b6 | |||
| 81dbbc89d3 | |||
| a4f9e5e552 | |||
| f9f2349850 | |||
| f1b15db258 | |||
| 12e314ccc9 | |||
| e594dbc133 | |||
| bb08a25edc | |||
| b7b7713526 | |||
| 92150bd1c5 | |||
| d0633f6228 | |||
| d46933abf2 | |||
| 26625f5a22 | |||
| 3e109fca5f | |||
| 763a86cd8e | |||
| 13a3c4b155 | |||
| 9fa667ce63 | |||
| 8bd39d2fc2 | |||
| c685e0696a | |||
| 9db8826b3b | |||
| 0aed62efe4 | |||
| 2b1a72d112 | |||
| a6837f05a4 | |||
| c35ce761aa | |||
| d2963b153e | |||
| f9293645af | |||
| 38305399a3 | |||
| 8f2082df69 | |||
| da8eb521e1 | |||
| dc53a59c49 | |||
| f21fdd1f76 | |||
| 5e719b0d3d | |||
| 9eb13884de | |||
| 85031d6df7 | |||
| 58b44f9f17 | |||
| c0ea21ece7 | |||
| 81edf577a3 | |||
| 484bb8f13d | |||
| ae1e7731a4 | |||
| 518745ea79 | |||
| 38b14706e2 | |||
| 747336f0a9 | |||
| e4de7b4957 | |||
| d5c0083085 | |||
| e6fbad13c6 | |||
| 62bda8e129 | |||
| 51d6ffb3be | |||
| 964aa7058a | |||
| 0f9d2ac0e4 | |||
| 5264469cbd | |||
| c096f35224 | |||
| 929cd767c1 | |||
| e1df2510d4 | |||
| 2ebaa9f520 | |||
| 5159eab694 | |||
| 75dfb26452 | |||
| 971da0bc09 | |||
| 0420842f4d | |||
| d30f8b6ed6 | |||
| 1f6b4a5e7b | |||
| 51fc78c100 | |||
| e4f2b9c003 | |||
| cff4493ca0 | |||
| 93ac7cec0d | |||
| b4503c1e37 | |||
| 3e6410f323 | |||
| 0f69d016a0 | |||
| ef8c1a187d | |||
| 08d0dc0d25 | |||
| 039186a2a3 | |||
| d6c892fe7b | |||
| cfe6b9765f | |||
| 539495ef45 | |||
| b13c993565 | |||
| 449bfe04f3 | |||
| 1903cf23c9 | |||
| 9734f349a3 | |||
| d87ff838eb | |||
| a360c82b40 | |||
| 4a2faeb2c5 | |||
| 99f00d396d | |||
| 049c5ed3b1 | |||
| cce02cd046 | |||
| be7bbba4ca | |||
| e668c8c1a7 | |||
| d62c9696fb | |||
| 343146f35c | |||
| 0285171484 | |||
| 1a22d92217 | |||
| 8416bb61d8 | |||
| 85ad157f3f | |||
| b123ffcea4 | |||
| ec9fd3fb38 | |||
| 5127b9d65b | |||
| 74b20ca234 | |||
| 3d73ee77d3 | |||
| 6b0f2cd697 | |||
| e354a3523b | |||
| f79f6b0e62 | |||
| 47db50b273 | |||
| b0fce80b50 | |||
| 718746dd21 | |||
| 6cbaed75c8 | |||
| a6200920f7 | |||
| 79467baf12 | |||
| c31aef06ba | |||
| 6bd18af5ef | |||
| 1789570f52 | |||
| a536ddcda8 | |||
| 18242bc26a | |||
| 18f4ffd656 | |||
| 7238c0a16f | |||
| c177e4dbc0 | |||
| 1d2c17753a | |||
| 470cbd5f53 | |||
| 62d4a6a77f | |||
| 339301b709 | |||
| aea4f57a2b | |||
| 2a17a3e56c | |||
| 34011c4800 | |||
| f8206b9bcc | |||
| b01ce95cb5 | |||
| 0658e4896f | |||
| 9d87d658ba | |||
| ec95d3560f | |||
| bf076aed61 | |||
| dee5602e56 | |||
| 4ef340f228 | |||
| 843a7ac445 | |||
| 8ed7d197a7 | |||
| 39ce7111bd | |||
| 06df31944c | |||
| 3ba8bc92cd | |||
| 0e7a129de7 | |||
| aeddcd9c5f | |||
| bf0b05e32c | |||
| 348a02d697 | |||
| 7273a2b5fd | |||
| 44bb381220 | |||
| 2e6b92fc56 | |||
| 51290e5842 | |||
| 22c35c8e31 |
+32
-378
@@ -6,61 +6,20 @@ commands:
|
||||
- run:
|
||||
name: "Pull submodules"
|
||||
command: git submodule update --init
|
||||
install-node:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install node.js"
|
||||
command: |
|
||||
wget https://nodejs.org/dist/v10.15.1/node-v10.15.1-linux-x64.tar.xz
|
||||
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
|
||||
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
|
||||
rm node-v10.15.1-linux-x64.tar.xz
|
||||
install-chrome:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install Chrome"
|
||||
command: |
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
sudo apt install ./google-chrome-stable_current_amd64.deb
|
||||
install-wasmtime:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install wasmtime"
|
||||
command: |
|
||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
|
||||
install-cmake:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install CMake"
|
||||
command: |
|
||||
wget https://github.com/Kitware/CMake/releases/download/v3.21.4/cmake-3.21.4-linux-x86_64.tar.gz
|
||||
sudo tar --strip-components=1 -C /usr/local -xf cmake-3.21.4-linux-x86_64.tar.gz
|
||||
install-xtensa-toolchain:
|
||||
parameters:
|
||||
variant:
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: "Install Xtensa toolchain"
|
||||
command: |
|
||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-v2
|
||||
- llvm-source-14-v3
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-v2
|
||||
key: llvm-source-14-v3
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/compiler-rt
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
hack-ninja-jobs:
|
||||
@@ -74,387 +33,82 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-v1
|
||||
- binaryen-linux-v2
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-v1
|
||||
key: binaryen-linux-v2
|
||||
paths:
|
||||
- build/wasm-opt
|
||||
build-binaryen-linux-stretch:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-stretch-v1
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
CC=$PWD/llvm-build/bin/clang make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-stretch-v1
|
||||
paths:
|
||||
- build/wasm-opt
|
||||
build-wasi-libc:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-v4
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-v4
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
test-linux:
|
||||
parameters:
|
||||
llvm:
|
||||
type: string
|
||||
fmt-check:
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y \
|
||||
llvm-<<parameters.llvm>>-dev \
|
||||
clang-<<parameters.llvm>> \
|
||||
libclang-<<parameters.llvm>>-dev \
|
||||
lld-<<parameters.llvm>> \
|
||||
gcc-avr \
|
||||
avr-libc \
|
||||
cmake \
|
||||
ninja-build
|
||||
- hack-ninja-jobs
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v3-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v3
|
||||
- wasi-libc-sysroot-systemclang-v6
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v3
|
||||
key: wasi-libc-sysroot-systemclang-v6
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- when:
|
||||
condition: <<parameters.fmt-check>>
|
||||
steps:
|
||||
- run:
|
||||
# Do this before gen-device so that it doesn't check the
|
||||
# formatting of generated files.
|
||||
name: Check Go code formatting
|
||||
command: make fmt-check
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
- run: make fmt-check
|
||||
assert-test-linux:
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
libgnutls30 libssl1.0.2 \
|
||||
qemu-system-arm \
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
avr-libc \
|
||||
ninja-build \
|
||||
python3
|
||||
- install-node
|
||||
- install-chrome
|
||||
- install-wasmtime
|
||||
- install-cmake
|
||||
- hack-ninja-jobs
|
||||
- install-xtensa-toolchain:
|
||||
variant: "linux-amd64"
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v4-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source (may only have headers right now)
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make ASSERT=1 llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v4-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-binaryen-linux-stretch
|
||||
- run:
|
||||
name: "Build TinyGo"
|
||||
command: |
|
||||
make ASSERT=1
|
||||
echo 'export PATH=$(pwd)/build:$PATH' >> $BASH_ENV
|
||||
- build-wasi-libc
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make ASSERT=1 test
|
||||
no_output_timeout: 20m
|
||||
environment:
|
||||
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
||||
# necessary to keep memory consumption down and avoid OOM (for a
|
||||
# 2CPU/4GB executor).
|
||||
GOFLAGS: -p=2
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest
|
||||
- run: make tinygo-test
|
||||
- run: make wasmtest
|
||||
build-linux:
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
libgnutls30 libssl1.0.2 \
|
||||
ninja-build \
|
||||
python3
|
||||
- install-cmake
|
||||
- hack-ninja-jobs
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v4-noassert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source (may only have headers right now)
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v4-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-binaryen-linux-stretch
|
||||
- build-wasi-libc
|
||||
- run:
|
||||
name: "Install fpm"
|
||||
command: |
|
||||
sudo apt-get install ruby ruby-dev
|
||||
sudo gem install --no-document fpm
|
||||
- run:
|
||||
name: "Build TinyGo release"
|
||||
command: |
|
||||
make release deb -j3
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||
- persist_to_workspace:
|
||||
root: /tmp
|
||||
paths:
|
||||
- tinygo.linux-amd64.tar.gz
|
||||
- store_artifacts:
|
||||
path: /tmp/tinygo.linux-amd64.tar.gz
|
||||
- store_artifacts:
|
||||
path: /tmp/tinygo_amd64.deb
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
test-linux-build:
|
||||
# Now run the smoke tests for the generated binary.
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- checkout
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
gcc-avr \
|
||||
avr-libc
|
||||
- install-xtensa-toolchain:
|
||||
variant: "linux-amd64"
|
||||
- run:
|
||||
name: "Extract release tarball"
|
||||
command: |
|
||||
mkdir -p ~/lib
|
||||
tar -C ~/lib -xf /tmp/workspace/tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||
tinygo version
|
||||
- run: make smoketest
|
||||
build-macos:
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu cmake ninja
|
||||
- install-xtensa-toolchain:
|
||||
variant: "macos"
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-macos-v3
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-macos-v3
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v5
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source (may only have headers right now)
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-macos-v5
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-macos-v1
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
if [ ! -f build/wasm-opt ]
|
||||
then
|
||||
make binaryen
|
||||
fi
|
||||
- save_cache:
|
||||
key: binaryen-macos-v1
|
||||
paths:
|
||||
- build/wasm-opt
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v4
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v4
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
no_output_timeout: 20m
|
||||
- run:
|
||||
name: "Build TinyGo release"
|
||||
command: |
|
||||
make release -j3
|
||||
cp -p build/release.tar.gz /tmp/tinygo.darwin-amd64.tar.gz
|
||||
- store_artifacts:
|
||||
path: /tmp/tinygo.darwin-amd64.tar.gz
|
||||
- run:
|
||||
name: "Extract release tarball"
|
||||
command: |
|
||||
mkdir -p ~/lib
|
||||
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
|
||||
tinygo version
|
||||
- run: make smoketest AVR=0
|
||||
- save_cache:
|
||||
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-llvm11-go115:
|
||||
test-llvm14-go118:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
- image: golang:1.18-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
test-llvm11-go116:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
assert-test-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.17-stretch
|
||||
steps:
|
||||
- assert-test-linux
|
||||
build-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.17-stretch
|
||||
steps:
|
||||
- build-linux
|
||||
test-linux-build:
|
||||
docker:
|
||||
- image: cimg/go:1.17
|
||||
steps:
|
||||
- test-linux-build
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: "11.1.0" # macOS 10.14
|
||||
steps:
|
||||
- build-macos
|
||||
|
||||
|
||||
llvm: "14"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-llvm11-go115
|
||||
- test-llvm11-go116
|
||||
- build-linux
|
||||
- test-linux-build:
|
||||
requires:
|
||||
- build-linux
|
||||
- build-macos
|
||||
- assert-test-linux
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm14-go118
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
build/
|
||||
llvm-*/
|
||||
.github
|
||||
.circleci
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
name: macOS
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
name: build-macos
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-15-macos-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-15-macos-v4
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v3
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v4
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-v -short"
|
||||
- name: Build TinyGo release tarball
|
||||
run: make release -j3
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
- name: Make release artifact
|
||||
shell: bash
|
||||
run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz
|
||||
- name: Publish release artifact
|
||||
# Note: this release artifact is double-zipped, see:
|
||||
# https://github.com/actions/upload-artifact/issues/39
|
||||
# We can essentially pick one of these:
|
||||
# - have a double-zipped artifact when downloaded from the UI
|
||||
# - have a very slow artifact upload
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/tinygo.darwin-amd64.tar.gz
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||
test-macos-homebrew:
|
||||
name: homebrew-install
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install LLVM
|
||||
shell: bash
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Build TinyGo
|
||||
run: go install
|
||||
- name: Check binary
|
||||
run: tinygo version
|
||||
@@ -1,45 +0,0 @@
|
||||
name: CI for tinygo-dev docker container
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, fix-docker-llvm-build ]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to GHCR/Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
ghcr.io/${{ github.repository }}/tinygo-dev
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -0,0 +1,100 @@
|
||||
# This is the Github action to build and push the tinygo/tinygo-dev Docker image.
|
||||
# If you are looking for the tinygo/tinygo "release" Docker image please see
|
||||
# https://github.com/tinygo-org/docker
|
||||
#
|
||||
name: Docker
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, fix-docker-llvm-build ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: build-push-dev
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
ghcr.io/${{ github.repository }}/tinygo-dev
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-15
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Trigger Drivers repo build on Github Actions
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
|
||||
-d '{"ref": "dev"}'
|
||||
- name: Trigger Bluetooth repo build on Github Actions
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
|
||||
-d '{"ref": "dev"}'
|
||||
- name: Trigger TinyFS repo build on Github Actions
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
|
||||
-d '{"ref": "dev"}'
|
||||
- name: Trigger TinyFont repo build on Github Actions
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
|
||||
-d '{"ref": "dev"}'
|
||||
- name: Trigger TinyDraw repo build on Github Actions
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \
|
||||
-d '{"ref": "dev"}'
|
||||
- name: Trigger TinyTerm repo build on Github Actions
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
|
||||
-d '{"ref": "dev"}'
|
||||
@@ -0,0 +1,501 @@
|
||||
name: Linux
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
# Build Linux binaries, ready for release.
|
||||
# This runs inside an Alpine Linux container so we can more easily create a
|
||||
# statically linked binary.
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.20-alpine
|
||||
steps:
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v3
|
||||
# git+openssh: needed for checkout (I think?)
|
||||
# ruby: needed to install fpm
|
||||
run: apk add tar git openssh make g++ ruby
|
||||
- name: Work around CVE-2022-24765
|
||||
# We're not on a multi-user machine, so this is safe.
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-15-linux-alpine-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-15-linux-alpine-v4
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
apk add cmake samurai python3
|
||||
# build!
|
||||
make llvm-build
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-alpine-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
apk add cmake samurai python3
|
||||
make binaryen STATIC=1
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v3
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-alpine-v1
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- name: Install fpm
|
||||
run: |
|
||||
gem install --version 4.0.7 public_suffix
|
||||
gem install --version 2.7.6 dotenv
|
||||
gem install --no-document fpm
|
||||
- name: Build TinyGo release
|
||||
run: |
|
||||
make release deb -j3 STATIC=1
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-amd64.tar.gz
|
||||
/tmp/tinygo_amd64.deb
|
||||
test-linux-build:
|
||||
# Test the binaries built in the build-linux job by running the smoke tests.
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||
curl https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract release tarball
|
||||
run: |
|
||||
mkdir -p ~/lib
|
||||
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||
- run: make tinygo-test-wasi-fast
|
||||
- run: make smoketest
|
||||
assert-test-linux:
|
||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||
# potential bugs.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
echo "Show cpuinfo; sometimes useful when troubleshooting"
|
||||
cat /proc/cpuinfo
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-system-arm \
|
||||
qemu-system-riscv32 \
|
||||
qemu-user \
|
||||
simavr \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-15-linux-asserts-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-15-linux-asserts-v4
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build ASSERT=1
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-asserts-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: make binaryen
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v3
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v5
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- run: make gen-device -j4
|
||||
- name: Test TinyGo
|
||||
run: make ASSERT=1 test
|
||||
- name: Build TinyGo
|
||||
run: |
|
||||
make ASSERT=1
|
||||
echo "$(pwd)/build" >> $GITHUB_PATH
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-baremetal
|
||||
build-linux-arm:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
# an older glibc version and therefore are compatible with a wide range of
|
||||
# Linux distributions.
|
||||
# It is set to "needs: build-linux" because it modifies the release created
|
||||
# in that process to avoid doing lots of duplicate work and to avoid
|
||||
# complications around precompiled libraries such as compiler-rt shipped as
|
||||
# part of the release tarball.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-arm-linux-gnueabihf \
|
||||
libc6-dev-armhf-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-15-linux-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-15-linux-arm-v4
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# Install build dependencies.
|
||||
sudo apt-get install --no-install-recommends ninja-build
|
||||
# build!
|
||||
make llvm-build CROSS=arm-linux-gnueabihf
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-arm-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends ninja-build
|
||||
git submodule update --init lib/binaryen
|
||||
make CROSS=arm-linux-gnueabihf binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo gem install --version 4.0.7 public_suffix
|
||||
sudo gem install --version 2.7.6 dotenv
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo binary
|
||||
run: |
|
||||
make CROSS=arm-linux-gnueabihf
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||
- name: Modify release
|
||||
run: |
|
||||
cp -p build/tinygo build/release/tinygo/bin
|
||||
cp -p build/wasm-opt build/release/tinygo/bin
|
||||
- name: Create arm release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=armhf
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_armhf.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux-arm-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-arm.tar.gz
|
||||
/tmp/tinygo_armhf.deb
|
||||
build-linux-arm64:
|
||||
# Build ARM64 Linux binaries, ready for release.
|
||||
# It is set to "needs: build-linux" because it modifies the release created
|
||||
# in that process to avoid doing lots of duplicate work and to avoid
|
||||
# complications around precompiled libraries such as compiler-rt shipped as
|
||||
# part of the release tarball.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-15-linux-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-15-linux-arm64-v4
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build CROSS=aarch64-linux-gnu
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v3
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-arm64-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git submodule update --init lib/binaryen
|
||||
make CROSS=aarch64-linux-gnu binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo gem install --version 4.0.7 public_suffix
|
||||
sudo gem install --version 2.7.6 dotenv
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo binary
|
||||
run: |
|
||||
make CROSS=aarch64-linux-gnu
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||
- name: Modify release
|
||||
run: |
|
||||
cp -p build/tinygo build/release/tinygo/bin
|
||||
cp -p build/wasm-opt build/release/tinygo/bin
|
||||
- name: Create arm64 release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=arm64
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux-arm64-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-arm64.tar.gz
|
||||
/tmp/tinygo_arm64.deb
|
||||
+153
-34
@@ -7,45 +7,66 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
with:
|
||||
go-version: '1.17'
|
||||
- name: Install QEMU
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
choco install qemu --version=2020.06.12
|
||||
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
|
||||
- name: Install Ninja
|
||||
shell: bash
|
||||
run: |
|
||||
choco install ninja
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-11-windows-v1
|
||||
key: llvm-source-15-windows-v4
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
- name: Save cached LLVM source
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore cached LLVM build
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-11-windows-v2
|
||||
key: llvm-build-15-windows-v5
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -55,31 +76,32 @@ jobs:
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build
|
||||
make llvm-build CCACHE=OFF
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save cached LLVM build
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v1
|
||||
key: wasi-libc-sysroot-v4
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v2
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-v1
|
||||
path: build/binaryen
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: make binaryen
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop install wasmtime
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test
|
||||
run: make test GOTESTFLAGS="-v -short"
|
||||
- name: Build TinyGo release tarball
|
||||
shell: bash
|
||||
run: make build/release -j4
|
||||
- name: Make release artifact
|
||||
shell: bash
|
||||
@@ -92,12 +114,109 @@ jobs:
|
||||
# - have a dobule-zipped artifact when downloaded from the UI
|
||||
# - have a very slow artifact upload
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/release/release.zip
|
||||
|
||||
smoke-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x release.zip -r
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
stdlib-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x release.zip -r
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
stdlib-wasi-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.3
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install binaryen wasmtime
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x release.zip -r
|
||||
- name: Test stdlib packages on wasi
|
||||
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
+6
-1
@@ -20,10 +20,15 @@ src/device/rp/*.s
|
||||
vendor
|
||||
llvm-build
|
||||
llvm-project
|
||||
build/*
|
||||
|
||||
# Ignore files generated by smoketest
|
||||
test
|
||||
test.bin
|
||||
test.elf
|
||||
test.exe
|
||||
test.gba
|
||||
test.hex
|
||||
test.nro
|
||||
test.wasm
|
||||
wasm.wasm
|
||||
wasm.wasm
|
||||
|
||||
+3
-4
@@ -10,10 +10,6 @@
|
||||
[submodule "lib/cmsis-svd"]
|
||||
path = lib/cmsis-svd
|
||||
url = https://github.com/tinygo-org/cmsis-svd
|
||||
[submodule "lib/compiler-rt"]
|
||||
path = lib/compiler-rt
|
||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||
branch = release_80
|
||||
[submodule "lib/wasi-libc"]
|
||||
path = lib/wasi-libc
|
||||
url = https://github.com/CraneStation/wasi-libc
|
||||
@@ -32,3 +28,6 @@
|
||||
[submodule "lib/mingw-w64"]
|
||||
path = lib/mingw-w64
|
||||
url = https://github.com/mingw-w64/mingw-w64.git
|
||||
[submodule "lib/macos-minimal-sdk"]
|
||||
path = lib/macos-minimal-sdk
|
||||
url = https://github.com/aykevl/macos-minimal-sdk.git
|
||||
|
||||
+2
-7
@@ -11,19 +11,14 @@ lld so that the binary can be easily moved between systems. It also shows how to
|
||||
build a release tarball that includes this binary and all necessary extra files.
|
||||
|
||||
**Note**: this documentation describes how to build a statically linked release
|
||||
tarball. If you want to develop TinyGo, you will probably want to follow a
|
||||
different guide:
|
||||
|
||||
* [Linux](https://tinygo.org/getting-started/linux/#source-install)
|
||||
* [macOS](https://tinygo.org/getting-started/macos/#source-install)
|
||||
* [Windows](https://tinygo.org/getting-started/windows/#source-install)
|
||||
tarball. If you want to help with development of TinyGo itself, you should follow the guide located at https://tinygo.org/docs/guides/build/
|
||||
|
||||
## Dependencies
|
||||
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.15+)
|
||||
* Go (1.18+)
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
|
||||
+585
@@ -1,3 +1,588 @@
|
||||
0.27.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- all: update musl
|
||||
- all: remove "acm:"` prefix for USB vid/pid pair
|
||||
- all: add support for LLVM 15
|
||||
- all: use DWARF version 4
|
||||
- all: add initial (incomplete) support for Go 1.20
|
||||
- all: add `-gc=custom` option
|
||||
- `main`: print ldflags including ThinLTO flags with -x
|
||||
- `main`: fix error message when a serial port can't be accessed
|
||||
- `main`: add `-timeout` flag to allow setting how long TinyGo will try looking for a MSD volume for flashing
|
||||
- `test`: print PASS on pass when running standalone test binaries
|
||||
- `test`: fix printing of benchmark output
|
||||
- `test`: print package name when compilation failed (not just when the test failed)
|
||||
* **compiler**
|
||||
- refactor to support LLVM 15
|
||||
- `builder`: print compiler commands while building a library
|
||||
- `compiler`: fix stack overflow when creating recursive pointer types (fix for LLVM 15+ only)
|
||||
- `compiler`: allow map keys and values of ≥256 bytes
|
||||
- `cgo`: add support for `C.float` and `C.double`
|
||||
- `cgo`: support anonymous enums included in multiple Go files
|
||||
- `cgo`: add support for bitwise operators
|
||||
- `interp`: add support for constant icmp instructions
|
||||
- `transform`: fix memory corruption issues
|
||||
* **standard library**
|
||||
- `machine/usb`: remove allocs in USB ISR
|
||||
- `machine/usb`: add `Port()` and deprecate `New()` to have the API better match the singleton that is actually being returned
|
||||
- `machine/usb`: change HID usage-maximum to 0xFF
|
||||
- `machine/usb`: add USB HID joystick support
|
||||
- `machine/usb`: change to not send before endpoint initialization
|
||||
- `net`: implement `Pipe`
|
||||
- `os`: add stub for `os.Chtimes`
|
||||
- `reflect`: stub out `Type.FieldByIndex`
|
||||
- `reflect`: add `Value.IsZero` method
|
||||
- `reflect`: fix bug in `.Field` method when the field fits in a pointer but the parent doesn't
|
||||
- `runtime`: switch some `panic()` calls in the gc to `runtimePanic()` for consistency
|
||||
- `runtime`: add xorshift-based fastrand64
|
||||
- `runtime`: fix alignment for arm64, arm, xtensa, riscv
|
||||
- `runtime`: implement precise GC
|
||||
- `runtime/debug`: stub `PrintStack`
|
||||
- `sync`: implement simple pooling in `sync.Pool`
|
||||
- `syscall`: stubbed `Setuid`, Exec and friends
|
||||
- `syscall`: add more stubs as needed for Go 1.20 support
|
||||
- `testing`: implement `t.Setenv`
|
||||
- `unsafe`: add support for Go 1.20 slice/string functions
|
||||
* **targets**
|
||||
- `all`: do not set stack size per board
|
||||
- `all`: update picolibc to v1.7.9
|
||||
- `atsame5x`: fix CAN extendedID handling
|
||||
- `atsame5x`: reduce heap allocation
|
||||
- `avr`: drop GNU toolchain dependency
|
||||
- `avr`: fix .data initialization for binaries over 64kB
|
||||
- `avr`: support ThinLTO
|
||||
- `baremetal`: implements calloc
|
||||
- `darwin`: fix `syscall.Open` on darwin/arm64
|
||||
- `darwin`: fix error with `tinygo lldb`
|
||||
- `esp`: use LLVM Xtensa linker instead of Espressif toolchain
|
||||
- `esp`: use ThinLTO for Xtensa
|
||||
- `esp32c3`: add SPI support
|
||||
- `linux`: include musl `getpagesize` function in release
|
||||
- `nrf51`: add ADC implementation
|
||||
- `nrf52840`: add PDM support
|
||||
- `riscv`: add "target-abi" metadata flag
|
||||
- `rp2040`: remove mem allocation in GPIO ISR
|
||||
- `rp2040`: avoid allocating clock on heap
|
||||
- `rp2040`: add basic GPIO support for PIO
|
||||
- `rp2040`: fix USB interrupt issue
|
||||
- `rp2040`: fix RP2040-E5 USB errata
|
||||
- `stm32`: always set ADC pins to pullups floating
|
||||
- `stm32f1`, `stm32f4`: fix ADC by clearing the correct bit for rank after each read
|
||||
- `stm32wl`: Fix incomplete RNG initialisation
|
||||
- `stm32wlx`: change order for init so clock speeds are set before peripheral start
|
||||
- `wasi`: makes wasmtime "run" explicit
|
||||
- `wasm`: fix GC scanning of allocas
|
||||
- `wasm`: allow custom malloc implementation
|
||||
- `wasm`: remove `-wasm-abi=` flag (use `-target` instead)
|
||||
- `wasm`: fix scanning of the stack
|
||||
- `wasm`: fix panic when allocating 0 bytes using malloc
|
||||
- `wasm`: always run wasm-opt even with `-scheduler=none`
|
||||
- `wasm`: avoid miscompile with ThinLTO
|
||||
- `wasm`: allow the emulator to expand `{tmpDir}`
|
||||
- `wasm`: support ThinLTO
|
||||
- `windows`: update mingw-w64 version to avoid linker warning
|
||||
- `windows`: add ARM64 support
|
||||
* **boards**
|
||||
- Add Waveshare RP2040 Zero
|
||||
- Add Arduino Leonardo support
|
||||
- Add Adafruit KB2040
|
||||
- Add Adafruit Feather M0 Express
|
||||
- Add Makerfabs ESP32C3SPI35 TFT Touchscreen board
|
||||
- Add Espressif ESP32-C3-DevKit-RUST-1 board
|
||||
- `lgt92`: fix OpenOCD configuration
|
||||
- `xiao-rp2040`: fix D9 and D10 constants
|
||||
- `xiao-rp2040`: add pin definitions
|
||||
|
||||
0.26.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- remove support for LLVM 13
|
||||
- remove calls to deprecated ioutil package
|
||||
- move from `os.IsFoo` to `errors.Is(err, ErrFoo)`
|
||||
- fix for builds using an Android host
|
||||
- make interp timeout configurable from command line
|
||||
- ignore ports with VID/PID if there is no candidates
|
||||
- drop support for Go 1.16 and Go 1.17
|
||||
- update serial package to v1.3.5 for latest bugfixes
|
||||
- remove GOARM from `tinygo info`
|
||||
- add flag for setting the goroutine stack size
|
||||
- add serial port monitoring functionality
|
||||
* **compiler**
|
||||
- `cgo`: implement support for static functions
|
||||
- `cgo`: fix panic when FuncType.Results is nil
|
||||
- `compiler`: add aliases for `edwards25519/field.feMul` and `field.feSquare`
|
||||
- `compiler`: fix incorrect DWARF type in some generic parameters
|
||||
- `compiler`: use LLVM math builtins everywhere
|
||||
- `compiler`: replace some math operation bodies with LLVM intrinsics
|
||||
- `compiler`: replace math aliases with intrinsics
|
||||
- `compiler`: fix `unsafe.Sizeof` for chan and map values
|
||||
- `compileopts`: use tags parser from buildutil
|
||||
- `compileopts`: use backticks for regexp to avoid extra escapes
|
||||
- `compileopts`: fail fast on duplicate values in target field slices
|
||||
- `compileopts`: fix windows/arm target triple
|
||||
- `compileopts`: improve error handling when loading target/*.json
|
||||
- `compileopts`: add support for stlink-dap programmer
|
||||
- `compileopts`: do not complain about `-no-debug` on MacOS
|
||||
- `goenv`: support `GOOS=android`
|
||||
- `interp`: fix reading from external global
|
||||
- `loader`: fix link error for `crypto/internal/boring/sig.StandardCrypto`
|
||||
* **standard library**
|
||||
- rename assembly files to .S extension
|
||||
- `machine`: add PWM peripheral comments to pins
|
||||
- `machine`: improve UARTParity slightly
|
||||
- `machine`: do not export DFU_MAGIC_* constants on nrf52840
|
||||
- `machine`: rename `PinInputPullUp`/`PinInputPullDown`
|
||||
- `machine`: add `KHz`, `MHz`, `GHz` constants, deprecate `TWI_FREQ_*` constants
|
||||
- `machine`: remove level triggered pin interrupts
|
||||
- `machine`: do not expose `RESET_MAGIC_VALUE`
|
||||
- `machine`: use `NoPin` constant where appropriate (instead of `0` for example)
|
||||
- `net`: sync net.go with Go 1.18 stdlib
|
||||
- `os`: add `SyscallError.Timeout`
|
||||
- `os`: add `ErrProcessDone` error
|
||||
- `reflect`: implement `CanInterface` and fix string `Index`
|
||||
- `runtime`: make `MemStats` available to leaking collector
|
||||
- `runtime`: add `MemStats.TotalAlloc`
|
||||
- `runtime`: add `MemStats.Mallocs` and `Frees`
|
||||
- `runtime`: add support for `time.NewTimer` and `time.NewTicker`
|
||||
- `runtime`: implement `resetTimer`
|
||||
- `runtime`: ensure some headroom for the GC to run
|
||||
- `runtime`: make gc and scheduler asserts settable with build tags
|
||||
- `runtime/pprof`: add `WriteHeapProfile`
|
||||
- `runtime/pprof`: `runtime/trace`: stub some additional functions
|
||||
- `sync`: implement `Map.LoadAndDelete`
|
||||
- `syscall`: group WASI consts by purpose
|
||||
- `syscall`: add WASI `{D,R}SYNC`, `NONBLOCK` FD flags
|
||||
- `syscall`: add ENOTCONN on darwin
|
||||
- `testing`: add support for -benchmem
|
||||
* **targets**
|
||||
- remove USB vid/pid pair of bootloader
|
||||
- `esp32c3`: remove unused `UARTStopBits` constants
|
||||
- `nrf`: implement `GetRNG` function
|
||||
- `nrf`: `rp2040`: add `machine.ReadTemperature`
|
||||
- `nrf52`: cleanup s140v6 and s140v7 uf2 targets
|
||||
- `rp2040`: implement semi-random RNG based on ROSC based on pico-sdk
|
||||
- `wasm`: add summary of wasm examples and fix callback bug
|
||||
- `wasm`: do not allow undefined symbols (`--allow-undefined`)
|
||||
- `wasm`: make sure buffers returned by `malloc` are kept until `free` is called
|
||||
- `windows`: save and restore xmm registers when switching goroutines
|
||||
* **boards**
|
||||
- add Pimoroni's Tufty2040
|
||||
- add XIAO ESP32C3
|
||||
- add Adafruit QT2040
|
||||
- add Adafruit QT Py RP2040
|
||||
- `esp32c3-12f`: `matrixportal-m4`: `p1am-100`: remove duplicate build tags
|
||||
- `hifive1-qemu`: remove this emulated board
|
||||
- `wioterminal`: add UART3 for RTL8720DN
|
||||
- `xiao-ble`: fix usbpid
|
||||
|
||||
|
||||
0.25.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- change to ignore PortReset failures
|
||||
* **compiler**
|
||||
- `compiler`: darwin/arm64 is aarch64, not arm
|
||||
- `compiler`: don't clobber X18 and FP registers on darwin/arm64
|
||||
- `compiler`: fix issue with methods on generic structs
|
||||
- `compiler`: do not try to build generic functions
|
||||
- `compiler`: fix type names for generic named structs
|
||||
- `compiler`: fix multiple defined function issue for generic functions
|
||||
- `compiler`: implement `unsafe.Alignof` and `unsafe.Sizeof` for generic code
|
||||
* **standard library**
|
||||
- `machine`: add DTR and RTS to Serialer interface
|
||||
- `machine`: reorder pin definitions to improve pin list on tinygo.org
|
||||
- `machine/usb`: add support for MIDI
|
||||
- `machine/usb`: adjust buffer alignment (samd21, samd51, nrf52840)
|
||||
- `machine/usb/midi`: add `NoteOn`, `NoteOff`, and `SendCC` methods
|
||||
- `machine/usb/midi`: add definition of MIDI note number
|
||||
- `runtime`: add benchmarks for memhash
|
||||
- `runtime`: add support for printing slices via print/println
|
||||
* **targets**
|
||||
- `avr`: fix some apparent mistake in atmega1280/atmega2560 pin constants
|
||||
- `esp32`: provide hardware pin constants
|
||||
- `esp32`: fix WDT reset on the MCH2022 badge
|
||||
- `esp32`: optimize SPI transmit
|
||||
- `esp32c3`: provide hardware pin constants
|
||||
- `esp8266`: provide hardware pin constants like `GPIO2`
|
||||
- `nrf51`: define and use `P0_xx` constants
|
||||
- `nrf52840`, `samd21`, `samd51`: unify bootloader entry process
|
||||
- `nrf52840`, `samd21`, `samd51`: change usbSetup and sendZlp to public
|
||||
- `nrf52840`, `samd21`, `samd51`: refactor handleStandardSetup and initEndpoint
|
||||
- `nrf52840`, `samd21`, `samd51`: improve usb-device initialization
|
||||
- `nrf52840`, `samd21`, `samd51`: move usbcdc to machine/usb/cdc
|
||||
- `rp2040`: add usb serial vendor/product ID
|
||||
- `rp2040`: add support for usb
|
||||
- `rp2040`: change default for serial to usb
|
||||
- `rp2040`: add support for `machine.EnterBootloader`
|
||||
- `rp2040`: turn off pullup/down when input type is not specified
|
||||
- `rp2040`: make picoprobe default openocd interface
|
||||
- `samd51`: add support for `DAC1`
|
||||
- `samd51`: improve TRNG
|
||||
- `wasm`: stub `runtime.buffered`, `runtime.getchar`
|
||||
- `wasi`: make leveldb runtime hash the default
|
||||
* **boards**
|
||||
- add Challenger RP2040 LoRa
|
||||
- add MCH2022 badge
|
||||
- add XIAO RP2040
|
||||
- `clue`: remove pins `D21`..`D28`
|
||||
- `feather-rp2040`, `macropad-rp2040`: fix qspi-flash settings
|
||||
- `xiao-ble`: add support for flash-1200-bps-reset
|
||||
- `gopherbot`, `gopherbot2`: add these aliases to simplify for newer users
|
||||
|
||||
|
||||
0.24.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- remove support for go 1.15
|
||||
- remove support for LLVM 11 and LLVM 12
|
||||
- add initial Go 1.19 beta support
|
||||
- `test`: fix package/... syntax
|
||||
* **compiler**
|
||||
- add support for the embed package
|
||||
- `builder`: improve error message for "command not found"
|
||||
- `builder`: add support for ThinLTO on MacOS and Windows
|
||||
- `builder`: free LLVM objects after use, to reduce memory leaking
|
||||
- `builder`: improve `-no-debug` error messages
|
||||
- `cgo`: be more strict: CGo now requires every Go file to import the headers it needs
|
||||
- `compiler`: alignof(func) is 1 pointer, not 2
|
||||
- `compiler`: add support for type parameters (aka generics)
|
||||
- `compiler`: implement `recover()` built-in function
|
||||
- `compiler`: support atomic, volatile, and LLVM memcpy-like functions in defer
|
||||
- `compiler`: drop support for macos syscalls via inline assembly
|
||||
- `interp`: do not try to interpret past task.Pause()
|
||||
- `interp`: fix some buggy localValue handling
|
||||
- `interp`: do not unroll loops
|
||||
- `transform`: fix MakeGCStackSlots that caused a possible GC bug on WebAssembly
|
||||
* **standard library**
|
||||
- `os`: enable os.Stdin for baremetal target
|
||||
- `reflect`: add `Value.UnsafePointer` method
|
||||
- `runtime`: scan GC globals conservatively on Windows, MacOS, Linux and Nintendo Switch
|
||||
- `runtime`: add per-map hash seeds
|
||||
- `runtime`: handle nil map write panics
|
||||
- `runtime`: add stronger hash functions
|
||||
- `syscall`: implement `Getpagesize`
|
||||
* **targets**
|
||||
- `atmega2560`: support UART1-3 + example for uart
|
||||
- `avr`: use compiler-rt for improved float64 support
|
||||
- `avr`: simplify timer-based time
|
||||
- `avr`: fix race condition in stack write
|
||||
- `darwin`: add support for `GOARCH=arm64` (aka Apple Silicon)
|
||||
- `darwin`: support `-size=short` and `-size=full` flag
|
||||
- `rp2040`: replace sleep 'busy loop' with timer alarm
|
||||
- `rp2040`: align api for `PortMaskSet`, `PortMaskClear`
|
||||
- `rp2040`: fix GPIO interrupts
|
||||
- `samd21`, `samd51`, `nrf52840`: add support for USBHID (keyboard / mouse)
|
||||
- `wasm`: update wasi-libc version
|
||||
- `wasm`: use newer WebAssembly features
|
||||
* **boards**
|
||||
- add Badger 2040
|
||||
- `matrixportal-m4`: attach USB DP to the correct pin
|
||||
- `teensy40`: add I2C support
|
||||
- `wioterminal`: fix I2C definition
|
||||
|
||||
|
||||
0.23.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- add `-work` flag
|
||||
- add Go 1.18 support
|
||||
- add LLVM 14 support
|
||||
- `run`: add support for command-line parameters
|
||||
- `build`: calculate default output path if `-o` is not specified
|
||||
- `build`: add JSON output
|
||||
- `test`: support multiple test binaries with `-c`
|
||||
- `test`: support flags like `-v` on all targets (including emulated firmware)
|
||||
* **compiler**
|
||||
- add support for ThinLTO
|
||||
- use compiler-rt from LLVM
|
||||
- `builder`: prefer GNU build ID over Go build ID for caching
|
||||
- `builder`: add support for cross compiling to Darwin
|
||||
- `builder`: support machine outlining pass in stacksize calculation
|
||||
- `builder`: disable asynchronous unwind tables
|
||||
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
|
||||
- `compiler`: move allocations > 256 bytes to the heap
|
||||
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
|
||||
- `compiler`: accept alias for slice `cap` builtin
|
||||
- `compiler`: allow slices of empty structs
|
||||
- `compiler`: fix difference in aliases in interface methods
|
||||
- `compiler`: make `RawSyscall` an alias for `Syscall`
|
||||
- `compiler`: remove support for memory references in `AsmFull`
|
||||
- `loader`: only add Clang header path for CGo
|
||||
- `transform`: fix poison value in heap-to-stack transform
|
||||
* **standard library**
|
||||
- `internal/fuzz`: add this package as a shim
|
||||
- `os`: implement readdir for darwin and linux
|
||||
- `os`: add `DirFS`, which is used by many programs to access readdir.
|
||||
- `os`: isWine: be compatible with older versions of wine, too
|
||||
- `os`: implement `RemoveAll`
|
||||
- `os`: Use a `uintptr` for `NewFile`
|
||||
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
|
||||
- `os`: export correct values for `DevNull` for each OS
|
||||
- `os`: improve support for `Signal` by fixing various bugs
|
||||
- `os`: implement `File.Fd` method
|
||||
- `os`: implement `UserHomeDir`
|
||||
- `os`: add `exec.ProcessState` stub
|
||||
- `os`: implement `Pipe` for darwin
|
||||
- `os`: define stub `ErrDeadlineExceeded`
|
||||
- `reflect`: add stubs for more missing methods
|
||||
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
|
||||
- `reflect`: add `Value.FieldByIndexErr` stub
|
||||
- `runtime`: fix various small GC bugs
|
||||
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
|
||||
- `runtime`: implement `memhash`
|
||||
- `runtime`: implement `fastrand`
|
||||
- `runtime`: add stub for `debug.ReadBuildInfo`
|
||||
- `runtime`: add stub for `NumCPU`
|
||||
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
|
||||
- `runtime`: add `Version`
|
||||
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
|
||||
- `runtime`: stub {Lock,Unlock}OSThread on Windows
|
||||
- `runtime`: be able to deal with a very small heap
|
||||
- `syscall`: make `Environ` return a copy of the environment
|
||||
- `syscall`: implement getpagesize and munmap
|
||||
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
|
||||
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
|
||||
- `syscall`: darwin: more complete list of signals
|
||||
- `syscall`: `wasi`: more complete list of signals
|
||||
- `syscall`: stub `WaitStatus`
|
||||
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
|
||||
- `testing`: implement `TempDir`
|
||||
- `testing`: nudge type TB closer to upstream; should be a no-op change.
|
||||
- `testing`: on baremetal platforms, use simpler test matcher
|
||||
* **targets**
|
||||
- `atsamd`: fix usbcdc initialization when `-serial=uart`
|
||||
- `atsamd51`: allow higher frequency when using SPI
|
||||
- `esp`: support CGo
|
||||
- `esp32c3`: add support for input pin
|
||||
- `esp32c3`: add support for GPIO interrupts
|
||||
- `esp32c3`: add support to receive UART data
|
||||
- `rp2040`: fix PWM bug at high frequency
|
||||
- `rp2040`: fix some minor I2C bugs
|
||||
- `rp2040`: fix incorrect inline assembly
|
||||
- `rp2040`: fix spurious i2c STOP during write+read transaction
|
||||
- `rp2040`: improve ADC support
|
||||
- `wasi`: remove `--export-dynamic` linker flag
|
||||
- `wasm`: remove heap allocator from wasi-libc
|
||||
* **boards**
|
||||
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
|
||||
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
|
||||
- `m5stamp-c3`: add pin setting of UART
|
||||
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
|
||||
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
|
||||
- `teensy41`: add the Teensy 4.1 board
|
||||
- `teensy40`: add ADC support
|
||||
- `teensy40`: add SPI support
|
||||
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
|
||||
- `wioterminal`: add DefaultUART
|
||||
- `wioterminal`: verify written data when flashing through OpenOCD
|
||||
- `xiao-ble`: add XIAO BLE nRF52840 support
|
||||
|
||||
|
||||
0.22.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- add asyncify to scheduler flag help
|
||||
- support -run for tests
|
||||
- remove FreeBSD target support
|
||||
- add LLVM 12 and LLVM 13 support, use LLVM 13 by default
|
||||
- add support for ARM64 MacOS
|
||||
- improve help
|
||||
- check /run/media as well as /media on Linux for non-debian-based distros
|
||||
- `test`: set cmd.Dir even when running emulators
|
||||
- `info`: add JSON output using the `-json` flag
|
||||
* **compiler**
|
||||
- `builder`: fix off-by-one in size calculation
|
||||
- `builder`: handle concurrent library header rename
|
||||
- `builder`: use flock to avoid double-compiles
|
||||
- `builder`: use build ID as cache key
|
||||
- `builder`: add -fno-stack-protector to musl build
|
||||
- `builder`: update clang header search path to look in /usr/lib
|
||||
- `builder`: explicitly disable unwind tables for ARM
|
||||
- `cgo`: add support for `C.CString` and related functions
|
||||
- `compiler`: fix ranging over maps with particular map types
|
||||
- `compiler`: add correct debug location to init instructions
|
||||
- `compiler`: fix emission of large object layouts
|
||||
- `compiler`: work around AVR atomics bugs
|
||||
- `compiler`: predeclare runtime.trackPointer
|
||||
- `interp`: work around AVR function pointers in globals
|
||||
- `interp`: run goroutine starts and checks at runtime
|
||||
- `interp`: always run atomic and volatile loads/stores at runtime
|
||||
- `interp`: bump timeout to 180 seconds
|
||||
- `interp`: handle type assertions on nil interfaces
|
||||
- `loader`: elminate goroot cache inconsistency
|
||||
- `loader`: respect $GOROOT when running `go list`
|
||||
- `transform`: allocate the correct amount of bytes in an alloca
|
||||
- `transform`: remove switched func lowering
|
||||
* **standard library**
|
||||
- `crypto/rand`: show error if platform has no rng
|
||||
- `device/*`: add `*_Msk` field for each bit field and avoid duplicates
|
||||
- `device/*`: provide Set/Get for each register field described in the SVD files
|
||||
- `internal/task`: swap stack chain when switching goroutines
|
||||
- `internal/task`: remove `-scheduler=coroutines`
|
||||
- `machine`: add `Device` string constant
|
||||
- `net`: add bare Interface implementation
|
||||
- `net`: add net.Buffers
|
||||
- `os`: stub out support for some features
|
||||
- `os`: obey TMPDIR on unix, TMP on Windows, etc
|
||||
- `os`: implement `ReadAt`, `Mkdir`, `Remove`, `Stat`, `Lstat`, `CreateTemp`, `MkdirAll`, `Chdir`, `Chmod`, `Clearenv`, `Unsetenv`, `Setenv`, `MkdirTemp`, `Rename`, `Seek`, `ExpandEnv`, `Symlink`, `Readlink`
|
||||
- `os`: implement `File.Stat`
|
||||
- `os`: fix `IsNotExist` on nonexistent path
|
||||
- `os`: fix opening files on WASI in read-only mode
|
||||
- `os`: work around lack of `syscall.seek` on 386 and arm
|
||||
- `reflect`: make sure indirect pointers are handled correctly
|
||||
- `runtime`: allow comparing interfaces
|
||||
- `runtime`: use LLVM intrinsic to read the stack pointer
|
||||
- `runtime`: strengthen hashmap hash function for structs and arrays
|
||||
- `runtime`: fix float/complex hashing
|
||||
- `runtime`: fix nil map dereference
|
||||
- `runtime`: add realloc implementation to GCs
|
||||
- `runtime`: handle negative sleep times
|
||||
- `runtime`: correct GC scan bounds
|
||||
- `runtime`: remove extalloc GC
|
||||
- `rumtime`: implement `__sync` libcalls as critical sections for most microcontrollers
|
||||
- `runtime`: add stubs for `Func.FileLine` and `Frame.PC`
|
||||
- `sync`: fix concurrent read-lock on write-locked RWMutex
|
||||
- `sync`: add a package doc
|
||||
- `sync`: add tests
|
||||
- `syscall`: add support for `Mmap` and `Mprotect`
|
||||
- `syscall`: fix array size for mmap slice creation
|
||||
- `syscall`: enable `Getwd` in wasi
|
||||
- `testing`: add a stub for `CoverMode`
|
||||
- `testing`: support -bench option to run benchmarks matching the given pattern.
|
||||
- `testing`: support b.SetBytes(); implement sub-benchmarks.
|
||||
- `testing`: replace spaces with underscores in test/benchmark names, as upstream does
|
||||
- `testing`: implement testing.Cleanup
|
||||
- `testing`: allow filtering subbenchmarks with the `-bench` flag
|
||||
- `testing`: implement `-benchtime` flag
|
||||
- `testing`: print duration
|
||||
- `testing`: allow filtering of subtests using `-run`
|
||||
* **targets**
|
||||
- `all`: change LLVM features to match vanilla Clang
|
||||
- `avr`: use interrupt-based timer which is much more accurate
|
||||
- `nrf`: fix races in I2C
|
||||
- `samd51`: implement TRNG for randomness
|
||||
- `stm32`: pull-up on I2C lines
|
||||
- `stm32`: fix timeout for i2c comms
|
||||
- `stm32f4`, `stm32f103`: initial implementation for ADC
|
||||
- `stm32f4`, `stm32f7`, `stm32l0x2`, `stm32l4`, `stm32l5`, `stm32wl`: TRNG implementation in crypto/rand
|
||||
- `stm32wl`: add I2C support
|
||||
- `windows`: add support for the `-size=` flag
|
||||
- `wasm`: add support for `tinygo test`
|
||||
- `wasi`, `wasm`: raise default stack size to 16 KiB
|
||||
* **boards**
|
||||
- add M5Stack
|
||||
- add lorae5 (stm32wle) support
|
||||
- add Generic Node Sensor Edition
|
||||
- add STM32F469 Discovery
|
||||
- add M5Stamp C3
|
||||
- add Blues Wireless Swan
|
||||
- `bluepill`: add definitions for ADC pins
|
||||
- `stm32f4disco`: add definitions for ADC pins
|
||||
- `stm32l552ze`: use supported stlink interface
|
||||
- `microbit-v2`: add some pin definitions
|
||||
|
||||
|
||||
0.21.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- drop support for LLVM 10
|
||||
- `build`: drop support for LLVM targets in the -target flag
|
||||
- `build`: fix paths in error messages on Windows
|
||||
- `build`: add -p flag to set parallelism
|
||||
- `lldb`: implement `tinygo lldb` subcommand
|
||||
- `test`: use emulator exit code instead of parsing test output
|
||||
- `test`: pass testing arguments to wasmtime
|
||||
* **compiler**
|
||||
- use -opt flag for optimization level in CFlags (-Os, etc)
|
||||
- `builder`: improve accuracy of the -size=full flag
|
||||
- `builder`: hardcode some more frame sizes for __aeabi_* functions
|
||||
- `builder`: add support for -size= flag for WebAssembly
|
||||
- `cgo`: fix line/column reporting in syntax error messages
|
||||
- `cgo`: support function definitions in CGo headers
|
||||
- `cgo`: implement rudimentary C array decaying
|
||||
- `cgo`: add support for stdio in picolibc and wasi-libc
|
||||
- `cgo`: run CGo parser per file, not per CGo fragment
|
||||
- `compiler`: fix unintentionally exported math functions
|
||||
- `compiler`: properly implement div and rem operations
|
||||
- `compiler`: add support for recursive function types
|
||||
- `compiler`: add support for the `go` keyword on interface methods
|
||||
- `compiler`: add minsize attribute for -Oz
|
||||
- `compiler`: add "target-cpu" and "target-features" attributes
|
||||
- `compiler`: fix indices into strings and arrays
|
||||
- `compiler`: fix string compare functions
|
||||
- `interp`: simplify some code to avoid some errors
|
||||
- `interp`: support recursive globals (like linked lists) in globals
|
||||
- `interp`: support constant globals
|
||||
- `interp`: fix reverting of extractvalue/insertvalue with multiple indices
|
||||
- `transform`: work around renamed return type after merging LLVM modules
|
||||
* **standard library**
|
||||
- `internal/bytealg`: fix indexing error in Compare()
|
||||
- `machine`: support Pin.Get() function when the pin is configured as output
|
||||
- `net`, `syscall`: Reduce code duplication by switching to internal/itoa.
|
||||
- `os`: don't try to read executable path on baremetal
|
||||
- `os`: implement Getwd
|
||||
- `os`: add File.WriteString and File.WriteAt
|
||||
- `reflect`: fix type.Size() to account for struct padding
|
||||
- `reflect`: don't construct an interface-in-interface value
|
||||
- `reflect`: implement Value.Elem() for interface values
|
||||
- `reflect`: fix Value.Index() in a specific case
|
||||
- `reflect`: add support for DeepEqual
|
||||
- `runtime`: add another set of invalid unicode runes to encodeUTF8()
|
||||
- `runtime`: only initialize os.runtime_args when needed
|
||||
- `runtime`: only use CRLF on baremetal systems for println
|
||||
- `runtime/debug`: stub `debug.SetMaxStack`
|
||||
- `runtime/debug`: stub `debug.Stack`
|
||||
- `testing`: add a stub for t.Parallel()
|
||||
- `testing`: add support for -test.short flag
|
||||
- `testing`: stub B.ReportAllocs()
|
||||
- `testing`: add `testing.Verbose`
|
||||
- `testing`: stub `testing.AllocsPerRun`
|
||||
* **targets**
|
||||
- fix gen-device-svd to handle 64-bit values
|
||||
- add CPU and Features property to all targets
|
||||
- match LLVM triple to the one Clang uses
|
||||
- `atsam`: simplify definition of SERCOM UART, I2C and SPI peripherals
|
||||
- `atsam`: move I2S0 to machine file
|
||||
- `esp32`: fix SPI configuration
|
||||
- `esp32c3`: add support for GDB debugging
|
||||
- `esp32c3`: add support for CPU interrupts
|
||||
- `esp32c3`: use tasks scheduler by default
|
||||
- `fe310`: increase CPU frequency from 16MHz to 320MHz
|
||||
- `fe310`: add support for bit banging drivers
|
||||
- `linux`: build static binaries using musl
|
||||
- `linux`: reduce binary size by calling `write` instead of `putchar`
|
||||
- `linux`: add support for GOARM
|
||||
- `riscv`: implement 32-bit atomic operations
|
||||
- `riscv`: align the heap to 16 bytes
|
||||
- `riscv`: switch to tasks-based scheduler
|
||||
- `rp2040`: add CPUFrequency()
|
||||
- `rp2040`: improve I2C baud rate configuration
|
||||
- `rp2040`: add pin interrupt API
|
||||
- `rp2040`: refactor PWM code and fix Period calculation
|
||||
- `stm32f103`: fix SPI
|
||||
- `stm32f103`: make SPI frequency selection more flexible
|
||||
- `qemu`: signal correct exit code to QEMU
|
||||
- `wasi`: run C/C++ constructors at startup
|
||||
- `wasm`: ensure heapptr is aligned
|
||||
- `wasm`: update wasi-libc dependency
|
||||
- `wasm`: wasi: use asyncify
|
||||
- `wasm`: support `-scheduler=none`
|
||||
- `windows`: add support for Windows (amd64 only for now)
|
||||
* **boards**
|
||||
- `feather-stm32f405`, `feather-rp2040`: add I2C pin names
|
||||
- `m5stack-core2`: add M5Stack Core2
|
||||
- `nano-33-ble`: SoftDevice s140v7 support
|
||||
- `nano-33-ble`: add constants for more on-board pins
|
||||
|
||||
|
||||
0.20.0
|
||||
---
|
||||
|
||||
|
||||
+1
-52
@@ -1,52 +1 @@
|
||||
# How to contribute
|
||||
|
||||
Thank you for your interest in improving TinyGo.
|
||||
|
||||
We would like your help to make this project better, so we appreciate any contributions. See if one of the following descriptions matches your situation:
|
||||
|
||||
### New to TinyGo
|
||||
|
||||
We'd love to get your feedback on getting started with TinyGo. Run into any difficulty, confusion, or anything else? You are not alone. We want to know about your experience, so we can help the next people. Please open a Github issue with your questions, or you can also get in touch directly with us on our Slack channel at [https://gophers.slack.com/messages/CDJD3SUP6](https://gophers.slack.com/messages/CDJD3SUP6).
|
||||
|
||||
### Something in TinyGo is not working as you expect
|
||||
|
||||
Please open a Github issue with your problem, and we will be happy to assist.
|
||||
|
||||
### Something in Go that you want/need does not appear to be in TinyGo
|
||||
|
||||
We probably have not implemented it yet. Please take a look at our [Roadmap](https://github.com/tinygo-org/tinygo/wiki/Roadmap). Your pull request adding the functionality to TinyGo would be greatly appreciated.
|
||||
|
||||
Please open a Github issue. We want to help, and also make sure that there is no duplications of efforts. Sometimes what you need is already being worked on by someone else.
|
||||
|
||||
A long tail of small (and large) language features haven't been implemented yet. In almost all cases, the compiler will show a `todo:` error from `compiler/compiler.go` when you try to use it. You can try implementing it, or open a bug report with a small code sample that fails to compile.
|
||||
|
||||
### Some specific hardware you want to use does not appear to be in TinyGo
|
||||
|
||||
As above, we probably have not implemented it yet. Your contribution adding the hardware support to TinyGo would be greatly appreciated.
|
||||
|
||||
Please start by opening a Github issue. We want to help you to help us to help you.
|
||||
|
||||
Lots of targets/boards are still unsupported. Adding an architecture often requires a few compiler changes, but if the architecture is supported you can try implementing support for a new chip or board in `src/runtime`. For details, see [this wiki entry on adding archs/chips/boards](https://github.com/tinygo-org/tinygo/wiki/Adding-a-new-board).
|
||||
|
||||
Microcontrollers have lots of peripherals (I2C, SPI, ADC, etc.) and many don't have an implementation yet in the `machine` package. Adding support for new peripherals is very useful.
|
||||
|
||||
## How to use our Github repository
|
||||
|
||||
The `release` branch of this repo will always have the latest released version of TinyGo. All of the active development work for the next release will take place in the `dev` branch. TinyGo will use semantic versioning and will create a tag/release for each release.
|
||||
|
||||
Here is how to contribute back some code or documentation:
|
||||
|
||||
- Fork repo
|
||||
- Create a feature branch off of the `dev` branch
|
||||
- Make some useful change
|
||||
- Make sure the tests still pass
|
||||
- Submit a pull request against the `dev` branch.
|
||||
- Be kind
|
||||
|
||||
## How to run tests
|
||||
|
||||
To run the tests:
|
||||
|
||||
```
|
||||
make test
|
||||
```
|
||||
Please take a look at our [Contributing](https://tinygo.org/docs/guides/contributing/) page on our web site for details. Thank you.
|
||||
|
||||
+28
-68
@@ -1,80 +1,40 @@
|
||||
# TinyGo base stage installs the most recent Go 1.17.x, LLVM 11 and the TinyGo compiler itself.
|
||||
FROM golang:1.17 AS tinygo-base
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.20 AS tinygo-llvm
|
||||
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-11 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-11 ninja-build
|
||||
|
||||
COPY ./Makefile /tinygo/Makefile
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make llvm-source
|
||||
|
||||
# tinygo-llvm-build stage build the custom llvm with xtensa support
|
||||
FROM tinygo-llvm AS tinygo-llvm-build
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make llvm-build
|
||||
|
||||
# tinygo-compiler stage builds the compiler itself
|
||||
FROM tinygo-llvm-build AS tinygo-compiler
|
||||
|
||||
COPY . /tinygo
|
||||
|
||||
# remove submodules directories and re-init them to fix any hard-coded paths
|
||||
# after copying the tinygo directory in the previous step.
|
||||
# update submodules
|
||||
RUN cd /tinygo/ && \
|
||||
rm -rf ./lib/* && \
|
||||
rm -rf ./lib/*/ && \
|
||||
git submodule sync && \
|
||||
git submodule update --init --recursive --force
|
||||
|
||||
COPY ./lib/picolibc-stdio.c /tinygo/lib/picolibc-stdio.c
|
||||
RUN cd /tinygo/ && \
|
||||
make
|
||||
|
||||
# tinygo-tools stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
FROM tinygo-compiler AS tinygo-tools
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
go install /tinygo/
|
||||
|
||||
# tinygo-wasm stage installs the needed dependencies to compile TinyGo programs for WASM.
|
||||
FROM tinygo-base AS tinygo-wasm
|
||||
|
||||
COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
|
||||
COPY --from=tinygo-base /tinygo/src /tinygo/src
|
||||
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y make clang-11 libllvm11 lld-11 cmake ninja-build && \
|
||||
mkdir build && \
|
||||
make wasi-libc binaryen
|
||||
|
||||
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
||||
FROM tinygo-base AS tinygo-avr
|
||||
|
||||
COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
|
||||
COPY --from=tinygo-base /tinygo/src /tinygo/src
|
||||
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
||||
COPY --from=tinygo-base /tinygo/Makefile /tinygo/
|
||||
COPY --from=tinygo-base /tinygo/tools /tinygo/tools
|
||||
COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils make binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device-avr && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
# tinygo-arm stage installs the needed dependencies to compile TinyGo programs for ARM microcontrollers.
|
||||
FROM tinygo-base AS tinygo-arm
|
||||
|
||||
COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
|
||||
COPY --from=tinygo-base /tinygo/src /tinygo/src
|
||||
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
||||
COPY --from=tinygo-base /tinygo/Makefile /tinygo/
|
||||
COPY --from=tinygo-base /tinygo/tools /tinygo/tools
|
||||
COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils make clang-11 && \
|
||||
make gen-device-nrf && make gen-device-stm32
|
||||
|
||||
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
FROM tinygo-wasm AS tinygo-all
|
||||
|
||||
COPY --from=tinygo-base /tinygo/Makefile /tinygo/
|
||||
COPY --from=tinygo-base /tinygo/tools /tinygo/tools
|
||||
COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils make clang-11 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device
|
||||
make wasi-libc binaryen && \
|
||||
make gen-device -j4 && \
|
||||
cp build/* $GOPATH/bin/
|
||||
|
||||
CMD ["tinygo"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Copyright (c) 2018-2021 TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2022 The TinyGo Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of the Go standard library.
|
||||
Copyright (c) 2009-2021 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2009-2022 The Go Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||
|
||||
@@ -9,25 +9,45 @@ CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
|
||||
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
||||
|
||||
# Try to autodetect LLVM build tools.
|
||||
detect = $(shell command -v $(1) 2> /dev/null && echo $(1))
|
||||
CLANG ?= $(word 1,$(abspath $(call detect,llvm-build/bin/clang))$(call detect,clang-11)$(call detect,clang))
|
||||
LLVM_AR ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-ar))$(call detect,llvm-ar-11)$(call detect,llvm-ar))
|
||||
LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detect,llvm-nm-11)$(call detect,llvm-nm))
|
||||
# Versions are listed here in descending priority order.
|
||||
LLVM_VERSIONS = 15 14 13 12 11
|
||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||
toolSearchPathsVersion = $(1)-$(2)
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
# Also explicitly search Brew's copy, which is not in PATH by default.
|
||||
BREW_PREFIX := $(shell brew --prefix)
|
||||
toolSearchPathsVersion += $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)-$(2) $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)
|
||||
endif
|
||||
# First search for a custom built copy, then move on to explicitly version-tagged binaries, then just see if the tool is in path with its normal name.
|
||||
findLLVMTool = $(call detect,$(1),$(abspath llvm-build/bin/$(1)) $(foreach ver,$(LLVM_VERSIONS),$(call toolSearchPathsVersion,$(1),$(ver))) $(1))
|
||||
CLANG ?= $(call findLLVMTool,clang)
|
||||
LLVM_AR ?= $(call findLLVMTool,llvm-ar)
|
||||
LLVM_NM ?= $(call findLLVMTool,llvm-nm)
|
||||
|
||||
# Go binary and GOROOT to select
|
||||
GO ?= go
|
||||
export GOROOT = $(shell $(GO) env GOROOT)
|
||||
|
||||
# Flags to pass to go test.
|
||||
GOTESTFLAGS ?= -v
|
||||
|
||||
# md5sum binary
|
||||
MD5SUM = md5sum
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= $(word 1,$(call detect,tinygo)$(call detect,build/tinygo))
|
||||
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
|
||||
# Check for ccache if the user hasn't set it to on or off.
|
||||
ifeq (, $(CCACHE))
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
CCACHE := ON
|
||||
else
|
||||
CCACHE := OFF
|
||||
endif
|
||||
endif
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||
|
||||
# Allow enabling LLVM assertions
|
||||
ifeq (1, $(ASSERT))
|
||||
@@ -36,9 +56,64 @@ else
|
||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||
endif
|
||||
|
||||
# Enable AddressSanitizer
|
||||
ifeq (1, $(ASAN))
|
||||
LLVM_OPTION += -DLLVM_USE_SANITIZER=Address
|
||||
CGO_LDFLAGS += -fsanitize=address
|
||||
endif
|
||||
|
||||
ifeq (1, $(STATIC))
|
||||
# Build TinyGo as a fully statically linked binary (no dynamically loaded
|
||||
# libraries such as a libc). This is not supported with glibc which is used
|
||||
# on most major Linux distributions. However, it is supported in Alpine
|
||||
# Linux with musl.
|
||||
CGO_LDFLAGS += -static
|
||||
# Also set the thread stack size to 1MB. This is necessary on musl as the
|
||||
# default stack size is 128kB and LLVM uses more than that.
|
||||
# For more information, see:
|
||||
# https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size
|
||||
CGO_LDFLAGS += -Wl,-z,stack-size=1048576
|
||||
# Build wasm-opt with static linking.
|
||||
# For details, see:
|
||||
# https://github.com/WebAssembly/binaryen/blob/version_102/.github/workflows/ci.yml#L181
|
||||
BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static"
|
||||
endif
|
||||
|
||||
# Cross compiling support.
|
||||
ifneq ($(CROSS),)
|
||||
CC = $(CROSS)-gcc
|
||||
CXX = $(CROSS)-g++
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_C_COMPILER=$(CC) \
|
||||
-DCMAKE_CXX_COMPILER=$(CXX) \
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
|
||||
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
|
||||
ifeq ($(CROSS), arm-linux-gnueabihf)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=ARM
|
||||
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else ifeq ($(CROSS), aarch64-linux-gnu)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=AArch64
|
||||
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else
|
||||
$(error Unknown cross compilation target: $(CROSS))
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE = .exe
|
||||
@@ -54,34 +129,37 @@ ifeq ($(OS),Windows_NT)
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
CGO_LDFLAGS_EXTRA += -lversion
|
||||
|
||||
BINARYEN_OPTION += -DCMAKE_EXE_LINKER_FLAGS='-static-libgcc -static-libstdc++'
|
||||
|
||||
LIBCLANG_NAME = libclang
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_NAME = clang
|
||||
|
||||
CGO_LDFLAGS += -lxar
|
||||
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_NAME = clang
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
else
|
||||
LIBCLANG_NAME = clang
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
endif
|
||||
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||
|
||||
# Libraries that should be linked in for the statically linked LLD.
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO lldMinGW lldReaderWriter lldWasm lldYAML
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
|
||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||
|
||||
# Other libraries that are needed to link TinyGo.
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA
|
||||
|
||||
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
||||
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
||||
|
||||
# These build targets appear to be the only ones necessary to build all TinyGo
|
||||
# dependencies. Only building a subset significantly speeds up rebuilding LLVM.
|
||||
@@ -89,20 +167,19 @@ EXTRA_LIB_NAMES = LLVMInterpreter
|
||||
# library path (for ninja).
|
||||
# This list also includes a few tools that are necessary as part of the full
|
||||
# TinyGo build.
|
||||
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIBCLANG_NAME) $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)))
|
||||
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES)))
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++14
|
||||
CGO_LDFLAGS+=$(abspath $(LLVM_BUILDDIR))/lib/lib$(LIBCLANG_NAME).a -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform
|
||||
fmt:
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
@@ -159,84 +236,217 @@ gen-device-rp: build/gen-device-svd
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
$(LLVM_BUILDDIR)/build.ninja:
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS)
|
||||
|
||||
ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
# Build Binaryen
|
||||
.PHONY: binaryen
|
||||
binaryen: build/wasm-opt
|
||||
build/wasm-opt:
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja
|
||||
cp lib/binaryen/bin/wasm-opt build/wasm-opt
|
||||
binaryen: build/wasm-opt$(EXE)
|
||||
build/wasm-opt$(EXE):
|
||||
mkdir -p build
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
||||
endif
|
||||
|
||||
# Build wasi-libc sysroot
|
||||
.PHONY: wasi-libc
|
||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG" WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
||||
cd lib/wasi-libc && make -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
TEST_PACKAGES = \
|
||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||
TEST_PACKAGES_SLOW = \
|
||||
compress/bzip2 \
|
||||
crypto/dsa \
|
||||
index/suffixarray \
|
||||
|
||||
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
|
||||
TEST_PACKAGES_FAST = \
|
||||
compress/zlib \
|
||||
container/heap \
|
||||
container/list \
|
||||
container/ring \
|
||||
crypto/des \
|
||||
crypto/dsa \
|
||||
crypto/md5 \
|
||||
crypto/rc4 \
|
||||
crypto/sha1 \
|
||||
crypto/sha256 \
|
||||
crypto/sha512 \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
encoding/base32 \
|
||||
encoding/base64 \
|
||||
encoding/csv \
|
||||
encoding/hex \
|
||||
go/scanner \
|
||||
hash \
|
||||
hash/adler32 \
|
||||
hash/fnv \
|
||||
hash/crc64 \
|
||||
hash/fnv \
|
||||
html \
|
||||
index/suffixarray \
|
||||
internal/itoa \
|
||||
internal/profile \
|
||||
math \
|
||||
math/cmplx \
|
||||
net \
|
||||
net/http/internal/ascii \
|
||||
net/mail \
|
||||
os \
|
||||
path \
|
||||
reflect \
|
||||
sync \
|
||||
testing \
|
||||
testing/iotest \
|
||||
text/scanner \
|
||||
unicode \
|
||||
unicode/utf16 \
|
||||
unicode/utf8 \
|
||||
$(nil)
|
||||
|
||||
# Assume this will go away before Go2, so only check minor version.
|
||||
ifeq ($(filter $(shell $(GO) env GOVERSION | cut -f 2 -d.), 16 17 18), )
|
||||
TEST_PACKAGES_FAST += crypto/internal/nistec/fiat
|
||||
else
|
||||
TEST_PACKAGES_FAST += crypto/elliptic/internal/fiat
|
||||
endif
|
||||
|
||||
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
||||
# bytes requires mmap
|
||||
# compress/flate appears to hang on wasi
|
||||
# compress/lzw appears to hang on wasi
|
||||
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
|
||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||
# image requires recover(), which is not yet supported on wasi
|
||||
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
# mime/quotedprintable requires syscall.Faccessat
|
||||
# strconv requires recover() which is not yet supported on wasi
|
||||
# text/tabwriter requries recover(), which is not yet supported on wasi
|
||||
# text/template/parse requires recover(), which is not yet supported on wasi
|
||||
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
|
||||
# Additional standard library packages that pass tests on individual platforms
|
||||
TEST_PACKAGES_LINUX := \
|
||||
archive/zip \
|
||||
bytes \
|
||||
compress/flate \
|
||||
compress/lzw \
|
||||
crypto/hmac \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
image \
|
||||
io/ioutil \
|
||||
mime/quotedprintable \
|
||||
strconv \
|
||||
testing/fstest \
|
||||
text/tabwriter \
|
||||
text/template/parse
|
||||
|
||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||
|
||||
TEST_PACKAGES_WINDOWS := \
|
||||
compress/flate \
|
||||
compress/lzw \
|
||||
crypto/hmac \
|
||||
strconv \
|
||||
text/template/parse \
|
||||
$(nil)
|
||||
|
||||
# Report platforms on which each standard library package is known to pass tests
|
||||
jointmp := $(shell echo /tmp/join.$$$$)
|
||||
report-stdlib-tests-pass:
|
||||
@for t in $(TEST_PACKAGES_DARWIN); do echo "$$t darwin"; done | sort > $(jointmp).darwin
|
||||
@for t in $(TEST_PACKAGES_LINUX); do echo "$$t linux"; done | sort > $(jointmp).linux
|
||||
@for t in $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW); do echo "$$t darwin linux wasi windows"; done | sort > $(jointmp).portable
|
||||
@join -a1 -a2 $(jointmp).darwin $(jointmp).linux | \
|
||||
join -a1 -a2 - $(jointmp).portable
|
||||
@rm $(jointmp).*
|
||||
|
||||
# Standard library packages that pass tests quickly on the current platform
|
||||
ifeq ($(shell uname),Darwin)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||
TEST_IOFS := true
|
||||
endif
|
||||
ifeq ($(shell uname),Linux)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||
TEST_IOFS := true
|
||||
endif
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test $(TEST_PACKAGES)
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||
@# For more details, see the comments on issue #3143.
|
||||
ifeq ($(TEST_IOFS),true)
|
||||
$(TINYGO) test -stack-size=6MB io/fs
|
||||
endif
|
||||
tinygo-test-fast:
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
||||
tinygo-bench:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-fast:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST)
|
||||
|
||||
# Same thing, except for wasi rather than the current platform.
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasi-fast:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
tinygo-bench-wasi:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-wasi-fast:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST)
|
||||
|
||||
# Test external packages in a large corpus.
|
||||
test-corpus:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml
|
||||
test-corpus-fast:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
||||
test-corpus-wasi: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasi
|
||||
|
||||
tinygo-baremetal:
|
||||
# Regression tests that run on a baremetal target and don't fit in either main_test.go or smoketest.
|
||||
# regression test for #2666: e.g. encoding/hex must pass on baremetal
|
||||
$(TINYGO) test -target cortex-m-qemu encoding/hex
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
$(TINYGO) version
|
||||
$(TINYGO) targets > /dev/null
|
||||
# regression test for #2892
|
||||
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||
# compile-only platform-independent examples
|
||||
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
||||
# regression test for #2563
|
||||
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
|
||||
# test all examples (except pwm)
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -252,6 +462,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@@ -268,19 +480,27 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=reelboard examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=pca10040 examples/blinky2
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=pca10056 examples/blinky2
|
||||
$(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/serial
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -330,6 +550,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=matrixportal-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||
@@ -368,6 +590,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy41 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
|
||||
@@ -394,6 +618,26 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=kb2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=tufty2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-zero examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -401,6 +645,13 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
# test usb
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -418,6 +669,8 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-wl55jc examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||
@@ -426,12 +679,19 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f469disco examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=lorae5 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@@ -446,7 +706,6 @@ ifneq ($(AVR), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
@@ -454,17 +713,27 @@ ifneq ($(XTENSA), 0)
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/serial
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||
endif
|
||||
# test various compiler flags
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -477,7 +746,10 @@ endif
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -o test.exe ./testdata/cgo
|
||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
ifneq ($(OS),Windows_NT)
|
||||
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||
# not garbage collected.
|
||||
@@ -488,11 +760,11 @@ endif
|
||||
wasmtest:
|
||||
$(GO) test ./tests/wasm
|
||||
|
||||
build/release: tinygo gen-device wasi-libc binaryen
|
||||
build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||
@mkdir -p build/release/tinygo/lib/musl/arch
|
||||
@@ -502,18 +774,18 @@ build/release: tinygo gen-device wasi-libc binaryen
|
||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc
|
||||
@mkdir -p build/release/tinygo/pkg/armv6m-unknown-unknown-eabi
|
||||
@mkdir -p build/release/tinygo/pkg/armv7m-unknown-unknown-eabi
|
||||
@mkdir -p build/release/tinygo/pkg/armv7em-unknown-unknown-eabi
|
||||
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0
|
||||
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus
|
||||
@mkdir -p build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4
|
||||
@echo copying source files
|
||||
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
||||
ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
|
||||
endif
|
||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
||||
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
|
||||
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
|
||||
@@ -527,8 +799,10 @@ build/release: tinygo gen-device wasi-libc binaryen
|
||||
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
||||
@@ -547,23 +821,32 @@ build/release: tinygo gen-device wasi-libc binaryen
|
||||
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp src build/release/tinygo/src
|
||||
@cp -rp targets build/release/tinygo/targets
|
||||
./build/tinygo build-library -target=armv6m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv6m-unknown-unknown-eabi/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=armv7m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7m-unknown-unknown-eabi/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=armv7em-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7em-unknown-unknown-eabi/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=armv6m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv6m-unknown-unknown-eabi/picolibc picolibc
|
||||
./build/tinygo build-library -target=armv7m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7m-unknown-unknown-eabi/picolibc picolibc
|
||||
./build/tinygo build-library -target=armv7em-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7em-unknown-unknown-eabi/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||
|
||||
release: build/release
|
||||
release:
|
||||
tar -czf build/release.tar.gz -C build/release tinygo
|
||||
|
||||
deb: build/release
|
||||
DEB_ARCH ?= native
|
||||
deb:
|
||||
@mkdir -p build/release-deb/usr/local/bin
|
||||
@mkdir -p build/release-deb/usr/local/lib
|
||||
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
||||
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
||||
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
|
||||
ifneq ($(RELEASEONLY), 1)
|
||||
release: build/release
|
||||
deb: build/release
|
||||
endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](https://circleci.com/gh/tinygo-org/tinygo/tree/dev) [](https://dev.azure.com/tinygo/tinygo/_build/latest?definitionId=1&branchName=dev)
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (Wasm), and command-line tools.
|
||||
|
||||
@@ -43,12 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 71 microcontroller boards are currently supported:
|
||||
The following 94 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [Adafruit Feather M0 Express](https://www.adafruit.com/product/3403)
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||
@@ -59,13 +60,17 @@ The following 71 microcontroller boards are currently supported:
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||
* [Adafruit KB2040](https://www.adafruit.com/product/5302)
|
||||
* [Adafruit MacroPad RP2040](https://www.adafruit.com/product/5100)
|
||||
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
|
||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
|
||||
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
||||
* [Adafruit QT Py RP2040](https://www.adafruit.com/product/4900)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Adafruit Trinkey QT2040](https://adafruit.com/product/5056)
|
||||
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||
@@ -79,14 +84,22 @@ The following 71 microcontroller boards are currently supported:
|
||||
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
* [BBC micro:bit v2](https://microbit.org/new-microbit/)
|
||||
* [blues wireless Swan](https://blues.io/products/swan/)
|
||||
* [Digispark](http://digistump.com/products/1)
|
||||
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
|
||||
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP32-C3-12f](https://www.espressif.com/en/products/socs/esp32-c3)
|
||||
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [iLabs Challenger RP2040 LoRa](https://ilabs.se/product/challenger-rp2040-lora/)
|
||||
* [M5Stack](https://docs.m5stack.com/en/core/basic)
|
||||
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
|
||||
* [M5Stamp C3](https://docs.m5stack.com/en/core/stamp_c3)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
|
||||
* [MCH2022 badge](https://badge.team/docs/badges/mch2022/)
|
||||
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
|
||||
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
|
||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||
@@ -98,25 +111,38 @@ The following 71 microcontroller boards are currently supported:
|
||||
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
|
||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [Pimoroni Badger2040](https://shop.pimoroni.com/products/badger-2040)
|
||||
* [Pimoroni Tufty2040](https://shop.pimoroni.com/products/tufty-2040)
|
||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||
* [PJRC Teensy 4.1](https://www.pjrc.com/store/teensy41.html)
|
||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
||||
* [Seeed XIAO ESP32C3](https://www.seeedstudio.com/Seeed-XIAO-ESP32C3-p-5431.html)
|
||||
* [Seeed XIAO RP2040](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html)
|
||||
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1-rev-b)
|
||||
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
|
||||
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
||||
* [ST Micro "Nucleo" L432KC](https://www.st.com/ja/evaluation-tools/nucleo-l432kc.html)
|
||||
* [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
|
||||
* [ST Micro "Nucleo" WL55JC](https://www.st.com/en/evaluation-tools/nucleo-wl55jc.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
* [ST Micro STM32F469 "Discovery"](https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html)
|
||||
* [The Things Industries Generic Node Sensor Edition](https://www.genericnode.com/docs/sensor-edition/)
|
||||
* [Waveshare RP2040-Zero](https://www.waveshare.com/wiki/RP2040-Zero)
|
||||
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
|
||||
|
||||
|
||||
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
|
||||
|
||||
## Currently supported features:
|
||||
@@ -142,7 +168,7 @@ should arrive fairly quickly (under 1 min): https://invite.slack.golangbridge.or
|
||||
|
||||
Your contributions are welcome!
|
||||
|
||||
Please take a look at our [CONTRIBUTING.md](./CONTRIBUTING.md) document for details.
|
||||
Please take a look at our [Contributing](https://tinygo.org/docs/guides/contributing/) page on our web site for details.
|
||||
|
||||
## Project Scope
|
||||
|
||||
@@ -156,7 +182,6 @@ Goals:
|
||||
|
||||
Non-goals:
|
||||
|
||||
* Using more than one core.
|
||||
* Be efficient while using zillions of goroutines. However, good goroutine support is certainly a goal.
|
||||
* Be as fast as `gc`. However, LLVM will probably be better at optimizing certain things so TinyGo might actually turn out to be faster for number crunching.
|
||||
* Be able to compile every Go program out there.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
+1
-1
@@ -18,7 +18,7 @@ import (
|
||||
// makeArchive creates an arcive for static linking from a list of object files
|
||||
// given as a parameter. It is equivalent to the following command:
|
||||
//
|
||||
// ar -rcs <archivePath> <objs...>
|
||||
// ar -rcs <archivePath> <objs...>
|
||||
func makeArchive(arfile *os.File, objs []string) error {
|
||||
// Open the archive file.
|
||||
arwriter := ar.NewWriter(arfile)
|
||||
|
||||
+469
-215
File diff suppressed because it is too large
Load Diff
+15
-10
@@ -2,9 +2,9 @@ package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -25,7 +25,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m0",
|
||||
"cortex-m0plus",
|
||||
"cortex-m3",
|
||||
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
||||
"cortex-m33",
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
@@ -59,8 +59,13 @@ func TestClangAttributes(t *testing.T) {
|
||||
{GOOS: "darwin", GOARCH: "amd64"},
|
||||
{GOOS: "darwin", GOARCH: "arm64"},
|
||||
{GOOS: "windows", GOARCH: "amd64"},
|
||||
{GOOS: "windows", GOARCH: "arm64"},
|
||||
} {
|
||||
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
|
||||
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
||||
if options.GOARCH == "arm" {
|
||||
name += ",GOARM=" + options.GOARM
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
testClangAttributes(t, options)
|
||||
})
|
||||
}
|
||||
@@ -85,7 +90,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
|
||||
// Create a very simple C input file.
|
||||
srcpath := filepath.Join(testDir, "test.c")
|
||||
err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
||||
err = os.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
||||
if err != nil {
|
||||
t.Fatalf("could not write target file %s: %s", srcpath, err)
|
||||
}
|
||||
@@ -131,12 +136,12 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
t.Errorf("target has CPU %#v but Clang makes it CPU %#v", config.CPU(), cpu)
|
||||
}
|
||||
if features != config.Features() {
|
||||
if llvm.Version != "11.0.0" {
|
||||
// This needs to be removed once we switch to LLVM 12.
|
||||
// LLVM 11.0.0 uses a different "target-features" string than LLVM
|
||||
// 11.1.0 for Thumb targets. The Xtensa fork is still based on LLVM
|
||||
// 11.0.0, so we need to skip this check on that version.
|
||||
t.Errorf("target has LLVM features %#v but Clang makes it %#v", config.Features(), features)
|
||||
if hasBuiltinTools || runtime.GOOS != "linux" {
|
||||
// Skip this step when using an external Clang invocation on Linux.
|
||||
// The reason is that Debian has patched Clang in a way that
|
||||
// modifies the LLVM features string, changing lots of FPU/float
|
||||
// related flags. We want to test vanilla Clang, not Debian Clang.
|
||||
t.Errorf("target has LLVM features\n\t%#v\nbut Clang makes it\n\t%#v", config.Features(), features)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// ReadBuildID reads the build ID from the currently running executable.
|
||||
func ReadBuildID() ([]byte, error) {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(executable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd", "android":
|
||||
// Read the GNU build id section. (Not sure about FreeBSD though...)
|
||||
file, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var gnuID, goID []byte
|
||||
for _, section := range file.Sections {
|
||||
if section.Type != elf.SHT_NOTE ||
|
||||
(section.Name != ".note.gnu.build-id" && section.Name != ".note.go.buildid") {
|
||||
continue
|
||||
}
|
||||
buf := make([]byte, section.Size)
|
||||
n, err := section.ReadAt(buf, 0)
|
||||
if uint64(n) != section.Size || err != nil {
|
||||
return nil, fmt.Errorf("could not read build id: %w", err)
|
||||
}
|
||||
if section.Name == ".note.gnu.build-id" {
|
||||
gnuID = buf
|
||||
} else {
|
||||
goID = buf
|
||||
}
|
||||
}
|
||||
if gnuID != nil {
|
||||
return gnuID, nil
|
||||
} else if goID != nil {
|
||||
return goID, nil
|
||||
}
|
||||
case "darwin":
|
||||
// Read the LC_UUID load command, which contains the equivalent of a
|
||||
// build ID.
|
||||
file, err := macho.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, load := range file.Loads {
|
||||
// Unfortunately, the debug/macho package doesn't support the
|
||||
// LC_UUID command directly. So we have to read it from
|
||||
// macho.LoadBytes.
|
||||
load, ok := load.(macho.LoadBytes)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
raw := load.Raw()
|
||||
command := binary.LittleEndian.Uint32(raw)
|
||||
if command != 0x1b {
|
||||
// Looking for the LC_UUID load command.
|
||||
// LC_UUID is defined here as 0x1b:
|
||||
// https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html
|
||||
continue
|
||||
}
|
||||
return raw[4:], nil
|
||||
}
|
||||
default:
|
||||
// On other platforms (such as Windows) there isn't such a convenient
|
||||
// build ID. Luckily, Go does have an equivalent of the build ID, which
|
||||
// is stored as a special symbol named go.buildid. You can read it
|
||||
// using `go tool buildid`, but the code below extracts it directly
|
||||
// from the binary.
|
||||
// Unfortunately, because of stripping with the -w flag, no symbol
|
||||
// table might be available. Therefore, we have to scan the binary
|
||||
// directly. Luckily the build ID is always at the start of the file.
|
||||
// For details, see:
|
||||
// https://github.com/golang/go/blob/master/src/cmd/internal/buildid/buildid.go
|
||||
fileStart := make([]byte, 4096)
|
||||
_, err := io.ReadFull(f, fileStart)
|
||||
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
|
||||
if index < 0 || index > len(fileStart)-103 {
|
||||
return nil, fmt.Errorf("could not find build id in %s", err)
|
||||
}
|
||||
buf := fileStart[index : index+103]
|
||||
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
|
||||
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("could not find build ID in %s", executable)
|
||||
}
|
||||
+36
-5
@@ -1,7 +1,11 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// These are the GENERIC_SOURCES according to CMakeList.txt.
|
||||
@@ -36,7 +40,6 @@ var genericBuiltins = []string{
|
||||
"divdf3.c",
|
||||
"divdi3.c",
|
||||
"divmoddi4.c",
|
||||
"divmodsi4.c",
|
||||
"divsc3.c",
|
||||
"divsf3.c",
|
||||
"divsi3.c",
|
||||
@@ -72,6 +75,7 @@ var genericBuiltins = []string{
|
||||
"floatunsisf.c",
|
||||
"floatuntidf.c",
|
||||
"floatuntisf.c",
|
||||
"fp_mode.c",
|
||||
//"int_util.c",
|
||||
"lshrdi3.c",
|
||||
"lshrti3.c",
|
||||
@@ -122,7 +126,6 @@ var genericBuiltins = []string{
|
||||
"ucmpti2.c",
|
||||
"udivdi3.c",
|
||||
"udivmoddi4.c",
|
||||
"udivmodsi4.c",
|
||||
"udivmodti4.c",
|
||||
"udivsi3.c",
|
||||
"udivti3.c",
|
||||
@@ -149,6 +152,23 @@ var aeabiBuiltins = []string{
|
||||
"arm/aeabi_memset.S",
|
||||
"arm/aeabi_uidivmod.S",
|
||||
"arm/aeabi_uldivmod.S",
|
||||
|
||||
// These two are not technically EABI builtins but are used by them and only
|
||||
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||
// other architectures.
|
||||
// Most importantly, they have a different calling convention on AVR so
|
||||
// should not be used on AVR.
|
||||
"divmodsi4.c",
|
||||
"udivmodsi4.c",
|
||||
}
|
||||
|
||||
var avrBuiltins = []string{
|
||||
"avr/divmodhi4.S",
|
||||
"avr/divmodqi4.S",
|
||||
"avr/mulhi3.S",
|
||||
"avr/mulqi3.S",
|
||||
"avr/udivmodhi4.S",
|
||||
"avr/udivmodqi4.S",
|
||||
}
|
||||
|
||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||
@@ -161,12 +181,23 @@ var CompilerRT = Library{
|
||||
cflags: func(target, headerPath string) []string {
|
||||
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
|
||||
},
|
||||
sourceDir: "lib/compiler-rt/lib/builtins",
|
||||
librarySources: func(target string) []string {
|
||||
sourceDir: func() string {
|
||||
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
|
||||
if _, err := os.Stat(llvmDir); err == nil {
|
||||
// Release build.
|
||||
return llvmDir
|
||||
}
|
||||
// Development build.
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||
},
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
builtins = append(builtins, aeabiBuiltins...)
|
||||
}
|
||||
return builtins
|
||||
if strings.HasPrefix(target, "avr") {
|
||||
builtins = append(builtins, avrBuiltins...)
|
||||
}
|
||||
return builtins, nil
|
||||
},
|
||||
}
|
||||
|
||||
+54
-41
@@ -10,7 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -33,36 +33,45 @@ import (
|
||||
// Because of this complexity, every file has in fact two cached build outputs:
|
||||
// the file itself, and the list of dependencies. Its operation is as follows:
|
||||
//
|
||||
// depfile = hash(path, compiler, cflags, ...)
|
||||
// if depfile exists:
|
||||
// outfile = hash of all files and depfile name
|
||||
// if outfile exists:
|
||||
// # cache hit
|
||||
// return outfile
|
||||
// # cache miss
|
||||
// tmpfile = compile file
|
||||
// read dependencies (side effect of compile)
|
||||
// write depfile
|
||||
// outfile = hash of all files and depfile name
|
||||
// rename tmpfile to outfile
|
||||
// depfile = hash(path, compiler, cflags, ...)
|
||||
// if depfile exists:
|
||||
// outfile = hash of all files and depfile name
|
||||
// if outfile exists:
|
||||
// # cache hit
|
||||
// return outfile
|
||||
// # cache miss
|
||||
// tmpfile = compile file
|
||||
// read dependencies (side effect of compile)
|
||||
// write depfile
|
||||
// outfile = hash of all files and depfile name
|
||||
// rename tmpfile to outfile
|
||||
//
|
||||
// There are a few edge cases that are not handled:
|
||||
// - If a file is added to an include path, that file may be included instead of
|
||||
// some other file. This would be fixed by also including lookup failures in the
|
||||
// dependencies file, but I'm not aware of a compiler which does that.
|
||||
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||
// details.
|
||||
// - A header file may be changed to add/remove an include. This invalidates the
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
|
||||
// - If a file is added to an include path, that file may be included instead of
|
||||
// some other file. This would be fixed by also including lookup failures in the
|
||||
// dependencies file, but I'm not aware of a compiler which does that.
|
||||
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||
// details.
|
||||
// - A header file may be changed to add/remove an include. This invalidates the
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Acquire a lock (if supported).
|
||||
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
|
||||
defer unlock()
|
||||
|
||||
ext := ".o"
|
||||
if thinlto {
|
||||
ext = ".bc"
|
||||
}
|
||||
|
||||
// Create cache key for the dependencies file.
|
||||
buf, err := json.Marshal(struct {
|
||||
Path string
|
||||
@@ -84,7 +93,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
// Load dependencies file, if possible.
|
||||
depfileName := "dep-" + depfileNameHash + ".json"
|
||||
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
|
||||
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
|
||||
depfileBuf, err := os.ReadFile(depfileCachePath)
|
||||
var dependencies []string // sorted list of dependency paths
|
||||
if err == nil {
|
||||
// There is a dependency file, that's great!
|
||||
@@ -95,31 +104,34 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
}
|
||||
|
||||
// Obtain hashes of all the files listed as a dependency.
|
||||
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
|
||||
outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext)
|
||||
if err == nil {
|
||||
if _, err := os.Stat(outpath); err == nil {
|
||||
return outpath, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||
// expected either nil or IsNotExist
|
||||
return "", err
|
||||
}
|
||||
|
||||
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
|
||||
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
objTmpFile.Close()
|
||||
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
|
||||
depTmpFile, err := os.CreateTemp(tmpdir, "dep-*.d")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
depTmpFile.Close()
|
||||
flags := append([]string{}, cflags...) // copy cflags
|
||||
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||
if thinlto {
|
||||
flags = append(flags, "-flto=thin")
|
||||
}
|
||||
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
||||
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
||||
@@ -154,7 +166,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
sort.Strings(dependencySlice)
|
||||
|
||||
// Write dependencies file.
|
||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||
f, err := os.CreateTemp(filepath.Dir(depfileCachePath), depfileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -177,7 +189,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
}
|
||||
|
||||
// Move temporary object file to final location.
|
||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -192,7 +204,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
// Create a cache path (a path in GOCACHE) to store the output of a compiler
|
||||
// job. This path is based on the dep file name (which is a hash of metadata
|
||||
// including compiler flags) and the hash of all input files in the paths slice.
|
||||
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
|
||||
func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) {
|
||||
// Hash all input files.
|
||||
fileHashes := make(map[string]string, len(paths))
|
||||
for _, path := range paths {
|
||||
@@ -217,7 +229,7 @@ func makeCFileCachePath(paths []string, depfileNameHash string) (string, error)
|
||||
outFileNameBuf := sha512.Sum512_224(buf)
|
||||
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
||||
|
||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
|
||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext)
|
||||
return outpath, nil
|
||||
}
|
||||
|
||||
@@ -240,13 +252,14 @@ func hashFile(path string) (string, error) {
|
||||
// file is assumed to have a single target named deps.
|
||||
//
|
||||
// There are roughly three make syntax variants:
|
||||
// - BSD make, which doesn't support any escaping. This means that many special
|
||||
// characters are not supported in file names.
|
||||
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||
// find a file it tries to fall back with the literal path name (to match BSD
|
||||
// make).
|
||||
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||
// any weird characters.
|
||||
// - BSD make, which doesn't support any escaping. This means that many special
|
||||
// characters are not supported in file names.
|
||||
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||
// find a file it tries to fall back with the literal path name (to match BSD
|
||||
// make).
|
||||
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||
// any weird characters.
|
||||
//
|
||||
// Clang supports two variants: a format that's a compromise between BSD and GNU
|
||||
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
|
||||
// is at least somewhat sane. This last format isn't perfect either: it does not
|
||||
@@ -254,7 +267,7 @@ func hashFile(path string) (string, error) {
|
||||
// allowed on Windows, but of course can be used on POSIX like systems. Still,
|
||||
// it's the most sane of any of the formats so readDepFile will use that format.
|
||||
func readDepFile(filename string) ([]string, error) {
|
||||
buf, err := ioutil.ReadFile(filename)
|
||||
buf, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+63
-44
@@ -1,4 +1,4 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||
//
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/OptTable.h"
|
||||
@@ -51,7 +52,6 @@
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@@ -101,6 +101,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
||||
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
||||
|
||||
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
@@ -115,29 +118,25 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
// Any DebugInfoKind implies GenDwarfForAssembly.
|
||||
Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ);
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections,
|
||||
OPT_compress_debug_sections_EQ)) {
|
||||
if (A->getOption().getID() == OPT_compress_debug_sections) {
|
||||
// TODO: be more clever about the compression type auto-detection
|
||||
Opts.CompressDebugSections = llvm::DebugCompressionType::GNU;
|
||||
} else {
|
||||
Opts.CompressDebugSections =
|
||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||
.Case("none", llvm::DebugCompressionType::None)
|
||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections_EQ)) {
|
||||
Opts.CompressDebugSections =
|
||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||
.Case("none", llvm::DebugCompressionType::None)
|
||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
|
||||
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
||||
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
|
||||
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
|
||||
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||
Opts.DwarfDebugFlags =
|
||||
std::string(Args.getLastArgValue(OPT_dwarf_debug_flags));
|
||||
Opts.DwarfDebugProducer =
|
||||
std::string(Args.getLastArgValue(OPT_dwarf_debug_producer));
|
||||
Opts.DebugCompilationDir =
|
||||
std::string(Args.getLastArgValue(OPT_fdebug_compilation_dir));
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_ffile_compilation_dir_EQ,
|
||||
options::OPT_fdebug_compilation_dir_EQ))
|
||||
Opts.DebugCompilationDir = A->getValue();
|
||||
Opts.MainFileName = std::string(Args.getLastArgValue(OPT_main_file_name));
|
||||
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
|
||||
@@ -207,6 +206,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
.Default(0);
|
||||
}
|
||||
|
||||
if (auto *A = Args.getLastArg(OPT_femit_dwarf_unwind_EQ)) {
|
||||
Opts.EmitDwarfUnwind =
|
||||
llvm::StringSwitch<EmitDwarfUnwindType>(A->getValue())
|
||||
.Case("always", EmitDwarfUnwindType::Always)
|
||||
.Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind)
|
||||
.Case("default", EmitDwarfUnwindType::Default);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
@@ -219,7 +226,7 @@ getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) {
|
||||
|
||||
std::error_code EC;
|
||||
auto Out = std::make_unique<raw_fd_ostream>(
|
||||
Path, EC, (Binary ? sys::fs::OF_None : sys::fs::OF_Text));
|
||||
Path, EC, (Binary ? sys::fs::OF_None : sys::fs::OF_TextWithCRLF));
|
||||
if (EC) {
|
||||
Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message();
|
||||
return nullptr;
|
||||
@@ -228,7 +235,8 @@ getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) {
|
||||
return Out;
|
||||
}
|
||||
|
||||
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
DiagnosticsEngine &Diags) {
|
||||
// Get the target specific parser.
|
||||
std::string Error;
|
||||
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||
@@ -236,7 +244,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile);
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||
|
||||
if (std::error_code EC = Buffer.getError()) {
|
||||
Error = EC.message();
|
||||
@@ -256,6 +264,8 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
assert(MRI && "Unable to create target register info!");
|
||||
|
||||
MCTargetOptions MCOptions;
|
||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||
|
||||
std::unique_ptr<MCAsmInfo> MAI(
|
||||
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
|
||||
assert(MAI && "Unable to create target asm info!");
|
||||
@@ -277,11 +287,15 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
if (!Opts.SplitDwarfOutput.empty())
|
||||
DwoOS = getOutputStream(Opts.SplitDwarfOutput, Diags, IsBinary);
|
||||
|
||||
// FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
|
||||
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
||||
std::unique_ptr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
|
||||
// Build up the feature string from the target feature list.
|
||||
std::string FS = llvm::join(Opts.Features, ",");
|
||||
|
||||
MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr, &MCOptions);
|
||||
std::unique_ptr<MCSubtargetInfo> STI(
|
||||
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||
assert(STI && "Unable to create subtarget info!");
|
||||
|
||||
MCContext Ctx(Triple(Opts.Triple), MAI.get(), MRI.get(), STI.get(), &SrcMgr,
|
||||
&MCOptions);
|
||||
|
||||
bool PIC = false;
|
||||
if (Opts.RelocationModel == "static") {
|
||||
@@ -294,7 +308,14 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
PIC = false;
|
||||
}
|
||||
|
||||
MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx);
|
||||
// FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
|
||||
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
||||
std::unique_ptr<MCObjectFileInfo> MOFI(
|
||||
TheTarget->createMCObjectFileInfo(Ctx, PIC));
|
||||
if (Opts.DarwinTargetVariantTriple)
|
||||
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
|
||||
Ctx.setObjectFileInfo(MOFI.get());
|
||||
|
||||
if (Opts.SaveTemporaryLabels)
|
||||
Ctx.setAllowTemporaryLabels(false);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
@@ -316,19 +337,16 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
|
||||
if (!Opts.MainFileName.empty())
|
||||
Ctx.setMainFileName(StringRef(Opts.MainFileName));
|
||||
Ctx.setDwarfFormat(Opts.Dwarf64 ? dwarf::DWARF64 : dwarf::DWARF32);
|
||||
Ctx.setDwarfVersion(Opts.DwarfVersion);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
Ctx.setGenDwarfRootFile(Opts.InputFile,
|
||||
SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer());
|
||||
|
||||
// Build up the feature string from the target feature list.
|
||||
std::string FS = llvm::join(Opts.Features, ",");
|
||||
|
||||
std::unique_ptr<MCStreamer> Str;
|
||||
|
||||
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
|
||||
std::unique_ptr<MCSubtargetInfo> STI(
|
||||
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||
assert(MCII && "Unable to create instruction info!");
|
||||
|
||||
raw_pwrite_stream *Out = FDOS.get();
|
||||
std::unique_ptr<buffer_ostream> BOS;
|
||||
@@ -344,7 +362,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE;
|
||||
if (Opts.ShowEncoding)
|
||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
|
||||
@@ -364,9 +382,11 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
}
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE(
|
||||
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
TheTarget->createMCCodeEmitter(*MCII, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
assert(MAB && "Unable to create asm backend!");
|
||||
|
||||
std::unique_ptr<MCObjectWriter> OW =
|
||||
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||
: MAB->createObjectWriter(*Out);
|
||||
@@ -376,16 +396,15 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ true));
|
||||
Str.get()->InitSections(Opts.NoExecStack);
|
||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||
}
|
||||
|
||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||
// is emitted in __LLVM,__asm section if the object file is MachO format.
|
||||
if (Opts.EmbedBitcode && Ctx.getObjectFileInfo()->getObjectFileType() ==
|
||||
MCObjectFileInfo::IsMachO) {
|
||||
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
|
||||
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||
Str.get()->SwitchSection(AsmLabel);
|
||||
Str.get()->switchSection(AsmLabel);
|
||||
Str.get()->emitZeros(1);
|
||||
}
|
||||
|
||||
@@ -419,12 +438,12 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
Failed = Parser->Run(Opts.NoInitialTextSection);
|
||||
}
|
||||
|
||||
// Close Streamer first.
|
||||
// It might have a reference to the output stream.
|
||||
Str.reset();
|
||||
// Close the output stream early.
|
||||
BOS.reset();
|
||||
FDOS.reset();
|
||||
return Failed;
|
||||
}
|
||||
|
||||
bool ExecuteAssembler(AssemblerInvocation &Opts,
|
||||
DiagnosticsEngine &Diags) {
|
||||
bool Failed = ExecuteAssemblerImpl(Opts, Diags);
|
||||
|
||||
// Delete output file if there were errors.
|
||||
if (Failed) {
|
||||
@@ -437,7 +456,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
return Failed;
|
||||
}
|
||||
|
||||
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
||||
static void LLVMErrorHandler(void *UserData, const char *Message,
|
||||
bool GenCrashDiag) {
|
||||
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||
|
||||
@@ -472,7 +491,7 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
return 1;
|
||||
|
||||
if (Asm.ShowHelp) {
|
||||
getDriverOptTable().PrintHelp(
|
||||
getDriverOptTable().printHelp(
|
||||
llvm::outs(), "clang -cc1as [options] file...",
|
||||
"Clang Integrated Assembler",
|
||||
/*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
|
||||
|
||||
@@ -39,6 +39,7 @@ struct AssemblerInvocation {
|
||||
unsigned SaveTemporaryLabels : 1;
|
||||
unsigned GenDwarfForAssembly : 1;
|
||||
unsigned RelaxELFRelocations : 1;
|
||||
unsigned Dwarf64 : 1;
|
||||
unsigned DwarfVersion;
|
||||
std::string DwarfDebugFlags;
|
||||
std::string DwarfDebugProducer;
|
||||
@@ -84,6 +85,9 @@ struct AssemblerInvocation {
|
||||
unsigned IncrementalLinkerCompatible : 1;
|
||||
unsigned EmbedBitcode : 1;
|
||||
|
||||
/// Whether to emit DWARF unwind info.
|
||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||
|
||||
/// The name of the relocation model to use.
|
||||
std::string RelocationModel;
|
||||
|
||||
@@ -91,6 +95,9 @@ struct AssemblerInvocation {
|
||||
/// otherwise.
|
||||
std::string TargetABI;
|
||||
|
||||
/// Darwin target variant triple, the variant of the deployment target
|
||||
/// for which the code is being compiled.
|
||||
llvm::Optional<llvm::Triple> DarwinTargetVariantTriple;
|
||||
/// @}
|
||||
|
||||
public:
|
||||
@@ -108,8 +115,10 @@ public:
|
||||
FatalWarnings = 0;
|
||||
NoWarn = 0;
|
||||
IncrementalLinkerCompatible = 0;
|
||||
Dwarf64 = 0;
|
||||
DwarfVersion = 0;
|
||||
EmbedBitcode = 0;
|
||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||
}
|
||||
|
||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
#include <clang/Basic/DiagnosticOptions.h>
|
||||
#include <clang/CodeGen/CodeGenAction.h>
|
||||
|
||||
+12
-2
@@ -2,6 +2,7 @@ package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
@@ -25,7 +26,16 @@ func init() {
|
||||
// Add the path to a Homebrew-installed LLVM for ease of use (no need to
|
||||
// manually set $PATH).
|
||||
if runtime.GOOS == "darwin" {
|
||||
prefix := "/usr/local/opt/llvm@" + llvmMajor + "/bin/"
|
||||
var prefix string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
prefix = "/usr/local/opt/llvm@" + llvmMajor + "/bin/"
|
||||
case "arm64":
|
||||
prefix = "/opt/homebrew/opt/llvm@" + llvmMajor + "/bin/"
|
||||
default:
|
||||
// unknown GOARCH
|
||||
panic(fmt.Sprintf("unknown GOARCH: %s on darwin", runtime.GOARCH))
|
||||
}
|
||||
commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor)
|
||||
commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld")
|
||||
@@ -64,7 +74,7 @@ func LookupCommand(name string) (string, error) {
|
||||
}
|
||||
return cmdName, nil
|
||||
}
|
||||
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
}
|
||||
|
||||
func execCommand(name string, args ...string) error {
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 || minor < 15 || minor > 17 {
|
||||
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||
if major != 1 || minor < 18 || minor > 20 {
|
||||
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Create a job that builds a Darwin libSystem.dylib stub library. This library
|
||||
// contains all the symbols needed so that we can link against it, but it
|
||||
// doesn't contain any real symbol implementations.
|
||||
func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJob {
|
||||
return &compileJob{
|
||||
description: "compile Darwin libSystem.dylib",
|
||||
run: func(job *compileJob) (err error) {
|
||||
arch := strings.Split(config.Triple(), "-")[0]
|
||||
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
||||
objpath := filepath.Join(tmpdir, "libSystem.o")
|
||||
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
||||
|
||||
// Compile assembly file to object file.
|
||||
flags := []string{
|
||||
"-nostdlib",
|
||||
"--target=" + config.Triple(),
|
||||
"-c",
|
||||
"-o", objpath,
|
||||
inpath,
|
||||
}
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", flags...)
|
||||
}
|
||||
err = runCCompiler(flags...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Link object file to dynamic library.
|
||||
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
||||
flags = []string{
|
||||
"-flavor", "darwin",
|
||||
"-demangle",
|
||||
"-dynamic",
|
||||
"-dylib",
|
||||
"-arch", arch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
"-install_name", "/usr/lib/libSystem.B.dylib",
|
||||
"-o", job.result,
|
||||
objpath,
|
||||
}
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("ld.lld", flags...)
|
||||
}
|
||||
return link("ld.lld", flags...)
|
||||
},
|
||||
}
|
||||
}
|
||||
+18
-2
@@ -1,6 +1,8 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -17,13 +19,13 @@ import (
|
||||
func getClangHeaderPath(TINYGOROOT string) string {
|
||||
// Check whether we're running from the source directory.
|
||||
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||
return path
|
||||
}
|
||||
|
||||
// Check whether we're running from the installation directory.
|
||||
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -84,6 +86,20 @@ func getClangHeaderPath(TINYGOROOT string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// On Arch Linux, the clang executable is stored in /usr/bin rather than being symlinked from there.
|
||||
// Search directly in /usr/lib for clang.
|
||||
if matches, err := filepath.Glob("/usr/lib/clang/" + llvmMajor + ".*.*"); err == nil {
|
||||
// Check for the highest version first.
|
||||
sort.Strings(matches)
|
||||
for i := len(matches) - 1; i >= 0; i-- {
|
||||
path := filepath.Join(matches[i], "include")
|
||||
_, err := os.Stat(filepath.Join(path, "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find it.
|
||||
return ""
|
||||
}
|
||||
|
||||
+32
-5
@@ -13,8 +13,9 @@ import (
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type espImageSegment struct {
|
||||
@@ -78,15 +79,31 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
// An added benefit is that we don't need to check for errors all the time.
|
||||
outf := &bytes.Buffer{}
|
||||
|
||||
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
|
||||
// image, not just a binary to be flashed at 0x1000 for example.
|
||||
chip := format
|
||||
makeImage := false
|
||||
if strings.HasSuffix(format, "-img") {
|
||||
makeImage = true
|
||||
chip = format[:len(format)-len("-img")]
|
||||
}
|
||||
|
||||
if makeImage {
|
||||
// The bootloader starts at 0x1000, or 4096.
|
||||
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||
// application in the bootloader location.
|
||||
outf.Write(make([]byte, 4096))
|
||||
}
|
||||
|
||||
// Chip IDs. Source:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
}[format]
|
||||
}[chip]
|
||||
|
||||
// Image header.
|
||||
switch format {
|
||||
switch chip {
|
||||
case "esp32", "esp32c3":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
@@ -155,12 +172,22 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||
outf.WriteByte(checksum)
|
||||
|
||||
if format != "esp8266" {
|
||||
if chip != "esp8266" {
|
||||
// SHA256 hash (to protect against image corruption, not for security).
|
||||
hash := sha256.Sum256(outf.Bytes())
|
||||
outf.Write(hash[:])
|
||||
}
|
||||
|
||||
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||
// image to be a certain size.
|
||||
if makeImage {
|
||||
// Use a default image size of 4MB.
|
||||
grow := 4096*1024 - outf.Len()
|
||||
if grow > 0 {
|
||||
outf.Write(make([]byte, grow))
|
||||
}
|
||||
}
|
||||
|
||||
// Write the image to the output file.
|
||||
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
||||
return os.WriteFile(outfile, outf.Bytes(), 0666)
|
||||
}
|
||||
|
||||
+114
-84
@@ -4,8 +4,12 @@ package builder
|
||||
// parallel while taking care of dependencies.
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -29,7 +33,6 @@ type compileJob struct {
|
||||
dependencies []*compileJob
|
||||
result string // result (path)
|
||||
run func(*compileJob) (err error)
|
||||
state jobState
|
||||
err error // error if finished
|
||||
duration time.Duration // how long it took to run this job (only set after finishing)
|
||||
}
|
||||
@@ -44,41 +47,20 @@ func dummyCompileJob(result string) *compileJob {
|
||||
}
|
||||
}
|
||||
|
||||
// readyToRun returns whether this job is ready to run: it is itself not yet
|
||||
// started and all dependencies are finished.
|
||||
func (job *compileJob) readyToRun() bool {
|
||||
if job.state != jobStateQueued {
|
||||
// Already running or finished, so shouldn't be run again.
|
||||
return false
|
||||
}
|
||||
|
||||
// Check dependencies.
|
||||
for _, dep := range job.dependencies {
|
||||
if dep.state != jobStateFinished {
|
||||
// A dependency is not finished, so this job has to wait until it
|
||||
// is.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// All conditions are satisfied.
|
||||
return true
|
||||
}
|
||||
|
||||
// runJobs runs the indicated job and all its dependencies. For every job, all
|
||||
// the dependencies are run first. It returns the error of the first job that
|
||||
// fails.
|
||||
// It runs all jobs in the order of the dependencies slice, depth-first.
|
||||
// Therefore, if some jobs are preferred to run before others, they should be
|
||||
// ordered as such in the job dependencies.
|
||||
func runJobs(job *compileJob, parallelism int) error {
|
||||
if parallelism == 0 {
|
||||
// Have a default, if the parallelism isn't set. This is useful for
|
||||
func runJobs(job *compileJob, sema chan struct{}) error {
|
||||
if sema == nil {
|
||||
// Have a default, if the semaphore isn't set. This is useful for
|
||||
// tests.
|
||||
parallelism = runtime.NumCPU()
|
||||
sema = make(chan struct{}, runtime.NumCPU())
|
||||
}
|
||||
if parallelism < 1 {
|
||||
return fmt.Errorf("-p flag must be at least 1, provided -p=%d", parallelism)
|
||||
if cap(sema) == 0 {
|
||||
return errors.New("cannot 0 jobs at a time")
|
||||
}
|
||||
|
||||
// Create a slice of jobs to run, where all dependencies are run in order.
|
||||
@@ -97,64 +79,91 @@ func runJobs(job *compileJob, parallelism int) error {
|
||||
}
|
||||
addJobs(job)
|
||||
|
||||
// Create channels to communicate with the workers.
|
||||
doneChan := make(chan *compileJob)
|
||||
workerChan := make(chan *compileJob)
|
||||
defer close(workerChan)
|
||||
|
||||
// Start a number of workers.
|
||||
for i := 0; i < parallelism; i++ {
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## starting worker", i)
|
||||
waiting := make(map[*compileJob]map[*compileJob]struct{}, len(jobs))
|
||||
dependents := make(map[*compileJob][]*compileJob, len(jobs))
|
||||
jidx := make(map[*compileJob]int)
|
||||
var ready intHeap
|
||||
for i, job := range jobs {
|
||||
jidx[job] = i
|
||||
if len(job.dependencies) == 0 {
|
||||
// This job is ready to run.
|
||||
ready.Push(i)
|
||||
continue
|
||||
}
|
||||
|
||||
// Construct a map for dependencies which the job is currently waiting on.
|
||||
waitDeps := make(map[*compileJob]struct{})
|
||||
waiting[job] = waitDeps
|
||||
|
||||
// Add the job to the dependents list of each dependency.
|
||||
for _, dep := range job.dependencies {
|
||||
dependents[dep] = append(dependents[dep], job)
|
||||
waitDeps[dep] = struct{}{}
|
||||
}
|
||||
go jobWorker(workerChan, doneChan)
|
||||
}
|
||||
|
||||
// Create a channel to accept notifications of completion.
|
||||
doneChan := make(chan *compileJob)
|
||||
|
||||
// Send each job in the jobs slice to a worker, taking care of job
|
||||
// dependencies.
|
||||
numRunningJobs := 0
|
||||
var totalTime time.Duration
|
||||
start := time.Now()
|
||||
for {
|
||||
// If there are free workers, try starting a new job (if one is
|
||||
// available). If it succeeds, try again to fill the entire worker pool.
|
||||
if numRunningJobs < parallelism {
|
||||
jobToRun := nextJob(jobs)
|
||||
if jobToRun != nil {
|
||||
// Start job.
|
||||
for len(ready.IntSlice) > 0 || numRunningJobs != 0 {
|
||||
var completed *compileJob
|
||||
if len(ready.IntSlice) > 0 {
|
||||
select {
|
||||
case sema <- struct{}{}:
|
||||
// Start a job.
|
||||
job := jobs[heap.Pop(&ready).(int)]
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## start: ", jobToRun.description)
|
||||
fmt.Println("## start: ", job.description)
|
||||
}
|
||||
jobToRun.state = jobStateRunning
|
||||
workerChan <- jobToRun
|
||||
go runJob(job, doneChan)
|
||||
numRunningJobs++
|
||||
continue
|
||||
|
||||
case completed = <-doneChan:
|
||||
// A job completed.
|
||||
}
|
||||
} else {
|
||||
// Wait for a job to complete.
|
||||
completed = <-doneChan
|
||||
}
|
||||
|
||||
// When there are no jobs running, all jobs in the jobs slice must have
|
||||
// been finished. Therefore, the work is done.
|
||||
if numRunningJobs == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Wait until a job is finished.
|
||||
job := <-doneChan
|
||||
job.state = jobStateFinished
|
||||
numRunningJobs--
|
||||
totalTime += job.duration
|
||||
<-sema
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## finished:", job.description, "(time "+job.duration.String()+")")
|
||||
}
|
||||
if job.err != nil {
|
||||
// Wait for running jobs to finish.
|
||||
if completed.err != nil {
|
||||
// Wait for any current jobs to finish.
|
||||
for numRunningJobs != 0 {
|
||||
<-doneChan
|
||||
numRunningJobs--
|
||||
}
|
||||
// Return error of first failing job.
|
||||
return job.err
|
||||
|
||||
// The build failed.
|
||||
return completed.err
|
||||
}
|
||||
|
||||
// Update total run time.
|
||||
totalTime += completed.duration
|
||||
|
||||
// Update dependent jobs.
|
||||
for _, j := range dependents[completed] {
|
||||
wait := waiting[j]
|
||||
delete(wait, completed)
|
||||
if len(wait) == 0 {
|
||||
// This job is now ready to run.
|
||||
ready.Push(jidx[j])
|
||||
delete(waiting, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(waiting) != 0 {
|
||||
// There is a dependency cycle preventing some jobs from running.
|
||||
return errDependencyCycle{waiting}
|
||||
}
|
||||
|
||||
// Some statistics, if debugging.
|
||||
@@ -171,29 +180,50 @@ func runJobs(job *compileJob, parallelism int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// nextJob returns the first ready-to-run job.
|
||||
// This is an implementation detail of runJobs.
|
||||
func nextJob(jobs []*compileJob) *compileJob {
|
||||
for _, job := range jobs {
|
||||
if job.readyToRun() {
|
||||
return job
|
||||
}
|
||||
}
|
||||
return nil
|
||||
type errDependencyCycle struct {
|
||||
waiting map[*compileJob]map[*compileJob]struct{}
|
||||
}
|
||||
|
||||
// jobWorker is the goroutine that runs received jobs.
|
||||
// This is an implementation detail of runJobs.
|
||||
func jobWorker(workerChan, doneChan chan *compileJob) {
|
||||
for job := range workerChan {
|
||||
start := time.Now()
|
||||
if job.run != nil {
|
||||
err := job.run(job)
|
||||
if err != nil {
|
||||
job.err = err
|
||||
}
|
||||
func (err errDependencyCycle) Error() string {
|
||||
waits := make([]string, 0, len(err.waiting))
|
||||
for j, wait := range err.waiting {
|
||||
deps := make([]string, 0, len(wait))
|
||||
for dep := range wait {
|
||||
deps = append(deps, dep.description)
|
||||
}
|
||||
job.duration = time.Since(start)
|
||||
doneChan <- job
|
||||
sort.Strings(deps)
|
||||
|
||||
waits = append(waits, fmt.Sprintf("\t%s is waiting for [%s]",
|
||||
j.description, strings.Join(deps, ", "),
|
||||
))
|
||||
}
|
||||
sort.Strings(waits)
|
||||
return "deadlock:\n" + strings.Join(waits, "\n")
|
||||
}
|
||||
|
||||
type intHeap struct {
|
||||
sort.IntSlice
|
||||
}
|
||||
|
||||
func (h *intHeap) Push(x interface{}) {
|
||||
h.IntSlice = append(h.IntSlice, x.(int))
|
||||
}
|
||||
|
||||
func (h *intHeap) Pop() interface{} {
|
||||
x := h.IntSlice[len(h.IntSlice)-1]
|
||||
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
||||
return x
|
||||
}
|
||||
|
||||
// runJob runs a compile job and notifies doneChan of completion.
|
||||
func runJob(job *compileJob, doneChan chan *compileJob) {
|
||||
start := time.Now()
|
||||
if job.run != nil {
|
||||
err := job.run(job)
|
||||
if err != nil {
|
||||
job.err = err
|
||||
}
|
||||
}
|
||||
job.duration = time.Since(start)
|
||||
doneChan <- job
|
||||
}
|
||||
|
||||
+105
-26
@@ -1,10 +1,13 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
@@ -22,11 +25,11 @@ type Library struct {
|
||||
// cflags returns the C flags specific to this library
|
||||
cflags func(target, headerPath string) []string
|
||||
|
||||
// The source directory, relative to TINYGOROOT.
|
||||
sourceDir string
|
||||
// The source directory.
|
||||
sourceDir func() string
|
||||
|
||||
// The source files, relative to sourceDir.
|
||||
librarySources func(target string) []string
|
||||
librarySources func(target string) ([]string, error)
|
||||
|
||||
// The source code for the crt1.o file, relative to sourceDir.
|
||||
crt1Source string
|
||||
@@ -36,11 +39,12 @@ type Library struct {
|
||||
// The resulting directory may be stored in the provided tmpdir, which is
|
||||
// expected to be removed after the Load call.
|
||||
func (l *Library) Load(config *compileopts.Config, tmpdir string) (dir string, err error) {
|
||||
job, err := l.load(config, tmpdir)
|
||||
job, unlock, err := l.load(config, tmpdir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = runJobs(job, config.Options.Parallelism)
|
||||
defer unlock()
|
||||
err = runJobs(job, config.Options.Semaphore)
|
||||
return filepath.Dir(job.result), err
|
||||
}
|
||||
|
||||
@@ -52,28 +56,38 @@ func (l *Library) Load(config *compileopts.Config, tmpdir string) (dir string, e
|
||||
// output archive file, it is expected to be removed after use.
|
||||
// As a side effect, this call creates the library header files if they didn't
|
||||
// exist yet.
|
||||
func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, err error) {
|
||||
func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, abortLock func(), err error) {
|
||||
outdir, precompiled := config.LibcPath(l.name)
|
||||
archiveFilePath := filepath.Join(outdir, "lib.a")
|
||||
if precompiled {
|
||||
// Found a precompiled library for this OS/architecture. Return the path
|
||||
// directly.
|
||||
return dummyCompileJob(archiveFilePath), nil
|
||||
return dummyCompileJob(archiveFilePath), func() {}, nil
|
||||
}
|
||||
|
||||
// Create a lock on the output (if supported).
|
||||
// This is a bit messy, but avoids a deadlock because it is ordered consistently with other library loads within a build.
|
||||
outname := filepath.Base(outdir)
|
||||
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), outname+".lock"))
|
||||
var ok bool
|
||||
defer func() {
|
||||
if !ok {
|
||||
unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
// Try to fetch this library from the cache.
|
||||
if _, err := os.Stat(archiveFilePath); err == nil {
|
||||
return dummyCompileJob(archiveFilePath), nil
|
||||
return dummyCompileJob(archiveFilePath), func() {}, nil
|
||||
}
|
||||
// Cache miss, build it now.
|
||||
|
||||
// Create the destination directory where the components of this library
|
||||
// (lib.a file, include directory) are placed.
|
||||
outname := filepath.Base(outdir)
|
||||
err = os.MkdirAll(filepath.Join(goenv.Get("GOCACHE"), outname), 0o777)
|
||||
if err != nil {
|
||||
// Could not create directory (and not because it already exists).
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Make headers if needed.
|
||||
@@ -81,18 +95,38 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
target := config.Triple()
|
||||
if l.makeHeaders != nil {
|
||||
if _, err = os.Stat(headerPath); err != nil {
|
||||
temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*")
|
||||
temporaryHeaderPath, err := os.MkdirTemp(outdir, "include.tmp*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer os.RemoveAll(temporaryHeaderPath)
|
||||
err = l.makeHeaders(target, temporaryHeaderPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
err = os.Chmod(temporaryHeaderPath, 0o755) // TempDir uses 0o700 by default
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = os.Rename(temporaryHeaderPath, headerPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrExist):
|
||||
// Another invocation of TinyGo also seems to have already created the headers.
|
||||
|
||||
case runtime.GOOS == "windows" && errors.Is(err, fs.ErrPermission):
|
||||
// On Windows, a rename with a destination directory that already
|
||||
// exists does not result in an IsExist error, but rather in an
|
||||
// access denied error. To be sure, check for this case by checking
|
||||
// whether the target directory exists.
|
||||
if _, err := os.Stat(headerPath); err == nil {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,33 +135,57 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
||||
err = os.Mkdir(dir, 0777)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Precalculate the flags to the compiler invocation.
|
||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||
// reproducible. Otherwise the temporary directory is stored in the archive
|
||||
// itself, which varies each run.
|
||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
cpu := config.CPU()
|
||||
if cpu != "" {
|
||||
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
||||
// However, ARM has not done this.
|
||||
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||
args = append(args, "-march="+cpu)
|
||||
} else if strings.HasPrefix(target, "avr") {
|
||||
args = append(args, "-mmcu="+cpu)
|
||||
} else {
|
||||
args = append(args, "-mcpu="+cpu)
|
||||
}
|
||||
}
|
||||
if config.ABI() != "" {
|
||||
args = append(args, "-mabi="+config.ABI())
|
||||
}
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
|
||||
if strings.Split(target, "-")[2] == "linux" {
|
||||
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
} else {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(target, "avr") {
|
||||
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||
// from what most code (and certainly compiler-rt) expects. So we need
|
||||
// to force the compiler to use 64-bit floating point numbers for
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv64-") {
|
||||
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
||||
args = append(args, "-march=rv64gc")
|
||||
}
|
||||
if strings.HasPrefix(target, "xtensa") {
|
||||
// Hack to work around an issue in the Xtensa port:
|
||||
// https://github.com/espressif/llvm-project/issues/52
|
||||
// Hopefully this will be fixed soon (LLVM 14).
|
||||
args = append(args, "-D__ELF__")
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
// Create job to put all the object files in a single archive. This archive
|
||||
// file is the (static) library file.
|
||||
@@ -136,8 +194,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
description: "ar " + l.name + "/lib.a",
|
||||
result: filepath.Join(goenv.Get("GOCACHE"), outname, "lib.a"),
|
||||
run: func(*compileJob) error {
|
||||
defer once.Do(unlock)
|
||||
|
||||
// Create an archive of all object files.
|
||||
f, err := ioutil.TempFile(outdir, "libc.a.tmp*")
|
||||
f, err := os.CreateTemp(outdir, "libc.a.tmp*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -149,20 +209,30 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(f.Name(), 0o644) // TempFile uses 0o600 by default
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
return os.Rename(f.Name(), archiveFilePath)
|
||||
},
|
||||
}
|
||||
|
||||
sourceDir := l.sourceDir()
|
||||
|
||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||
// archive job above, so must be run first.
|
||||
for _, path := range l.librarySources(target) {
|
||||
paths, err := l.librarySources(target)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, path := range paths {
|
||||
// Strip leading "../" parts off the path.
|
||||
cleanpath := path
|
||||
for strings.HasPrefix(cleanpath, "../") {
|
||||
cleanpath = cleanpath[3:]
|
||||
}
|
||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
|
||||
srcpath := filepath.Join(sourceDir, path)
|
||||
objpath := filepath.Join(dir, cleanpath+".o")
|
||||
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||
objs = append(objs, objpath)
|
||||
@@ -172,6 +242,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", compileArgs...)
|
||||
}
|
||||
err := runCCompiler(compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
@@ -186,18 +259,21 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// (It could be done in parallel with creating the ar file, but it probably
|
||||
// won't make much of a difference in speed).
|
||||
if l.crt1Source != "" {
|
||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
|
||||
srcpath := filepath.Join(sourceDir, l.crt1Source)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*")
|
||||
tmpfile, err := os.CreateTemp(outdir, "crt1.o.tmp*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpfile.Close()
|
||||
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", compileArgs...)
|
||||
}
|
||||
err = runCCompiler(compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
@@ -207,5 +283,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
})
|
||||
}
|
||||
|
||||
return job, nil
|
||||
ok = true
|
||||
return job, func() {
|
||||
once.Do(unlock)
|
||||
}, nil
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,4 +1,4 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
// This file provides C wrappers for liblld.
|
||||
|
||||
@@ -8,17 +8,22 @@ extern "C" {
|
||||
|
||||
bool tinygo_link_elf(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
||||
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
|
||||
bool tinygo_link_macho(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
|
||||
bool tinygo_link_mingw(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
|
||||
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
|
||||
bool tinygo_link_wasm(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
||||
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
}
|
||||
|
||||
} // external "C"
|
||||
|
||||
+19
-6
@@ -1,6 +1,7 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -26,13 +27,14 @@ var MinGW = Library{
|
||||
_, err = io.Copy(outf, inf)
|
||||
return err
|
||||
},
|
||||
sourceDir: func() string { return "" }, // unused
|
||||
cflags: func(target, headerPath string) []string {
|
||||
// No flags necessary because there are no files to compile.
|
||||
return nil
|
||||
},
|
||||
librarySources: func(target string) []string {
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
// We only use the UCRT DLL file. No source files necessary.
|
||||
return nil
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ var MinGW = Library{
|
||||
//
|
||||
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
|
||||
// compile these files.
|
||||
func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
||||
var jobs []*compileJob
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
||||
@@ -51,7 +53,7 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||
for _, name := range []string{
|
||||
"kernel32.def.in",
|
||||
"api-ms-win-crt-conio-l1-1-0.def",
|
||||
"api-ms-win-crt-convert-l1-1-0.def",
|
||||
"api-ms-win-crt-convert-l1-1-0.def.in",
|
||||
"api-ms-win-crt-environment-l1-1-0.def",
|
||||
"api-ms-win-crt-filesystem-l1-1-0.def",
|
||||
"api-ms-win-crt-heap-l1-1-0.def",
|
||||
@@ -73,16 +75,27 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||
result: outpath,
|
||||
run: func(job *compileJob) error {
|
||||
defpath := inpath
|
||||
var archDef, emulation string
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
archDef = "-DDEF_X64"
|
||||
emulation = "i386pep"
|
||||
case "arm64":
|
||||
archDef = "-DDEF_ARM64"
|
||||
emulation = "arm64pe"
|
||||
default:
|
||||
return fmt.Errorf("unsupported architecture for mingw-w64: %s", goarch)
|
||||
}
|
||||
if strings.HasSuffix(inpath, ".in") {
|
||||
// .in files need to be preprocessed by a preprocessor (-E)
|
||||
// first.
|
||||
defpath = outpath + ".def"
|
||||
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
||||
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", archDef, "-DDATA", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
|
||||
return link("ld.lld", "-m", emulation, "-o", outpath, defpath)
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
|
||||
+29
-10
@@ -3,14 +3,15 @@ package builder
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var Musl = Library{
|
||||
@@ -35,7 +36,7 @@ var Musl = Library{
|
||||
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||
}
|
||||
for _, infile := range infiles {
|
||||
data, err := ioutil.ReadFile(infile)
|
||||
data, err := os.ReadFile(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,7 +64,7 @@ var Musl = Library{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
||||
data, err := os.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -78,7 +79,7 @@ var Musl = Library{
|
||||
cflags: func(target, headerPath string) []string {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
|
||||
return []string{
|
||||
cflags := []string{
|
||||
"-std=c99", // same as in musl
|
||||
"-D_XOPEN_SOURCE=700", // same as in musl
|
||||
// Musl triggers some warnings and we don't want to show any
|
||||
@@ -90,6 +91,8 @@ var Musl = Library{
|
||||
"-Wno-shift-op-parentheses",
|
||||
"-Wno-ignored-attributes",
|
||||
"-Wno-string-plus-int",
|
||||
"-Wno-ignored-pragmas",
|
||||
"-Wno-tautological-constant-out-of-range-compare",
|
||||
"-Qunused-arguments",
|
||||
// Select include dirs. Don't include standard library includes
|
||||
// (that would introduce host dependencies and other complications),
|
||||
@@ -101,10 +104,17 @@ var Musl = Library{
|
||||
"-I" + muslDir + "/src/internal",
|
||||
"-I" + headerPath,
|
||||
"-I" + muslDir + "/include",
|
||||
"-fno-stack-protector",
|
||||
}
|
||||
llvmMajor, _ := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if llvmMajor >= 15 {
|
||||
// This flag was added in Clang 15. It is not present in LLVM 14.
|
||||
cflags = append(cflags, "-Wno-deprecated-non-prototype")
|
||||
}
|
||||
return cflags
|
||||
},
|
||||
sourceDir: "lib/musl/src",
|
||||
librarySources: func(target string) []string {
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
globs := []string{
|
||||
"env/*.c",
|
||||
@@ -114,17 +124,23 @@ var Musl = Library{
|
||||
"internal/libc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"malloc/*.c",
|
||||
"malloc/mallocng/*.c",
|
||||
"mman/*.c",
|
||||
"math/*.c",
|
||||
"signal/*.c",
|
||||
"stdio/*.c",
|
||||
"string/*.c",
|
||||
"thread/" + arch + "/*.s",
|
||||
"thread/" + arch + "/*.c",
|
||||
"thread/*.c",
|
||||
"time/*.c",
|
||||
"unistd/*.c",
|
||||
}
|
||||
if arch == "arm" {
|
||||
// These files need to be added to the start for some reason.
|
||||
globs = append([]string{"thread/arm/*.c"}, globs...)
|
||||
}
|
||||
|
||||
var sources []string
|
||||
seenSources := map[string]struct{}{}
|
||||
@@ -138,13 +154,16 @@ var Musl = Library{
|
||||
// > ErrBadPattern, when pattern is malformed.
|
||||
// So the only possible error is when the (statically defined)
|
||||
// pattern is wrong. In other words, a programming bug.
|
||||
panic("could not glob source dirs: " + err.Error())
|
||||
return nil, fmt.Errorf("musl: could not glob source dirs: %w", err)
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return nil, fmt.Errorf("musl: did not find any files for pattern %#v", pattern)
|
||||
}
|
||||
for _, match := range matches {
|
||||
relpath, err := filepath.Rel(basepath, match)
|
||||
if err != nil {
|
||||
// Not sure if this is even possible.
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
// Make sure architecture specific files override generic files.
|
||||
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
|
||||
@@ -156,7 +175,7 @@ var Musl = Library{
|
||||
sources = append(sources, relpath)
|
||||
}
|
||||
}
|
||||
return sources
|
||||
return sources, nil
|
||||
},
|
||||
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -18,7 +18,7 @@ func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string)
|
||||
}
|
||||
|
||||
cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
|
||||
cmd.Stdout = ioutil.Discard
|
||||
cmd.Stdout = io.Discard
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package builder
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
@@ -87,7 +87,7 @@ func extractROM(path string) (uint64, []byte, error) {
|
||||
// Pad the difference
|
||||
rom = append(rom, make([]byte, diff)...)
|
||||
}
|
||||
data, err := ioutil.ReadAll(prog.Open())
|
||||
data, err := io.ReadAll(prog.Open())
|
||||
if err != nil {
|
||||
return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err}
|
||||
}
|
||||
|
||||
+384
-207
@@ -19,227 +19,404 @@ var Picolibc = Library{
|
||||
return f.Close()
|
||||
},
|
||||
cflags: func(target, headerPath string) []string {
|
||||
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
||||
newlibDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib")
|
||||
return []string{
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-std=gnu11",
|
||||
"-D_COMPILING_NEWLIB",
|
||||
"-DHAVE_ALIAS_ATTRIBUTE",
|
||||
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||
"-DTINY_STDIO",
|
||||
"-DPOSIX_IO",
|
||||
"-D_IEEE_LIBM",
|
||||
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||
"-D_WANT_IO_C99_FORMATS",
|
||||
"-nostdlibinc",
|
||||
"-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include",
|
||||
"-I" + picolibcDir + "/tinystdio",
|
||||
"-isystem", newlibDir + "/libc/include",
|
||||
"-I" + newlibDir + "/libc/tinystdio",
|
||||
"-I" + newlibDir + "/libm/common",
|
||||
"-I" + headerPath,
|
||||
}
|
||||
},
|
||||
sourceDir: "lib/picolibc/newlib/libc",
|
||||
librarySources: func(target string) []string {
|
||||
return picolibcSources
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
return picolibcSources, nil
|
||||
},
|
||||
}
|
||||
|
||||
var picolibcSources = []string{
|
||||
"../../../picolibc-stdio.c",
|
||||
"../../picolibc-stdio.c",
|
||||
|
||||
"tinystdio/asprintf.c",
|
||||
"tinystdio/atod_engine.c",
|
||||
"tinystdio/atod_ryu.c",
|
||||
"tinystdio/atof_engine.c",
|
||||
"tinystdio/atof_ryu.c",
|
||||
//"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
|
||||
"tinystdio/clearerr.c",
|
||||
"tinystdio/compare_exchange.c",
|
||||
"tinystdio/dtoa_data.c",
|
||||
"tinystdio/dtoa_engine.c",
|
||||
"tinystdio/dtoa_ryu.c",
|
||||
"tinystdio/ecvtbuf.c",
|
||||
"tinystdio/ecvt.c",
|
||||
"tinystdio/ecvt_data.c",
|
||||
"tinystdio/ecvtfbuf.c",
|
||||
"tinystdio/ecvtf.c",
|
||||
"tinystdio/ecvtf_data.c",
|
||||
"tinystdio/exchange.c",
|
||||
//"tinystdio/fclose.c", // posix-io
|
||||
"tinystdio/fcvtbuf.c",
|
||||
"tinystdio/fcvt.c",
|
||||
"tinystdio/fcvtfbuf.c",
|
||||
"tinystdio/fcvtf.c",
|
||||
"tinystdio/fdevopen.c",
|
||||
//"tinystdio/fdopen.c", // posix-io
|
||||
"tinystdio/feof.c",
|
||||
"tinystdio/ferror.c",
|
||||
"tinystdio/fflush.c",
|
||||
"tinystdio/fgetc.c",
|
||||
"tinystdio/fgets.c",
|
||||
"tinystdio/fileno.c",
|
||||
"tinystdio/filestrget.c",
|
||||
"tinystdio/filestrputalloc.c",
|
||||
"tinystdio/filestrput.c",
|
||||
//"tinystdio/fopen.c", // posix-io
|
||||
"tinystdio/fprintf.c",
|
||||
"tinystdio/fputc.c",
|
||||
"tinystdio/fputs.c",
|
||||
"tinystdio/fread.c",
|
||||
"tinystdio/fscanf.c",
|
||||
"tinystdio/fseek.c",
|
||||
"tinystdio/ftell.c",
|
||||
"tinystdio/ftoa_data.c",
|
||||
"tinystdio/ftoa_engine.c",
|
||||
"tinystdio/ftoa_ryu.c",
|
||||
"tinystdio/fwrite.c",
|
||||
"tinystdio/gcvtbuf.c",
|
||||
"tinystdio/gcvt.c",
|
||||
"tinystdio/gcvtfbuf.c",
|
||||
"tinystdio/gcvtf.c",
|
||||
"tinystdio/getchar.c",
|
||||
"tinystdio/gets.c",
|
||||
"tinystdio/matchcaseprefix.c",
|
||||
"tinystdio/perror.c",
|
||||
//"tinystdio/posixiob.c", // posix-io
|
||||
//"tinystdio/posixio.c", // posix-io
|
||||
"tinystdio/printf.c",
|
||||
"tinystdio/putchar.c",
|
||||
"tinystdio/puts.c",
|
||||
"tinystdio/ryu_divpow2.c",
|
||||
"tinystdio/ryu_log10.c",
|
||||
"tinystdio/ryu_log2pow5.c",
|
||||
"tinystdio/ryu_pow5bits.c",
|
||||
"tinystdio/ryu_table.c",
|
||||
"tinystdio/ryu_umul128.c",
|
||||
"tinystdio/scanf.c",
|
||||
"tinystdio/setbuf.c",
|
||||
"tinystdio/setvbuf.c",
|
||||
//"tinystdio/sflags.c", // posix-io
|
||||
"tinystdio/snprintf.c",
|
||||
"tinystdio/snprintfd.c",
|
||||
"tinystdio/snprintff.c",
|
||||
"tinystdio/sprintf.c",
|
||||
"tinystdio/sprintfd.c",
|
||||
"tinystdio/sprintff.c",
|
||||
"tinystdio/sscanf.c",
|
||||
"tinystdio/strfromd.c",
|
||||
"tinystdio/strfromf.c",
|
||||
"tinystdio/strtod.c",
|
||||
"tinystdio/strtod_l.c",
|
||||
"tinystdio/strtof.c",
|
||||
//"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
|
||||
//"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
|
||||
"tinystdio/ungetc.c",
|
||||
"tinystdio/vasprintf.c",
|
||||
"tinystdio/vfiprintf.c",
|
||||
"tinystdio/vfiscanf.c",
|
||||
"tinystdio/vfprintf.c",
|
||||
"tinystdio/vfprintff.c",
|
||||
"tinystdio/vfscanf.c",
|
||||
"tinystdio/vfscanff.c",
|
||||
"tinystdio/vprintf.c",
|
||||
"tinystdio/vscanf.c",
|
||||
"tinystdio/vsnprintf.c",
|
||||
"tinystdio/vsprintf.c",
|
||||
"tinystdio/vsscanf.c",
|
||||
// srcs_tinystdio
|
||||
"libc/tinystdio/asprintf.c",
|
||||
"libc/tinystdio/bufio.c",
|
||||
"libc/tinystdio/clearerr.c",
|
||||
"libc/tinystdio/ecvt_r.c",
|
||||
"libc/tinystdio/ecvt.c",
|
||||
"libc/tinystdio/ecvtf_r.c",
|
||||
"libc/tinystdio/ecvtf.c",
|
||||
"libc/tinystdio/fcvt.c",
|
||||
"libc/tinystdio/fcvt_r.c",
|
||||
"libc/tinystdio/fcvtf.c",
|
||||
"libc/tinystdio/fcvtf_r.c",
|
||||
"libc/tinystdio/gcvt.c",
|
||||
"libc/tinystdio/gcvtf.c",
|
||||
"libc/tinystdio/fclose.c",
|
||||
"libc/tinystdio/fdevopen.c",
|
||||
"libc/tinystdio/feof.c",
|
||||
"libc/tinystdio/ferror.c",
|
||||
"libc/tinystdio/fflush.c",
|
||||
"libc/tinystdio/fgetc.c",
|
||||
"libc/tinystdio/fgets.c",
|
||||
"libc/tinystdio/fileno.c",
|
||||
"libc/tinystdio/filestrget.c",
|
||||
"libc/tinystdio/filestrput.c",
|
||||
"libc/tinystdio/filestrputalloc.c",
|
||||
"libc/tinystdio/fmemopen.c",
|
||||
"libc/tinystdio/fprintf.c",
|
||||
"libc/tinystdio/fputc.c",
|
||||
"libc/tinystdio/fputs.c",
|
||||
"libc/tinystdio/fread.c",
|
||||
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
|
||||
"libc/tinystdio/fscanf.c",
|
||||
"libc/tinystdio/fseek.c",
|
||||
"libc/tinystdio/fseeko.c",
|
||||
"libc/tinystdio/ftell.c",
|
||||
"libc/tinystdio/ftello.c",
|
||||
"libc/tinystdio/fwrite.c",
|
||||
"libc/tinystdio/getchar.c",
|
||||
"libc/tinystdio/gets.c",
|
||||
"libc/tinystdio/matchcaseprefix.c",
|
||||
"libc/tinystdio/mktemp.c",
|
||||
"libc/tinystdio/perror.c",
|
||||
"libc/tinystdio/printf.c",
|
||||
"libc/tinystdio/putchar.c",
|
||||
"libc/tinystdio/puts.c",
|
||||
"libc/tinystdio/rewind.c",
|
||||
"libc/tinystdio/scanf.c",
|
||||
"libc/tinystdio/setbuf.c",
|
||||
"libc/tinystdio/setbuffer.c",
|
||||
"libc/tinystdio/setlinebuf.c",
|
||||
"libc/tinystdio/setvbuf.c",
|
||||
"libc/tinystdio/snprintf.c",
|
||||
"libc/tinystdio/sprintf.c",
|
||||
"libc/tinystdio/snprintfd.c",
|
||||
"libc/tinystdio/snprintff.c",
|
||||
"libc/tinystdio/sprintff.c",
|
||||
"libc/tinystdio/sprintfd.c",
|
||||
"libc/tinystdio/sscanf.c",
|
||||
"libc/tinystdio/strfromf.c",
|
||||
"libc/tinystdio/strfromd.c",
|
||||
"libc/tinystdio/strtof.c",
|
||||
"libc/tinystdio/strtof_l.c",
|
||||
"libc/tinystdio/strtod.c",
|
||||
"libc/tinystdio/strtod_l.c",
|
||||
"libc/tinystdio/ungetc.c",
|
||||
"libc/tinystdio/vasprintf.c",
|
||||
"libc/tinystdio/vfiprintf.c",
|
||||
"libc/tinystdio/vfprintf.c",
|
||||
"libc/tinystdio/vfprintff.c",
|
||||
"libc/tinystdio/vfscanf.c",
|
||||
"libc/tinystdio/vfiscanf.c",
|
||||
"libc/tinystdio/vfscanff.c",
|
||||
"libc/tinystdio/vprintf.c",
|
||||
"libc/tinystdio/vscanf.c",
|
||||
"libc/tinystdio/vsscanf.c",
|
||||
"libc/tinystdio/vsnprintf.c",
|
||||
"libc/tinystdio/vsprintf.c",
|
||||
|
||||
"string/bcmp.c",
|
||||
"string/bcopy.c",
|
||||
"string/bzero.c",
|
||||
"string/explicit_bzero.c",
|
||||
"string/ffsl.c",
|
||||
"string/ffsll.c",
|
||||
"string/fls.c",
|
||||
"string/flsl.c",
|
||||
"string/flsll.c",
|
||||
"string/gnu_basename.c",
|
||||
"string/index.c",
|
||||
"string/memccpy.c",
|
||||
"string/memchr.c",
|
||||
"string/memcmp.c",
|
||||
"string/memcpy.c",
|
||||
"string/memmem.c",
|
||||
"string/memmove.c",
|
||||
"string/mempcpy.c",
|
||||
"string/memrchr.c",
|
||||
"string/memset.c",
|
||||
"string/rawmemchr.c",
|
||||
"string/rindex.c",
|
||||
"string/stpcpy.c",
|
||||
"string/stpncpy.c",
|
||||
"string/strcasecmp.c",
|
||||
"string/strcasecmp_l.c",
|
||||
"string/strcasestr.c",
|
||||
"string/strcat.c",
|
||||
"string/strchr.c",
|
||||
"string/strchrnul.c",
|
||||
"string/strcmp.c",
|
||||
"string/strcoll.c",
|
||||
"string/strcoll_l.c",
|
||||
"string/strcpy.c",
|
||||
"string/strcspn.c",
|
||||
"string/strdup.c",
|
||||
"string/strerror.c",
|
||||
"string/strerror_r.c",
|
||||
"string/strlcat.c",
|
||||
"string/strlcpy.c",
|
||||
"string/strlen.c",
|
||||
"string/strlwr.c",
|
||||
"string/strncasecmp.c",
|
||||
"string/strncasecmp_l.c",
|
||||
"string/strncat.c",
|
||||
"string/strncmp.c",
|
||||
"string/strncpy.c",
|
||||
"string/strndup.c",
|
||||
"string/strnlen.c",
|
||||
"string/strnstr.c",
|
||||
"string/strpbrk.c",
|
||||
"string/strrchr.c",
|
||||
"string/strsep.c",
|
||||
"string/strsignal.c",
|
||||
"string/strspn.c",
|
||||
"string/strstr.c",
|
||||
"string/strtok.c",
|
||||
"string/strtok_r.c",
|
||||
"string/strupr.c",
|
||||
"string/strverscmp.c",
|
||||
"string/strxfrm.c",
|
||||
"string/strxfrm_l.c",
|
||||
"string/swab.c",
|
||||
"string/timingsafe_bcmp.c",
|
||||
"string/timingsafe_memcmp.c",
|
||||
"string/u_strerr.c",
|
||||
"string/wcpcpy.c",
|
||||
"string/wcpncpy.c",
|
||||
"string/wcscasecmp.c",
|
||||
"string/wcscasecmp_l.c",
|
||||
"string/wcscat.c",
|
||||
"string/wcschr.c",
|
||||
"string/wcscmp.c",
|
||||
"string/wcscoll.c",
|
||||
"string/wcscoll_l.c",
|
||||
"string/wcscpy.c",
|
||||
"string/wcscspn.c",
|
||||
"string/wcsdup.c",
|
||||
"string/wcslcat.c",
|
||||
"string/wcslcpy.c",
|
||||
"string/wcslen.c",
|
||||
"string/wcsncasecmp.c",
|
||||
"string/wcsncasecmp_l.c",
|
||||
"string/wcsncat.c",
|
||||
"string/wcsncmp.c",
|
||||
"string/wcsncpy.c",
|
||||
"string/wcsnlen.c",
|
||||
"string/wcspbrk.c",
|
||||
"string/wcsrchr.c",
|
||||
"string/wcsspn.c",
|
||||
"string/wcsstr.c",
|
||||
"string/wcstok.c",
|
||||
"string/wcswidth.c",
|
||||
"string/wcsxfrm.c",
|
||||
"string/wcsxfrm_l.c",
|
||||
"string/wcwidth.c",
|
||||
"string/wmemchr.c",
|
||||
"string/wmemcmp.c",
|
||||
"string/wmemcpy.c",
|
||||
"string/wmemmove.c",
|
||||
"string/wmempcpy.c",
|
||||
"string/wmemset.c",
|
||||
"string/xpg_strerror_r.c",
|
||||
"libc/string/bcmp.c",
|
||||
"libc/string/bcopy.c",
|
||||
"libc/string/bzero.c",
|
||||
"libc/string/explicit_bzero.c",
|
||||
"libc/string/ffsl.c",
|
||||
"libc/string/ffsll.c",
|
||||
"libc/string/fls.c",
|
||||
"libc/string/flsl.c",
|
||||
"libc/string/flsll.c",
|
||||
"libc/string/gnu_basename.c",
|
||||
"libc/string/index.c",
|
||||
"libc/string/memccpy.c",
|
||||
"libc/string/memchr.c",
|
||||
"libc/string/memcmp.c",
|
||||
"libc/string/memcpy.c",
|
||||
"libc/string/memmem.c",
|
||||
"libc/string/memmove.c",
|
||||
"libc/string/mempcpy.c",
|
||||
"libc/string/memrchr.c",
|
||||
"libc/string/memset.c",
|
||||
"libc/string/rawmemchr.c",
|
||||
"libc/string/rindex.c",
|
||||
"libc/string/stpcpy.c",
|
||||
"libc/string/stpncpy.c",
|
||||
"libc/string/strcasecmp.c",
|
||||
"libc/string/strcasecmp_l.c",
|
||||
"libc/string/strcasestr.c",
|
||||
"libc/string/strcat.c",
|
||||
"libc/string/strchr.c",
|
||||
"libc/string/strchrnul.c",
|
||||
"libc/string/strcmp.c",
|
||||
"libc/string/strcoll.c",
|
||||
"libc/string/strcoll_l.c",
|
||||
"libc/string/strcpy.c",
|
||||
"libc/string/strcspn.c",
|
||||
"libc/string/strdup.c",
|
||||
"libc/string/strerror.c",
|
||||
"libc/string/strerror_r.c",
|
||||
"libc/string/strlcat.c",
|
||||
"libc/string/strlcpy.c",
|
||||
"libc/string/strlen.c",
|
||||
"libc/string/strlwr.c",
|
||||
"libc/string/strncasecmp.c",
|
||||
"libc/string/strncasecmp_l.c",
|
||||
"libc/string/strncat.c",
|
||||
"libc/string/strncmp.c",
|
||||
"libc/string/strncpy.c",
|
||||
"libc/string/strndup.c",
|
||||
"libc/string/strnlen.c",
|
||||
"libc/string/strnstr.c",
|
||||
"libc/string/strpbrk.c",
|
||||
"libc/string/strrchr.c",
|
||||
"libc/string/strsep.c",
|
||||
"libc/string/strsignal.c",
|
||||
"libc/string/strspn.c",
|
||||
"libc/string/strstr.c",
|
||||
"libc/string/strtok.c",
|
||||
"libc/string/strtok_r.c",
|
||||
"libc/string/strupr.c",
|
||||
"libc/string/strverscmp.c",
|
||||
"libc/string/strxfrm.c",
|
||||
"libc/string/strxfrm_l.c",
|
||||
"libc/string/swab.c",
|
||||
"libc/string/timingsafe_bcmp.c",
|
||||
"libc/string/timingsafe_memcmp.c",
|
||||
"libc/string/u_strerr.c",
|
||||
"libc/string/wcpcpy.c",
|
||||
"libc/string/wcpncpy.c",
|
||||
"libc/string/wcscasecmp.c",
|
||||
"libc/string/wcscasecmp_l.c",
|
||||
"libc/string/wcscat.c",
|
||||
"libc/string/wcschr.c",
|
||||
"libc/string/wcscmp.c",
|
||||
"libc/string/wcscoll.c",
|
||||
"libc/string/wcscoll_l.c",
|
||||
"libc/string/wcscpy.c",
|
||||
"libc/string/wcscspn.c",
|
||||
"libc/string/wcsdup.c",
|
||||
"libc/string/wcslcat.c",
|
||||
"libc/string/wcslcpy.c",
|
||||
"libc/string/wcslen.c",
|
||||
"libc/string/wcsncasecmp.c",
|
||||
"libc/string/wcsncasecmp_l.c",
|
||||
"libc/string/wcsncat.c",
|
||||
"libc/string/wcsncmp.c",
|
||||
"libc/string/wcsncpy.c",
|
||||
"libc/string/wcsnlen.c",
|
||||
"libc/string/wcspbrk.c",
|
||||
"libc/string/wcsrchr.c",
|
||||
"libc/string/wcsspn.c",
|
||||
"libc/string/wcsstr.c",
|
||||
"libc/string/wcstok.c",
|
||||
"libc/string/wcswidth.c",
|
||||
"libc/string/wcsxfrm.c",
|
||||
"libc/string/wcsxfrm_l.c",
|
||||
"libc/string/wcwidth.c",
|
||||
"libc/string/wmemchr.c",
|
||||
"libc/string/wmemcmp.c",
|
||||
"libc/string/wmemcpy.c",
|
||||
"libc/string/wmemmove.c",
|
||||
"libc/string/wmempcpy.c",
|
||||
"libc/string/wmemset.c",
|
||||
"libc/string/xpg_strerror_r.c",
|
||||
|
||||
"libm/common/sf_finite.c",
|
||||
"libm/common/sf_copysign.c",
|
||||
"libm/common/sf_modf.c",
|
||||
"libm/common/sf_scalbn.c",
|
||||
"libm/common/sf_cbrt.c",
|
||||
"libm/common/sf_exp10.c",
|
||||
"libm/common/sf_expm1.c",
|
||||
"libm/common/sf_ilogb.c",
|
||||
"libm/common/sf_infinity.c",
|
||||
"libm/common/sf_isinf.c",
|
||||
"libm/common/sf_isinff.c",
|
||||
"libm/common/sf_isnan.c",
|
||||
"libm/common/sf_isnanf.c",
|
||||
"libm/common/sf_issignaling.c",
|
||||
"libm/common/sf_log1p.c",
|
||||
"libm/common/sf_nan.c",
|
||||
"libm/common/sf_nextafter.c",
|
||||
"libm/common/sf_pow10.c",
|
||||
"libm/common/sf_rint.c",
|
||||
"libm/common/sf_logb.c",
|
||||
"libm/common/sf_fdim.c",
|
||||
"libm/common/sf_fma.c",
|
||||
"libm/common/sf_fmax.c",
|
||||
"libm/common/sf_fmin.c",
|
||||
"libm/common/sf_fpclassify.c",
|
||||
"libm/common/sf_lrint.c",
|
||||
"libm/common/sf_llrint.c",
|
||||
"libm/common/sf_lround.c",
|
||||
"libm/common/sf_llround.c",
|
||||
"libm/common/sf_nearbyint.c",
|
||||
"libm/common/sf_remquo.c",
|
||||
"libm/common/sf_round.c",
|
||||
"libm/common/sf_scalbln.c",
|
||||
"libm/common/sf_trunc.c",
|
||||
"libm/common/sf_exp.c",
|
||||
"libm/common/sf_exp2.c",
|
||||
"libm/common/sf_exp2_data.c",
|
||||
"libm/common/sf_log.c",
|
||||
"libm/common/sf_log_data.c",
|
||||
"libm/common/sf_log2.c",
|
||||
"libm/common/sf_log2_data.c",
|
||||
"libm/common/sf_pow_log2_data.c",
|
||||
"libm/common/sf_pow.c",
|
||||
|
||||
"libm/common/s_finite.c",
|
||||
"libm/common/s_copysign.c",
|
||||
"libm/common/s_modf.c",
|
||||
"libm/common/s_scalbn.c",
|
||||
"libm/common/s_cbrt.c",
|
||||
"libm/common/s_exp10.c",
|
||||
"libm/common/s_expm1.c",
|
||||
"libm/common/s_ilogb.c",
|
||||
"libm/common/s_infinity.c",
|
||||
"libm/common/s_iseqsig.c",
|
||||
"libm/common/s_isinf.c",
|
||||
"libm/common/s_isinfd.c",
|
||||
"libm/common/s_isnan.c",
|
||||
"libm/common/s_isnand.c",
|
||||
"libm/common/s_issignaling.c",
|
||||
"libm/common/s_log1p.c",
|
||||
"libm/common/s_nan.c",
|
||||
"libm/common/s_nextafter.c",
|
||||
"libm/common/s_pow10.c",
|
||||
"libm/common/s_rint.c",
|
||||
"libm/common/s_logb.c",
|
||||
"libm/common/s_log2.c",
|
||||
"libm/common/s_fdim.c",
|
||||
"libm/common/s_fma.c",
|
||||
"libm/common/s_fmax.c",
|
||||
"libm/common/s_fmin.c",
|
||||
"libm/common/s_fpclassify.c",
|
||||
"libm/common/s_getpayload.c",
|
||||
"libm/common/s_lrint.c",
|
||||
"libm/common/s_llrint.c",
|
||||
"libm/common/s_lround.c",
|
||||
"libm/common/s_llround.c",
|
||||
"libm/common/s_nearbyint.c",
|
||||
"libm/common/s_remquo.c",
|
||||
"libm/common/s_round.c",
|
||||
"libm/common/s_scalbln.c",
|
||||
"libm/common/s_signbit.c",
|
||||
"libm/common/s_trunc.c",
|
||||
"libm/common/exp.c",
|
||||
"libm/common/exp2.c",
|
||||
"libm/common/exp_data.c",
|
||||
"libm/common/math_err_with_errno.c",
|
||||
"libm/common/math_err_uflow.c",
|
||||
"libm/common/math_err_oflow.c",
|
||||
"libm/common/math_err_divzero.c",
|
||||
"libm/common/math_err_invalid.c",
|
||||
"libm/common/math_err_may_uflow.c",
|
||||
"libm/common/math_err_check_uflow.c",
|
||||
"libm/common/math_err_check_oflow.c",
|
||||
"libm/common/math_inexact.c",
|
||||
"libm/common/math_inexactf.c",
|
||||
"libm/common/log.c",
|
||||
"libm/common/log_data.c",
|
||||
"libm/common/log2.c",
|
||||
"libm/common/log2_data.c",
|
||||
"libm/common/pow.c",
|
||||
"libm/common/pow_log_data.c",
|
||||
|
||||
"libm/math/k_cos.c",
|
||||
"libm/math/k_rem_pio2.c",
|
||||
"libm/math/k_sin.c",
|
||||
"libm/math/k_tan.c",
|
||||
"libm/math/kf_cos.c",
|
||||
"libm/math/kf_rem_pio2.c",
|
||||
"libm/math/kf_sin.c",
|
||||
"libm/math/kf_tan.c",
|
||||
"libm/math/s_acos.c",
|
||||
"libm/math/s_acosh.c",
|
||||
"libm/math/s_asin.c",
|
||||
"libm/math/s_asinh.c",
|
||||
"libm/math/s_atan.c",
|
||||
"libm/math/s_atan2.c",
|
||||
"libm/math/s_atanh.c",
|
||||
"libm/math/s_ceil.c",
|
||||
"libm/math/s_cos.c",
|
||||
"libm/math/s_cosh.c",
|
||||
"libm/math/s_drem.c",
|
||||
"libm/math/s_erf.c",
|
||||
"libm/math/s_exp.c",
|
||||
"libm/math/s_exp2.c",
|
||||
"libm/math/s_fabs.c",
|
||||
"libm/math/s_floor.c",
|
||||
"libm/math/s_fmod.c",
|
||||
"libm/math/s_frexp.c",
|
||||
"libm/math/s_gamma.c",
|
||||
"libm/math/s_hypot.c",
|
||||
"libm/math/s_j0.c",
|
||||
"libm/math/s_j1.c",
|
||||
"libm/math/s_jn.c",
|
||||
"libm/math/s_lgamma.c",
|
||||
"libm/math/s_log.c",
|
||||
"libm/math/s_log10.c",
|
||||
"libm/math/s_pow.c",
|
||||
"libm/math/s_rem_pio2.c",
|
||||
"libm/math/s_remainder.c",
|
||||
"libm/math/s_scalb.c",
|
||||
"libm/math/s_signif.c",
|
||||
"libm/math/s_sin.c",
|
||||
"libm/math/s_sincos.c",
|
||||
"libm/math/s_sinh.c",
|
||||
"libm/math/s_sqrt.c",
|
||||
"libm/math/s_tan.c",
|
||||
"libm/math/s_tanh.c",
|
||||
"libm/math/s_tgamma.c",
|
||||
"libm/math/sf_acos.c",
|
||||
"libm/math/sf_acosh.c",
|
||||
"libm/math/sf_asin.c",
|
||||
"libm/math/sf_asinh.c",
|
||||
"libm/math/sf_atan.c",
|
||||
"libm/math/sf_atan2.c",
|
||||
"libm/math/sf_atanh.c",
|
||||
"libm/math/sf_ceil.c",
|
||||
"libm/math/sf_cos.c",
|
||||
"libm/math/sf_cosh.c",
|
||||
"libm/math/sf_drem.c",
|
||||
"libm/math/sf_erf.c",
|
||||
"libm/math/sf_exp.c",
|
||||
"libm/math/sf_exp2.c",
|
||||
"libm/math/sf_fabs.c",
|
||||
"libm/math/sf_floor.c",
|
||||
"libm/math/sf_fmod.c",
|
||||
"libm/math/sf_frexp.c",
|
||||
"libm/math/sf_gamma.c",
|
||||
"libm/math/sf_hypot.c",
|
||||
"libm/math/sf_j0.c",
|
||||
"libm/math/sf_j1.c",
|
||||
"libm/math/sf_jn.c",
|
||||
"libm/math/sf_lgamma.c",
|
||||
"libm/math/sf_log.c",
|
||||
"libm/math/sf_log10.c",
|
||||
"libm/math/sf_log2.c",
|
||||
"libm/math/sf_pow.c",
|
||||
"libm/math/sf_rem_pio2.c",
|
||||
"libm/math/sf_remainder.c",
|
||||
"libm/math/sf_scalb.c",
|
||||
"libm/math/sf_signif.c",
|
||||
"libm/math/sf_sin.c",
|
||||
"libm/math/sf_sincos.c",
|
||||
"libm/math/sf_sinh.c",
|
||||
"libm/math/sf_sqrt.c",
|
||||
"libm/math/sf_tan.c",
|
||||
"libm/math/sf_tanh.c",
|
||||
"libm/math/sf_tgamma.c",
|
||||
"libm/math/sr_lgamma.c",
|
||||
"libm/math/srf_lgamma.c",
|
||||
}
|
||||
|
||||
+279
-17
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -96,6 +98,17 @@ const (
|
||||
memoryStack
|
||||
)
|
||||
|
||||
func (t memoryType) String() string {
|
||||
return [...]string{
|
||||
0: "-",
|
||||
memoryCode: "code",
|
||||
memoryData: "data",
|
||||
memoryROData: "rodata",
|
||||
memoryBSS: "bss",
|
||||
memoryStack: "stack",
|
||||
}[t]
|
||||
}
|
||||
|
||||
// Regular expressions to match particular symbol names. These are not stored as
|
||||
// DWARF variables because they have no mapping to source code global variables.
|
||||
var (
|
||||
@@ -104,17 +117,13 @@ var (
|
||||
// alloc: heap allocations during init interpretation
|
||||
// pack: data created when storing a constant in an interface for example
|
||||
// string: buffer behind strings
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
||||
|
||||
// Reflect sidetables. Created by the reflect lowering pass.
|
||||
// See src/reflect/sidetables.go.
|
||||
reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
||||
)
|
||||
|
||||
// readProgramSizeFromDWARF reads the source location for each line of code and
|
||||
// each variable in the program, as far as this is stored in the DWARF debug
|
||||
// information.
|
||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
|
||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone bool) ([]addressLine, error) {
|
||||
r := data.Reader()
|
||||
var lines []*dwarf.LineFile
|
||||
var addresses []addressLine
|
||||
@@ -156,7 +165,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 {
|
||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 && skipTombstone {
|
||||
// Tombstone value. This symbol has been removed, for
|
||||
// example by the --gc-sections linker flag. It is still
|
||||
// here in the debug information because the linker can't
|
||||
@@ -165,6 +174,10 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
// skipped.
|
||||
// For more details, see (among others):
|
||||
// https://reviews.llvm.org/D84825
|
||||
// The value 0 can however really occur in object files,
|
||||
// that typically start at address 0. So don't skip
|
||||
// tombstone values in object files (like when parsing MachO
|
||||
// files).
|
||||
for {
|
||||
err := lr.Next(&lineEntry)
|
||||
if err != nil {
|
||||
@@ -243,6 +256,65 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// Read a MachO object file and return a line table.
|
||||
// Also return an index from symbol name to start address in the line table.
|
||||
func readMachOSymbolAddresses(path string) (map[string]int, []addressLine, error) {
|
||||
// Some constants from mach-o/nlist.h
|
||||
// See: https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
|
||||
const (
|
||||
N_STAB = 0xe0
|
||||
N_TYPE = 0x0e // bitmask for N_TYPE field
|
||||
N_SECT = 0xe // one of the possible type in the N_TYPE field
|
||||
)
|
||||
|
||||
// Read DWARF from the given object file.
|
||||
file, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
dwarf, err := file.DWARF()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
lines, err := readProgramSizeFromDWARF(dwarf, 0, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Make a map from start addresses to indices in the line table (because the
|
||||
// line table is a slice, not a map).
|
||||
addressToLine := make(map[uint64]int, len(lines))
|
||||
for i, line := range lines {
|
||||
if _, ok := addressToLine[line.Address]; ok {
|
||||
addressToLine[line.Address] = -1
|
||||
continue
|
||||
}
|
||||
addressToLine[line.Address] = i
|
||||
}
|
||||
|
||||
// Make a map that for each symbol gives the start index in the line table.
|
||||
addresses := make(map[string]int, len(addressToLine))
|
||||
for _, symbol := range file.Symtab.Syms {
|
||||
if symbol.Type&N_STAB != 0 {
|
||||
continue // STABS entry, ignore
|
||||
}
|
||||
if symbol.Type&0x0e != N_SECT {
|
||||
continue // undefined symbol
|
||||
}
|
||||
if index, ok := addressToLine[symbol.Value]; ok && index >= 0 {
|
||||
if _, ok := addresses[symbol.Name]; ok {
|
||||
// There is a duplicate. Mark it as unavailable.
|
||||
addresses[symbol.Name] = -1
|
||||
continue
|
||||
}
|
||||
addresses[symbol.Name] = index
|
||||
}
|
||||
}
|
||||
|
||||
return addresses, lines, nil
|
||||
}
|
||||
|
||||
// loadProgramSize calculate a program/data size breakdown of each package for a
|
||||
// given ELF file.
|
||||
// If the file doesn't contain DWARF debug information, the returned program
|
||||
@@ -265,7 +337,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -299,7 +371,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
if section.Flags&elf.SHF_ALLOC == 0 {
|
||||
continue
|
||||
}
|
||||
if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
|
||||
if packageSymbolRegexp.MatchString(symbol.Name) {
|
||||
addresses = append(addresses, addressLine{
|
||||
Address: symbol.Value,
|
||||
Length: symbol.Size,
|
||||
@@ -356,6 +428,194 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if file, err := macho.NewFile(f); err == nil {
|
||||
// Read segments, for use while reading through sections.
|
||||
segments := map[string]*macho.Segment{}
|
||||
for _, load := range file.Loads {
|
||||
switch load := load.(type) {
|
||||
case *macho.Segment:
|
||||
segments[load.Name] = load
|
||||
}
|
||||
}
|
||||
|
||||
// Read MachO sections.
|
||||
for _, section := range file.Sections {
|
||||
sectionType := section.Flags & 0xff
|
||||
sectionFlags := section.Flags >> 8
|
||||
segment := segments[section.Seg]
|
||||
// For the constants used here, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
|
||||
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
|
||||
// Section containing only instructions.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryCode,
|
||||
})
|
||||
} else if sectionType == 1 { // S_ZEROFILL
|
||||
// Section filled with zeroes on demand.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryBSS,
|
||||
})
|
||||
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
|
||||
// Protection doesn't allow writes, so mark this section read-only.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryROData,
|
||||
})
|
||||
} else {
|
||||
// The rest is assumed to be regular data.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Read DWARF information.
|
||||
// The data isn't stored directly in the binary as in most executable
|
||||
// formats. Instead, it is left in the object files that were used as a
|
||||
// basis for linking. The executable does however contain STABS debug
|
||||
// information that points to the source object file and is used by
|
||||
// debuggers.
|
||||
// For more information:
|
||||
// http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme
|
||||
var objSymbolNames map[string]int
|
||||
var objAddresses []addressLine
|
||||
var previousSymbol macho.Symbol
|
||||
for _, symbol := range file.Symtab.Syms {
|
||||
// STABS constants, from mach-o/stab.h:
|
||||
// https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/stab.h.auto.html
|
||||
const (
|
||||
N_GSYM = 0x20
|
||||
N_FUN = 0x24
|
||||
N_STSYM = 0x26
|
||||
N_SO = 0x64
|
||||
N_OSO = 0x66
|
||||
)
|
||||
if symbol.Type == N_OSO {
|
||||
// Found an object file. Now try to parse it.
|
||||
objSymbolNames, objAddresses, err = readMachOSymbolAddresses(symbol.Name)
|
||||
if err != nil && sizesDebug {
|
||||
// Errors are normally ignored. If there is an error, it's
|
||||
// simply treated as that the DWARF is not available.
|
||||
fmt.Fprintf(os.Stderr, "could not read DWARF from file %s: %s\n", symbol.Name, err)
|
||||
}
|
||||
} else if symbol.Type == N_FUN {
|
||||
// Found a function.
|
||||
// The way this is encoded is a bit weird. MachO symbols don't
|
||||
// have a length. What I've found is that the length is encoded
|
||||
// by first having a N_FUN symbol as usual, and then having a
|
||||
// symbol with a zero-length name that has the value not set to
|
||||
// the address of the symbol but to the length. So in order to
|
||||
// get both the address and the length, we look for a symbol
|
||||
// with a name followed by a symbol without a name.
|
||||
if symbol.Name == "" && previousSymbol.Type == N_FUN && previousSymbol.Name != "" {
|
||||
// Functions are encoded as many small chunks in the line
|
||||
// table (one or a few instructions per source line). But
|
||||
// the symbol length covers the whole symbols, over many
|
||||
// lines and possibly including inlined functions. So we
|
||||
// continue to iterate through the objAddresses slice until
|
||||
// we've found all the source lines that are part of this
|
||||
// symbol.
|
||||
address := previousSymbol.Value
|
||||
length := symbol.Value
|
||||
if index, ok := objSymbolNames[previousSymbol.Name]; ok && index >= 0 {
|
||||
for length > 0 {
|
||||
line := objAddresses[index]
|
||||
line.Address = address
|
||||
if line.Length > length {
|
||||
// Line extends beyond the end of te symbol?
|
||||
// Weird, shouldn't happen.
|
||||
break
|
||||
}
|
||||
addresses = append(addresses, line)
|
||||
index++
|
||||
length -= line.Length
|
||||
address += line.Length
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if symbol.Type == N_GSYM || symbol.Type == N_STSYM {
|
||||
// Global variables.
|
||||
if index, ok := objSymbolNames[symbol.Name]; ok {
|
||||
address := objAddresses[index]
|
||||
address.Address = symbol.Value
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
}
|
||||
previousSymbol = symbol
|
||||
}
|
||||
} else if file, err := pe.NewFile(f); err == nil {
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Read COFF sections.
|
||||
optionalHeader := file.OptionalHeader.(*pe.OptionalHeader64)
|
||||
for _, section := range file.Sections {
|
||||
// For more information:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header
|
||||
const (
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
|
||||
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
|
||||
IMAGE_SCN_MEM_READ = 0x40000000
|
||||
IMAGE_SCN_MEM_WRITE = 0x80000000
|
||||
)
|
||||
if section.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
|
||||
// Debug sections, etc.
|
||||
continue
|
||||
}
|
||||
address := uint64(section.VirtualAddress) + optionalHeader.ImageBase
|
||||
if section.Characteristics&IMAGE_SCN_CNT_CODE != 0 {
|
||||
// .text
|
||||
sections = append(sections, memorySection{
|
||||
Address: address,
|
||||
Size: uint64(section.VirtualSize),
|
||||
Type: memoryCode,
|
||||
})
|
||||
} else if section.Characteristics&IMAGE_SCN_CNT_INITIALIZED_DATA != 0 {
|
||||
if section.Characteristics&IMAGE_SCN_MEM_WRITE != 0 {
|
||||
// .data
|
||||
sections = append(sections, memorySection{
|
||||
Address: address,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryData,
|
||||
})
|
||||
if section.Size < section.VirtualSize {
|
||||
// Equivalent of a .bss section.
|
||||
// Note: because of how the PE/COFF format is
|
||||
// structured, not all zero-initialized data is marked
|
||||
// as such. A portion may be at the end of the .data
|
||||
// section and is thus marked as initialized data.
|
||||
sections = append(sections, memorySection{
|
||||
Address: address + uint64(section.Size),
|
||||
Size: uint64(section.VirtualSize) - uint64(section.Size),
|
||||
Type: memoryBSS,
|
||||
})
|
||||
}
|
||||
} else if section.Characteristics&IMAGE_SCN_MEM_READ != 0 {
|
||||
// .rdata, .buildid, .pdata
|
||||
sections = append(sections, memorySection{
|
||||
Address: address,
|
||||
Size: uint64(section.VirtualSize),
|
||||
Type: memoryROData,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if file, err := wasm.Parse(f); err == nil {
|
||||
// File is in WebAssembly format.
|
||||
|
||||
@@ -364,9 +624,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
const codeOffset = 0x8000_0000_0000_0000
|
||||
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, err := file.DWARF()
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset)
|
||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -513,8 +773,11 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
// section. We start at the beginning.
|
||||
addr := section.Address
|
||||
sectionEnd := section.Address + section.Size
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: %s\n", addr, sectionEnd, section.Size, section.Type)
|
||||
}
|
||||
for _, line := range addresses {
|
||||
if line.Address < section.Address || line.Address+line.Length >= sectionEnd {
|
||||
if line.Address < section.Address || line.Address+line.Length > sectionEnd {
|
||||
// Check that this line is entirely within the section.
|
||||
// Don't bother dealing with line entries that cross sections (that
|
||||
// seems rather unlikely anyway).
|
||||
@@ -525,7 +788,7 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
// previous line entry.
|
||||
addSize("(unknown)", line.Address-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %4d: unknown (gap)\n", addr, line.Address, line.Address-addr)
|
||||
fmt.Printf("%08x..%08x %5d: unknown (gap)\n", addr, line.Address, line.Address-addr)
|
||||
}
|
||||
}
|
||||
if addr > line.Address+line.Length {
|
||||
@@ -550,7 +813,7 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
// There is a gap at the end of the section.
|
||||
addSize("(unknown)", sectionEnd-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %4d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
|
||||
fmt.Printf("%08x..%08x %5d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,9 +832,8 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
|
||||
} else if packageSymbolRegexp.MatchString(path) {
|
||||
// Parse symbol names like main$alloc or runtime$string.
|
||||
packagePath = path[:strings.LastIndex(path, "$")]
|
||||
} else if reflectDataRegexp.MatchString(path) {
|
||||
// Parse symbol names like reflect.structTypesSidetable.
|
||||
packagePath = "Go reflect data"
|
||||
} else if path == "<Go type>" {
|
||||
packagePath = "Go types"
|
||||
} else if path == "<Go interface assert>" {
|
||||
// Interface type assert, generated by the interface lowering pass.
|
||||
packagePath = "Go interface assert"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
package builder
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
#include <stdlib.h>
|
||||
bool tinygo_clang_driver(int argc, char **argv);
|
||||
bool tinygo_link_elf(int argc, char **argv);
|
||||
bool tinygo_link_macho(int argc, char **argv);
|
||||
bool tinygo_link_mingw(int argc, char **argv);
|
||||
bool tinygo_link_wasm(int argc, char **argv);
|
||||
*/
|
||||
@@ -26,8 +27,13 @@ const hasBuiltinTools = true
|
||||
// linking statically with LLVM (with the byollvm build tag).
|
||||
func RunTool(tool string, args ...string) error {
|
||||
linker := "elf"
|
||||
if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" {
|
||||
linker = "mingw"
|
||||
if tool == "ld.lld" && len(args) >= 2 {
|
||||
if args[0] == "-m" && (args[1] == "i386pep" || args[1] == "arm64pe") {
|
||||
linker = "mingw"
|
||||
} else if args[0] == "-flavor" {
|
||||
linker = args[1]
|
||||
args = args[2:]
|
||||
}
|
||||
}
|
||||
args = append([]string{"tinygo:" + tool}, args...)
|
||||
|
||||
@@ -47,6 +53,8 @@ func RunTool(tool string, args ...string) error {
|
||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld":
|
||||
switch linker {
|
||||
case "darwin":
|
||||
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
||||
case "elf":
|
||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||
case "mingw":
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !byollvm
|
||||
//go:build !byollvm
|
||||
|
||||
package builder
|
||||
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ package builder
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(outfile, output, 0644)
|
||||
return os.WriteFile(outfile, output, 0644)
|
||||
}
|
||||
|
||||
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
||||
|
||||
+494
-592
File diff suppressed because it is too large
Load Diff
+101
-24
@@ -5,13 +5,13 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -22,31 +22,30 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
||||
|
||||
// normalizeResult normalizes Go source code that comes out of tests across
|
||||
// platforms and Go versions.
|
||||
func normalizeResult(result string) string {
|
||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
||||
return actual
|
||||
func normalizeResult(t *testing.T, result string) string {
|
||||
result = strings.ReplaceAll(result, "\r\n", "\n")
|
||||
|
||||
// This changed to 'undefined:', in Go 1.20.
|
||||
result = strings.ReplaceAll(result, ": undeclared name:", ": undefined:")
|
||||
// Go 1.20 added a bit more detail
|
||||
result = regexp.MustCompile(`(unknown field z in struct literal).*`).ReplaceAllString(result, "$1")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func TestCGo(t *testing.T) {
|
||||
var cflags = []string{"--target=armv6m-unknown-unknown-eabi"}
|
||||
|
||||
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
||||
for _, name := range []string{
|
||||
"basic",
|
||||
"errors",
|
||||
"types",
|
||||
"symbols",
|
||||
"flags",
|
||||
"const",
|
||||
} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Skip tests that require specific Go version.
|
||||
if name == "errors" {
|
||||
ok := false
|
||||
for _, version := range build.Default.ReleaseTags {
|
||||
if version == "go1.16" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
t.Skip("Results for errors test are only valid for Go 1.16+")
|
||||
}
|
||||
}
|
||||
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
fset := token.NewFileSet()
|
||||
@@ -56,7 +55,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "")
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
@@ -96,11 +95,11 @@ func TestCGo(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("could not write out CGo AST: %v", err)
|
||||
}
|
||||
actual := normalizeResult(buf.String())
|
||||
actual := normalizeResult(t, buf.String())
|
||||
|
||||
// Read the file with the expected output, to compare against.
|
||||
outfile := filepath.Join("testdata", name+".out.go")
|
||||
expectedBytes, err := ioutil.ReadFile(outfile)
|
||||
expectedBytes, err := os.ReadFile(outfile)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read expected output: %v", err)
|
||||
}
|
||||
@@ -111,7 +110,7 @@ func TestCGo(t *testing.T) {
|
||||
// It is not. Test failed.
|
||||
if *flagUpdate {
|
||||
// Update the file with the expected data.
|
||||
err := ioutil.WriteFile(outfile, []byte(actual), 0666)
|
||||
err := os.WriteFile(outfile, []byte(actual), 0666)
|
||||
if err != nil {
|
||||
t.Error("could not write updated output file:", err)
|
||||
}
|
||||
@@ -123,6 +122,84 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cgoPackage_isEquivalentAST(t *testing.T) {
|
||||
fieldA := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "a"}}
|
||||
fieldB := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "b"}}
|
||||
listOfFieldA := &ast.FieldList{List: []*ast.Field{fieldA}}
|
||||
listOfFieldB := &ast.FieldList{List: []*ast.Field{fieldB}}
|
||||
funcDeclA := &ast.FuncDecl{Name: &ast.Ident{Name: "a"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldA}}
|
||||
funcDeclB := &ast.FuncDecl{Name: &ast.Ident{Name: "b"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldB}}
|
||||
funcDeclNoResults := &ast.FuncDecl{Name: &ast.Ident{Name: "C"}, Type: &ast.FuncType{Params: &ast.FieldList{}}}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
a, b ast.Node
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "both nil",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "not same type",
|
||||
a: fieldA,
|
||||
b: &ast.FuncDecl{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Field same",
|
||||
a: fieldA,
|
||||
b: fieldA,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Field different",
|
||||
a: fieldA,
|
||||
b: fieldB,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results nil",
|
||||
a: funcDeclNoResults,
|
||||
b: funcDeclNoResults,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results same",
|
||||
a: funcDeclA,
|
||||
b: funcDeclA,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results different",
|
||||
a: funcDeclA,
|
||||
b: funcDeclB,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results a nil",
|
||||
a: funcDeclNoResults,
|
||||
b: funcDeclB,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results b nil",
|
||||
a: funcDeclA,
|
||||
b: funcDeclNoResults,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := &cgoPackage{}
|
||||
if got := p.isEquivalentAST(tc.a, tc.b); tc.expected != got {
|
||||
t.Errorf("expected %v, got %v", tc.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// simpleImporter implements the types.Importer interface, but only allows
|
||||
// importing the unsafe package.
|
||||
type simpleImporter struct {
|
||||
|
||||
+25
-2
@@ -14,6 +14,9 @@ import (
|
||||
var (
|
||||
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
|
||||
precedences = map[token.Token]int{
|
||||
token.OR: precedenceOr,
|
||||
token.XOR: precedenceXor,
|
||||
token.AND: precedenceAnd,
|
||||
token.ADD: precedenceAdd,
|
||||
token.SUB: precedenceAdd,
|
||||
token.MUL: precedenceMul,
|
||||
@@ -24,6 +27,9 @@ var (
|
||||
|
||||
const (
|
||||
precedenceLowest = iota + 1
|
||||
precedenceOr
|
||||
precedenceXor
|
||||
precedenceAnd
|
||||
precedenceAdd
|
||||
precedenceMul
|
||||
precedencePrefix
|
||||
@@ -76,7 +82,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
|
||||
|
||||
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
|
||||
switch t.peekToken {
|
||||
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
||||
case token.OR, token.XOR, token.AND, token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
||||
t.Next()
|
||||
leftExpr, err = parseBinaryExpr(t, leftExpr)
|
||||
}
|
||||
@@ -199,7 +205,18 @@ func (t *tokenizer) Next() {
|
||||
// https://en.cppreference.com/w/cpp/string/byte/isspace
|
||||
t.peekPos++
|
||||
t.buf = t.buf[1:]
|
||||
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
|
||||
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&"):
|
||||
// Two-character tokens.
|
||||
switch c {
|
||||
case '&':
|
||||
t.peekToken = token.LAND
|
||||
case '|':
|
||||
t.peekToken = token.LOR
|
||||
}
|
||||
t.peekValue = t.buf[:2]
|
||||
t.buf = t.buf[2:]
|
||||
return
|
||||
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
|
||||
// Single-character tokens.
|
||||
// TODO: ++ (increment) and -- (decrement) operators.
|
||||
switch c {
|
||||
@@ -217,6 +234,12 @@ func (t *tokenizer) Next() {
|
||||
t.peekToken = token.QUO
|
||||
case '%':
|
||||
t.peekToken = token.REM
|
||||
case '&':
|
||||
t.peekToken = token.AND
|
||||
case '|':
|
||||
t.peekToken = token.OR
|
||||
case '^':
|
||||
t.peekToken = token.XOR
|
||||
}
|
||||
t.peekValue = t.buf[:1]
|
||||
t.buf = t.buf[1:]
|
||||
|
||||
@@ -37,6 +37,10 @@ func TestParseConst(t *testing.T) {
|
||||
{`5*5`, `5 * 5`},
|
||||
{`5/5`, `5 / 5`},
|
||||
{`5%5`, `5 % 5`},
|
||||
{`5&5`, `5 & 5`},
|
||||
{`5|5`, `5 | 5`},
|
||||
{`5^5`, `5 ^ 5`},
|
||||
{`5||5`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet
|
||||
{`(5/5)`, `(5 / 5)`},
|
||||
{`1 - 2`, `1 - 2`},
|
||||
{`1 - 2 + 3`, `1 - 2 + 3`},
|
||||
|
||||
+436
-201
@@ -4,7 +4,9 @@ package cgo
|
||||
// modification. It does not touch the AST itself.
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/scanner"
|
||||
@@ -13,10 +15,13 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -40,6 +45,8 @@ typedef struct {
|
||||
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
|
||||
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
|
||||
CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
|
||||
CXString tinygo_clang_getCursorPrettyPrinted(GoCXCursor c, CXPrintingPolicy Policy);
|
||||
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(GoCXCursor c);
|
||||
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
|
||||
CXType tinygo_clang_getCursorType(GoCXCursor c);
|
||||
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
|
||||
@@ -47,6 +54,7 @@ CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
|
||||
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(GoCXCursor c);
|
||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||
@@ -72,7 +80,21 @@ var diagnosticSeverity = [...]string{
|
||||
C.CXDiagnostic_Fatal: "fatal",
|
||||
}
|
||||
|
||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
||||
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
|
||||
// theory decoupled from Clang) can also use this type.
|
||||
type clangCursor = C.GoCXCursor
|
||||
|
||||
func init() {
|
||||
// Check that we haven't messed up LLVM versioning.
|
||||
// This can happen when llvm_config_*.go files in either this or the
|
||||
// tinygo.org/x/go-llvm packages is incorrect. It should not ever happen
|
||||
// with byollvm.
|
||||
if C.LLVM_VERSION_STRING != llvm.Version {
|
||||
panic("incorrect build: using LLVM version " + llvm.Version + " in the tinygo.org/x/llvm package, and version " + C.LLVM_VERSION_STRING + " in the ./cgo package")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
|
||||
index := C.clang_createIndex(0, 0)
|
||||
defer C.clang_disposeIndex(index)
|
||||
|
||||
@@ -119,8 +141,8 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||
pos := p.getClangLocationPosition(location, unit)
|
||||
p.addError(pos, severity+": "+spelling)
|
||||
pos := f.getClangLocationPosition(location, unit)
|
||||
f.addError(pos, severity+": "+spelling)
|
||||
}
|
||||
for i := 0; i < numDiagnostics; i++ {
|
||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||
@@ -135,7 +157,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||
}
|
||||
|
||||
// Extract information required by CGo.
|
||||
ref := storedRefs.Put(p)
|
||||
ref := storedRefs.Put(f)
|
||||
defer storedRefs.Remove(ref)
|
||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||
@@ -155,35 +177,88 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := p.visitedFiles[path]; !ok {
|
||||
if _, ok := f.visitedFiles[path]; !ok {
|
||||
// already stored
|
||||
sum := sha512.Sum512_224(data)
|
||||
p.visitedFiles[path] = sum[:]
|
||||
f.visitedFiles[path] = sum[:]
|
||||
}
|
||||
}
|
||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||
defer storedRefs.Remove(inclusionCallbackRef)
|
||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||
|
||||
// Do all the C AST operations inside a callback. This makes sure that
|
||||
// libclang related memory is only freed after it is not necessary anymore.
|
||||
callback(f.names)
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||
// it.
|
||||
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
kind := C.tinygo_clang_getCursorKind(c)
|
||||
pos := p.getCursorPosition(c)
|
||||
pos := f.getCursorPosition(c)
|
||||
switch kind {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
fn := &functionInfo{
|
||||
pos: pos,
|
||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "C." + name,
|
||||
}
|
||||
exportName := name
|
||||
localName := name
|
||||
var stringSignature string
|
||||
if C.tinygo_clang_Cursor_getStorageClass(c) == C.CX_SC_Static {
|
||||
// A static function is assigned a globally unique symbol name based
|
||||
// on the file path (like _Cgo_static_2d09198adbf58f4f4655_foo) and
|
||||
// has a different Go name in the form of C.foo!symbols.go instead
|
||||
// of just C.foo.
|
||||
path := f.importPath + "/" + filepath.Base(f.fset.File(f.file.Pos()).Name())
|
||||
staticIDBuf := sha256.Sum256([]byte(path))
|
||||
staticID := hex.EncodeToString(staticIDBuf[:10])
|
||||
exportName = "_Cgo_static_" + staticID + "_" + name
|
||||
localName = name + "!" + filepath.Base(path)
|
||||
|
||||
// Create a signature. This is necessary for MacOS to forward the
|
||||
// call, because MacOS doesn't support aliases like ELF and PE do.
|
||||
// (There is N_INDR but __attribute__((alias("..."))) doesn't work).
|
||||
policy := C.tinygo_clang_getCursorPrintingPolicy(c)
|
||||
defer C.clang_PrintingPolicy_dispose(policy)
|
||||
C.clang_PrintingPolicy_setProperty(policy, C.CXPrintingPolicy_TerseOutput, 1)
|
||||
stringSignature = getString(C.tinygo_clang_getCursorPrettyPrinted(c, policy))
|
||||
stringSignature = strings.Replace(stringSignature, " "+name+"(", " "+exportName+"(", 1)
|
||||
stringSignature = strings.TrimPrefix(stringSignature, "static ")
|
||||
}
|
||||
args := make([]*ast.Field, numArgs)
|
||||
decl := &ast.FuncDecl{
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: pos - 1,
|
||||
Text: "//export " + exportName,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + localName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: pos,
|
||||
List: args,
|
||||
Closing: pos,
|
||||
},
|
||||
},
|
||||
}
|
||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:variadic",
|
||||
})
|
||||
}
|
||||
p.functions[name] = fn
|
||||
for i := 0; i < numArgs; i++ {
|
||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||
@@ -191,50 +266,108 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
if argName == "" {
|
||||
argName = "$" + strconv.Itoa(i)
|
||||
}
|
||||
fn.args = append(fn.args, paramInfo{
|
||||
name: argName,
|
||||
typeExpr: p.makeDecayingASTType(argType, pos),
|
||||
})
|
||||
args[i] = &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: argName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: argName,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: f.makeDecayingASTType(argType, pos),
|
||||
}
|
||||
}
|
||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||
if resultType.kind != C.CXType_Void {
|
||||
fn.results = &ast.FieldList{
|
||||
decl.Type.Results = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{
|
||||
Type: p.makeASTType(resultType, pos),
|
||||
Type: f.makeASTType(resultType, pos),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case C.CXCursor_StructDecl:
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
obj.Decl = decl
|
||||
return decl, stringSignature
|
||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||
typ := f.makeASTRecordType(c, pos)
|
||||
typeName := "C." + name
|
||||
typeExpr := typ.typeExpr
|
||||
if typ.unionSize != 0 {
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = f.makeUnionField(typ)
|
||||
}
|
||||
p.makeASTType(typ, pos)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, typ
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
typeName := "C." + name
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
p.makeASTType(typedefType, pos)
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
}
|
||||
if underlyingType.kind != C.CXType_Enum {
|
||||
typeSpec.Assign = pos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
p.globals[name] = globalInfo{
|
||||
typeExpr: p.makeASTType(cursorType, pos),
|
||||
pos: pos,
|
||||
typeExpr := f.makeASTType(cursorType, pos)
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: pos,
|
||||
Tok: token.VAR,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:extern " + name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||
start := C.clang_getRangeStart(sourceRange)
|
||||
end := C.clang_getRangeEnd(sourceRange)
|
||||
@@ -242,17 +375,17 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
var startOffset, endOffset C.unsigned
|
||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||
if file == nil {
|
||||
p.addError(pos, "internal error: could not find file where macro is defined")
|
||||
break
|
||||
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||
return nil, nil
|
||||
}
|
||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||
if file != endFile {
|
||||
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
break
|
||||
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
return nil, nil
|
||||
}
|
||||
if startOffset > endOffset {
|
||||
p.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
break
|
||||
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// read file contents and extract the relevant byte range
|
||||
@@ -260,31 +393,94 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
if endOffset >= C.uint(size) {
|
||||
p.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
break
|
||||
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
return nil, nil
|
||||
}
|
||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||
if !strings.HasPrefix(source, name) {
|
||||
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
break
|
||||
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
return nil, nil
|
||||
}
|
||||
value := source[len(name):]
|
||||
// Try to convert this #define into a Go constant expression.
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||
if scannerError != nil {
|
||||
p.errors = append(p.errors, *scannerError)
|
||||
f.errors = append(f.errors, *scannerError)
|
||||
return nil, nil
|
||||
}
|
||||
if expr != nil {
|
||||
// Parsing was successful.
|
||||
p.constants[name] = constantInfo{expr, pos}
|
||||
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_EnumDecl:
|
||||
// Visit all enums, because the fields may be used even when the enum
|
||||
// type itself is not.
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
p.makeASTType(typ, pos)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name,
|
||||
}
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||
// instead of types such as C.int, which are used here.
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlying, pos),
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
expr := &ast.BasicLit{
|
||||
ValuePos: pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(value), 10),
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
default:
|
||||
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
|
||||
return nil, nil
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
func getString(clangString C.CXString) (s string) {
|
||||
@@ -294,6 +490,69 @@ func getString(clangString C.CXString) (s string) {
|
||||
return
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
|
||||
switch C.tinygo_clang_getCursorKind(c) {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_StructDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
f.names["struct_"+name] = c
|
||||
}
|
||||
case C.CXCursor_UnionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
f.names["union_"+name] = c
|
||||
}
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_EnumDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
// Named enum, which can be referenced from Go using C.enum_foo.
|
||||
f.names["enum_"+name] = c
|
||||
}
|
||||
// The enum fields are in global scope, so recurse to visit them.
|
||||
return C.CXChildVisit_Recurse
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
// We arrive here because of the "Recurse" above.
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
// Get the precise location in the source code. Used for uniquely identifying
|
||||
// source locations.
|
||||
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} {
|
||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||
var file C.CXFile
|
||||
var line C.unsigned
|
||||
var column C.unsigned
|
||||
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||
location := token.Position{
|
||||
Filename: getString(C.clang_getFileName(file)),
|
||||
Line: int(line),
|
||||
Column: int(column),
|
||||
}
|
||||
if location.Filename == "" || location.Line == 0 {
|
||||
// Not sure when this would happen, but protect from it anyway.
|
||||
f.addError(pos, "could not find file/line information")
|
||||
}
|
||||
return location
|
||||
}
|
||||
|
||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||
@@ -391,7 +650,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
||||
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||
// makeASTType.
|
||||
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
// Strip typedefs, if any.
|
||||
underlyingType := typ
|
||||
if underlyingType.kind == C.CXType_Typedef {
|
||||
@@ -417,15 +676,15 @@ func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
pointeeType := C.clang_getElementType(underlyingType)
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
X: f.makeASTType(pointeeType, pos),
|
||||
}
|
||||
}
|
||||
return p.makeASTType(typ, pos)
|
||||
return f.makeASTType(typ, pos)
|
||||
}
|
||||
|
||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||
// it converts a libclang type to a type in the Go AST.
|
||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_Char_S, C.CXType_Char_U:
|
||||
@@ -486,7 +745,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
X: f.makeASTType(pointeeType, pos),
|
||||
}
|
||||
case C.CXType_ConstantArray:
|
||||
return &ast.ArrayType{
|
||||
@@ -496,7 +755,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||
},
|
||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
||||
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
|
||||
}
|
||||
case C.CXType_FunctionProto:
|
||||
// Be compatible with gc, which uses the *[0]byte type for function
|
||||
@@ -517,71 +776,21 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
case C.CXType_Typedef:
|
||||
name := getString(C.clang_getTypedefName(typ))
|
||||
if _, ok := p.typedefs[name]; !ok {
|
||||
p.typedefs[name] = nil // don't recurse
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
expr := p.makeASTType(underlyingType, pos)
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
expr := expr.(*ast.Ident)
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch expr.Name {
|
||||
case "C.char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||
}
|
||||
switch underlyingType.kind {
|
||||
case C.CXType_Char_S:
|
||||
expr.Name = "int8"
|
||||
case C.CXType_Char_U:
|
||||
expr.Name = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "int8"
|
||||
case 2:
|
||||
expr.Name = "int16"
|
||||
case 4:
|
||||
expr.Name = "int32"
|
||||
case 8:
|
||||
expr.Name = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "uint8"
|
||||
case 2:
|
||||
expr.Name = "uint16"
|
||||
case 4:
|
||||
expr.Name = "uint32"
|
||||
case 8:
|
||||
expr.Name = "uint64"
|
||||
}
|
||||
}
|
||||
}
|
||||
p.typedefs[name] = &typedefInfo{
|
||||
typeExpr: expr,
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Name: f.getASTDeclName(name, c, false),
|
||||
}
|
||||
case C.CXType_Elaborated:
|
||||
underlying := C.clang_Type_getNamedType(typ)
|
||||
switch underlying.kind {
|
||||
case C.CXType_Record:
|
||||
return p.makeASTType(underlying, pos)
|
||||
return f.makeASTType(underlying, pos)
|
||||
case C.CXType_Enum:
|
||||
return p.makeASTType(underlying, pos)
|
||||
return f.makeASTType(underlying, pos)
|
||||
default:
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
typeName = "<unknown>"
|
||||
}
|
||||
case C.CXType_Record:
|
||||
@@ -599,63 +808,35 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
if name == "" {
|
||||
// Anonymous record, probably inside a typedef.
|
||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
||||
// This record is a union or is a struct with bitfields, so we
|
||||
// have to declare it as a named type (for getters/setters to
|
||||
// work).
|
||||
p.anonStructNum++
|
||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
||||
p.elaboratedTypes[cgoName] = typeInfo
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
}
|
||||
return typeInfo.typeExpr
|
||||
location := f.getUniqueLocationID(pos, cursor)
|
||||
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||
} else {
|
||||
cgoName := cgoRecordPrefix + name
|
||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
name = cgoRecordPrefix + name
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, cursor, false),
|
||||
}
|
||||
case C.CXType_Enum:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
||||
if name == "" {
|
||||
// anonymous enum
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
return p.makeASTType(underlying, pos)
|
||||
// Anonymous enum, probably inside a typedef.
|
||||
location := f.getUniqueLocationID(pos, cursor)
|
||||
name = f.getUnnamedDeclName("_Ctype_enum___", location)
|
||||
} else {
|
||||
// named enum
|
||||
if _, ok := p.enums[name]; !ok {
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
p.enums[name] = enumInfo{
|
||||
typeExpr: p.makeASTType(underlying, pos),
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C.enum_" + name,
|
||||
}
|
||||
name = "enum_" + name
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, cursor, false),
|
||||
}
|
||||
}
|
||||
if typeName == "" {
|
||||
// Report this as an error.
|
||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
typeName = "C.<unknown>"
|
||||
}
|
||||
return &ast.Ident{
|
||||
@@ -664,9 +845,80 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// getIntegerType returns an AST node that defines types such as C.int.
|
||||
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
|
||||
pos := p.getCursorPosition(cursor)
|
||||
|
||||
// Find a Go type that matches the size and signedness of the given C type.
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
|
||||
var goName string
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch name {
|
||||
case "C.char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||
}
|
||||
switch underlyingType.kind {
|
||||
case C.CXType_Char_S:
|
||||
goName = "int8"
|
||||
case C.CXType_Char_U:
|
||||
goName = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "int8"
|
||||
case 2:
|
||||
goName = "int16"
|
||||
case 4:
|
||||
goName = "int32"
|
||||
case 8:
|
||||
goName = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "uint8"
|
||||
case 2:
|
||||
goName = "uint16"
|
||||
case 4:
|
||||
goName = "uint32"
|
||||
case 8:
|
||||
goName = "uint64"
|
||||
}
|
||||
}
|
||||
|
||||
if goName == "" { // should not happen
|
||||
p.addError(pos, "internal error: did not find Go type for C type "+name)
|
||||
goName = "int"
|
||||
}
|
||||
|
||||
// Construct an *ast.TypeSpec for this type.
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: name,
|
||||
}
|
||||
spec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: goName,
|
||||
},
|
||||
}
|
||||
obj.Decl = spec
|
||||
return spec
|
||||
}
|
||||
|
||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||
// a Go struct type.
|
||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
@@ -676,11 +928,11 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
bitfieldNum := 0
|
||||
ref := storedRefs.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
pkg *cgoPackage
|
||||
file *cgoFile
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
renameFieldKeywords(fieldList)
|
||||
@@ -709,13 +961,13 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
}
|
||||
if bitfieldList != nil {
|
||||
// This is valid C... but please don't do this.
|
||||
p.addError(pos, "bitfield in a union is not supported")
|
||||
f.addError(pos, "bitfield in a union is not supported")
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(cursor)
|
||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||
if sizeInBytes == 0 {
|
||||
p.addError(pos, "zero-length union is not supported")
|
||||
f.addError(pos, "zero-length union is not supported")
|
||||
}
|
||||
typeInfo.unionSize = sizeInBytes
|
||||
typeInfo.unionAlign = alignInBytes
|
||||
@@ -723,7 +975,7 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
default:
|
||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
return &elaboratedTypeInfo{
|
||||
typeExpr: &ast.StructType{
|
||||
Struct: pos,
|
||||
@@ -737,17 +989,17 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||
fieldList *ast.FieldList
|
||||
pkg *cgoPackage
|
||||
file *cgoFile
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
})
|
||||
fieldList := passed.fieldList
|
||||
p := passed.pkg
|
||||
f := passed.file
|
||||
inBitfield := passed.inBitfield
|
||||
bitfieldNum := passed.bitfieldNum
|
||||
bitfieldList := passed.bitfieldList
|
||||
pos := p.getCursorPosition(c)
|
||||
pos := f.getCursorPosition(c)
|
||||
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
||||
case C.CXCursor_FieldDecl:
|
||||
// Expected. This is a regular field.
|
||||
@@ -756,7 +1008,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
return C.CXChildVisit_Continue
|
||||
default:
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
@@ -767,14 +1019,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
field := &ast.Field{
|
||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
||||
Type: f.makeASTType(typ, f.getCursorPosition(c)),
|
||||
}
|
||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||
bitfieldOffset := offsetof % alignOf
|
||||
if bitfieldOffset != 0 {
|
||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||
p.addError(pos, "expected a bitfield")
|
||||
f.addError(pos, "expected a bitfield")
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
if !*inBitfield {
|
||||
@@ -821,23 +1073,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_enum_visitor
|
||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
pos := p.getCursorPosition(c)
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
p.constants[name] = constantInfo{
|
||||
expr: &ast.BasicLit{
|
||||
ValuePos: pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(value), 10),
|
||||
},
|
||||
pos: pos,
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_inclusion_visitor
|
||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// +build !byollvm
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm14
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && !llvm14
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-15/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@15/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
+13
-1
@@ -3,7 +3,7 @@
|
||||
// are slightly different from the ones defined in libclang.go, but they
|
||||
// should be ABI compatible.
|
||||
|
||||
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||
|
||||
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
|
||||
return clang_getTranslationUnitCursor(tu);
|
||||
@@ -17,6 +17,14 @@ CXString tinygo_clang_getCursorSpelling(CXCursor c) {
|
||||
return clang_getCursorSpelling(c);
|
||||
}
|
||||
|
||||
CXString tinygo_clang_getCursorPrettyPrinted(CXCursor c, CXPrintingPolicy policy) {
|
||||
return clang_getCursorPrettyPrinted(c, policy);
|
||||
}
|
||||
|
||||
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(CXCursor c) {
|
||||
return clang_getCursorPrintingPolicy(c);
|
||||
}
|
||||
|
||||
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
|
||||
return clang_getCursorKind(c);
|
||||
}
|
||||
@@ -45,6 +53,10 @@ CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
|
||||
return clang_Cursor_getArgument(c, i);
|
||||
}
|
||||
|
||||
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(CXCursor c) {
|
||||
return clang_Cursor_getStorageClass(c);
|
||||
}
|
||||
|
||||
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
||||
return clang_getCursorLocation(c);
|
||||
}
|
||||
|
||||
Vendored
+33
-20
@@ -4,23 +4,36 @@ import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
Vendored
+35
-22
@@ -4,26 +4,39 @@ import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
const C.bar = C.foo
|
||||
const C.foo = 3
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.foo = 3
|
||||
const C.bar = C.foo
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ import "C"
|
||||
//line errors.go:100
|
||||
var (
|
||||
// constant too large
|
||||
_ C.uint8_t = 2 << 10
|
||||
_ C.char = 2 << 10
|
||||
|
||||
// z member does not exist
|
||||
_ C.point_t = C.point_t{z: 3}
|
||||
|
||||
Vendored
+39
-25
@@ -6,11 +6,11 @@
|
||||
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||
// testdata/errors.go:105: unknown field z in struct literal
|
||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||
// testdata/errors.go:108: undefined: C.SOME_CONST_1
|
||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
// testdata/errors.go:112: undeclared name: C.SOME_CONST_4
|
||||
// testdata/errors.go:112: undefined: C.SOME_CONST_4
|
||||
|
||||
package main
|
||||
|
||||
@@ -18,29 +18,43 @@ import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.point_t = struct {
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C._Ctype_struct___0 struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point_t = C._Ctype_struct___0
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
|
||||
Vendored
+34
-21
@@ -9,26 +9,39 @@ import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.BAR = 3
|
||||
const C.FOO_H = 1
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
// Function signatures.
|
||||
int foo(int a, int b);
|
||||
void variadic0();
|
||||
void variadic2(int x, int y, ...);
|
||||
static void staticfunc(int x);
|
||||
|
||||
// Global variable signatures.
|
||||
extern int someValue;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Test function signatures.
|
||||
func accessFunctions() {
|
||||
C.foo(3, 4)
|
||||
C.variadic0()
|
||||
C.variadic2(3, 5)
|
||||
C.staticfunc(3)
|
||||
}
|
||||
|
||||
func accessGlobals() {
|
||||
_ = C.someValue
|
||||
}
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
//export foo
|
||||
func C.foo(a C.int, b C.int) C.int
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic0
|
||||
//go:variadic
|
||||
func C.variadic0()
|
||||
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic2
|
||||
//go:variadic
|
||||
func C.variadic2(x C.int, y C.int)
|
||||
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
|
||||
//export _Cgo_static_173c95a79b6df1980521_staticfunc
|
||||
func C.staticfunc!symbols.go(x C.int)
|
||||
|
||||
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
|
||||
|
||||
//go:extern someValue
|
||||
var C.someValue C.int
|
||||
Vendored
+4
-10
@@ -104,10 +104,6 @@ typedef struct {
|
||||
unsigned char e : 3;
|
||||
// Note that C++ allows bitfields bigger than the underlying type.
|
||||
} bitfield_t;
|
||||
|
||||
// Function signatures.
|
||||
void variadic0();
|
||||
void variadic2(int x, int y, ...);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -116,6 +112,10 @@ import "C"
|
||||
import "C"
|
||||
|
||||
var (
|
||||
// aliases
|
||||
_ C.float
|
||||
_ C.double
|
||||
|
||||
// Simple typedefs.
|
||||
_ C.myint
|
||||
|
||||
@@ -171,9 +171,3 @@ func accessUnion() {
|
||||
var _ *C.int = union2d.unionfield_i()
|
||||
var _ *[2]float64 = union2d.unionfield_d()
|
||||
}
|
||||
|
||||
// Test function signatures.
|
||||
func accessFunctions() {
|
||||
C.variadic0()
|
||||
C.variadic2(3, 5)
|
||||
}
|
||||
|
||||
Vendored
+116
-107
@@ -4,137 +4,146 @@ import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
func C.variadic0() //go:variadic
|
||||
func C.variadic2(x C.int, y C.int) //go:variadic
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
|
||||
const C.option2A = 20
|
||||
const C.optionA = 0
|
||||
const C.optionB = 1
|
||||
const C.optionC = -5
|
||||
const C.optionD = -4
|
||||
const C.optionE = 10
|
||||
const C.optionF = 11
|
||||
const C.optionG = 12
|
||||
const C.unused1 = 5
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.bitfield_t = C.struct_4
|
||||
type C.myIntArray = [10]C.int
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C.myint = C.int
|
||||
type C.option2_t = C.uint
|
||||
type C.option_t = C.enum_option
|
||||
type C.point2d_t = struct {
|
||||
type C._Ctype_struct___0 struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_nested_t = struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C.union_2
|
||||
}
|
||||
type C.types_t = struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.union1_t = struct{ i C.int }
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.union3_t = C.union_1
|
||||
type C.union_nested_t = C.union_3
|
||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||
|
||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.struct_4 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
}
|
||||
type C.point2d_t = C._Ctype_struct___0
|
||||
type C.struct_point3d struct {
|
||||
x C.int
|
||||
y C.int
|
||||
z C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_type1 struct {
|
||||
_type C.int
|
||||
__type C.int
|
||||
___type C.int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
type C._Ctype_union___1 struct{ i C.int }
|
||||
type C.union1_t = C._Ctype_union___1
|
||||
type C._Ctype_union___2 struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||
|
||||
type C.union_1 struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
||||
return (*float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_2 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_3 struct{ $union [2]uint64 }
|
||||
type C.union3_t = C._Ctype_union___2
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
type C.enum_option C.int
|
||||
type C.enum_unused C.uint
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
||||
type C.unionarray_t = C._Ctype_union___3
|
||||
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C._Ctype_struct___4 struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C._Ctype_union___5
|
||||
}
|
||||
type C.struct_nested_t = C._Ctype_struct___4
|
||||
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_nested_t = C._Ctype_union___6
|
||||
type C.enum_option = C.int
|
||||
type C.option_t = C.enum_option
|
||||
type C._Ctype_enum___7 = C.uint
|
||||
type C.option2_t = C._Ctype_enum___7
|
||||
type C._Ctype_struct___8 struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.types_t = C._Ctype_struct___8
|
||||
type C.myIntArray = [10]C.int
|
||||
type C._Ctype_struct___9 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
}
|
||||
|
||||
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.bitfield_t = C._Ctype_struct___9
|
||||
|
||||
+168
-59
@@ -10,6 +10,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
@@ -46,6 +47,12 @@ func (c *Config) Features() string {
|
||||
return c.Target.Features + "," + c.Options.LLVMFeatures
|
||||
}
|
||||
|
||||
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
||||
// string is returned if the target doesn't specify an ABI.
|
||||
func (c *Config) ABI() string {
|
||||
return c.Target.ABI
|
||||
}
|
||||
|
||||
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
||||
// for example, bare-metal targets will usually pretend to be linux to get the
|
||||
// standard library to compile.
|
||||
@@ -68,13 +75,12 @@ func (c *Config) GOARM() string {
|
||||
|
||||
// BuildTags returns the complete list of build tags used during this build.
|
||||
func (c *Config) BuildTags() []string {
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||
targetTags := filterTags(c.Target.BuildTags, c.Options.Tags)
|
||||
tags := append(targetTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 {
|
||||
tags = append(tags, extraTags...)
|
||||
}
|
||||
tags = append(tags, c.Options.Tags...)
|
||||
return tags
|
||||
}
|
||||
|
||||
@@ -85,7 +91,7 @@ func (c *Config) CgoEnabled() bool {
|
||||
}
|
||||
|
||||
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||
// values are "none", "leaking", "extalloc", and "conservative".
|
||||
// values are "none", "leaking", "conservative" and "precise".
|
||||
func (c *Config) GC() string {
|
||||
if c.Options.GC != "" {
|
||||
return c.Options.GC
|
||||
@@ -100,7 +106,7 @@ func (c *Config) GC() string {
|
||||
// that can be traced by the garbage collector.
|
||||
func (c *Config) NeedsStackObjects() bool {
|
||||
switch c.GC() {
|
||||
case "conservative", "extalloc":
|
||||
case "conservative", "custom", "precise":
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "tinygo.wasm" {
|
||||
return true
|
||||
@@ -114,7 +120,7 @@ func (c *Config) NeedsStackObjects() bool {
|
||||
}
|
||||
|
||||
// Scheduler returns the scheduler implementation. Valid values are "none",
|
||||
//"coroutines" and "tasks".
|
||||
// "asyncify" and "tasks".
|
||||
func (c *Config) Scheduler() string {
|
||||
if c.Options.Scheduler != "" {
|
||||
return c.Options.Scheduler
|
||||
@@ -122,8 +128,8 @@ func (c *Config) Scheduler() string {
|
||||
if c.Target.Scheduler != "" {
|
||||
return c.Target.Scheduler
|
||||
}
|
||||
// Fall back to coroutines, which are supported everywhere.
|
||||
return "coroutines"
|
||||
// Fall back to none.
|
||||
return "none"
|
||||
}
|
||||
|
||||
// Serial returns the serial implementation for this build configuration: uart,
|
||||
@@ -159,31 +165,6 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
||||
}
|
||||
}
|
||||
|
||||
// FuncImplementation picks an appropriate func value implementation for the
|
||||
// target.
|
||||
func (c *Config) FuncImplementation() string {
|
||||
switch c.Scheduler() {
|
||||
case "tasks", "asyncify":
|
||||
// A func value is implemented as a pair of pointers:
|
||||
// {context, function pointer}
|
||||
// where the context may be a pointer to a heap-allocated struct
|
||||
// containing the free variables, or it may be undef if the function
|
||||
// being pointed to doesn't need a context. The function pointer is a
|
||||
// regular function pointer.
|
||||
return "doubleword"
|
||||
case "none", "coroutines":
|
||||
// As "doubleword", but with the function pointer replaced by a unique
|
||||
// ID per function signature. Function values are called by using a
|
||||
// switch statement and choosing which function to call.
|
||||
// Pick the switch implementation with the coroutines scheduler, as it
|
||||
// allows the use of blocking inside a function that is used as a func
|
||||
// value.
|
||||
return "switch"
|
||||
default:
|
||||
panic("unknown scheduler type")
|
||||
}
|
||||
}
|
||||
|
||||
// PanicStrategy returns the panic strategy selected for this target. Valid
|
||||
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
|
||||
// instruction).
|
||||
@@ -201,6 +182,23 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// StackSize returns the default stack size to be used for goroutines, if the
|
||||
// stack size could not be determined automatically at compile time.
|
||||
func (c *Config) StackSize() uint64 {
|
||||
if c.Options.StackSize != 0 {
|
||||
return c.Options.StackSize
|
||||
}
|
||||
return c.Target.DefaultStackSize
|
||||
}
|
||||
|
||||
// UseThinLTO returns whether ThinLTO should be used for the given target.
|
||||
func (c *Config) UseThinLTO() bool {
|
||||
// All architectures support ThinLTO now. However, this code is kept for the
|
||||
// time being in case there are regressions. The non-ThinLTO code support
|
||||
// should be removed when it is proven to work reliably.
|
||||
return true
|
||||
}
|
||||
|
||||
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||
func (c *Config) RP2040BootPatch() bool {
|
||||
@@ -224,8 +222,16 @@ func MuslArchitecture(triple string) string {
|
||||
// a precompiled libc shipped with a TinyGo build, or a libc path in the cache
|
||||
// directory (which might not yet be built).
|
||||
func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
archname := c.Triple()
|
||||
if c.CPU() != "" {
|
||||
archname += "-" + c.CPU()
|
||||
}
|
||||
if c.ABI() != "" {
|
||||
archname += "-" + c.ABI()
|
||||
}
|
||||
|
||||
// Try to load a precompiled library.
|
||||
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", c.Triple(), name)
|
||||
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
|
||||
if _, err := os.Stat(precompiledDir); err == nil {
|
||||
// Found a precompiled library for this OS/architecture. Return the path
|
||||
// directly.
|
||||
@@ -234,13 +240,30 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
|
||||
// No precompiled library found. Determine the path name that will be used
|
||||
// in the build cache.
|
||||
var outname string
|
||||
if c.CPU() != "" {
|
||||
outname = name + "-" + c.Triple() + "-" + c.CPU()
|
||||
} else {
|
||||
outname = name + "-" + c.Triple()
|
||||
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
|
||||
}
|
||||
|
||||
// DefaultBinaryExtension returns the default extension for binaries, such as
|
||||
// .exe, .wasm, or no extension (depending on the target).
|
||||
func (c *Config) DefaultBinaryExtension() string {
|
||||
parts := strings.Split(c.Triple(), "-")
|
||||
if parts[0] == "wasm32" {
|
||||
// WebAssembly files always have the .wasm file extension.
|
||||
return ".wasm"
|
||||
}
|
||||
return filepath.Join(goenv.Get("GOCACHE"), outname), false
|
||||
if len(parts) >= 3 && parts[2] == "windows" {
|
||||
// Windows uses .exe.
|
||||
return ".exe"
|
||||
}
|
||||
if len(parts) >= 3 && parts[2] == "unknown" {
|
||||
// There appears to be a convention to use the .elf file extension for
|
||||
// ELF files intended for microcontrollers. I'm not aware of the origin
|
||||
// of this, it's just something that is used by many projects.
|
||||
// I think it's a good tradition, so let's keep it.
|
||||
return ".elf"
|
||||
}
|
||||
// Linux, MacOS, etc, don't use a file extension. Use it as a fallback.
|
||||
return ""
|
||||
}
|
||||
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
@@ -251,23 +274,30 @@ func (c *Config) CFlags() []string {
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"),
|
||||
)
|
||||
case "picolibc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||
path, _ := c.LibcPath("picolibc")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+path,
|
||||
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "include"),
|
||||
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "tinystdio"),
|
||||
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
)
|
||||
case "musl":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("musl")
|
||||
arch := MuslArchitecture(c.Triple())
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+path,
|
||||
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "musl", "include"),
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||
)
|
||||
case "wasi-libc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
@@ -277,8 +307,8 @@ func (c *Config) CFlags() []string {
|
||||
path, _ := c.LibcPath("mingw-w64")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+path,
|
||||
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||
"-D_UCRT",
|
||||
)
|
||||
case "":
|
||||
@@ -289,7 +319,7 @@ func (c *Config) CFlags() []string {
|
||||
panic("unknown libc: " + c.Target.Libc)
|
||||
}
|
||||
// Always emit debug information. It is optionally stripped at link time.
|
||||
cflags = append(cflags, "-g")
|
||||
cflags = append(cflags, "-gdwarf-4")
|
||||
// Use the same optimization level as TinyGo.
|
||||
cflags = append(cflags, "-O"+c.Options.Opt)
|
||||
// Set the LLVM target triple.
|
||||
@@ -307,6 +337,10 @@ func (c *Config) CFlags() []string {
|
||||
cflags = append(cflags, "-mcpu="+c.Target.CPU)
|
||||
}
|
||||
}
|
||||
// Set the -mabi flag, if needed.
|
||||
if c.ABI() != "" {
|
||||
cflags = append(cflags, "-mabi="+c.ABI())
|
||||
}
|
||||
return cflags
|
||||
}
|
||||
|
||||
@@ -346,8 +380,8 @@ func (c *Config) VerifyIR() bool {
|
||||
}
|
||||
|
||||
// Debug returns whether debug (DWARF) information should be retained by the
|
||||
// linker. By default, debug information is retained but it can be removed with
|
||||
// the -no-debug flag.
|
||||
// linker. By default, debug information is retained, but it can be removed
|
||||
// with the -no-debug flag.
|
||||
func (c *Config) Debug() bool {
|
||||
return c.Options.Debug
|
||||
}
|
||||
@@ -362,6 +396,13 @@ func (c *Config) BinaryFormat(ext string) string {
|
||||
return c.Target.BinaryFormat
|
||||
}
|
||||
return "bin"
|
||||
case ".img":
|
||||
// Image file. Only defined for the ESP32 at the moment, where it is a
|
||||
// full (runnable) image that can be used in the Espressif QEMU fork.
|
||||
if c.Target.BinaryFormat != "" {
|
||||
return c.Target.BinaryFormat + "-img"
|
||||
}
|
||||
return "bin"
|
||||
case ".hex":
|
||||
// Similar to bin, but includes the start address and is thus usually a
|
||||
// better format.
|
||||
@@ -411,13 +452,13 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
||||
if openocdInterface == "" {
|
||||
return nil, errors.New("OpenOCD programmer not set")
|
||||
}
|
||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(openocdInterface) {
|
||||
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(openocdInterface) {
|
||||
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface)
|
||||
}
|
||||
if c.Target.OpenOCDTarget == "" {
|
||||
return nil, errors.New("OpenOCD chip not set")
|
||||
}
|
||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(c.Target.OpenOCDTarget) {
|
||||
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(c.Target.OpenOCDTarget) {
|
||||
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget)
|
||||
}
|
||||
if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" {
|
||||
@@ -428,7 +469,14 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
||||
args = append(args, "-c", cmd)
|
||||
}
|
||||
if c.Target.OpenOCDTransport != "" {
|
||||
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
|
||||
transport := c.Target.OpenOCDTransport
|
||||
if transport == "swd" {
|
||||
switch openocdInterface {
|
||||
case "stlink-dap":
|
||||
transport = "dapdirect_swd"
|
||||
}
|
||||
}
|
||||
args = append(args, "-c", "transport select "+transport)
|
||||
}
|
||||
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
|
||||
return args, nil
|
||||
@@ -453,16 +501,77 @@ func (c *Config) RelocationModel() string {
|
||||
return "static"
|
||||
}
|
||||
|
||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file, and
|
||||
// the value is overridden by `-wasm-abi` flag if it is provided
|
||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file.
|
||||
func (c *Config) WasmAbi() string {
|
||||
if c.Options.WasmAbi != "" {
|
||||
return c.Options.WasmAbi
|
||||
}
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
// EmulatorName is a shorthand to get the command for this emulator, something
|
||||
// like qemu-system-arm or simavr.
|
||||
func (c *Config) EmulatorName() string {
|
||||
parts := strings.SplitN(c.Target.Emulator, " ", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// EmulatorFormat returns the binary format for the emulator and the associated
|
||||
// file extension. An empty string means to pass directly whatever the linker
|
||||
// produces directly without conversion (usually ELF format).
|
||||
func (c *Config) EmulatorFormat() (format, fileExt string) {
|
||||
switch {
|
||||
case strings.Contains(c.Target.Emulator, "{img}"):
|
||||
return "img", ".img"
|
||||
default:
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
|
||||
// Emulator returns a ready-to-run command to run the given binary in an
|
||||
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
|
||||
// the compiled binary.
|
||||
func (c *Config) Emulator(format, binary string) ([]string, error) {
|
||||
parts, err := shlex.Split(c.Target.Emulator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse emulator command: %w", err)
|
||||
}
|
||||
var emulator []string
|
||||
for _, s := range parts {
|
||||
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
|
||||
// Allow replacement of what's usually /tmp except notably Windows.
|
||||
s = strings.ReplaceAll(s, "{tmpDir}", os.TempDir())
|
||||
s = strings.ReplaceAll(s, "{"+format+"}", binary)
|
||||
emulator = append(emulator, s)
|
||||
}
|
||||
return emulator, nil
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
}
|
||||
|
||||
// filterTags removes predefined build tags for a target if a conflicting option
|
||||
// is provided by the user.
|
||||
func filterTags(targetTags []string, userTags []string) []string {
|
||||
var filtered []string
|
||||
for _, t := range targetTags {
|
||||
switch {
|
||||
case strings.HasPrefix(t, "runtime_memhash_"):
|
||||
overridden := false
|
||||
for _, ut := range userTags {
|
||||
if strings.HasPrefix(ut, "runtime_memhash_") {
|
||||
overridden = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !overridden {
|
||||
filtered = append(filtered, t)
|
||||
}
|
||||
default:
|
||||
filtered = append(filtered, t)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package compileopts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildTags(t *testing.T) {
|
||||
tests := []struct {
|
||||
targetTags []string
|
||||
userTags []string
|
||||
result []string
|
||||
}{
|
||||
{
|
||||
targetTags: []string{},
|
||||
userTags: []string{},
|
||||
result: []string{
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
},
|
||||
},
|
||||
{
|
||||
targetTags: []string{"bear"},
|
||||
userTags: []string{},
|
||||
result: []string{
|
||||
"bear",
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
},
|
||||
},
|
||||
{
|
||||
targetTags: []string{},
|
||||
userTags: []string{"cat"},
|
||||
result: []string{
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
"cat",
|
||||
},
|
||||
},
|
||||
{
|
||||
targetTags: []string{"bear"},
|
||||
userTags: []string{"cat"},
|
||||
result: []string{
|
||||
"bear",
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
"cat",
|
||||
},
|
||||
},
|
||||
{
|
||||
targetTags: []string{"bear", "runtime_memhash_leveldb"},
|
||||
userTags: []string{"cat"},
|
||||
result: []string{
|
||||
"bear",
|
||||
"runtime_memhash_leveldb",
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
"cat",
|
||||
},
|
||||
},
|
||||
{
|
||||
targetTags: []string{"bear", "runtime_memhash_leveldb"},
|
||||
userTags: []string{"cat", "runtime_memhash_leveldb"},
|
||||
result: []string{
|
||||
"bear",
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
"cat",
|
||||
"runtime_memhash_leveldb",
|
||||
},
|
||||
},
|
||||
{
|
||||
targetTags: []string{"bear", "runtime_memhash_leveldb"},
|
||||
userTags: []string{"cat", "runtime_memhash_sip"},
|
||||
result: []string{
|
||||
"bear",
|
||||
"tinygo",
|
||||
"math_big_pure_go",
|
||||
"gc.conservative",
|
||||
"scheduler.none",
|
||||
"serial.none",
|
||||
"cat",
|
||||
"runtime_memhash_sip",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tt := tc
|
||||
t.Run(fmt.Sprintf("%s+%s", strings.Join(tt.targetTags, ","), strings.Join(tt.userTags, ",")), func(t *testing.T) {
|
||||
c := &Config{
|
||||
Target: &TargetSpec{
|
||||
BuildTags: tt.targetTags,
|
||||
},
|
||||
Options: &Options{
|
||||
Tags: tt.userTags,
|
||||
},
|
||||
}
|
||||
|
||||
res := c.BuildTags()
|
||||
|
||||
if len(res) != len(tt.result) {
|
||||
t.Errorf("expected %d tags, got %d", len(tt.result), len(res))
|
||||
}
|
||||
|
||||
for i, tag := range tt.result {
|
||||
if tag != res[i] {
|
||||
t.Errorf("tag %d: expected %s, got %s", i, tt.result[i], tag)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+14
-6
@@ -4,11 +4,12 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "coroutines", "asyncify"}
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||
validSerialOptions = []string{"none", "uart", "usb"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
@@ -27,23 +28,30 @@ type Options struct {
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||
Serial string
|
||||
Work bool // -work flag to print temporary build directory
|
||||
InterpTimeout time.Duration
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands func(cmd string, args ...string)
|
||||
Parallelism int // -p flag
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags string
|
||||
WasmAbi string
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Directory string
|
||||
PrintJSON bool
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
func TestVerifyOptions(t *testing.T) {
|
||||
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
|
||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines, asyncify`)
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
|
||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||
|
||||
@@ -42,18 +42,18 @@ func TestVerifyOptions(t *testing.T) {
|
||||
GC: "leaking",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionExtalloc",
|
||||
opts: compileopts.Options{
|
||||
GC: "extalloc",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionConservative",
|
||||
opts: compileopts.Options{
|
||||
GC: "conservative",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionCustom",
|
||||
opts: compileopts.Options{
|
||||
GC: "custom",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidSchedulerOption",
|
||||
opts: compileopts.Options{
|
||||
@@ -73,12 +73,6 @@ func TestVerifyOptions(t *testing.T) {
|
||||
Scheduler: "tasks",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SchedulerOptionCoroutines",
|
||||
opts: compileopts.Options{
|
||||
Scheduler: "coroutines",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidPrintSizeOption",
|
||||
opts: compileopts.Options{
|
||||
|
||||
+88
-44
@@ -26,6 +26,7 @@ type TargetSpec struct {
|
||||
Inherits []string `json:"inherits"`
|
||||
Triple string `json:"llvm-target"`
|
||||
CPU string `json:"cpu"`
|
||||
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
|
||||
Features string `json:"features"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
@@ -42,12 +43,12 @@ type TargetSpec struct {
|
||||
LDFlags []string `json:"ldflags"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator string `json:"emulator"`
|
||||
FlashCommand string `json:"flash-command"`
|
||||
GDB []string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid"
|
||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid"
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume string `json:"msd-volume-name"`
|
||||
FlashFilename string `json:"msd-firmware-name"`
|
||||
@@ -57,6 +58,7 @@ type TargetSpec struct {
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
OpenOCDCommands []string `json:"openocd-commands"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
RelocationModel string `json:"relocation-model"`
|
||||
@@ -64,7 +66,7 @@ type TargetSpec struct {
|
||||
}
|
||||
|
||||
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
||||
func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
||||
func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
|
||||
specType := reflect.TypeOf(spec).Elem()
|
||||
specValue := reflect.ValueOf(spec).Elem()
|
||||
childValue := reflect.ValueOf(child).Elem()
|
||||
@@ -87,23 +89,22 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
||||
if !src.IsNil() {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Slice: // for slices...
|
||||
if src.Len() > 0 { // ... if not empty ...
|
||||
switch tag := field.Tag.Get("override"); tag {
|
||||
case "copy":
|
||||
// copy the field of child to spec
|
||||
dst.Set(src)
|
||||
case "append", "":
|
||||
// or append the field of child to spec
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
default:
|
||||
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
||||
case reflect.Slice: // for slices, append the field and check for duplicates
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
for i := 0; i < dst.Len(); i++ {
|
||||
v := dst.Index(i).String()
|
||||
for j := i + 1; j < dst.Len(); j++ {
|
||||
w := dst.Index(j).String()
|
||||
if v == w {
|
||||
return fmt.Errorf("duplicate value '%s' in field %s", v, field.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("unknown field type : " + kind.String())
|
||||
return fmt.Errorf("unknown field type: %s", kind)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// load reads a target specification from the JSON in the given io.Reader. It
|
||||
@@ -118,10 +119,10 @@ func (spec *TargetSpec) load(r io.Reader) error {
|
||||
}
|
||||
|
||||
// loadFromGivenStr loads the TargetSpec from the given string that could be:
|
||||
// - targets/ directory inside the compiler sources
|
||||
// - a relative or absolute path to custom (project specific) target specification .json file;
|
||||
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
|
||||
// as well as path to custom files (ex. myAwesomeProject.json)
|
||||
// - targets/ directory inside the compiler sources
|
||||
// - a relative or absolute path to custom (project specific) target specification .json file;
|
||||
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
|
||||
// as well as path to custom files (ex. myAwesomeProject.json)
|
||||
func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||
path := ""
|
||||
if strings.HasSuffix(str, ".json") {
|
||||
@@ -151,11 +152,17 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSpec.overrideProperties(subtarget)
|
||||
err = newSpec.overrideProperties(subtarget)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// When all properties are loaded, make sure they are properly inherited.
|
||||
newSpec.overrideProperties(spec)
|
||||
err := newSpec.overrideProperties(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*spec = *newSpec
|
||||
|
||||
return nil
|
||||
@@ -188,6 +195,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
default:
|
||||
llvmarch = options.GOARCH
|
||||
}
|
||||
llvmvendor := "unknown"
|
||||
llvmos := options.GOOS
|
||||
if llvmos == "darwin" {
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||
@@ -197,17 +205,18 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
// Looks like Apple prefers to call this architecture ARM64
|
||||
// instead of AArch64.
|
||||
llvmarch = "arm64"
|
||||
llvmos = "macosx11.0.0"
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
}
|
||||
// Target triples (which actually have four components, but are called
|
||||
// triples for historical reasons) have the form:
|
||||
// arch-vendor-os-environment
|
||||
target := llvmarch + "-unknown-" + llvmos
|
||||
if options.GOARCH == "arm" {
|
||||
target += "-gnueabihf"
|
||||
}
|
||||
target := llvmarch + "-" + llvmvendor + "-" + llvmos
|
||||
if options.GOOS == "windows" {
|
||||
target += "-gnu"
|
||||
} else if options.GOARCH == "arm" {
|
||||
target += "-gnueabihf"
|
||||
}
|
||||
return defaultTarget(options.GOOS, options.GOARCH, target)
|
||||
}
|
||||
@@ -223,7 +232,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
// it includes all parents as specified in the "inherits" key.
|
||||
err = spec.resolveInherits()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s : %w", options.Target, err)
|
||||
}
|
||||
|
||||
if spec.Scheduler == "asyncify" {
|
||||
@@ -241,6 +250,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
GC: "precise",
|
||||
Scheduler: "tasks",
|
||||
Linker: "cc",
|
||||
DefaultStackSize: 1024 * 64, // 64kB
|
||||
@@ -256,21 +266,30 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "arm":
|
||||
spec.CPU = "generic"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
switch strings.Split(triple, "-")[0] {
|
||||
case "armv5":
|
||||
spec.Features = "+armv5t,+strict-align,-thumb-mode"
|
||||
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
case "armv6":
|
||||
spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-thumb-mode"
|
||||
spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
case "armv7":
|
||||
spec.Features = "+armv7-a,+dsp,+fp64,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,-thumb-mode"
|
||||
spec.Features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
}
|
||||
case "arm64":
|
||||
spec.CPU = "generic"
|
||||
spec.Features = "+neon"
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "darwin-libSystem"
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-flavor", "darwin",
|
||||
"-dead_strip",
|
||||
"-arch", arch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
)
|
||||
} else if goos == "linux" {
|
||||
spec.Linker = "ld.lld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
@@ -279,39 +298,64 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
} else if goos == "windows" {
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "mingw-w64"
|
||||
// Note: using a medium code model, low image base and no ASLR
|
||||
// because Go doesn't really need those features. ASLR patches
|
||||
// around issues for unsafe languages like C/C++ that are not
|
||||
// normally present in Go (without explicitly opting in).
|
||||
// For more discussion:
|
||||
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "i386pep",
|
||||
"--image-base", "0x400000",
|
||||
)
|
||||
case "arm64":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "arm64pe",
|
||||
)
|
||||
}
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "i386pep",
|
||||
"-Bdynamic",
|
||||
"--image-base", "0x400000",
|
||||
"--gc-sections",
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
if goarch != "wasm" {
|
||||
suffix := ""
|
||||
if goos == "windows" {
|
||||
// Windows uses a different calling convention from other operating
|
||||
// systems so we need separate assembly files.
|
||||
if goos == "windows" && goarch == "amd64" {
|
||||
// Windows uses a different calling convention on amd64 from other
|
||||
// operating systems so we need separate assembly files.
|
||||
suffix = "_windows"
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
spec.GDB = []string{"gdb-multiarch"}
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
spec.Emulator = []string{"qemu-arm"}
|
||||
}
|
||||
if goarch == "arm64" && goos == "linux" {
|
||||
spec.Emulator = []string{"qemu-aarch64"}
|
||||
if goos == "linux" {
|
||||
switch goarch {
|
||||
case "386":
|
||||
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
|
||||
if runtime.GOARCH != "amd64" {
|
||||
spec.Emulator = "qemu-i386 {}"
|
||||
}
|
||||
case "amd64":
|
||||
spec.Emulator = "qemu-x86_64 {}"
|
||||
case "arm":
|
||||
spec.Emulator = "qemu-arm {}"
|
||||
case "arm64":
|
||||
spec.Emulator = "qemu-aarch64 {}"
|
||||
}
|
||||
}
|
||||
}
|
||||
if goos != runtime.GOOS {
|
||||
if goos == "windows" {
|
||||
spec.Emulator = []string{"wine"}
|
||||
spec.Emulator = "wine {}"
|
||||
}
|
||||
}
|
||||
return &spec, nil
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package compileopts
|
||||
|
||||
import (
|
||||
"os"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -17,7 +18,7 @@ func TestLoadTarget(t *testing.T) {
|
||||
t.Error("LoadTarget should have failed with non existing target")
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
t.Error("LoadTarget failed for wrong reason:", err)
|
||||
}
|
||||
}
|
||||
@@ -29,7 +30,6 @@ func TestOverrideProperties(t *testing.T) {
|
||||
CPU: "baseCpu",
|
||||
CFlags: []string{"-base-foo", "-base-bar"},
|
||||
BuildTags: []string{"bt1", "bt2"},
|
||||
Emulator: []string{"be1", "be2"},
|
||||
DefaultStackSize: 42,
|
||||
AutoStackSize: &baseAutoStackSize,
|
||||
}
|
||||
@@ -38,7 +38,6 @@ func TestOverrideProperties(t *testing.T) {
|
||||
GOOS: "",
|
||||
CPU: "chlidCpu",
|
||||
CFlags: []string{"-child-foo", "-child-bar"},
|
||||
Emulator: []string{"ce1", "ce2"},
|
||||
AutoStackSize: &childAutoStackSize,
|
||||
DefaultStackSize: 64,
|
||||
}
|
||||
@@ -57,9 +56,6 @@ func TestOverrideProperties(t *testing.T) {
|
||||
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.BuildTags)
|
||||
}
|
||||
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.Emulator)
|
||||
}
|
||||
if *base.AutoStackSize != false {
|
||||
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
||||
}
|
||||
|
||||
+4
-45
@@ -16,6 +16,8 @@ import "tinygo.org/x/go-llvm"
|
||||
|
||||
var stdlibAliases = map[string]string{
|
||||
// crypto packages
|
||||
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
|
||||
"crypto/ed25519/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
|
||||
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||
@@ -23,53 +25,10 @@ var stdlibAliases = map[string]string{
|
||||
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||
|
||||
// math package
|
||||
"math.Asin": "math.asin",
|
||||
"math.Asinh": "math.asinh",
|
||||
"math.Acos": "math.acos",
|
||||
"math.Acosh": "math.acosh",
|
||||
"math.Atan": "math.atan",
|
||||
"math.Atanh": "math.atanh",
|
||||
"math.Atan2": "math.atan2",
|
||||
"math.Cbrt": "math.cbrt",
|
||||
"math.Ceil": "math.ceil",
|
||||
"math.archCeil": "math.ceil",
|
||||
"math.Cos": "math.cos",
|
||||
"math.Cosh": "math.cosh",
|
||||
"math.Erf": "math.erf",
|
||||
"math.Erfc": "math.erfc",
|
||||
"math.Exp": "math.exp",
|
||||
"math.archExp": "math.exp",
|
||||
"math.Expm1": "math.expm1",
|
||||
"math.Exp2": "math.exp2",
|
||||
"math.archExp2": "math.exp2",
|
||||
"math.Floor": "math.floor",
|
||||
"math.archFloor": "math.floor",
|
||||
"math.Frexp": "math.frexp",
|
||||
"math.Hypot": "math.hypot",
|
||||
"math.archHypot": "math.hypot",
|
||||
"math.Ldexp": "math.ldexp",
|
||||
"math.Log": "math.log",
|
||||
"math.archLog": "math.log",
|
||||
"math.Log1p": "math.log1p",
|
||||
"math.Log10": "math.log10",
|
||||
"math.Log2": "math.log2",
|
||||
"math.Max": "math.max",
|
||||
"math.archMax": "math.max",
|
||||
"math.Min": "math.min",
|
||||
"math.archMin": "math.min",
|
||||
"math.Mod": "math.mod",
|
||||
"math.Modf": "math.modf",
|
||||
"math.archModf": "math.modf",
|
||||
"math.Pow": "math.pow",
|
||||
"math.Remainder": "math.remainder",
|
||||
"math.Sin": "math.sin",
|
||||
"math.Sinh": "math.sinh",
|
||||
"math.Sqrt": "math.sqrt",
|
||||
"math.archSqrt": "math.sqrt",
|
||||
"math.Tan": "math.tan",
|
||||
"math.Tanh": "math.tanh",
|
||||
"math.Trunc": "math.trunc",
|
||||
"math.archTrunc": "math.trunc",
|
||||
}
|
||||
|
||||
// createAlias implements the function (in the builder) as a call to the alias
|
||||
@@ -86,14 +45,14 @@ func (b *builder) createAlias(alias llvm.Value) {
|
||||
pos := b.program.Fset.Position(b.fn.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||
entryBlock := b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
if b.llvmFn.Type() != alias.Type() {
|
||||
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||
b.CreateUnreachable()
|
||||
return
|
||||
}
|
||||
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
||||
result := b.CreateCall(alias.GlobalValueType(), alias, b.llvmFn.Params(), "")
|
||||
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateRetVoid()
|
||||
} else {
|
||||
|
||||
+11
-8
@@ -89,10 +89,11 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
|
||||
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||
}
|
||||
|
||||
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
||||
// function must panic if the ptr/len parameters are invalid.
|
||||
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
||||
// From the documentation of unsafe.Slice:
|
||||
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice
|
||||
// and unsafe.String. This function must panic if the ptr/len parameters are
|
||||
// invalid.
|
||||
func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, elementType llvm.Type, lenType *types.Basic) {
|
||||
// From the documentation of unsafe.Slice and unsafe.String:
|
||||
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||
// > zero, a run-time panic occurs.
|
||||
// However, in practice, it is also necessary to check that the length is
|
||||
@@ -105,7 +106,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
|
||||
|
||||
// Determine the maximum slice size, and therefore the maximum value of the
|
||||
// len parameter.
|
||||
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
||||
maxSize := b.maxSliceSize(elementType)
|
||||
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||
|
||||
// Do the check. By using unsigned greater than for the length check, signed
|
||||
@@ -117,7 +118,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
|
||||
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
||||
b.createRuntimeAssert(assert, name, "unsafeSlicePanic")
|
||||
}
|
||||
|
||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||
@@ -145,7 +146,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
||||
}
|
||||
// Make the maxBufSize actually the maximum allowed value (in number of
|
||||
// elements in the channel buffer).
|
||||
maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false))
|
||||
maxBufSize = b.CreateUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false), "")
|
||||
|
||||
// Make sure maxBufSize has the same type as bufSize.
|
||||
if maxBufSize.Type() != bufSize.Type() {
|
||||
@@ -240,8 +241,10 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
||||
}
|
||||
}
|
||||
|
||||
// Put the fault block at the end of the function and the next block at the
|
||||
// current insert position.
|
||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
|
||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
|
||||
+56
-47
@@ -1,30 +1,44 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation and true if the call could
|
||||
// be lowered inline, and false otherwise.
|
||||
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
name := call.Value.(*ssa.Function).Name()
|
||||
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation, or a zero llvm.Value if
|
||||
// the result is void.
|
||||
func (b *builder) createAtomicOp(name string) llvm.Value {
|
||||
switch name {
|
||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
// AtomicRMW does not work on AVR as intended:
|
||||
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
|
||||
// - The result is the new value instead of the old value
|
||||
vType := val.Type()
|
||||
name := fmt.Sprintf("__sync_fetch_and_add_%d", vType.IntTypeWidth()/8)
|
||||
fn := b.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false))
|
||||
}
|
||||
oldVal := b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val}, "")
|
||||
// Return the new value, not the original value returned.
|
||||
return b.CreateAdd(oldVal, val, "")
|
||||
}
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
// Return the new value, not the original value returned by atomicrmw.
|
||||
return b.CreateAdd(oldVal, val, ""), true
|
||||
return b.CreateAdd(oldVal, val, "")
|
||||
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
|
||||
if isPointer {
|
||||
// atomicrmw only supports integers, so cast to an integer.
|
||||
// TODO: this is fixed in LLVM 15.
|
||||
val = b.CreatePtrToInt(val, b.uintptrType, "")
|
||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
|
||||
}
|
||||
@@ -32,52 +46,47 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
if isPointer {
|
||||
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
||||
}
|
||||
return oldVal, true
|
||||
return oldVal
|
||||
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
old := b.getValue(call.Args[1])
|
||||
newVal := b.getValue(call.Args[2])
|
||||
if strings.HasSuffix(name, "64") {
|
||||
if strings.HasPrefix(b.Triple, "thumb") {
|
||||
// Work around a bug in LLVM, at least LLVM 11:
|
||||
// https://reviews.llvm.org/D95891
|
||||
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
||||
if compareAndSwap.IsNil() {
|
||||
// Declare the function if it isn't already declared.
|
||||
i64Type := b.ctx.Int64Type()
|
||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
||||
}
|
||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
||||
// The __sync_val_compare_and_swap_8 function returns the old
|
||||
// value. However, we shouldn't return the old value, we should
|
||||
// return whether the compare/exchange was successful. This is
|
||||
// easily done by comparing the returned (actual) old value with
|
||||
// the expected old value passed to
|
||||
// __sync_val_compare_and_swap_8.
|
||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
||||
return swapped, true
|
||||
}
|
||||
}
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
old := b.getValue(b.fn.Params[1])
|
||||
newVal := b.getValue(b.fn.Params[2])
|
||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||
return swapped, true
|
||||
return swapped
|
||||
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.CreateLoad(ptr, "")
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.CreateLoad(b.getLLVMType(b.fn.Signature.Results().At(0).Type()), ptr, "")
|
||||
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return val, true
|
||||
return val
|
||||
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
ptr := b.getValue(b.fn.Params[0])
|
||||
val := b.getValue(b.fn.Params[1])
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
|
||||
vType := val.Type()
|
||||
isPointer := vType.TypeKind() == llvm.PointerTypeKind
|
||||
if isPointer {
|
||||
// libcalls only supports integers, so cast to an integer.
|
||||
vType = b.uintptrType
|
||||
val = b.CreatePtrToInt(val, vType, "")
|
||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(vType, 0), "")
|
||||
}
|
||||
name := fmt.Sprintf("__atomic_store_%d", vType.IntTypeWidth()/8)
|
||||
fn := b.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
||||
}
|
||||
b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
|
||||
return llvm.Value{}
|
||||
}
|
||||
store := b.CreateStore(val, ptr)
|
||||
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return store, true
|
||||
return llvm.Value{}
|
||||
default:
|
||||
return llvm.Value{}, false
|
||||
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name())
|
||||
return llvm.Value{}
|
||||
}
|
||||
}
|
||||
|
||||
+59
-42
@@ -20,7 +20,7 @@ const maxFieldsPerParam = 3
|
||||
type paramInfo struct {
|
||||
llvmType llvm.Type
|
||||
name string // name, possibly with suffixes for e.g. struct fields
|
||||
flags paramFlags
|
||||
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||
}
|
||||
|
||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||
@@ -33,27 +33,54 @@ const (
|
||||
paramIsDeferenceableOrNull = 1 << iota
|
||||
)
|
||||
|
||||
// createCall creates a new call to runtime.<fnName> with the given arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
// createRuntimeInvoke instead.
|
||||
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||
llvmFn := b.getFunction(fn)
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||
}
|
||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||
args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle
|
||||
return b.createCall(llvmFn, args, name)
|
||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||
if isInvoke {
|
||||
return b.createInvoke(fnType, llvmFn, args, name)
|
||||
}
|
||||
return b.createCall(fnType, llvmFn, args, name)
|
||||
}
|
||||
|
||||
// createRuntimeCall creates a new call to runtime.<fnName> with the given
|
||||
// arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
return b.createRuntimeCallCommon(fnName, args, name, false)
|
||||
}
|
||||
|
||||
// createRuntimeInvoke creates a new call to runtime.<fnName> with the given
|
||||
// arguments. If the runtime call panics, control flow is diverted to the
|
||||
// landing pad block.
|
||||
// Note that "invoke" here is meant in the LLVM sense (a call that can
|
||||
// panic/throw), not in the Go sense (an interface method call).
|
||||
func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
return b.createRuntimeCallCommon(fnName, args, name, true)
|
||||
}
|
||||
|
||||
// createCall creates a call to the given function with the arguments possibly
|
||||
// expanded.
|
||||
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
expanded := make([]llvm.Value, 0, len(args))
|
||||
for _, arg := range args {
|
||||
fragments := b.expandFormalParam(arg)
|
||||
expanded = append(expanded, fragments...)
|
||||
}
|
||||
return b.CreateCall(fn, expanded, name)
|
||||
return b.CreateCall(fnType, fn, expanded, name)
|
||||
}
|
||||
|
||||
// createInvoke is like createCall but continues execution at the landing pad if
|
||||
// the call resulted in a panic.
|
||||
func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
if b.hasDeferFrame() {
|
||||
b.createInvokeCheckpoint()
|
||||
}
|
||||
return b.createCall(fnType, fn, args, name)
|
||||
}
|
||||
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
@@ -69,13 +96,7 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
|
||||
// failed to expand this parameter: too many fields
|
||||
}
|
||||
// TODO: split small arrays
|
||||
return []paramInfo{
|
||||
{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: getTypeFlags(goType),
|
||||
},
|
||||
}
|
||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||
}
|
||||
|
||||
// expandFormalParamOffsets returns a list of offsets from the start of an
|
||||
@@ -125,7 +146,6 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||
// with the passed in type if this is not possible.
|
||||
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
typeFlags := getTypeFlags(goType)
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
var paramInfos []paramInfo
|
||||
@@ -156,40 +176,37 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
}
|
||||
}
|
||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||
for i := range subInfos {
|
||||
subInfos[i].flags |= typeFlags
|
||||
}
|
||||
paramInfos = append(paramInfos, subInfos...)
|
||||
}
|
||||
return paramInfos
|
||||
default:
|
||||
return []paramInfo{
|
||||
{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: typeFlags,
|
||||
},
|
||||
}
|
||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||
}
|
||||
}
|
||||
|
||||
// getTypeFlags returns the type flags for a given type. It will not recurse
|
||||
// into sub-types (such as in structs).
|
||||
func getTypeFlags(t types.Type) paramFlags {
|
||||
if t == nil {
|
||||
return 0
|
||||
// getParamInfo collects information about a parameter. For example, if this
|
||||
// parameter is pointer-like, it will also store the element type for the
|
||||
// dereferenceable_or_null attribute.
|
||||
func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Type) paramInfo {
|
||||
info := paramInfo{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
}
|
||||
switch t.Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
// Pointers in Go must either point to an object or be nil.
|
||||
return paramIsDeferenceableOrNull
|
||||
case *types.Chan, *types.Map:
|
||||
// Channels and maps are implemented as pointers pointing to some
|
||||
// object, and follow the same rules as *types.Pointer.
|
||||
return paramIsDeferenceableOrNull
|
||||
default:
|
||||
return 0
|
||||
if goType != nil {
|
||||
switch underlying := goType.Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
// Pointers in Go must either point to an object or be nil.
|
||||
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMType(underlying.Elem()))
|
||||
case *types.Chan:
|
||||
// Channels are implemented simply as a *runtime.channel.
|
||||
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("channel"))
|
||||
case *types.Map:
|
||||
// Maps are similar to channels: they are implemented as a
|
||||
// *runtime.hashmap.
|
||||
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("hashmap"))
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// extractSubfield extracts a field from a struct, or returns null if this is
|
||||
|
||||
+10
-10
@@ -43,7 +43,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
}
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
|
||||
// Do the send.
|
||||
@@ -75,7 +75,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
}
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
|
||||
// Do the receive.
|
||||
@@ -84,7 +84,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
if isZeroSize {
|
||||
received = llvm.ConstNull(valueType)
|
||||
} else {
|
||||
received = b.CreateLoad(valueAlloca, "chan.received")
|
||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
}
|
||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
@@ -173,7 +173,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
||||
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||
recvbuf = b.CreateGEP(recvbufAlloca, []llvm.Value{
|
||||
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.recvbuf")
|
||||
@@ -184,13 +184,13 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
||||
for i, state := range selectStates {
|
||||
// Set each slice element to the appropriate channel.
|
||||
gep := b.CreateGEP(statesAlloca, []llvm.Value{
|
||||
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}, "")
|
||||
b.CreateStore(state, gep)
|
||||
}
|
||||
statesPtr := b.CreateGEP(statesAlloca, []llvm.Value{
|
||||
statesPtr := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.states")
|
||||
@@ -204,7 +204,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
||||
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
||||
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||
chBlockPtr := b.CreateGEP(chBlockAlloca, []llvm.Value{
|
||||
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.block")
|
||||
@@ -264,8 +264,8 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
||||
// receive can proceed at a time) so we'll get that alloca, bitcast
|
||||
// it to the correct type, and dereference it.
|
||||
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
||||
typ := llvm.PointerType(b.getLLVMType(expr.Type()), 0)
|
||||
ptr := b.CreateBitCast(recvbuf, typ, "")
|
||||
return b.CreateLoad(ptr, "")
|
||||
typ := b.getLLVMType(expr.Type())
|
||||
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "")
|
||||
return b.CreateLoad(typ, ptr, "")
|
||||
}
|
||||
}
|
||||
|
||||
+571
-219
File diff suppressed because it is too large
Load Diff
+28
-27
@@ -3,7 +3,7 @@ package compiler
|
||||
import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -28,19 +28,13 @@ type testCase struct {
|
||||
func TestCompiler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Check LLVM version.
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||
}
|
||||
if llvmMajor < 11 {
|
||||
// It is likely this version needs to be bumped in the future.
|
||||
// The goal is to at least test the LLVM version that's used by default
|
||||
// in TinyGo and (if possible without too many workarounds) also some
|
||||
// previous versions.
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
t.Fatal("could not read Go version:", err)
|
||||
}
|
||||
|
||||
// Determine which tests to run, depending on the Go and LLVM versions.
|
||||
tests := []testCase{
|
||||
{"basic.go", "", ""},
|
||||
{"pointer.go", "", ""},
|
||||
@@ -48,23 +42,16 @@ func TestCompiler(t *testing.T) {
|
||||
{"string.go", "", ""},
|
||||
{"float.go", "", ""},
|
||||
{"interface.go", "", ""},
|
||||
{"func.go", "", "coroutines"},
|
||||
{"func.go", "", ""},
|
||||
{"defer.go", "cortex-m-qemu", ""},
|
||||
{"pragma.go", "", ""},
|
||||
{"goroutine.go", "wasm", "asyncify"},
|
||||
{"goroutine.go", "wasm", "coroutines"},
|
||||
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||
{"channel.go", "", ""},
|
||||
{"intrinsics.go", "cortex-m-qemu", ""},
|
||||
{"intrinsics.go", "wasm", ""},
|
||||
{"gc.go", "", ""},
|
||||
}
|
||||
|
||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read Go version:", err)
|
||||
}
|
||||
if minor >= 17 {
|
||||
tests = append(tests, testCase{"go1.17.go", "", ""})
|
||||
if goMinor >= 20 {
|
||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -95,22 +82,25 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
Features: config.Features(),
|
||||
ABI: config.ABI(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
Scheduler: config.Scheduler(),
|
||||
FuncImplementation: config.FuncImplementation(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
DefaultStackSize: config.StackSize(),
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
}
|
||||
machine, err := NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -158,14 +148,14 @@ func TestCompiler(t *testing.T) {
|
||||
|
||||
// Update test if needed. Do not check the result.
|
||||
if *flagUpdate {
|
||||
err := ioutil.WriteFile(outPath, []byte(mod.String()), 0666)
|
||||
err := os.WriteFile(outPath, []byte(mod.String()), 0666)
|
||||
if err != nil {
|
||||
t.Error("failed to write updated output file:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(outPath)
|
||||
expected, err := os.ReadFile(outPath)
|
||||
if err != nil {
|
||||
t.Fatal("failed to read golden file:", err)
|
||||
}
|
||||
@@ -201,6 +191,12 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
||||
// stripped out.
|
||||
func filterIrrelevantIRLines(lines []string) []string {
|
||||
var out []string
|
||||
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if err != nil {
|
||||
// Note: this should never happen and if it does, it will always happen
|
||||
// for a particular build because llvm.Version is a constant.
|
||||
panic(err)
|
||||
}
|
||||
for _, line := range lines {
|
||||
line = strings.Split(line, ";")[0] // strip out comments/info
|
||||
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
|
||||
@@ -210,6 +206,11 @@ func filterIrrelevantIRLines(lines []string) []string {
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
||||
// The datalayout string may vary betewen LLVM versions.
|
||||
// Right now test outputs are for LLVM 14 and higher.
|
||||
continue
|
||||
}
|
||||
out = append(out, line)
|
||||
}
|
||||
return out
|
||||
|
||||
+225
-53
@@ -15,12 +15,39 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// supportsRecover returns whether the compiler supports the recover() builtin
|
||||
// for the current architecture.
|
||||
func (b *builder) supportsRecover() bool {
|
||||
switch b.archFamily() {
|
||||
case "wasm32":
|
||||
// Probably needs to be implemented using the exception handling
|
||||
// proposal of WebAssembly:
|
||||
// https://github.com/WebAssembly/exception-handling
|
||||
return false
|
||||
case "riscv64", "xtensa":
|
||||
// TODO: add support for these architectures
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// hasDeferFrame returns whether the current function needs to catch panics and
|
||||
// run defers.
|
||||
func (b *builder) hasDeferFrame() bool {
|
||||
if b.fn.Recover == nil {
|
||||
return false
|
||||
}
|
||||
return b.supportsRecover()
|
||||
}
|
||||
|
||||
// deferInitFunc sets up this function for future deferred calls. It must be
|
||||
// called from within the entry block when this function contains deferred
|
||||
// calls.
|
||||
@@ -36,6 +63,151 @@ func (b *builder) deferInitFunc() {
|
||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
|
||||
|
||||
if b.hasDeferFrame() {
|
||||
// Set up the defer frame with the current stack pointer.
|
||||
// This assumes that the stack pointer doesn't move outside of the
|
||||
// function prologue/epilogue (an invariant maintained by TinyGo but
|
||||
// possibly broken by the C alloca function).
|
||||
// The frame pointer is _not_ saved, because it is marked as clobbered
|
||||
// in the setjmp-like inline assembly.
|
||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||
stackPointer := b.readStackPointer()
|
||||
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
||||
|
||||
// Create the landing pad block, which is where control transfers after
|
||||
// a panic.
|
||||
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
||||
}
|
||||
}
|
||||
|
||||
// createLandingPad fills in the landing pad block. This block runs the deferred
|
||||
// functions and returns (by jumping to the recover block). If the function is
|
||||
// still panicking after the defers are run, the panic will be re-raised in
|
||||
// destroyDeferFrame.
|
||||
func (b *builder) createLandingPad() {
|
||||
b.SetInsertPointAtEnd(b.landingpad)
|
||||
|
||||
// Add debug info, if needed.
|
||||
// The location used is the closing bracket of the function.
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(b.fn.Syntax().End())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
b.createRunDefers()
|
||||
|
||||
// Continue at the 'recover' block, which returns to the parent in an
|
||||
// appropriate way.
|
||||
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||
}
|
||||
|
||||
// createInvokeCheckpoint saves the function state at the given point, to
|
||||
// continue at the landing pad if a panic happened. This is implemented using a
|
||||
// setjmp-like construct.
|
||||
func (b *builder) createInvokeCheckpoint() {
|
||||
// Construct inline assembly equivalents of setjmp.
|
||||
// The assembly works as follows:
|
||||
// * All registers (both callee-saved and caller saved) are clobbered
|
||||
// after the inline assembly returns.
|
||||
// * The assembly stores the address just past the end of the assembly
|
||||
// into the jump buffer.
|
||||
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||
// assembly but set to an unspecified non-zero value when jumping using
|
||||
// a longjmp.
|
||||
var asmString, constraints string
|
||||
resultType := b.uintptrType
|
||||
switch b.archFamily() {
|
||||
case "i386":
|
||||
asmString = `
|
||||
xorl %eax, %eax
|
||||
movl $$1f, 4(%ebx)
|
||||
1:`
|
||||
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
// This doesn't include the floating point stack because TinyGo uses
|
||||
// newer floating point instructions.
|
||||
case "x86_64":
|
||||
asmString = `
|
||||
leaq 1f(%rip), %rax
|
||||
movq %rax, 8(%rbx)
|
||||
xorq %rax, %rax
|
||||
1:`
|
||||
constraints = "={rax},{rbx},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
// This list doesn't include AVX/AVX512 registers because TinyGo
|
||||
// doesn't currently enable support for AVX instructions.
|
||||
case "arm":
|
||||
// Note: the following assembly takes into account that the PC is
|
||||
// always 4 bytes ahead on ARM. The PC that is stored always points
|
||||
// to the instruction just after the assembly fragment so that
|
||||
// tinygo_longjmp lands at the correct instruction.
|
||||
if b.isThumb() {
|
||||
// Instructions are 2 bytes in size.
|
||||
asmString = `
|
||||
movs r0, #0
|
||||
mov r2, pc
|
||||
str r2, [r1, #4]`
|
||||
} else {
|
||||
// Instructions are 4 bytes in size.
|
||||
asmString = `
|
||||
str pc, [r1, #4]
|
||||
movs r0, #0`
|
||||
}
|
||||
constraints = "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"
|
||||
case "aarch64":
|
||||
asmString = `
|
||||
adr x2, 1f
|
||||
str x2, [x1, #8]
|
||||
mov x0, #0
|
||||
1:
|
||||
`
|
||||
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
||||
if b.GOOS != "darwin" && b.GOOS != "windows" {
|
||||
// These registers cause the following warning when compiling for
|
||||
// MacOS and Windows:
|
||||
// warning: inline asm clobber list contains reserved registers:
|
||||
// X18, FP
|
||||
// Reserved registers on the clobber list may not be preserved
|
||||
// across the asm statement, and clobbering them may lead to
|
||||
// undefined behaviour.
|
||||
constraints += ",~{x18},~{fp}"
|
||||
}
|
||||
// TODO: SVE registers, which we don't use in TinyGo at the moment.
|
||||
case "avr":
|
||||
// Note: the Y register (R28:R29) is a fixed register and therefore
|
||||
// needs to be saved manually. TODO: do this only once per function with
|
||||
// a defer frame, not for every call.
|
||||
resultType = b.ctx.Int8Type()
|
||||
asmString = `
|
||||
ldi r24, pm_lo8(1f)
|
||||
ldi r25, pm_hi8(1f)
|
||||
std z+2, r24
|
||||
std z+3, r25
|
||||
std z+4, r28
|
||||
std z+5, r29
|
||||
ldi r24, 0
|
||||
1:`
|
||||
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
||||
case "riscv32":
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sw a2, 4(a1)
|
||||
li a0, 0
|
||||
1:`
|
||||
constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
|
||||
default:
|
||||
// This case should have been handled by b.supportsRecover().
|
||||
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
||||
}
|
||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
b.blockExits[b.currentBlock] = continueBB
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
@@ -77,7 +249,8 @@ func isInLoop(start *ssa.BasicBlock) bool {
|
||||
func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// The pointer to the previous defer struct, which we will replace to
|
||||
// make a linked list.
|
||||
next := b.CreateLoad(b.deferPtr, "defer.next")
|
||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
|
||||
|
||||
var values []llvm.Value
|
||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
||||
@@ -98,7 +271,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||
values = []llvm.Value{callback, next, typecode, receiverValue}
|
||||
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
||||
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
|
||||
for _, arg := range instr.Call.Args {
|
||||
val := b.getValue(arg)
|
||||
values = append(values, val)
|
||||
@@ -201,30 +374,31 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
}
|
||||
}
|
||||
|
||||
// Make a struct out of the collected values to put in the defer frame.
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFrame := llvm.ConstNull(deferFrameType)
|
||||
// Make a struct out of the collected values to put in the deferred call
|
||||
// struct.
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCall := llvm.ConstNull(deferredCallType)
|
||||
for i, value := range values {
|
||||
deferFrame = b.CreateInsertValue(deferFrame, value, i, "")
|
||||
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca")
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||
} else {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := b.targetData.TypeAllocSize(deferFrameType)
|
||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
nilPtr := llvm.ConstNull(b.i8ptrType)
|
||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc")
|
||||
}
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
}
|
||||
b.CreateStore(deferFrame, alloca)
|
||||
b.CreateStore(deferredCall, alloca)
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
@@ -233,6 +407,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
// createRunDefers emits code to run all deferred functions.
|
||||
func (b *builder) createRunDefers() {
|
||||
deferType := b.getLLVMRuntimeType("_defer")
|
||||
deferPtrType := llvm.PointerType(deferType, 0)
|
||||
|
||||
// Add a loop like the following:
|
||||
// for stack != nil {
|
||||
// _stack := stack
|
||||
@@ -248,17 +425,17 @@ func (b *builder) createRunDefers() {
|
||||
// }
|
||||
// }
|
||||
|
||||
// Create loop.
|
||||
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
|
||||
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
|
||||
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
|
||||
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
|
||||
// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end.
|
||||
end := b.insertBasicBlock("rundefers.end")
|
||||
unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default")
|
||||
loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop")
|
||||
loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead")
|
||||
b.CreateBr(loophead)
|
||||
|
||||
// Create loop head:
|
||||
// for stack != nil {
|
||||
b.SetInsertPointAtEnd(loophead)
|
||||
deferData := b.CreateLoad(b.deferPtr, "")
|
||||
deferData := b.CreateLoad(deferPtrType, b.deferPtr, "")
|
||||
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
|
||||
b.CreateCondBr(stackIsNil, end, loop)
|
||||
|
||||
@@ -267,24 +444,24 @@ func (b *builder) createRunDefers() {
|
||||
// stack = stack.next
|
||||
// switch stack.callback {
|
||||
b.SetInsertPointAtEnd(loop)
|
||||
nextStackGEP := b.CreateInBoundsGEP(deferData, []llvm.Value{
|
||||
nextStackGEP := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
|
||||
}, "stack.next.gep")
|
||||
nextStack := b.CreateLoad(nextStackGEP, "stack.next")
|
||||
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next")
|
||||
b.CreateStore(nextStack, b.deferPtr)
|
||||
gep := b.CreateInBoundsGEP(deferData, []llvm.Value{
|
||||
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field
|
||||
}, "callback.gep")
|
||||
callback := b.CreateLoad(gep, "callback")
|
||||
callback := b.CreateLoad(b.uintptrType, gep, "callback")
|
||||
sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
|
||||
|
||||
for i, callback := range b.allDeferFuncs {
|
||||
// Create switch case, for example:
|
||||
// case 0:
|
||||
// // run first deferred call
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
|
||||
block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i))
|
||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
|
||||
b.SetInsertPointAtEnd(block)
|
||||
switch callback := callback.(type) {
|
||||
@@ -295,30 +472,31 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
|
||||
if !callback.IsInvoke() {
|
||||
//Expect funcValue to be passed through the defer frame.
|
||||
//Expect funcValue to be passed through the deferred call.
|
||||
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||
} else {
|
||||
//Expect typecode
|
||||
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
||||
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
|
||||
}
|
||||
|
||||
for _, arg := range callback.Args {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||
}
|
||||
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct (including receiver).
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
var fnPtr llvm.Value
|
||||
var fnType llvm.Type
|
||||
|
||||
if !callback.IsInvoke() {
|
||||
// Isolate the func value.
|
||||
@@ -326,8 +504,8 @@ func (b *builder) createRunDefers() {
|
||||
forwardParams = forwardParams[1:]
|
||||
|
||||
//Get function pointer and context
|
||||
fp, context := b.decodeFuncValue(funcValue, callback.Signature())
|
||||
fnPtr = fp
|
||||
var context llvm.Value
|
||||
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature())
|
||||
|
||||
//Pass context
|
||||
forwardParams = append(forwardParams, context)
|
||||
@@ -336,6 +514,7 @@ func (b *builder) createRunDefers() {
|
||||
// parameters.
|
||||
forwardParams = append(forwardParams[1:], forwardParams[0])
|
||||
fnPtr = b.getInvokeFunction(callback)
|
||||
fnType = fnPtr.GlobalValueType()
|
||||
|
||||
// Add the context parameter. An interface call cannot also be a
|
||||
// closure but we have to supply the parameter anyway for platforms
|
||||
@@ -343,10 +522,7 @@ func (b *builder) createRunDefers() {
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
}
|
||||
|
||||
// Parent coroutine handle.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
|
||||
b.createCall(fnPtr, forwardParams, "")
|
||||
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||
|
||||
case *ssa.Function:
|
||||
// Direct call.
|
||||
@@ -356,15 +532,15 @@ func (b *builder) createRunDefers() {
|
||||
for _, param := range getParams(callback.Signature) {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||
}
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := range getParams(callback.Signature) {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
@@ -374,13 +550,11 @@ func (b *builder) createRunDefers() {
|
||||
// Add the context parameter. We know it is ignored by the receiving
|
||||
// function, but we have to pass one anyway.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
|
||||
// Parent coroutine handle.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
}
|
||||
|
||||
// Call real function.
|
||||
b.createCall(b.getFunction(callback), forwardParams, "")
|
||||
fnType, fn := b.getFunction(callback)
|
||||
b.createInvoke(fnType, fn, forwardParams, "")
|
||||
|
||||
case *ssa.MakeClosure:
|
||||
// Get the real defer struct type and cast to it.
|
||||
@@ -391,23 +565,21 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
valueTypes = append(valueTypes, b.i8ptrType) // closure
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
// Parent coroutine handle.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
|
||||
// Call deferred function.
|
||||
b.createCall(b.getFunction(fn), forwardParams, "")
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
b.createCall(fnType, llvmFn, forwardParams, "")
|
||||
case *ssa.Builtin:
|
||||
db := b.deferBuiltinFuncs[callback]
|
||||
|
||||
@@ -420,15 +592,15 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||
|
||||
// Extract the params from the struct.
|
||||
var argValues []llvm.Value
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||
argValues = append(argValues, forwardParam)
|
||||
}
|
||||
|
||||
|
||||
+16
-59
@@ -19,29 +19,8 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat
|
||||
// createFuncValue creates a function value from a raw function pointer with no
|
||||
// context.
|
||||
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||
var funcValueScalar llvm.Value
|
||||
switch c.FuncImplementation {
|
||||
case "doubleword":
|
||||
// Closure is: {context, function pointer}
|
||||
funcValueScalar = llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
|
||||
case "switch":
|
||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||
if funcValueWithSignatureGlobal.IsNil() {
|
||||
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
||||
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
||||
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
||||
c.getFuncSignatureID(sig),
|
||||
})
|
||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
||||
funcValueWithSignatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
||||
default:
|
||||
panic("unimplemented func value variant")
|
||||
}
|
||||
// Closure is: {context, function pointer}
|
||||
funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
|
||||
funcValueType := c.getFuncType(sig)
|
||||
funcValue := llvm.Undef(funcValueType)
|
||||
funcValue = builder.CreateInsertValue(funcValue, context, 0, "")
|
||||
@@ -76,48 +55,26 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||
|
||||
// decodeFuncValue extracts the context and the function pointer from this func
|
||||
// value. This may be an expensive operation.
|
||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) {
|
||||
context = b.CreateExtractValue(funcValue, 0, "")
|
||||
switch b.FuncImplementation {
|
||||
case "doubleword":
|
||||
bitcast := b.CreateExtractValue(funcValue, 1, "")
|
||||
if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
|
||||
funcPtr = bitcast.Operand(0)
|
||||
return
|
||||
}
|
||||
llvmSig := b.getRawFuncType(sig)
|
||||
funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
|
||||
case "switch":
|
||||
if !funcValue.IsAConstant().IsNil() {
|
||||
// If this is a constant func value, the underlying function is
|
||||
// known and can be returned directly.
|
||||
funcValueWithSignatureGlobal := llvm.ConstExtractValue(funcValue, []uint32{1}).Operand(0)
|
||||
funcPtr = llvm.ConstExtractValue(funcValueWithSignatureGlobal.Initializer(), []uint32{0}).Operand(0)
|
||||
return
|
||||
}
|
||||
llvmSig := b.getRawFuncType(sig)
|
||||
sigGlobal := b.getFuncSignatureID(sig)
|
||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
|
||||
default:
|
||||
panic("unimplemented func value variant")
|
||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||
if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast {
|
||||
funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers)
|
||||
}
|
||||
if sig != nil {
|
||||
funcType = b.getRawFuncType(sig)
|
||||
llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace)
|
||||
funcPtr = b.CreateBitCast(funcPtr, llvmSig, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getFuncType returns the type of a func value given a signature.
|
||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||
switch c.FuncImplementation {
|
||||
case "doubleword":
|
||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
||||
case "switch":
|
||||
return c.getLLVMRuntimeType("funcValue")
|
||||
default:
|
||||
panic("unimplemented func value variant")
|
||||
}
|
||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
||||
}
|
||||
|
||||
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
||||
// getRawFuncType returns a LLVM function type for a given signature.
|
||||
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
// Get the return type.
|
||||
var returnType llvm.Type
|
||||
@@ -160,10 +117,9 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
}
|
||||
// All functions take these parameters at the end.
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
||||
|
||||
// Make a func type out of the signature.
|
||||
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
|
||||
return llvm.FunctionType(returnType, paramTypes, false)
|
||||
}
|
||||
|
||||
// parseMakeClosure makes a function value (with context) from the given
|
||||
@@ -187,5 +143,6 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
context := b.emitPointerPack(boundVars)
|
||||
|
||||
// Create the closure.
|
||||
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
|
||||
_, fn := b.getFunction(f)
|
||||
return b.createFuncValue(fn, context, f.Signature), nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ func (b *builder) trackPointer(value llvm.Value) {
|
||||
if value.Type() != b.i8ptrType {
|
||||
value = b.CreateBitCast(value, b.i8ptrType, "")
|
||||
}
|
||||
b.createRuntimeCall("trackPointer", []llvm.Value{value}, "")
|
||||
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
|
||||
}
|
||||
|
||||
// typeHasPointers returns whether this type is a pointer or contains pointers.
|
||||
|
||||
+56
-83
@@ -7,7 +7,6 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -22,6 +21,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
|
||||
var prefix string
|
||||
var funcPtr llvm.Value
|
||||
var funcPtrType llvm.Type
|
||||
hasContext := false
|
||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||
// Static callee is known. This makes it easier to start a new
|
||||
@@ -30,11 +30,6 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
switch value := instr.Call.Value.(type) {
|
||||
case *ssa.Function:
|
||||
// Goroutine call is regular function call. No context is necessary.
|
||||
if b.Scheduler == "coroutines" {
|
||||
// The context parameter is assumed to be always present in the
|
||||
// coroutines scheduler.
|
||||
context = llvm.Undef(b.i8ptrType)
|
||||
}
|
||||
case *ssa.MakeClosure:
|
||||
// A goroutine call on a func value, but the callee is trivial to find. For
|
||||
// example: immediately applied functions.
|
||||
@@ -47,7 +42,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
params = append(params, context) // context parameter
|
||||
hasContext = true
|
||||
}
|
||||
funcPtr = b.getFunction(callee)
|
||||
funcPtrType, funcPtr = b.getFunction(callee)
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
// We cheat. None of the builtins do any long or blocking operation, so
|
||||
// we might as well run these builtins right away without the program
|
||||
@@ -85,6 +80,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||
funcPtrType = funcPtr.GlobalValueType()
|
||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||
params = append(params, itfTypeCode) // end with typecode
|
||||
} else {
|
||||
@@ -94,66 +90,47 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
var context llvm.Value
|
||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context) // context parameter
|
||||
funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context, funcPtr)
|
||||
hasContext = true
|
||||
switch b.Scheduler {
|
||||
case "none", "coroutines":
|
||||
// There are no additional parameters needed for the goroutine start operation.
|
||||
case "tasks", "asyncify":
|
||||
// Add the function pointer as a parameter to start the goroutine.
|
||||
params = append(params, funcPtr)
|
||||
default:
|
||||
panic("unknown scheduler type")
|
||||
}
|
||||
prefix = b.fn.RelString(nil)
|
||||
}
|
||||
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
var callee, stackSize llvm.Value
|
||||
switch b.Scheduler {
|
||||
case "none", "tasks", "asyncify":
|
||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
|
||||
if b.AutomaticStackSize {
|
||||
// The stack size is not known until after linking. Call a dummy
|
||||
// function that will be replaced with a load from a special ELF
|
||||
// section that contains the stack size (and is modified after
|
||||
// linking).
|
||||
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// constant, it can be optimized.
|
||||
if (b.Scheduler == "tasks" || b.Scheduler == "asyncify") && b.DefaultStackSize == 0 {
|
||||
b.addError(instr.Pos(), "default stack size for goroutines is not set")
|
||||
}
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
var stackSize llvm.Value
|
||||
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos())
|
||||
if b.AutomaticStackSize {
|
||||
// The stack size is not known until after linking. Call a dummy
|
||||
// function that will be replaced with a load from a special ELF
|
||||
// section that contains the stack size (and is modified after
|
||||
// linking).
|
||||
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// constant, it can be optimized.
|
||||
if (b.Scheduler == "tasks" || b.Scheduler == "asyncify") && b.DefaultStackSize == 0 {
|
||||
b.addError(instr.Pos(), "default stack size for goroutines is not set")
|
||||
}
|
||||
case "coroutines":
|
||||
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
|
||||
// There is no goroutine stack size: coroutines are used instead of
|
||||
// stacks.
|
||||
stackSize = llvm.Undef(b.uintptrType)
|
||||
default:
|
||||
panic("unreachable")
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
}
|
||||
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
|
||||
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "")
|
||||
}
|
||||
|
||||
// createGoroutineStartWrapper creates a wrapper for the task-based
|
||||
// implementation of goroutines. For example, to call a function like this:
|
||||
//
|
||||
// func add(x, y int) int { ... }
|
||||
// func add(x, y int) int { ... }
|
||||
//
|
||||
// It creates a wrapper like this:
|
||||
//
|
||||
// func add$gowrapper(ptr *unsafe.Pointer) {
|
||||
// args := (*struct{
|
||||
// x, y int
|
||||
// })(ptr)
|
||||
// add(args.x, args.y)
|
||||
// }
|
||||
// func add$gowrapper(ptr *unsafe.Pointer) {
|
||||
// args := (*struct{
|
||||
// x, y int
|
||||
// })(ptr)
|
||||
// add(args.x, args.y)
|
||||
// }
|
||||
//
|
||||
// This is useful because the task-based goroutine start implementation only
|
||||
// allows a single (pointer) argument to the newly started goroutine. Also, it
|
||||
@@ -164,15 +141,19 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
// to last parameter of the function) is used for this wrapper. If hasContext is
|
||||
// false, the parameter bundle is assumed to have no context parameter and undef
|
||||
// is passed instead.
|
||||
func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
|
||||
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
|
||||
var wrapper llvm.Value
|
||||
|
||||
builder := c.ctx.NewBuilder()
|
||||
defer builder.Dispose()
|
||||
b := &builder{
|
||||
compilerContext: c,
|
||||
Builder: c.ctx.NewBuilder(),
|
||||
}
|
||||
defer b.Dispose()
|
||||
|
||||
var deadlock llvm.Value
|
||||
var deadlockType llvm.Type
|
||||
if c.Scheduler == "asyncify" {
|
||||
deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
||||
deadlockType, deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
||||
}
|
||||
|
||||
if !fn.IsAFunction().IsNil() {
|
||||
@@ -191,7 +172,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(pos)
|
||||
@@ -212,27 +193,25 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
Optimized: true,
|
||||
})
|
||||
wrapper.SetSubprogram(difunc)
|
||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Create the list of params for the call.
|
||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip parentHandle parameter
|
||||
paramTypes := fnType.ParamTypes()
|
||||
if !hasContext {
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||
}
|
||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
||||
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||
if !hasContext {
|
||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
||||
}
|
||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy parentHandle parameter
|
||||
|
||||
// Create the call.
|
||||
builder.CreateCall(fn, params, "")
|
||||
b.CreateCall(fnType, fn, params, "")
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
builder.CreateCall(deadlock, []llvm.Value{
|
||||
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
|
||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||
llvm.Undef(c.i8ptrType),
|
||||
}, "")
|
||||
}
|
||||
|
||||
@@ -262,7 +241,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(pos)
|
||||
@@ -283,43 +262,37 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
Optimized: true,
|
||||
})
|
||||
wrapper.SetSubprogram(difunc)
|
||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Get the list of parameters, with the extra parameters at the end.
|
||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
||||
paramTypes[len(paramTypes)-1] = fn.Type() // the last element is the function pointer
|
||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
||||
paramTypes := fnType.ParamTypes()
|
||||
paramTypes = append(paramTypes, fn.Type()) // the last element is the function pointer
|
||||
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||
|
||||
// Get the function pointer.
|
||||
fnPtr := params[len(params)-1]
|
||||
|
||||
// The last parameter in the packed object has somewhat of a dual role.
|
||||
// Inside the parameter bundle it's the function pointer, stored right
|
||||
// after the context pointer. But in the IR call instruction, it's the
|
||||
// parentHandle function that's always undef outside of the coroutines
|
||||
// scheduler. Thus, make the parameter undef here.
|
||||
params[len(params)-1] = llvm.Undef(c.i8ptrType)
|
||||
params = params[:len(params)-1]
|
||||
|
||||
// Create the call.
|
||||
builder.CreateCall(fnPtr, params, "")
|
||||
b.CreateCall(fnType, fnPtr, params, "")
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
builder.CreateCall(deadlock, []llvm.Value{
|
||||
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
|
||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||
llvm.Undef(c.i8ptrType),
|
||||
}, "")
|
||||
}
|
||||
}
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
// The goroutine was terminated via deadlock.
|
||||
builder.CreateUnreachable()
|
||||
b.CreateUnreachable()
|
||||
} else {
|
||||
// Finish the function. Every basic block must end in a terminator, and
|
||||
// because goroutines never return a value we can simply return void.
|
||||
builder.CreateRetVoid()
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// Return a ptrtoint of the wrapper, not the function itself.
|
||||
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
|
||||
return b.CreatePtrToInt(wrapper, c.uintptrType, "")
|
||||
}
|
||||
|
||||
+42
-39
@@ -17,31 +17,31 @@ import (
|
||||
// operands or return values. It is useful for trivial instructions, like wfi in
|
||||
// ARM or sleep in AVR.
|
||||
//
|
||||
// func Asm(asm string)
|
||||
// func Asm(asm string)
|
||||
//
|
||||
// The provided assembly must be a constant.
|
||||
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
// Magic function: insert inline assembly instead of calling it.
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
|
||||
asm := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
|
||||
return b.CreateCall(target, nil, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, nil, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin, which allows assembly to be called in a flexible
|
||||
// way.
|
||||
//
|
||||
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
//
|
||||
// The asm parameter must be a constant string. The regs parameter must be
|
||||
// provided immediately. For example:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
registers := map[string]llvm.Value{}
|
||||
@@ -98,7 +98,10 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
case llvm.IntegerTypeKind:
|
||||
constraints = append(constraints, "r")
|
||||
case llvm.PointerTypeKind:
|
||||
constraints = append(constraints, "*m")
|
||||
// Memory references require a type in LLVM 14, probably as a
|
||||
// preparation for opaque pointers.
|
||||
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
|
||||
return s
|
||||
default:
|
||||
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
||||
return s
|
||||
@@ -116,8 +119,8 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
outputType = b.ctx.VoidType()
|
||||
}
|
||||
fnType := llvm.FunctionType(outputType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
|
||||
result := b.CreateCall(target, args, "")
|
||||
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0, false)
|
||||
result := b.CreateCall(fnType, target, args, "")
|
||||
if hasOutput {
|
||||
return result, nil
|
||||
} else {
|
||||
@@ -129,11 +132,11 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||
// be one of:
|
||||
//
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
//
|
||||
// The num parameter must be a constant. All other parameters may be any scalar
|
||||
// value supported by LLVM inline assembly.
|
||||
@@ -159,18 +162,18 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
||||
// marked as clobbered.
|
||||
constraints += ",~{r1},~{r2},~{r3}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
||||
return b.CreateCall(target, llvmArgs, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||
// be one of:
|
||||
//
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
//
|
||||
// The num parameter must be a constant. All other parameters may be any scalar
|
||||
// value supported by LLVM inline assembly.
|
||||
@@ -197,16 +200,16 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
|
||||
// marked as clobbered.
|
||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
||||
return b.CreateCall(target, llvmArgs, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
||||
//
|
||||
// func (csr CSR) Get() uintptr
|
||||
// func (csr CSR) Set(uintptr)
|
||||
// func (csr CSR) SetBits(uintptr) uintptr
|
||||
// func (csr CSR) ClearBits(uintptr) uintptr
|
||||
// func (csr CSR) Get() uintptr
|
||||
// func (csr CSR) Set(uintptr)
|
||||
// func (csr CSR) SetBits(uintptr) uintptr
|
||||
// func (csr CSR) ClearBits(uintptr) uintptr
|
||||
//
|
||||
// The csr parameter (method receiver) must be a constant. Other parameter can
|
||||
// be any value.
|
||||
@@ -222,25 +225,25 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
// marked as such.
|
||||
fnType := llvm.FunctionType(b.uintptrType, nil, false)
|
||||
asm := fmt.Sprintf("csrr $0, %d", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
|
||||
return b.CreateCall(target, nil, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, nil, ""), nil
|
||||
case "Set":
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrw %d, $0", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0)
|
||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
case "SetBits":
|
||||
// Note: it may be possible to optimize this to csrrsi in many cases.
|
||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
case "ClearBits":
|
||||
// Note: it may be possible to optimize this to csrrci in many cases.
|
||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||
}
|
||||
|
||||
+387
-221
@@ -6,6 +6,7 @@ package compiler
|
||||
// interface-lowering.go for more details.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
@@ -15,6 +16,49 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Type kinds for basic types.
|
||||
// They must match the constants for the Kind type in src/reflect/type.go.
|
||||
var basicTypes = [...]uint8{
|
||||
types.Bool: 1,
|
||||
types.Int: 2,
|
||||
types.Int8: 3,
|
||||
types.Int16: 4,
|
||||
types.Int32: 5,
|
||||
types.Int64: 6,
|
||||
types.Uint: 7,
|
||||
types.Uint8: 8,
|
||||
types.Uint16: 9,
|
||||
types.Uint32: 10,
|
||||
types.Uint64: 11,
|
||||
types.Uintptr: 12,
|
||||
types.Float32: 13,
|
||||
types.Float64: 14,
|
||||
types.Complex64: 15,
|
||||
types.Complex128: 16,
|
||||
types.String: 17,
|
||||
types.UnsafePointer: 18,
|
||||
}
|
||||
|
||||
// These must also match the constants for the Kind type in src/reflect/type.go.
|
||||
const (
|
||||
typeKindChan = 19
|
||||
typeKindInterface = 20
|
||||
typeKindPointer = 21
|
||||
typeKindSlice = 22
|
||||
typeKindArray = 23
|
||||
typeKindSignature = 24
|
||||
typeKindMap = 25
|
||||
typeKindStruct = 26
|
||||
)
|
||||
|
||||
// Flags stored in the first byte of the struct field byte array. Must be kept
|
||||
// up to date with src/reflect/type.go.
|
||||
const (
|
||||
structFieldFlagAnonymous = 1 << iota
|
||||
structFieldFlagHasTag
|
||||
structFieldFlagIsExported
|
||||
)
|
||||
|
||||
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
|
||||
// It tries to put the type in the interface value, but if that's not possible,
|
||||
// it will do an allocation of the right size and put that in the interface
|
||||
@@ -23,124 +67,271 @@ import (
|
||||
// An interface value is a {typecode, value} tuple named runtime._interface.
|
||||
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
||||
itfValue := b.emitPointerPack([]llvm.Value{val})
|
||||
itfTypeCodeGlobal := b.getTypeCode(typ)
|
||||
itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
|
||||
itfType := b.getTypeCode(typ)
|
||||
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
||||
itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||
itf = b.CreateInsertValue(itf, itfType, 0, "")
|
||||
itf = b.CreateInsertValue(itf, itfValue, 1, "")
|
||||
return itf
|
||||
}
|
||||
|
||||
// extractValueFromInterface extract the value from an interface value
|
||||
// (runtime._interface) under the assumption that it is of the type given in
|
||||
// llvmType. The behavior is undefied if the interface is nil or llvmType
|
||||
// doesn't match the underlying type of the interface.
|
||||
func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value {
|
||||
valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
|
||||
return b.emitPointerUnpack(valuePtr, []llvm.Type{llvmType})[0]
|
||||
}
|
||||
|
||||
// getTypeCode returns a reference to a type code.
|
||||
// It returns a pointer to an external global which should be replaced with the
|
||||
// real type in the interface lowering pass.
|
||||
// A type code is a pointer to a constant global that describes the type.
|
||||
// This function returns a pointer to the 'kind' field (which might not be the
|
||||
// first field in the struct).
|
||||
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
ms := c.program.MethodSets.MethodSet(typ)
|
||||
hasMethodSet := ms.Len() != 0
|
||||
if _, ok := typ.Underlying().(*types.Interface); ok {
|
||||
hasMethodSet = false
|
||||
}
|
||||
globalName := "reflect/types.type:" + getTypeCodeName(typ)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if global.IsNil() {
|
||||
// Create a new typecode global.
|
||||
global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
|
||||
// Some type classes contain more information for underlying types or
|
||||
// element types. Store it directly in the typecode global to make
|
||||
// reflect lowering simpler.
|
||||
var references llvm.Value
|
||||
var length int64
|
||||
var methodSet llvm.Value
|
||||
var ptrTo llvm.Value
|
||||
var typeAssert llvm.Value
|
||||
var typeFields []llvm.Value
|
||||
// Define the type fields. These must match the structs in
|
||||
// src/reflect/type.go (ptrType, arrayType, etc). See the comment at the
|
||||
// top of src/reflect/type.go for more information on the layout of these structs.
|
||||
typeFieldTypes := []*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
|
||||
}
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
case *types.Named:
|
||||
references = c.getTypeCode(typ.Underlying())
|
||||
case *types.Chan:
|
||||
references = c.getTypeCode(typ.Elem())
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
case *types.Chan, *types.Slice:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
case *types.Pointer:
|
||||
references = c.getTypeCode(typ.Elem())
|
||||
case *types.Slice:
|
||||
references = c.getTypeCode(typ.Elem())
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
case *types.Array:
|
||||
references = c.getTypeCode(typ.Elem())
|
||||
length = typ.Len()
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||
)
|
||||
case *types.Map:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
case *types.Struct:
|
||||
// Take a pointer to the typecodeID of the first field (if it exists).
|
||||
structGlobal := c.makeStructTypeFields(typ)
|
||||
references = llvm.ConstBitCast(structGlobal, global.Type())
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
|
||||
)
|
||||
case *types.Interface:
|
||||
methodSetGlobal := c.getInterfaceMethodSet(typ)
|
||||
references = llvm.ConstBitCast(methodSetGlobal, global.Type())
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
// TODO: methods
|
||||
case *types.Signature:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
)
|
||||
// TODO: signature params and return values
|
||||
}
|
||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||
methodSet = c.getTypeMethodSet(typ)
|
||||
} else {
|
||||
typeAssert = c.getInterfaceImplementsFunc(typ)
|
||||
typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
|
||||
if hasMethodSet {
|
||||
// This method set is appended at the start of the struct. It is
|
||||
// removed in the interface lowering pass.
|
||||
// TODO: don't remove these and instead do what upstream Go is doing
|
||||
// instead. See: https://research.swtch.com/interfaces. This can
|
||||
// likely be optimized in LLVM using
|
||||
// https://llvm.org/docs/TypeMetadata.html.
|
||||
typeFieldTypes = append([]*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "methodSet", types.Typ[types.UnsafePointer]),
|
||||
}, typeFieldTypes...)
|
||||
}
|
||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
||||
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
||||
globalType := types.NewStruct(typeFieldTypes, nil)
|
||||
global = llvm.AddGlobal(c.mod, c.getLLVMType(globalType), globalName)
|
||||
metabyte := getTypeKind(typ)
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
case *types.Named:
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
}
|
||||
metabyte |= 1 << 5 // "named" flag
|
||||
case *types.Chan:
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Elem()), // elementType
|
||||
}
|
||||
case *types.Slice:
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Elem()), // elementType
|
||||
}
|
||||
case *types.Pointer:
|
||||
typeFields = []llvm.Value{c.getTypeCode(typ.Elem())}
|
||||
case *types.Array:
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Elem()), // elementType
|
||||
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
|
||||
}
|
||||
case *types.Map:
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
}
|
||||
case *types.Struct:
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
}
|
||||
structFieldType := c.getLLVMRuntimeType("structField")
|
||||
var fields []llvm.Value
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
field := typ.Field(i)
|
||||
var flags uint8
|
||||
if field.Anonymous() {
|
||||
flags |= structFieldFlagAnonymous
|
||||
}
|
||||
if typ.Tag(i) != "" {
|
||||
flags |= structFieldFlagHasTag
|
||||
}
|
||||
if token.IsExported(field.Name()) {
|
||||
flags |= structFieldFlagIsExported
|
||||
}
|
||||
data := string(flags) + field.Name() + "\x00"
|
||||
if typ.Tag(i) != "" {
|
||||
if len(typ.Tag(i)) > 0xff {
|
||||
c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i))))
|
||||
}
|
||||
data += string([]byte{byte(len(typ.Tag(i)))}) + typ.Tag(i)
|
||||
}
|
||||
dataInitializer := c.ctx.ConstString(data, false)
|
||||
dataGlobal := llvm.AddGlobal(c.mod, dataInitializer.Type(), globalName+"."+field.Name())
|
||||
dataGlobal.SetInitializer(dataInitializer)
|
||||
dataGlobal.SetAlignment(1)
|
||||
dataGlobal.SetUnnamedAddr(true)
|
||||
dataGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
dataGlobal.SetGlobalConstant(true)
|
||||
fieldType := c.getTypeCode(field.Type())
|
||||
fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{
|
||||
fieldType,
|
||||
llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
}),
|
||||
}))
|
||||
}
|
||||
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
|
||||
case *types.Interface:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: methods
|
||||
case *types.Signature:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: params, return values, etc
|
||||
}
|
||||
globalValue := llvm.ConstNull(global.Type().ElementType())
|
||||
if !references.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||
}
|
||||
if length != 0 {
|
||||
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
||||
}
|
||||
if !methodSet.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
|
||||
}
|
||||
if !ptrTo.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
||||
}
|
||||
if !typeAssert.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
|
||||
// Prepend metadata byte.
|
||||
typeFields = append([]llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false),
|
||||
}, typeFields...)
|
||||
if hasMethodSet {
|
||||
typeFields = append([]llvm.Value{
|
||||
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType),
|
||||
}, typeFields...)
|
||||
}
|
||||
alignment := c.targetData.TypeAllocSize(c.i8ptrType)
|
||||
globalValue := c.ctx.ConstStruct(typeFields, false)
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetAlignment(int(alignment))
|
||||
if c.Debug {
|
||||
file := c.getDIFile("<Go type>")
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
|
||||
Name: "type " + typ.String(),
|
||||
File: file,
|
||||
Line: 1,
|
||||
Type: c.getDIType(globalType),
|
||||
LocalToUnit: false,
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
AlignInBits: uint32(alignment * 8),
|
||||
})
|
||||
global.AddMetadata(0, diglobal)
|
||||
}
|
||||
}
|
||||
return global
|
||||
offset := uint64(0)
|
||||
if hasMethodSet {
|
||||
// The pointer to the method set is always the first element of the
|
||||
// global (if there is a method set). However, the pointer we return
|
||||
// should point to the 'kind' field not the method set.
|
||||
offset = 1
|
||||
}
|
||||
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||
llvm.ConstInt(llvm.Int32Type(), offset, false),
|
||||
})
|
||||
}
|
||||
|
||||
// makeStructTypeFields creates a new global that stores all type information
|
||||
// related to this struct type, and returns the resulting global. This global is
|
||||
// actually an array of all the fields in the structs.
|
||||
func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
||||
// The global is an array of runtime.structField structs.
|
||||
runtimeStructField := c.getLLVMRuntimeType("structField")
|
||||
structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields())
|
||||
structGlobal := llvm.AddGlobal(c.mod, structGlobalType, "reflect/types.structFields")
|
||||
structGlobalValue := llvm.ConstNull(structGlobalType)
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
fieldGlobalValue := llvm.ConstNull(runtimeStructField)
|
||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
|
||||
fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
|
||||
fieldName.SetLinkage(llvm.PrivateLinkage)
|
||||
fieldName.SetUnnamedAddr(true)
|
||||
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
})
|
||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
|
||||
if typ.Tag(i) != "" {
|
||||
fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
|
||||
fieldTag.SetLinkage(llvm.PrivateLinkage)
|
||||
fieldTag.SetUnnamedAddr(true)
|
||||
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
})
|
||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
|
||||
}
|
||||
if typ.Field(i).Embedded() {
|
||||
fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
|
||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldEmbedded, []uint32{3})
|
||||
}
|
||||
structGlobalValue = llvm.ConstInsertValue(structGlobalValue, fieldGlobalValue, []uint32{uint32(i)})
|
||||
// getTypeKind returns the type kind for the given type, as defined by
|
||||
// reflect.Kind.
|
||||
func getTypeKind(t types.Type) uint8 {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return basicTypes[t.Kind()]
|
||||
case *types.Chan:
|
||||
return typeKindChan
|
||||
case *types.Interface:
|
||||
return typeKindInterface
|
||||
case *types.Pointer:
|
||||
return typeKindPointer
|
||||
case *types.Slice:
|
||||
return typeKindSlice
|
||||
case *types.Array:
|
||||
return typeKindArray
|
||||
case *types.Signature:
|
||||
return typeKindSignature
|
||||
case *types.Map:
|
||||
return typeKindMap
|
||||
case *types.Struct:
|
||||
return typeKindStruct
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
structGlobal.SetInitializer(structGlobalValue)
|
||||
structGlobal.SetUnnamedAddr(true)
|
||||
structGlobal.SetLinkage(llvm.PrivateLinkage)
|
||||
return structGlobal
|
||||
}
|
||||
|
||||
var basicTypeNames = [...]string{
|
||||
types.Bool: "bool",
|
||||
types.Int: "int",
|
||||
types.Int8: "int8",
|
||||
types.Int16: "int16",
|
||||
types.Int32: "int32",
|
||||
types.Int64: "int64",
|
||||
types.Uint: "uint",
|
||||
types.Uint8: "uint8",
|
||||
types.Uint16: "uint16",
|
||||
types.Uint32: "uint32",
|
||||
types.Uint64: "uint64",
|
||||
types.Uintptr: "uintptr",
|
||||
types.Float32: "float32",
|
||||
types.Float64: "float64",
|
||||
types.Complex64: "complex64",
|
||||
types.Complex128: "complex128",
|
||||
types.String: "string",
|
||||
types.UnsafePointer: "unsafe.Pointer",
|
||||
}
|
||||
|
||||
// getTypeCodeName returns a name for this type that can be used in the
|
||||
@@ -153,48 +344,7 @@ func getTypeCodeName(t types.Type) string {
|
||||
case *types.Array:
|
||||
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
||||
case *types.Basic:
|
||||
var kind string
|
||||
switch t.Kind() {
|
||||
case types.Bool:
|
||||
kind = "bool"
|
||||
case types.Int:
|
||||
kind = "int"
|
||||
case types.Int8:
|
||||
kind = "int8"
|
||||
case types.Int16:
|
||||
kind = "int16"
|
||||
case types.Int32:
|
||||
kind = "int32"
|
||||
case types.Int64:
|
||||
kind = "int64"
|
||||
case types.Uint:
|
||||
kind = "uint"
|
||||
case types.Uint8:
|
||||
kind = "uint8"
|
||||
case types.Uint16:
|
||||
kind = "uint16"
|
||||
case types.Uint32:
|
||||
kind = "uint32"
|
||||
case types.Uint64:
|
||||
kind = "uint64"
|
||||
case types.Uintptr:
|
||||
kind = "uintptr"
|
||||
case types.Float32:
|
||||
kind = "float32"
|
||||
case types.Float64:
|
||||
kind = "float64"
|
||||
case types.Complex64:
|
||||
kind = "complex64"
|
||||
case types.Complex128:
|
||||
kind = "complex128"
|
||||
case types.String:
|
||||
kind = "string"
|
||||
case types.UnsafePointer:
|
||||
kind = "unsafeptr"
|
||||
default:
|
||||
panic("unknown basic type: " + t.Name())
|
||||
}
|
||||
return "basic:" + kind
|
||||
return "basic:" + basicTypeNames[t.Kind()]
|
||||
case *types.Chan:
|
||||
return "chan:" + getTypeCodeName(t.Elem())
|
||||
case *types.Interface:
|
||||
@@ -246,75 +396,40 @@ func getTypeCodeName(t types.Type) string {
|
||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||
// method set should be unreferenced after the interface lowering pass.
|
||||
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
if !global.IsNil() {
|
||||
// the method set already exists
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
globalName := typ.String() + "$methodset"
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if global.IsNil() {
|
||||
ms := c.program.MethodSets.MethodSet(typ)
|
||||
|
||||
ms := c.program.MethodSets.MethodSet(typ)
|
||||
if ms.Len() == 0 {
|
||||
// no methods, so can leave that one out
|
||||
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
|
||||
}
|
||||
|
||||
methods := make([]llvm.Value, ms.Len())
|
||||
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
method := ms.At(i)
|
||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||
fn := c.program.MethodValue(method)
|
||||
llvmFn := c.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
// compiler error, so panic
|
||||
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
|
||||
// Create method set.
|
||||
var signatures, wrappers []llvm.Value
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
method := ms.At(i)
|
||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||
signatures = append(signatures, signatureGlobal)
|
||||
fn := c.program.MethodValue(method)
|
||||
llvmFnType, llvmFn := c.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
// compiler error, so panic
|
||||
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
|
||||
}
|
||||
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
|
||||
wrappers = append(wrappers, wrapper)
|
||||
}
|
||||
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn)
|
||||
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
||||
signatureGlobal,
|
||||
llvm.ConstPtrToInt(wrapper, c.uintptrType),
|
||||
})
|
||||
methods[i] = methodInfo
|
||||
}
|
||||
arrayType := llvm.ArrayType(interfaceMethodInfoType, len(methods))
|
||||
value := llvm.ConstArray(interfaceMethodInfoType, methods)
|
||||
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
// getInterfaceMethodSet returns a global variable with the method set of the
|
||||
// given named interface type. This method set is used by the interface lowering
|
||||
// pass.
|
||||
func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
||||
name := typ.String()
|
||||
if _, ok := typ.(*types.Named); !ok {
|
||||
// Anonymous interface.
|
||||
name = "reflect/types.interface:" + name
|
||||
// Construct global value.
|
||||
globalValue := c.ctx.ConstStruct([]llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
|
||||
llvm.ConstArray(c.i8ptrType, signatures),
|
||||
c.ctx.ConstStruct(wrappers, false),
|
||||
}, false)
|
||||
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
global := c.mod.NamedGlobal(name + "$interface")
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
if !global.IsNil() {
|
||||
// method set already exist, return it
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
// Every method is a *i8 reference indicating the signature of this method.
|
||||
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
||||
for i := range methods {
|
||||
method := typ.Underlying().(*types.Interface).Method(i)
|
||||
methods[i] = c.getMethodSignature(method)
|
||||
}
|
||||
|
||||
value := llvm.ConstArray(c.i8ptrType, methods)
|
||||
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
return global
|
||||
}
|
||||
|
||||
// getMethodSignatureName returns a unique name (that can be used as the name of
|
||||
@@ -372,7 +487,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// implements each method of the interface. See:
|
||||
// https://research.swtch.com/interfaces
|
||||
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||
commaOk = b.CreateCall(fn, []llvm.Value{actualTypeNum}, "")
|
||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||
|
||||
} else {
|
||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||
@@ -400,8 +515,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// value.
|
||||
|
||||
prevBlock := b.GetInsertBlock()
|
||||
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next")
|
||||
okBlock := b.insertBasicBlock("typeassert.ok")
|
||||
nextBlock := b.insertBasicBlock("typeassert.next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
@@ -416,8 +531,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
} else {
|
||||
// Type assert on concrete type. Extract the underlying type from
|
||||
// the interface (but only after checking it matches).
|
||||
valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
|
||||
valueOk = b.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0]
|
||||
valueOk = b.extractValueFromInterface(itf, assertedType)
|
||||
}
|
||||
b.CreateBr(nextBlock)
|
||||
|
||||
@@ -455,7 +569,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
|
||||
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
|
||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||
c.addStandardDeclaredAttributes(llvmFn)
|
||||
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||
@@ -476,8 +590,8 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
paramTuple = append(paramTuple, sig.Params().At(i))
|
||||
}
|
||||
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
|
||||
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)).ElementType()
|
||||
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
|
||||
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||
c.addStandardDeclaredAttributes(llvmFn)
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
||||
@@ -492,7 +606,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||
// value, dereferences or unpacks it if necessary, and calls the real method.
|
||||
// If the method to wrap has a pointer receiver, no wrapping is necessary and
|
||||
// the function is returned directly.
|
||||
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llvm.Value) llvm.Value {
|
||||
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType llvm.Type, llvmFn llvm.Value) llvm.Value {
|
||||
wrapperName := llvmFn.Name() + "$invoke"
|
||||
wrapper := c.mod.NamedFunction(wrapperName)
|
||||
if !wrapper.IsNil() {
|
||||
@@ -517,12 +631,10 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||
}
|
||||
|
||||
// create wrapper function
|
||||
fnType := llvmFn.Type().ElementType()
|
||||
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
|
||||
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
|
||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||
c.addStandardAttributes(wrapper)
|
||||
wrapper.LastParam().SetName("parentHandle")
|
||||
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
@@ -547,11 +659,11 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||
|
||||
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
|
||||
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
|
||||
if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateCall(llvmFnType, llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
} else {
|
||||
ret := b.CreateCall(llvmFn, params, "ret")
|
||||
ret := b.CreateCall(llvmFnType, llvmFn, params, "ret")
|
||||
b.CreateRet(ret)
|
||||
}
|
||||
|
||||
@@ -563,8 +675,8 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||
// internally to match interfaces and to call the correct method on an
|
||||
// interface. Examples:
|
||||
//
|
||||
// String() string
|
||||
// Read([]byte) (int, error)
|
||||
// String() string
|
||||
// Read([]byte) (int, error)
|
||||
func methodSignature(method *types.Func) string {
|
||||
return method.Name() + signature(method.Type().(*types.Signature))
|
||||
}
|
||||
@@ -572,8 +684,8 @@ func methodSignature(method *types.Func) string {
|
||||
// Make a readable version of a function (pointer) signature.
|
||||
// Examples:
|
||||
//
|
||||
// () string
|
||||
// (string, int) (int, error)
|
||||
// () string
|
||||
// (string, int) (int, error)
|
||||
func signature(sig *types.Signature) string {
|
||||
s := ""
|
||||
if sig.Params().Len() == 0 {
|
||||
@@ -584,23 +696,77 @@ func signature(sig *types.Signature) string {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += sig.Params().At(i).Type().String()
|
||||
s += typestring(sig.Params().At(i).Type())
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
if sig.Results().Len() == 0 {
|
||||
// keep as-is
|
||||
} else if sig.Results().Len() == 1 {
|
||||
s += " " + sig.Results().At(0).Type().String()
|
||||
s += " " + typestring(sig.Results().At(0).Type())
|
||||
} else {
|
||||
s += " ("
|
||||
for i := 0; i < sig.Results().Len(); i++ {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += sig.Results().At(i).Type().String()
|
||||
s += typestring(sig.Results().At(i).Type())
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// typestring returns a stable (human-readable) type string for the given type
|
||||
// that can be used for interface equality checks. It is almost (but not
|
||||
// exactly) the same as calling t.String(). The main difference is some
|
||||
// normalization around `byte` vs `uint8` for example.
|
||||
func typestring(t types.Type) string {
|
||||
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
|
||||
switch t := t.(type) {
|
||||
case *types.Array:
|
||||
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
||||
case *types.Basic:
|
||||
return basicTypeNames[t.Kind()]
|
||||
case *types.Chan:
|
||||
switch t.Dir() {
|
||||
case types.SendRecv:
|
||||
return "chan (" + typestring(t.Elem()) + ")"
|
||||
case types.SendOnly:
|
||||
return "chan<- (" + typestring(t.Elem()) + ")"
|
||||
case types.RecvOnly:
|
||||
return "<-chan (" + typestring(t.Elem()) + ")"
|
||||
default:
|
||||
panic("unknown channel direction")
|
||||
}
|
||||
case *types.Interface:
|
||||
methods := make([]string, t.NumMethods())
|
||||
for i := range methods {
|
||||
method := t.Method(i)
|
||||
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
|
||||
}
|
||||
return "interface{" + strings.Join(methods, ";") + "}"
|
||||
case *types.Map:
|
||||
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
|
||||
case *types.Named:
|
||||
return t.String()
|
||||
case *types.Pointer:
|
||||
return "*" + typestring(t.Elem())
|
||||
case *types.Signature:
|
||||
return "func" + signature(t)
|
||||
case *types.Slice:
|
||||
return "[]" + typestring(t.Elem())
|
||||
case *types.Struct:
|
||||
fields := make([]string, t.NumFields())
|
||||
for i := range fields {
|
||||
field := t.Field(i)
|
||||
fields[i] = field.Name() + " " + typestring(field.Type())
|
||||
if tag := t.Tag(i); tag != "" {
|
||||
fields[i] += " " + strconv.Quote(tag)
|
||||
}
|
||||
}
|
||||
return "struct{" + strings.Join(fields, ";") + "}"
|
||||
default:
|
||||
panic("unknown type: " + t.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
// Fall back to a generic error.
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
||||
}
|
||||
funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
|
||||
_, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
|
||||
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
|
||||
|
||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||
@@ -49,9 +49,11 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
initializer := llvm.ConstNull(globalLLVMType)
|
||||
initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
|
||||
initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
|
||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
|
||||
initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
|
||||
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
|
||||
initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{
|
||||
llvm.ConstInt(b.intType, uint64(id.Int64()), true),
|
||||
}), 2, "")
|
||||
global.SetInitializer(initializer)
|
||||
|
||||
// Add debug info to the interrupt global.
|
||||
@@ -85,7 +87,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
|
||||
}
|
||||
b.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
||||
b.CreateCall(useFn.GlobalValueType(), useFn, []llvm.Value{interrupt}, "")
|
||||
}
|
||||
|
||||
return interrupt, nil
|
||||
|
||||
+104
-52
@@ -3,102 +3,154 @@ package compiler
|
||||
// This file contains helper functions to create calls to LLVM intrinsics.
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove
|
||||
// Define unimplemented intrinsic functions.
|
||||
//
|
||||
// Some functions are either normally implemented in Go assembly (like
|
||||
// sync/atomic functions) or intentionally left undefined to be implemented
|
||||
// directly in the compiler (like runtime/volatile functions). Either way, look
|
||||
// for these and implement them if this is the case.
|
||||
func (b *builder) defineIntrinsicFunction() {
|
||||
name := b.fn.RelString(nil)
|
||||
switch {
|
||||
case name == "runtime.memcpy" || name == "runtime.memmove":
|
||||
b.createMemoryCopyImpl()
|
||||
case name == "runtime.memzero":
|
||||
b.createMemoryZeroImpl()
|
||||
case name == "runtime.KeepAlive":
|
||||
b.createKeepAliveImpl()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
b.createVolatileLoad()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
b.createVolatileStore()
|
||||
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
|
||||
b.createFunctionStart(true)
|
||||
returnValue := b.createAtomicOp(b.fn.Name())
|
||||
if !returnValue.IsNil() {
|
||||
b.CreateRet(returnValue)
|
||||
} else {
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
|
||||
// function, declaring this function if needed. These calls are treated
|
||||
// specially by optimization passes possibly resulting in better generated code,
|
||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||
func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) {
|
||||
fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
func (b *builder) createMemoryCopyImpl() {
|
||||
b.createFunctionStart(true)
|
||||
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
var params []llvm.Value
|
||||
for _, param := range args {
|
||||
for _, param := range b.fn.Params {
|
||||
params = append(params, b.getValue(param))
|
||||
}
|
||||
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of
|
||||
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||
// memory, declaring the function if needed. These calls will be lowered to
|
||||
// regular libc memset calls if they aren't optimized out in a different way.
|
||||
func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
||||
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
func (b *builder) createMemoryZeroImpl() {
|
||||
b.createFunctionStart(true)
|
||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
params := []llvm.Value{
|
||||
b.getValue(args[0]),
|
||||
b.getValue(b.fn.Params[0]),
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||
b.getValue(args[1]),
|
||||
b.getValue(b.fn.Params[1]),
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||
}
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
||||
// using inline assembly.
|
||||
func (b *builder) createKeepAliveImpl() {
|
||||
b.createFunctionStart(true)
|
||||
|
||||
// Get the underlying value of the interface value.
|
||||
interfaceValue := b.getValue(b.fn.Params[0])
|
||||
pointerValue := b.CreateExtractValue(interfaceValue, 1, "")
|
||||
|
||||
// Create an equivalent of the following C code, which is basically just a
|
||||
// nop but ensures the pointerValue is kept alive:
|
||||
//
|
||||
// __asm__ __volatile__("" : : "r"(pointerValue))
|
||||
//
|
||||
// It should be portable to basically everything as the "r" register type
|
||||
// exists basically everywhere.
|
||||
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false)
|
||||
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
|
||||
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
||||
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
"math.Exp2": "llvm.exp2.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Log": "llvm.log.f64",
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
||||
// possible. It returns the call result if possible, and a boolean whether it
|
||||
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
||||
// intrinsic.
|
||||
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
// Check whether this intrinsic is supported on the given GOARCH.
|
||||
// If it is unsupported, this can have two reasons:
|
||||
//
|
||||
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
||||
// the result doesn't pass the tests of the math package.
|
||||
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
||||
// libm function call, but the libm function call also fails the math
|
||||
// package tests.
|
||||
//
|
||||
// Whatever the implementation, it must pass the tests in the math package
|
||||
// so unfortunately only the below intrinsic+architecture combinations are
|
||||
// supported.
|
||||
name := call.StaticCallee().RelString(nil)
|
||||
switch name {
|
||||
case "math.Ceil", "math.Floor", "math.Trunc":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
case "math.Sqrt":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, false // only the above functions are supported.
|
||||
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
|
||||
// instead of the regular Go implementation. This allows LLVM to reason about
|
||||
// the math operation and (depending on the architecture) allows it to lower the
|
||||
// operation to very fast floating point instructions. If this is not possible,
|
||||
// LLVM will emit a call to a libm function that implements the same operation.
|
||||
//
|
||||
// One example of an optimization that LLVM can do is to convert
|
||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||
// more expensive than 32-bit ones.
|
||||
func (b *builder) defineMathOp() {
|
||||
b.createFunctionStart(true)
|
||||
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if llvmName == "" {
|
||||
panic("unreachable: unknown math operation") // sanity check
|
||||
}
|
||||
|
||||
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
||||
llvmFn := b.mod.NamedFunction(llvmName)
|
||||
if llvmFn.IsNil() {
|
||||
// The intrinsic doesn't exist yet, so declare it.
|
||||
// At the moment, all supported intrinsics have the form "double
|
||||
// foo(double %x)" so we can hardcode the signature here.
|
||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
||||
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
args := make([]llvm.Value, len(call.Args))
|
||||
for i, arg := range call.Args {
|
||||
args[i] = b.getValue(arg)
|
||||
args := make([]llvm.Value, len(b.fn.Params))
|
||||
for i, param := range b.fn.Params {
|
||||
args[i] = b.getValue(param)
|
||||
}
|
||||
return b.CreateCall(llvmFn, args, ""), true
|
||||
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||
b.CreateRet(result)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
||||
return fmt.Errorf("type %q uses global context", t.String())
|
||||
default:
|
||||
// we used some other context by accident
|
||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
|
||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx)
|
||||
}
|
||||
|
||||
// if this is a composite type, check the components of the type
|
||||
@@ -70,10 +70,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
||||
return fmt.Errorf("failed to verify element type of array type %s: %s", t.String(), err.Error())
|
||||
}
|
||||
case llvm.PointerTypeKind:
|
||||
// check underlying type
|
||||
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
||||
return fmt.Errorf("failed to verify underlying type of pointer type %s: %s", t.String(), err.Error())
|
||||
}
|
||||
// Pointers can't be checked in an opaque pointer world.
|
||||
case llvm.VectorTypeKind:
|
||||
// check element type
|
||||
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
||||
|
||||
+226
-7
@@ -5,6 +5,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -23,6 +24,23 @@ func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitca
|
||||
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
||||
}
|
||||
|
||||
// insertBasicBlock inserts a new basic block after the current basic block.
|
||||
// This is useful when inserting new basic blocks while converting a
|
||||
// *ssa.BasicBlock to a llvm.BasicBlock and the LLVM basic block needs some
|
||||
// extra blocks.
|
||||
// It does not update b.blockExits, this must be done by the caller.
|
||||
func (b *builder) insertBasicBlock(name string) llvm.BasicBlock {
|
||||
currentBB := b.Builder.GetInsertBlock()
|
||||
nextBB := llvm.NextBasicBlock(currentBB)
|
||||
if nextBB.IsNil() {
|
||||
// Last basic block in the function, so add one to the end.
|
||||
return b.ctx.AddBasicBlock(b.llvmFn, name)
|
||||
}
|
||||
// Insert a basic block before the next basic block - that is, at the
|
||||
// current insert location.
|
||||
return b.ctx.InsertBasicBlock(nextBB, name)
|
||||
}
|
||||
|
||||
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
@@ -33,29 +51,175 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||
// emitPointerPack packs the list of values into a single pointer value using
|
||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||
// pointer value directly. It returns the pointer with the packed data.
|
||||
// If the values are all constants, they are be stored in a constant global and
|
||||
// deduplicated.
|
||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.pkg.Path(), b.NeedsStackObjects, values)
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
valueTypes[i] = value.Type()
|
||||
}
|
||||
packedType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Allocate memory for the packed data.
|
||||
size := b.targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
return llvm.ConstPointerNull(b.i8ptrType)
|
||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||
return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr")
|
||||
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
|
||||
// Packed data fits in a pointer, so store it directly inside the
|
||||
// pointer.
|
||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||
// Try to keep this cast in SSA form.
|
||||
return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int")
|
||||
}
|
||||
|
||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||
// it in a *i8 alloca first and load the *i8 value from there. This is
|
||||
// effectively a bitcast.
|
||||
packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "")
|
||||
|
||||
if size < b.targetData.TypeAllocSize(b.i8ptrType) {
|
||||
// The alloca is bigger than the value that will be stored in it.
|
||||
// To avoid having some bits undefined, zero the alloca first.
|
||||
// Hopefully this will get optimized away.
|
||||
b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc)
|
||||
}
|
||||
|
||||
// Store all values in the alloca.
|
||||
packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "")
|
||||
b.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Load value (the *i8) from the alloca.
|
||||
result := b.CreateLoad(b.i8ptrType, packedAlloc, "")
|
||||
|
||||
// End the lifetime of the alloca, to help the optimizer.
|
||||
packedPtr := b.CreateBitCast(packedAlloc, b.i8ptrType, "")
|
||||
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||
b.emitLifetimeEnd(packedPtr, packedSize)
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Check if the values are all constants.
|
||||
constant := true
|
||||
for _, v := range values {
|
||||
if !v.IsConstant() {
|
||||
constant = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if constant {
|
||||
// The data is known at compile time, so store it in a constant global.
|
||||
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
|
||||
global := llvm.AddGlobal(b.mod, packedType, b.pkg.Path()+"$pack")
|
||||
global.SetInitializer(b.ctx.ConstStruct(values, false))
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
return llvm.ConstBitCast(global, b.i8ptrType)
|
||||
}
|
||||
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
||||
packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
||||
sizeValue,
|
||||
llvm.ConstNull(b.i8ptrType),
|
||||
llvm.Undef(b.i8ptrType), // unused context parameter
|
||||
}, "")
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(packedHeapAlloc)
|
||||
}
|
||||
packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||
|
||||
// Store all values in the heap pointer.
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||
b.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Return the original heap allocation pointer, which already is an *i8.
|
||||
return packedHeapAlloc
|
||||
}
|
||||
}
|
||||
|
||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||
return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
|
||||
packedType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Get a correctly-typed pointer to the packed data.
|
||||
var packedAlloc, packedRawAlloc llvm.Value
|
||||
size := b.targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
// No data to unpack.
|
||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||
// A single pointer is always stored directly.
|
||||
return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
||||
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
|
||||
// Packed data stored directly in pointer.
|
||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||
// Keep this cast in SSA form.
|
||||
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||
}
|
||||
// Fallback: load it using an alloca.
|
||||
packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc")
|
||||
packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value")
|
||||
b.CreateStore(packedRawValue, packedRawAlloc)
|
||||
packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
||||
} else {
|
||||
// Packed data stored on the heap. Bitcast the passed-in pointer to the
|
||||
// correct pointer type.
|
||||
packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
|
||||
}
|
||||
// Load each value from the packed data.
|
||||
values := make([]llvm.Value, len(valueTypes))
|
||||
for i, valueType := range valueTypes {
|
||||
if b.targetData.TypeAllocSize(valueType) == 0 {
|
||||
// This value has length zero, so there's nothing to load.
|
||||
values[i] = llvm.ConstNull(valueType)
|
||||
continue
|
||||
}
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||
values[i] = b.CreateLoad(valueType, gep, "")
|
||||
}
|
||||
if !packedRawAlloc.IsNil() {
|
||||
allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "")
|
||||
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
|
||||
b.emitLifetimeEnd(allocPtr, allocSize)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||
// contents, and returns the global.
|
||||
// contents, and returns the global and initializer type.
|
||||
// Note that it is left with the default linkage etc., you should set
|
||||
// linkage/constant/etc properties yourself.
|
||||
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value {
|
||||
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) (llvm.Type, llvm.Value) {
|
||||
globalType := llvm.ArrayType(elementType, len(buf))
|
||||
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||
value := llvm.Undef(globalType)
|
||||
for i := 0; i < len(buf); i++ {
|
||||
ch := uint64(buf[i])
|
||||
value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
|
||||
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||
}
|
||||
global.SetInitializer(value)
|
||||
return global
|
||||
return globalType, global
|
||||
}
|
||||
|
||||
// createObjectLayout returns a LLVM value (of type i8*) that describes where
|
||||
@@ -66,6 +230,8 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
||||
// which words contain a pointer (indicated by setting the given bit to 1). For
|
||||
// arrays, only the element is stored. This works because the GC knows the
|
||||
// object size and can therefore know how this value is repeated in the object.
|
||||
//
|
||||
// For details on what's in this value, see src/runtime/gc_precise.go.
|
||||
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||
// Use the element type for arrays. This works even for nested arrays.
|
||||
for {
|
||||
@@ -147,7 +313,8 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
|
||||
// Create the global initializer.
|
||||
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
||||
copy(bitmapBytes, bitmap.Bytes())
|
||||
bitmap.FillBytes(bitmapBytes)
|
||||
reverseBytes(bitmapBytes) // big-endian to little-endian
|
||||
var bitmapByteValues []llvm.Value
|
||||
for _, b := range bitmapBytes {
|
||||
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
||||
@@ -253,3 +420,55 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
|
||||
panic("unknown LLVM type")
|
||||
}
|
||||
}
|
||||
|
||||
// archFamily returns the archtecture from the LLVM triple but with some
|
||||
// architecture names ("armv6", "thumbv7m", etc) merged into a single
|
||||
// architecture name ("arm").
|
||||
func (c *compilerContext) archFamily() string {
|
||||
arch := strings.Split(c.Triple, "-")[0]
|
||||
if strings.HasPrefix(arch, "arm64") {
|
||||
return "aarch64"
|
||||
}
|
||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||
return "arm"
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
// isThumb returns whether we're in ARM or in Thumb mode. It panics if the
|
||||
// features string is not one for an ARM architecture.
|
||||
func (c *compilerContext) isThumb() bool {
|
||||
var isThumb, isNotThumb bool
|
||||
for _, feature := range strings.Split(c.Features, ",") {
|
||||
if feature == "+thumb-mode" {
|
||||
isThumb = true
|
||||
}
|
||||
if feature == "-thumb-mode" {
|
||||
isNotThumb = true
|
||||
}
|
||||
}
|
||||
if isThumb == isNotThumb {
|
||||
panic("unexpected feature flags")
|
||||
}
|
||||
return isThumb
|
||||
}
|
||||
|
||||
// readStackPointer emits a LLVM intrinsic call that returns the current stack
|
||||
// pointer as an *i8.
|
||||
func (b *builder) readStackPointer() llvm.Value {
|
||||
stacksave := b.mod.NamedFunction("llvm.stacksave")
|
||||
if stacksave.IsNil() {
|
||||
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
|
||||
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
||||
}
|
||||
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||
}
|
||||
|
||||
// Reverse a slice of bytes. From the wiki:
|
||||
// https://github.com/golang/go/wiki/SliceTricks#reversing
|
||||
func reverseBytes(buf []byte) {
|
||||
for i := len(buf)/2 - 1; i >= 0; i-- {
|
||||
opp := len(buf) - 1 - i
|
||||
buf[i], buf[opp] = buf[opp], buf[i]
|
||||
}
|
||||
}
|
||||
|
||||
+73
-15
@@ -7,7 +7,22 @@
|
||||
// places would be a big risk if only one of them is updated.
|
||||
package llvmutil
|
||||
|
||||
import "tinygo.org/x/go-llvm"
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Major returns the LLVM major version.
|
||||
func Major() int {
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if err != nil {
|
||||
// sanity check, should be unreachable
|
||||
panic("could not parse LLVM version: " + err.Error())
|
||||
}
|
||||
return llvmMajor
|
||||
}
|
||||
|
||||
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
|
||||
// the IR builder is located elsewhere. It assumes that the insert point is
|
||||
@@ -34,11 +49,13 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||
ctx := t.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
||||
fnType, fn := getLifetimeStartFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,19 +63,22 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
||||
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
|
||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||
builder.SetInsertPointBefore(inst)
|
||||
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
||||
fnType, fn := getLifetimeStartFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||
builder.SetInsertPointBefore(next)
|
||||
} else {
|
||||
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||
}
|
||||
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, bitcast}, "")
|
||||
fnType, fn = getLifetimeEndFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
||||
return alloca
|
||||
}
|
||||
|
||||
@@ -66,33 +86,42 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
|
||||
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, ptr}, "")
|
||||
fnType, fn := getLifetimeEndFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
|
||||
}
|
||||
|
||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||
// first if it doesn't exist yet.
|
||||
func getLifetimeStartFunc(mod llvm.Module) llvm.Value {
|
||||
fn := mod.NamedFunction("llvm.lifetime.start.p0i8")
|
||||
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||
fnName := "llvm.lifetime.start.p0"
|
||||
if Major() < 15 { // compatibility with LLVM 14
|
||||
fnName = "llvm.lifetime.start.p0i8"
|
||||
}
|
||||
fn := mod.NamedFunction(fnName)
|
||||
ctx := mod.Context()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||
if fn.IsNil() {
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||
fn = llvm.AddFunction(mod, "llvm.lifetime.start.p0i8", fnType)
|
||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||
}
|
||||
return fn
|
||||
return fnType, fn
|
||||
}
|
||||
|
||||
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||
// first if it doesn't exist yet.
|
||||
func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
||||
fn := mod.NamedFunction("llvm.lifetime.end.p0i8")
|
||||
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||
fnName := "llvm.lifetime.end.p0"
|
||||
if Major() < 15 {
|
||||
fnName = "llvm.lifetime.end.p0i8"
|
||||
}
|
||||
fn := mod.NamedFunction(fnName)
|
||||
ctx := mod.Context()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||
if fn.IsNil() {
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||
fn = llvm.AddFunction(mod, "llvm.lifetime.end.p0i8", fnType)
|
||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||
}
|
||||
return fn
|
||||
return fnType, fn
|
||||
}
|
||||
|
||||
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||
@@ -166,3 +195,32 @@ func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llv
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
// Append the given values to a global array like llvm.used. The global might
|
||||
// not exist yet. The values can be any pointer type, they will be cast to i8*.
|
||||
func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
|
||||
// Read the existing values in the llvm.used array (if it exists).
|
||||
var usedValues []llvm.Value
|
||||
if used := mod.NamedGlobal(globalName); !used.IsNil() {
|
||||
builder := mod.Context().NewBuilder()
|
||||
defer builder.Dispose()
|
||||
usedInitializer := used.Initializer()
|
||||
num := usedInitializer.Type().ArrayLength()
|
||||
for i := 0; i < num; i++ {
|
||||
usedValues = append(usedValues, builder.CreateExtractValue(usedInitializer, i, ""))
|
||||
}
|
||||
used.EraseFromParentAsGlobal()
|
||||
}
|
||||
|
||||
// Add the new values.
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
for _, value := range values {
|
||||
usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType))
|
||||
}
|
||||
|
||||
// Create a new array (with the old and new values).
|
||||
usedInitializer := llvm.ConstArray(i8ptrType, usedValues)
|
||||
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
|
||||
used.SetInitializer(usedInitializer)
|
||||
used.SetLinkage(llvm.AppendingLinkage)
|
||||
}
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
package llvmutil
|
||||
|
||||
// This file contains utility functions to pack and unpack sets of values. It
|
||||
// can take in a list of values and tries to store it efficiently in the pointer
|
||||
// itself if possible and legal.
|
||||
|
||||
import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// EmitPointerPack packs the list of values into a single pointer value using
|
||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||
// pointer value directly. It returns the pointer with the packed data.
|
||||
// If the values are all constants, they are be stored in a constant global and deduplicated.
|
||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
valueTypes[i] = value.Type()
|
||||
}
|
||||
packedType := ctx.StructType(valueTypes, false)
|
||||
|
||||
// Allocate memory for the packed data.
|
||||
size := targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
return llvm.ConstPointerNull(i8ptrType)
|
||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||
return builder.CreateBitCast(values[0], i8ptrType, "pack.ptr")
|
||||
} else if size <= targetData.TypeAllocSize(i8ptrType) {
|
||||
// Packed data fits in a pointer, so store it directly inside the
|
||||
// pointer.
|
||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||
// Try to keep this cast in SSA form.
|
||||
return builder.CreateIntToPtr(values[0], i8ptrType, "pack.int")
|
||||
}
|
||||
|
||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||
// it in a *i8 alloca first and load the *i8 value from there. This is
|
||||
// effectively a bitcast.
|
||||
packedAlloc, _, _ := CreateTemporaryAlloca(builder, mod, i8ptrType, "")
|
||||
|
||||
if size < targetData.TypeAllocSize(i8ptrType) {
|
||||
// The alloca is bigger than the value that will be stored in it.
|
||||
// To avoid having some bits undefined, zero the alloca first.
|
||||
// Hopefully this will get optimized away.
|
||||
builder.CreateStore(llvm.ConstNull(i8ptrType), packedAlloc)
|
||||
}
|
||||
|
||||
// Store all values in the alloca.
|
||||
packedAllocCast := builder.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := builder.CreateInBoundsGEP(packedAllocCast, indices, "")
|
||||
builder.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Load value (the *i8) from the alloca.
|
||||
result := builder.CreateLoad(packedAlloc, "")
|
||||
|
||||
// End the lifetime of the alloca, to help the optimizer.
|
||||
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
|
||||
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Check if the values are all constants.
|
||||
constant := true
|
||||
for _, v := range values {
|
||||
if !v.IsConstant() {
|
||||
constant = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if constant {
|
||||
// The data is known at compile time, so store it in a constant global.
|
||||
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
|
||||
global := llvm.AddGlobal(mod, packedType, prefix+"$pack")
|
||||
global.SetInitializer(ctx.ConstStruct(values, false))
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
return llvm.ConstBitCast(global, i8ptrType)
|
||||
}
|
||||
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(uintptrType, size, false)
|
||||
alloc := mod.NamedFunction("runtime.alloc")
|
||||
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
|
||||
sizeValue,
|
||||
llvm.ConstNull(i8ptrType),
|
||||
llvm.Undef(i8ptrType), // unused context parameter
|
||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||
}, "")
|
||||
if needsStackObjects {
|
||||
trackPointer := mod.NamedFunction("runtime.trackPointer")
|
||||
builder.CreateCall(trackPointer, []llvm.Value{
|
||||
packedHeapAlloc,
|
||||
llvm.Undef(i8ptrType), // unused context parameter
|
||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||
}, "")
|
||||
}
|
||||
packedAlloc := builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||
|
||||
// Store all values in the heap pointer.
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||
builder.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Return the original heap allocation pointer, which already is an *i8.
|
||||
return packedHeapAlloc
|
||||
}
|
||||
}
|
||||
|
||||
// EmitPointerUnpack extracts a list of values packed using EmitPointerPack.
|
||||
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
|
||||
packedType := ctx.StructType(valueTypes, false)
|
||||
|
||||
// Get a correctly-typed pointer to the packed data.
|
||||
var packedAlloc, packedRawAlloc llvm.Value
|
||||
size := targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
// No data to unpack.
|
||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||
// A single pointer is always stored directly.
|
||||
return []llvm.Value{builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
||||
} else if size <= targetData.TypeAllocSize(i8ptrType) {
|
||||
// Packed data stored directly in pointer.
|
||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||
// Keep this cast in SSA form.
|
||||
return []llvm.Value{builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||
}
|
||||
// Fallback: load it using an alloca.
|
||||
packedRawAlloc, _, _ = CreateTemporaryAlloca(builder, mod, llvm.PointerType(i8ptrType, 0), "unpack.raw.alloc")
|
||||
packedRawValue := builder.CreateBitCast(ptr, llvm.PointerType(i8ptrType, 0), "unpack.raw.value")
|
||||
builder.CreateStore(packedRawValue, packedRawAlloc)
|
||||
packedAlloc = builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
||||
} else {
|
||||
// Packed data stored on the heap. Bitcast the passed-in pointer to the
|
||||
// correct pointer type.
|
||||
packedAlloc = builder.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
|
||||
}
|
||||
// Load each value from the packed data.
|
||||
values := make([]llvm.Value, len(valueTypes))
|
||||
for i, valueType := range valueTypes {
|
||||
if targetData.TypeAllocSize(valueType) == 0 {
|
||||
// This value has length zero, so there's nothing to load.
|
||||
values[i] = llvm.ConstNull(valueType)
|
||||
continue
|
||||
}
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||
values[i] = builder.CreateLoad(gep, "")
|
||||
}
|
||||
if !packedRawAlloc.IsNil() {
|
||||
allocPtr := builder.CreateBitCast(packedRawAlloc, i8ptrType, "")
|
||||
allocSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(uintptrType), false)
|
||||
EmitLifetimeEnd(builder, mod, allocPtr, allocSize)
|
||||
}
|
||||
return values
|
||||
}
|
||||
+76
-4
@@ -10,6 +10,13 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// constants for hashmap algorithms; must match src/runtime/hashmap.go
|
||||
const (
|
||||
hashmapAlgorithmBinary = iota
|
||||
hashmapAlgorithmString
|
||||
hashmapAlgorithmInterface
|
||||
)
|
||||
|
||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||
// initializing an appropriately sized object.
|
||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
@@ -17,22 +24,27 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
keyType := mapType.Key().Underlying()
|
||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||
var llvmKeyType llvm.Type
|
||||
var alg uint64 // must match values in src/runtime/hashmap.go
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// String keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = hashmapAlgorithmString
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// Trivially comparable keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = hashmapAlgorithmBinary
|
||||
} else {
|
||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||
// implementation.
|
||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||
alg = hashmapAlgorithmInterface
|
||||
}
|
||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
||||
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = b.getValue(expr.Reserve)
|
||||
var err error
|
||||
@@ -41,7 +53,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
||||
return hashmap, nil
|
||||
}
|
||||
|
||||
@@ -94,7 +106,7 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
||||
|
||||
// Load the resulting value from the hashmap. The value is set to the zero
|
||||
// value if the key doesn't exist in the hashmap.
|
||||
mapValue := b.CreateLoad(mapValueAlloca, "")
|
||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
||||
|
||||
if commaOk {
|
||||
@@ -167,6 +179,66 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
|
||||
}
|
||||
}
|
||||
|
||||
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
|
||||
// map. It returns a tuple of {bool, key, value} with the result of the
|
||||
// iteration.
|
||||
func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llvm.Value) llvm.Value {
|
||||
// Determine the type of the values to return from the *ssa.Next
|
||||
// instruction. It is returned as {bool, keyType, valueType}.
|
||||
keyType := rangeVal.Type().Underlying().(*types.Map).Key()
|
||||
valueType := rangeVal.Type().Underlying().(*types.Map).Elem()
|
||||
llvmKeyType := b.getLLVMType(keyType)
|
||||
llvmValueType := b.getLLVMType(valueType)
|
||||
|
||||
// There is a special case in which keys are stored as an interface value
|
||||
// instead of the value they normally are. This happens for non-trivially
|
||||
// comparable types such as float32 or some structs.
|
||||
isKeyStoredAsInterface := false
|
||||
if t, ok := keyType.Underlying().(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
} else {
|
||||
// The key is stored as an interface value, and may or may not be an
|
||||
// interface type (for example, float32 keys are stored as an interface
|
||||
// value).
|
||||
if _, ok := keyType.Underlying().(*types.Interface); !ok {
|
||||
isKeyStoredAsInterface = true
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the type of the key as stored in the map.
|
||||
llvmStoredKeyType := llvmKeyType
|
||||
if isKeyStoredAsInterface {
|
||||
llvmStoredKeyType = b.getLLVMRuntimeType("_interface")
|
||||
}
|
||||
|
||||
// Extract the key and value from the map.
|
||||
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
||||
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
||||
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||
|
||||
if isKeyStoredAsInterface {
|
||||
// The key is stored as an interface but it isn't of interface type.
|
||||
// Extract the underlying value.
|
||||
mapKey = b.extractValueFromInterface(mapKey, llvmKeyType)
|
||||
}
|
||||
|
||||
// End the lifetimes of the allocas, because we're done with them.
|
||||
b.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||
b.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
||||
|
||||
// Construct the *ssa.Next return value: {ok, mapKey, mapValue}
|
||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
|
||||
tuple = b.CreateInsertValue(tuple, ok, 0, "")
|
||||
tuple = b.CreateInsertValue(tuple, mapKey, 1, "")
|
||||
tuple = b.CreateInsertValue(tuple, mapValue, 2, "")
|
||||
|
||||
return tuple
|
||||
}
|
||||
|
||||
// Returns true if this key type does not contain strings, interfaces etc., so
|
||||
// can be compared with runtime.memequal.
|
||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
|
||||
+5
-1
@@ -45,7 +45,11 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
||||
if t.Info()&types.IsString != 0 {
|
||||
return s.PtrSize
|
||||
}
|
||||
case *types.Signature:
|
||||
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
|
||||
return s.PtrSize
|
||||
}
|
||||
|
||||
a := s.Sizeof(T) // may be 0
|
||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||
if a < 1 {
|
||||
@@ -148,7 +152,7 @@ func (s *stdSizes) Sizeof(T types.Type) int64 {
|
||||
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
|
||||
case *types.Interface:
|
||||
return s.PtrSize * 2
|
||||
case *types.Pointer:
|
||||
case *types.Pointer, *types.Chan, *types.Map:
|
||||
return s.PtrSize
|
||||
case *types.Signature:
|
||||
// Func values in TinyGo are two words in size.
|
||||
|
||||
+51
-48
@@ -10,6 +10,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -53,11 +54,11 @@ const (
|
||||
|
||||
// getFunction returns the LLVM function for the given *ssa.Function, creating
|
||||
// it if needed. It can later be filled with compilerContext.createFunction().
|
||||
func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) {
|
||||
info := c.getFunctionInfo(fn)
|
||||
llvmFn := c.mod.NamedFunction(info.linkName)
|
||||
if !llvmFn.IsNil() {
|
||||
return llvmFn
|
||||
return llvmFn.GlobalValueType(), llvmFn
|
||||
}
|
||||
|
||||
var retType llvm.Type
|
||||
@@ -83,8 +84,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
// Add an extra parameter as the function context. This context is used in
|
||||
// closures and bound methods, but should be optimized away when not used.
|
||||
if !info.exported {
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0})
|
||||
}
|
||||
|
||||
var paramTypes []llvm.Type
|
||||
@@ -113,17 +113,8 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
|
||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||
for i, info := range paramInfos {
|
||||
if info.flags¶mIsDeferenceableOrNull == 0 {
|
||||
continue
|
||||
}
|
||||
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||
el := info.llvmType.ElementType()
|
||||
size := c.targetData.TypeAllocSize(el)
|
||||
if size == 0 {
|
||||
// dereferenceable_or_null(0) appears to be illegal in LLVM.
|
||||
continue
|
||||
}
|
||||
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, size)
|
||||
if info.elemSize != 0 {
|
||||
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize)
|
||||
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
||||
}
|
||||
}
|
||||
@@ -159,23 +150,38 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
// that the only thing we'll do is read the pointer.
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "__mulsi3", "__divmodsi4", "__udivmodsi4":
|
||||
if strings.Split(c.Triple, "-")[0] == "avr" {
|
||||
// These functions are compiler-rt/libgcc functions that are
|
||||
// currently implemented in Go. Assembly versions should appear in
|
||||
// LLVM 16 hopefully. Until then, they need to be made available to
|
||||
// the linker and the best way to do that is llvm.compiler.used.
|
||||
// I considered adding a pragma for this, but the LLVM language
|
||||
// reference explicitly says that this feature should not be exposed
|
||||
// to source languages:
|
||||
// > This is a rare construct that should only be used in rare
|
||||
// > circumstances, and should not be exposed to source languages.
|
||||
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
||||
}
|
||||
}
|
||||
|
||||
// External/exported functions may not retain pointer values.
|
||||
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
||||
if info.exported {
|
||||
// Set the wasm-import-module attribute if the function's module is set.
|
||||
if info.module != "" {
|
||||
|
||||
if c.archFamily() == "wasm32" {
|
||||
// We need to add the wasm-import-module and the wasm-import-name
|
||||
wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module)
|
||||
llvmFn.AddFunctionAttr(wasmImportModuleAttr)
|
||||
|
||||
// Add the Wasm Import Name, if we are a named wasm import
|
||||
if info.importName != "" {
|
||||
wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName)
|
||||
llvmFn.AddFunctionAttr(wasmImportNameAttr)
|
||||
// attributes.
|
||||
module := info.module
|
||||
if module == "" {
|
||||
module = "env"
|
||||
}
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
|
||||
|
||||
name := info.importName
|
||||
if name == "" {
|
||||
name = info.linkName
|
||||
}
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
|
||||
}
|
||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||
@@ -192,7 +198,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
// should be created right away.
|
||||
// The exception is the package initializer, which does appear in the
|
||||
// *ssa.Package members and so shouldn't be created here.
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createFunction()
|
||||
@@ -201,21 +207,16 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
llvmFn.SetUnnamedAddr(true)
|
||||
}
|
||||
|
||||
return llvmFn
|
||||
return fnType, llvmFn
|
||||
}
|
||||
|
||||
// getFunctionInfo returns information about a function that is not directly
|
||||
// present in *ssa.Function, such as the link name and whether it should be
|
||||
// exported.
|
||||
func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
||||
info := functionInfo{}
|
||||
if strings.HasPrefix(f.Name(), "C.") {
|
||||
// Created by CGo: such a name cannot be created by regular C code.
|
||||
info.linkName = f.Name()[2:]
|
||||
info.exported = true
|
||||
} else {
|
||||
info := functionInfo{
|
||||
// Pick the default linkName.
|
||||
info.linkName = f.RelString(nil)
|
||||
linkName: f.RelString(nil),
|
||||
}
|
||||
// Check for //go: pragmas, which may change the link name (among others).
|
||||
info.parsePragmas(f)
|
||||
@@ -366,7 +367,15 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
|
||||
if strings.Split(c.Triple, "-")[0] == "x86_64" {
|
||||
// Required by the ABI.
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
|
||||
if llvmutil.Major() < 15 {
|
||||
// Needed for LLVM 14 support.
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
|
||||
} else {
|
||||
// The uwtable has two possible values: sync (1) or async (2). We
|
||||
// use sync because we currently don't use async unwind tables.
|
||||
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,20 +469,14 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
|
||||
// getGlobalInfo returns some information about a specific global.
|
||||
func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
|
||||
info := globalInfo{}
|
||||
if strings.HasPrefix(g.Name(), "C.") {
|
||||
// Created by CGo: such a name cannot be created by regular C code.
|
||||
info.linkName = g.Name()[2:]
|
||||
info.extern = true
|
||||
} else {
|
||||
info := globalInfo{
|
||||
// Pick the default linkName.
|
||||
info.linkName = g.RelString(nil)
|
||||
// Check for //go: pragmas, which may change the link name (among
|
||||
// others).
|
||||
doc := c.astComments[info.linkName]
|
||||
if doc != nil {
|
||||
info.parsePragmas(doc)
|
||||
}
|
||||
linkName: g.RelString(nil),
|
||||
}
|
||||
// Check for //go: pragmas, which may change the link name (among others).
|
||||
doc := c.astComments[info.linkName]
|
||||
if doc != nil {
|
||||
info.parsePragmas(doc)
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
+13
-46
@@ -16,20 +16,7 @@ import (
|
||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := b.getValue(call.Args[0])
|
||||
switch {
|
||||
case b.GOARCH == "amd64":
|
||||
if b.GOOS == "darwin" {
|
||||
// Darwin adds this magic number to system call numbers:
|
||||
//
|
||||
// > Syscall classes for 64-bit system call entry.
|
||||
// > For 64-bit users, the 32-bit syscall number is partitioned
|
||||
// > with the high-order bits representing the class and low-order
|
||||
// > bits being the syscall number within that class.
|
||||
// > The high-order 32-bits of the 64-bit syscall number are unused.
|
||||
// > All system classes enter the kernel via the syscall instruction.
|
||||
//
|
||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||
num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "")
|
||||
}
|
||||
case b.GOARCH == "amd64" && b.GOOS == "linux":
|
||||
// Sources:
|
||||
// https://stackoverflow.com/a/2538212
|
||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||
@@ -56,8 +43,8 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
constraints += ",~{rcx},~{r11}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||
// Sources:
|
||||
// syscall(2) man page
|
||||
@@ -82,8 +69,8 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||
// Implement the EABI system call convention for Linux.
|
||||
// Source: syscall(2) man page.
|
||||
@@ -114,8 +101,8 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
@@ -146,8 +133,8 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
constraints += ",~{x16},~{x17}" // scratch registers
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
@@ -157,7 +144,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
// functions, depending on the target OS/arch.
|
||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
switch b.GOOS {
|
||||
case "linux", "freebsd":
|
||||
case "linux":
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
@@ -179,26 +166,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
case "darwin":
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
}
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult != 0 {
|
||||
// err = syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
|
||||
errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
||||
retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
||||
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
case "windows":
|
||||
// On Windows, syscall.Syscall* is basically just a function pointer
|
||||
// call. This is complicated in gc because of stack switching and the
|
||||
@@ -238,9 +205,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
// Note that SetLastError/GetLastError could be replaced with direct
|
||||
// access to the thread control block, which is probably smaller and
|
||||
// faster. The Go runtime does this in assembly.
|
||||
b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||
syscallResult := b.CreateCall(fnPtr, params, "")
|
||||
errResult := b.CreateCall(getLastError, nil, "err")
|
||||
b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
|
||||
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||
if b.uintptrType != b.ctx.Int32Type() {
|
||||
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||
}
|
||||
|
||||
Vendored
+14
-4
@@ -75,14 +75,24 @@ func complexMul(x, y complex64) complex64 {
|
||||
// A type 'kv' also exists in function foo. Test that these two types don't
|
||||
// conflict with each other.
|
||||
type kv struct {
|
||||
v float32
|
||||
v float32
|
||||
x, y, z int
|
||||
}
|
||||
|
||||
func foo(a *kv) {
|
||||
var kvGlobal kv
|
||||
|
||||
func foo() {
|
||||
// Define a new 'kv' type.
|
||||
type kv struct {
|
||||
v byte
|
||||
v byte
|
||||
x, y, z int
|
||||
}
|
||||
// Use this type.
|
||||
func(b *kv) {}(nil)
|
||||
func(b kv) {}(kv{})
|
||||
}
|
||||
|
||||
type T1 []T1
|
||||
type T2 [2]*T2
|
||||
|
||||
var a T1
|
||||
var b T2
|
||||
|
||||
Vendored
+60
-43
@@ -1,43 +1,45 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%main.kv = type { float }
|
||||
%main.kv.0 = type { i8 }
|
||||
%main.kv = type { float, i32, i32, i32 }
|
||||
%main.kv.0 = type { i8, i32, i32, i32 }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
@main.kvGlobal = hidden global %main.kv zeroinitializer, align 4
|
||||
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
||||
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = add i32 %x, %y
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = icmp eq i32 %y, -1
|
||||
%2 = icmp eq i32 %x, -2147483648
|
||||
@@ -45,35 +47,35 @@ divbyzero.next: ; preds = %entry
|
||||
%4 = select i1 %3, i32 1, i32 %y
|
||||
%5 = sdiv i32 %x, %4
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.divideByZeroPanic(i8*, i8*)
|
||||
declare void @runtime.divideByZeroPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = udiv i32 %x, %y
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = icmp eq i32 %y, -1
|
||||
%2 = icmp eq i32 %x, -2147483648
|
||||
@@ -81,79 +83,83 @@ divbyzero.next: ; preds = %entry
|
||||
%4 = select i1 %3, i32 1, i32 %y
|
||||
%5 = srem i32 %x, %4
|
||||
ret i32 %5
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq i32 %y, 0
|
||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||
unreachable
|
||||
|
||||
divbyzero.next: ; preds = %entry
|
||||
%1 = urem i32 %x, %y
|
||||
ret i32 %1
|
||||
|
||||
divbyzero.throw: ; preds = %entry
|
||||
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp oeq float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp une float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp olt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp ole float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp ogt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fcmp oge float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float %x.r
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float %x.i
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
@@ -163,7 +169,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
@@ -173,7 +179,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
@@ -187,16 +193,27 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef)
|
||||
%complit = alloca %main.kv.0, align 8
|
||||
%stackalloc = alloca i8, align 1
|
||||
store %main.kv.0 zeroinitializer, ptr %complit, align 8
|
||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%b1 = alloca %main.kv.0, align 8
|
||||
%stackalloc = alloca i8, align 1
|
||||
store %main.kv.0 zeroinitializer, ptr %b1, align 8
|
||||
call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store %main.kv.0 %b, ptr %b1, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Vendored
+60
-67
@@ -1,106 +1,97 @@
|
||||
; ModuleID = 'channel.go'
|
||||
source_filename = "channel.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||
%"internal/task.stackState" = type { i32, i32 }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
%runtime.channelBlockedList = type { ptr, ptr, ptr, { ptr, i32, i32 } }
|
||||
%runtime.chanSelectState = type { ptr, ptr }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
store i32 3, i32* %chan.value, align 4
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
store i32 3, ptr %chan.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: argmemonly nounwind willreturn
|
||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
|
||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
||||
|
||||
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #0
|
||||
|
||||
; Function Attrs: argmemonly nounwind willreturn
|
||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
|
||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.value = alloca i32, align 4
|
||||
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%complit = alloca {}, align 8
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
|
||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %ch2, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||
%select.send.value = alloca i32, align 4
|
||||
store i32 1, i32* %select.send.value, align 4
|
||||
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
|
||||
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
|
||||
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
|
||||
%0 = bitcast i8** %.repack1 to i32**
|
||||
store i32* %select.send.value, i32** %0, align 4
|
||||
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
|
||||
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
|
||||
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
||||
store i8* null, i8** %.repack4, align 4
|
||||
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
|
||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef, i8* null) #0
|
||||
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||
store i32 1, ptr %select.send.value, align 4
|
||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||
store ptr %ch1, ptr %select.states.alloca, align 8
|
||||
%select.states.alloca.repack1 = getelementptr inbounds %runtime.chanSelectState, ptr %select.states.alloca, i32 0, i32 1
|
||||
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
||||
%0 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1
|
||||
store ptr %ch2, ptr %0, align 8
|
||||
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1
|
||||
store ptr null, ptr %.repack3, align 4
|
||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #3
|
||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||
%2 = icmp eq i32 %1, 0
|
||||
br i1 %2, label %select.done, label %select.next
|
||||
@@ -116,7 +107,9 @@ select.body: ; preds = %select.next
|
||||
br label %select.done
|
||||
}
|
||||
|
||||
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*)
|
||||
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #0
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { argmemonly nounwind willreturn }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { argmemonly nocallback nofree nosync nounwind willreturn }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
; ModuleID = 'defer.go'
|
||||
source_filename = "defer.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
%runtime._defer = type { i32, ptr }
|
||||
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.external(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%defer.alloca = alloca { i32, ptr }, align 4
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%0 = call ptr @llvm.stacksave()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
|
||||
store ptr null, ptr %defer.alloca.repack15, align 4
|
||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
br i1 %setjmp.result, label %1, label %lpad
|
||||
|
||||
1: ; preds = %entry
|
||||
call void @main.external(ptr undef) #3
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.loophead: ; preds = %3, %1
|
||||
%2 = load ptr, ptr %deferPtr, align 4
|
||||
%stackIsNil = icmp eq ptr %2, null
|
||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||
|
||||
rundefers.loop: ; preds = %rundefers.loophead
|
||||
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
|
||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||
store ptr %stack.next, ptr %deferPtr, align 4
|
||||
%callback = load i32, ptr %2, align 4
|
||||
switch i32 %callback, label %rundefers.default [
|
||||
i32 0, label %rundefers.callback0
|
||||
]
|
||||
|
||||
rundefers.callback0: ; preds = %rundefers.loop
|
||||
%setjmp1 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result2 = icmp eq i32 %setjmp1, 0
|
||||
br i1 %setjmp.result2, label %3, label %lpad
|
||||
|
||||
3: ; preds = %rundefers.callback0
|
||||
call void @"main.deferSimple$1"(ptr undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.default: ; preds = %rundefers.loop
|
||||
unreachable
|
||||
|
||||
rundefers.end: ; preds = %rundefers.loophead
|
||||
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||
ret void
|
||||
|
||||
recover: ; preds = %rundefers.end3
|
||||
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||
ret void
|
||||
|
||||
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
|
||||
br label %rundefers.loophead6
|
||||
|
||||
rundefers.loophead6: ; preds = %5, %lpad
|
||||
%4 = load ptr, ptr %deferPtr, align 4
|
||||
%stackIsNil7 = icmp eq ptr %4, null
|
||||
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
||||
|
||||
rundefers.loop5: ; preds = %rundefers.loophead6
|
||||
%stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %4, i32 0, i32 1
|
||||
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
|
||||
store ptr %stack.next9, ptr %deferPtr, align 4
|
||||
%callback11 = load i32, ptr %4, align 4
|
||||
switch i32 %callback11, label %rundefers.default4 [
|
||||
i32 0, label %rundefers.callback012
|
||||
]
|
||||
|
||||
rundefers.callback012: ; preds = %rundefers.loop5
|
||||
%setjmp13 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result14 = icmp eq i32 %setjmp13, 0
|
||||
br i1 %setjmp.result14, label %5, label %lpad
|
||||
|
||||
5: ; preds = %rundefers.callback012
|
||||
call void @"main.deferSimple$1"(ptr undef)
|
||||
br label %rundefers.loophead6
|
||||
|
||||
rundefers.default4: ; preds = %rundefers.loop5
|
||||
unreachable
|
||||
|
||||
rundefers.end3: ; preds = %rundefers.loophead6
|
||||
br label %recover
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||
declare ptr @llvm.stacksave() #2
|
||||
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printint32(i32 3, ptr undef) #3
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #0
|
||||
|
||||
declare void @runtime.printint32(i32, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%defer.alloca2 = alloca { i32, ptr }, align 4
|
||||
%defer.alloca = alloca { i32, ptr }, align 4
|
||||
%deferPtr = alloca ptr, align 4
|
||||
store ptr null, ptr %deferPtr, align 4
|
||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||
%0 = call ptr @llvm.stacksave()
|
||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3
|
||||
store i32 0, ptr %defer.alloca, align 4
|
||||
%defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
|
||||
store ptr null, ptr %defer.alloca.repack22, align 4
|
||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||
store i32 1, ptr %defer.alloca2, align 4
|
||||
%defer.alloca2.repack23 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1
|
||||
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
|
||||
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||
br i1 %setjmp.result, label %1, label %lpad
|
||||
|
||||
1: ; preds = %entry
|
||||
call void @main.external(ptr undef) #3
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.loophead: ; preds = %4, %3, %1
|
||||
%2 = load ptr, ptr %deferPtr, align 4
|
||||
%stackIsNil = icmp eq ptr %2, null
|
||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||
|
||||
rundefers.loop: ; preds = %rundefers.loophead
|
||||
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
|
||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||
store ptr %stack.next, ptr %deferPtr, align 4
|
||||
%callback = load i32, ptr %2, align 4
|
||||
switch i32 %callback, label %rundefers.default [
|
||||
i32 0, label %rundefers.callback0
|
||||
i32 1, label %rundefers.callback1
|
||||
]
|
||||
|
||||
rundefers.callback0: ; preds = %rundefers.loop
|
||||
%setjmp3 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result4 = icmp eq i32 %setjmp3, 0
|
||||
br i1 %setjmp.result4, label %3, label %lpad
|
||||
|
||||
3: ; preds = %rundefers.callback0
|
||||
call void @"main.deferMultiple$1"(ptr undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.callback1: ; preds = %rundefers.loop
|
||||
%setjmp5 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result6 = icmp eq i32 %setjmp5, 0
|
||||
br i1 %setjmp.result6, label %4, label %lpad
|
||||
|
||||
4: ; preds = %rundefers.callback1
|
||||
call void @"main.deferMultiple$2"(ptr undef)
|
||||
br label %rundefers.loophead
|
||||
|
||||
rundefers.default: ; preds = %rundefers.loop
|
||||
unreachable
|
||||
|
||||
rundefers.end: ; preds = %rundefers.loophead
|
||||
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||
ret void
|
||||
|
||||
recover: ; preds = %rundefers.end7
|
||||
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||
ret void
|
||||
|
||||
lpad: ; preds = %rundefers.callback119, %rundefers.callback016, %rundefers.callback1, %rundefers.callback0, %entry
|
||||
br label %rundefers.loophead10
|
||||
|
||||
rundefers.loophead10: ; preds = %7, %6, %lpad
|
||||
%5 = load ptr, ptr %deferPtr, align 4
|
||||
%stackIsNil11 = icmp eq ptr %5, null
|
||||
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
|
||||
|
||||
rundefers.loop9: ; preds = %rundefers.loophead10
|
||||
%stack.next.gep12 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1
|
||||
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
|
||||
store ptr %stack.next13, ptr %deferPtr, align 4
|
||||
%callback15 = load i32, ptr %5, align 4
|
||||
switch i32 %callback15, label %rundefers.default8 [
|
||||
i32 0, label %rundefers.callback016
|
||||
i32 1, label %rundefers.callback119
|
||||
]
|
||||
|
||||
rundefers.callback016: ; preds = %rundefers.loop9
|
||||
%setjmp17 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result18 = icmp eq i32 %setjmp17, 0
|
||||
br i1 %setjmp.result18, label %6, label %lpad
|
||||
|
||||
6: ; preds = %rundefers.callback016
|
||||
call void @"main.deferMultiple$1"(ptr undef)
|
||||
br label %rundefers.loophead10
|
||||
|
||||
rundefers.callback119: ; preds = %rundefers.loop9
|
||||
%setjmp20 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||
%setjmp.result21 = icmp eq i32 %setjmp20, 0
|
||||
br i1 %setjmp.result21, label %7, label %lpad
|
||||
|
||||
7: ; preds = %rundefers.callback119
|
||||
call void @"main.deferMultiple$2"(ptr undef)
|
||||
br label %rundefers.loophead10
|
||||
|
||||
rundefers.default8: ; preds = %rundefers.loop9
|
||||
unreachable
|
||||
|
||||
rundefers.end7: ; preds = %rundefers.loophead10
|
||||
br label %recover
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printint32(i32 3, ptr undef) #3
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.printint32(i32 5, ptr undef) #3
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||
attributes #2 = { nocallback nofree nosync nounwind willreturn }
|
||||
attributes #3 = { nounwind }
|
||||
attributes #4 = { nounwind returns_twice }
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
func external()
|
||||
|
||||
func deferSimple() {
|
||||
defer func() {
|
||||
print(3)
|
||||
}()
|
||||
external()
|
||||
}
|
||||
|
||||
func deferMultiple() {
|
||||
defer func() {
|
||||
print(3)
|
||||
}()
|
||||
defer func() {
|
||||
print(5)
|
||||
}()
|
||||
external()
|
||||
}
|
||||
Vendored
+15
-12
@@ -1,18 +1,20 @@
|
||||
; ModuleID = 'float.go'
|
||||
source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -24,25 +26,25 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret float 0x41F0000000000000
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = uitofp i32 %v to float
|
||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||
@@ -52,7 +54,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -65,7 +67,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||
@@ -77,7 +79,7 @@ entry:
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||
@@ -90,4 +92,5 @@ entry:
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
|
||||
Vendored
-53
@@ -1,53 +0,0 @@
|
||||
; ModuleID = 'func.go'
|
||||
source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||
|
||||
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||
@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
%0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) #0
|
||||
%1 = icmp eq i32 %0, 0
|
||||
br i1 %1, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(i8* undef, i8* null) #0
|
||||
unreachable
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
%2 = inttoptr i32 %0 to void (i32, i8*, i8*)*
|
||||
call void %2(i32 3, i8* %callback.context, i8* undef) #0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||
|
||||
declare void @runtime.nilPanic(i8*, i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
call void @main.foo(i8* undef, i32 ptrtoint (%runtime.funcValueWithSignature* @"main.someFunc$withSignature" to i32), i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
; ModuleID = 'func.go'
|
||||
source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = icmp eq ptr %callback.funcptr, null
|
||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
call void %callback.funcptr(i32 3, ptr %callback.context) #2
|
||||
ret void
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(ptr undef) #2
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @runtime.nilPanic(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
Vendored
+8
-5
@@ -20,6 +20,10 @@ var (
|
||||
y [60]uintptr
|
||||
z *byte
|
||||
}
|
||||
struct4 *struct {
|
||||
x *byte
|
||||
y [61]uintptr
|
||||
}
|
||||
|
||||
slice1 []byte
|
||||
slice2 []*int
|
||||
@@ -50,14 +54,13 @@ func newStruct() {
|
||||
y [60]uintptr
|
||||
z *byte
|
||||
})
|
||||
struct4 = new(struct {
|
||||
x *byte
|
||||
y [61]uintptr
|
||||
})
|
||||
}
|
||||
|
||||
func newFuncValue() *func() {
|
||||
// On some platforms that use runtime.funcValue ("switch" style) function
|
||||
// values, a func value is allocated as having two pointer words while the
|
||||
// struct looks like {unsafe.Pointer; uintptr}. This is so that the interp
|
||||
// package won't get confused, see getPointerBitmap in compiler/llvm.go for
|
||||
// details.
|
||||
return new(func())
|
||||
}
|
||||
|
||||
|
||||
Vendored
+99
-72
@@ -1,112 +1,139 @@
|
||||
; ModuleID = 'gc.go'
|
||||
source_filename = "gc.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
|
||||
@main.scalar1 = hidden global i8* null, align 4
|
||||
@main.scalar2 = hidden global i32* null, align 4
|
||||
@main.scalar3 = hidden global i64* null, align 4
|
||||
@main.scalar4 = hidden global float* null, align 4
|
||||
@main.array1 = hidden global [3 x i8]* null, align 4
|
||||
@main.array2 = hidden global [71 x i8]* null, align 4
|
||||
@main.array3 = hidden global [3 x i8*]* null, align 4
|
||||
@main.struct1 = hidden global {}* null, align 4
|
||||
@main.struct2 = hidden global { i32, i32 }* null, align 4
|
||||
@main.struct3 = hidden global { i8*, [60 x i32], i8* }* null, align 4
|
||||
@main.slice1 = hidden global { i8*, i32, i32 } zeroinitializer, align 8
|
||||
@main.slice2 = hidden global { i32**, i32, i32 } zeroinitializer, align 8
|
||||
@main.slice3 = hidden global { { i8*, i32, i32 }*, i32, i32 } zeroinitializer, align 8
|
||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c" \00\00\00\00\00\00\01" }
|
||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 }
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:complex128", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||
@main.scalar1 = hidden global ptr null, align 4
|
||||
@main.scalar2 = hidden global ptr null, align 4
|
||||
@main.scalar3 = hidden global ptr null, align 4
|
||||
@main.scalar4 = hidden global ptr null, align 4
|
||||
@main.array1 = hidden global ptr null, align 4
|
||||
@main.array2 = hidden global ptr null, align 4
|
||||
@main.array3 = hidden global ptr null, align 4
|
||||
@main.struct1 = hidden global ptr null, align 4
|
||||
@main.struct2 = hidden global ptr null, align 4
|
||||
@main.struct3 = hidden global ptr null, align 4
|
||||
@main.struct4 = hidden global ptr null, align 4
|
||||
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
|
||||
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
|
||||
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
|
||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 16, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newScalar(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new, i8** @main.scalar1, align 4
|
||||
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
|
||||
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
|
||||
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new, ptr @main.scalar1, align 4
|
||||
%new1 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new1, ptr @main.scalar2, align 4
|
||||
%new2 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new2, ptr @main.scalar3, align 4
|
||||
%new3 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new3, ptr @main.scalar4, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newArray(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
|
||||
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
|
||||
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new, ptr @main.array1, align 4
|
||||
%new1 = call ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new1, ptr @main.array2, align 4
|
||||
%new2 = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new2, ptr @main.array3, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.newStruct(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
|
||||
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
|
||||
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef, i8* null) #0
|
||||
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new, ptr @main.struct1, align 4
|
||||
%new1 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new1, ptr @main.struct2, align 4
|
||||
%new2 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new2, ptr @main.struct3, align 4
|
||||
%new3 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %new3, ptr @main.struct4, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef, i8* null) #0
|
||||
%0 = bitcast i8* %new to { i8*, void ()* }*
|
||||
ret { i8*, void ()* }* %0
|
||||
%stackalloc = alloca i8, align 1
|
||||
%new = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret ptr %new
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.makeSlice(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
|
||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 1), align 4
|
||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8
|
||||
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
|
||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 1), align 4
|
||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8
|
||||
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef, i8* null) #0
|
||||
store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
|
||||
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 1), align 4
|
||||
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8
|
||||
%stackalloc = alloca i8, align 1
|
||||
%makeslice = call ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %makeslice, ptr @main.slice1, align 8
|
||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4
|
||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 8
|
||||
%makeslice1 = call ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %makeslice1, ptr @main.slice2, align 8
|
||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4
|
||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 8
|
||||
%makeslice3 = call ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store ptr %makeslice3, ptr @main.slice3, align 8
|
||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
|
||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0
|
||||
%.repack = bitcast i8* %0 to double*
|
||||
store double %v.r, double* %.repack, align 8
|
||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%1 = bitcast i8* %.repack1 to double*
|
||||
store double %v.i, double* %1, align 8
|
||||
%2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
|
||||
ret %runtime._interface %2
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||
store double %v.r, ptr %0, align 8
|
||||
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
|
||||
store double %v.i, ptr %.repack1, align 8
|
||||
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #2
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||
ret %runtime._interface %1
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user