mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
881 Commits
wasm-recover
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| b0298c6a69 | |||
| 0fe4c619eb | |||
| 16fc1ea2bb | |||
| 9105cce340 | |||
| 7756941f39 | |||
| 259842dc2e | |||
| 528476f2b3 | |||
| 0c68e1215e | |||
| 9c2eafce6f | |||
| 3797e89600 | |||
| 40ed956d6c | |||
| 2a49216152 | |||
| 1e3baff966 | |||
| 4fcf03414c | |||
| 888d0c0f97 | |||
| fc17c3565e | |||
| 6aa966af0a | |||
| aee0581a32 | |||
| 2dea20b769 | |||
| 0c17b918fb | |||
| 8727b696a5 | |||
| 94736ac0e5 | |||
| 9e9be09e9a | |||
| 9e7d89d4d5 | |||
| 3b7c9e24f5 | |||
| bc35d087c2 | |||
| deaf532d61 | |||
| 5ec2632461 | |||
| 39a105dab9 | |||
| aa914bea5c | |||
| 3071e339cd | |||
| b536dd6f79 | |||
| 3ad913acb8 | |||
| 9e9f1d0b96 | |||
| cd4e479dd5 | |||
| cf5bed8227 | |||
| babdfc9e10 | |||
| 2602d4c25d | |||
| 0a9b3f91bc | |||
| 3b137c3053 | |||
| 5f02d6b659 | |||
| b8adb803c4 | |||
| 36d3b83e9c | |||
| a2a638c198 | |||
| 0ffa3eb748 | |||
| 922f583dd4 | |||
| 91a56b28a2 | |||
| 9c864f0d79 | |||
| 6203b624c5 | |||
| ea003da13f | |||
| 7994d2e912 | |||
| f1c39e8356 | |||
| 801bd484ab | |||
| be7b6b316e | |||
| ec184e90c1 | |||
| 07c9cdcbc4 | |||
| bd1d11d166 | |||
| 919c998355 | |||
| a3375fb2c4 | |||
| 09bda77b78 | |||
| 047ed57005 | |||
| c7923d1c64 | |||
| 73db9776b1 | |||
| 6d49f0177b | |||
| 35a61ac8c5 | |||
| c1a4ed1489 | |||
| 140c82e012 | |||
| dc23ce73d6 | |||
| e039ce131c | |||
| d59c706e60 | |||
| 26d3645317 | |||
| 432ebe13ed | |||
| 16eefc59eb | |||
| fb1bf2e6bf | |||
| 0922e3e956 | |||
| bd61913973 | |||
| 2dbfdbae10 | |||
| 05029c4fb5 | |||
| a7b30639d8 | |||
| 4adc661197 | |||
| 311c071ae6 | |||
| f1fdfe2313 | |||
| 7c3dc05117 | |||
| e569dcfe38 | |||
| 20038817ae | |||
| 9ce73cfafa | |||
| 0033c23848 | |||
| 5c15a68d18 | |||
| 02384c9414 | |||
| e79edb58b2 | |||
| bf80e9b446 | |||
| 30355cec9f | |||
| 9111927f85 | |||
| 10224a4bf8 | |||
| 33ddee76db | |||
| 7fa50a55cf | |||
| 1c0c26b8b7 | |||
| 3abe58448b | |||
| 364ac2dcb2 | |||
| e111b489ec | |||
| f31b39775f | |||
| 6c30583062 | |||
| d29e8e5f22 | |||
| a93640662e | |||
| 3fb0a90854 | |||
| df09dbdf0f | |||
| 134de98ba5 | |||
| 221ea6a54c | |||
| 7ffbcbc666 | |||
| 5f66260c3a | |||
| ba95eb3e1b | |||
| f16697624d | |||
| cb311ed25e | |||
| 6f686d12af | |||
| 6a0acfc735 | |||
| 27f8ec17fc | |||
| a683881eb7 | |||
| 335b085fd2 | |||
| 11c2b76e29 | |||
| 6605b6e43f | |||
| ec18e89365 | |||
| 39cde9f9a3 | |||
| 2747027ef2 | |||
| 4465360f3d | |||
| c33113ab5f | |||
| eab43851ac | |||
| c3f1832d9a | |||
| 2328d1e799 | |||
| 5fabc203db | |||
| d9d19e812e | |||
| 45dd431f19 | |||
| 088a660c00 | |||
| 1ff348fb1f | |||
| cd364a9315 | |||
| ca584de84a | |||
| 594be6db63 | |||
| aef70a5839 | |||
| e6e7250a47 | |||
| 7b3dc19445 | |||
| 8e36d2758b | |||
| 7cbbfd6fc5 | |||
| e8bb38f7c9 | |||
| 1b9bb143bf | |||
| 41f666c670 | |||
| 469e2434fd | |||
| 278aa09819 | |||
| dded832238 | |||
| 8ffabbea64 | |||
| 29b4c6723f | |||
| 039f48f3d2 | |||
| eed4afda63 | |||
| ce888e1042 | |||
| f5d0ec93ef | |||
| 3a0d72457b | |||
| c980e48ad4 | |||
| f5bd750a4d | |||
| 0a5875ab2e | |||
| 54ecf74261 | |||
| 9b83acffe6 | |||
| 67aa15e92e | |||
| 0d6efc6429 | |||
| 1bd3ade825 | |||
| fb8ed8fde8 | |||
| c698eeacba | |||
| 906a20d3bf | |||
| 1be377c47d | |||
| 4dbb5dd4da | |||
| 997974376d | |||
| 7d51044892 | |||
| 37c080083b | |||
| 70a0dee8c8 | |||
| de83d67364 | |||
| 7a5a50d949 | |||
| d348d8b4cf | |||
| 443de9fe1c | |||
| c72f1d5e62 | |||
| ca8fcc9479 | |||
| d01a932201 | |||
| 85d223c5e2 | |||
| 7b486e398a | |||
| da69f18e91 | |||
| 6971b8a69e | |||
| 6682e41ab4 | |||
| 18033ebc36 | |||
| 89d9e33bca | |||
| e80e7e5c10 | |||
| 6233915ecf | |||
| fd8e0bd70b | |||
| 45e2863e9f | |||
| 4204f3d065 | |||
| ddbd65beec | |||
| 1a1506ef79 | |||
| 8793dc37fa | |||
| e9d78a78ea | |||
| 7ec9cfc61c | |||
| 294cf28e43 | |||
| 2f7a66e6d3 | |||
| a4f9c9d1b6 | |||
| aa10b682e4 | |||
| eafbe4ee69 | |||
| cf5912412d | |||
| 1f114b2acf | |||
| 94ebddbdfe | |||
| aa1ca27789 | |||
| 9999320cd0 | |||
| 74429b4a14 | |||
| bcb191c01e | |||
| ddb848816f | |||
| 889911d0a6 | |||
| a4dd38de81 | |||
| 486ee37a40 | |||
| 944176beef | |||
| c396418187 | |||
| 1b4ab772d2 | |||
| aca0fe0412 | |||
| af511c1118 | |||
| 5ba8766cbc | |||
| b792c10680 | |||
| 12d41ba0c1 | |||
| 020cca8c3c | |||
| 4cbd34d32f | |||
| 6426bf79b4 | |||
| aed8116b22 | |||
| f6c759ccbf | |||
| 4b389b90e8 | |||
| 4c3680635e | |||
| 0755145824 | |||
| eabd1ddce0 | |||
| 0e84235f9a | |||
| 894839484c | |||
| 1fadd0e772 | |||
| eba6e7a428 | |||
| f4a6e0373b | |||
| d274545e63 | |||
| f74d25c882 | |||
| 618c689246 | |||
| 9d29971755 | |||
| 451c57af92 | |||
| 59feac28b9 | |||
| c1f48fc79a | |||
| 3c07a36e95 | |||
| 98b3c27c76 | |||
| ddacdfacd5 | |||
| 2d477e069d | |||
| ed1a56785a | |||
| f496571747 | |||
| 17fed5980b | |||
| 2ff04f1a87 | |||
| f763865c5c | |||
| 52edf52245 | |||
| 8161b766d3 | |||
| 709349e24b | |||
| 5fd4878574 | |||
| a24dc8c543 | |||
| c3d514a751 | |||
| 5b7a2c73db | |||
| aaf6a36c55 | |||
| dc6e0e23b9 | |||
| 44b0c79eaf | |||
| 27e5baeb57 | |||
| a6a4f85e61 | |||
| 3386316b9f | |||
| 410bb9c103 | |||
| f176b3a480 | |||
| 9b1a3a2236 | |||
| b29edadf48 | |||
| d04332d688 | |||
| 547288529c | |||
| bd8de8719f | |||
| 2fd44ac341 | |||
| dc30aef687 | |||
| 29cadaa23b | |||
| a7c149f8dc | |||
| c2a04e1a45 | |||
| f748dc6fd8 | |||
| bfc21ad906 | |||
| 41726fab9d | |||
| 82f4fcf2a3 | |||
| 3a00f00f62 | |||
| a71f776203 | |||
| d2f60c02bb | |||
| 7ca53138bc | |||
| a9c0501826 | |||
| 60fb4b95fa | |||
| 323b62b405 | |||
| 40fb3466c1 | |||
| 2333ef2454 | |||
| 0ac7bf8c8b | |||
| 38a3ccee52 | |||
| 47115f0380 | |||
| 1f00425eb3 | |||
| 71245845c8 | |||
| d9f6f3b81f | |||
| b81a7c2bca | |||
| 0ec2f0818a | |||
| 4a6a82ae79 | |||
| b14ee51ff6 | |||
| 394116e998 | |||
| f902437848 | |||
| 86cc3b6c12 | |||
| 6bb6ea2432 | |||
| 3c590e36ad | |||
| 69a4dda8c8 | |||
| 8fbaffe56e | |||
| bfafef8df1 | |||
| 26ff622148 | |||
| 88a68eaf7e | |||
| 46a2a177c1 | |||
| b0ea05a853 | |||
| f3b79c412f | |||
| f4f3ad5051 | |||
| 0757ccc657 | |||
| 47282be619 | |||
| 9bdbe943ce | |||
| 180220a2e3 | |||
| 8ef4af45d7 | |||
| 9215aa7ece | |||
| b497c5da71 | |||
| bf4d2fef4a | |||
| c053b8fc8a | |||
| d364ac6024 | |||
| d78be5112b | |||
| b5233eea56 | |||
| 09a6612742 | |||
| 1778ad620f | |||
| 458b50bc07 | |||
| fca8784dc3 | |||
| a846ee2321 | |||
| df087b592f | |||
| eade931650 | |||
| a7649f5b42 | |||
| f0b953dec9 | |||
| bb8196653f | |||
| a1b44ddd58 | |||
| 48b0d7f434 | |||
| d8b6b257f4 | |||
| c98972a3e0 | |||
| b93759936c | |||
| d62dda8140 | |||
| ca36fba7e4 | |||
| 743bf45e00 | |||
| 9bc5d21185 | |||
| c8b56bd161 | |||
| fe20079300 | |||
| 068d7c1b44 | |||
| 42fed60e61 | |||
| 53c1ccaa42 | |||
| 72f667cac9 | |||
| 4fff472808 | |||
| cfd74c2954 | |||
| 9bcca974ba | |||
| a40586d092 | |||
| bc9708f51a | |||
| 8ef36ed939 | |||
| c7f62bae9b | |||
| 1b741bf2b7 | |||
| 266d70816f | |||
| 27cedf09e8 | |||
| db9f1182f5 | |||
| f7fd5cf2d8 | |||
| 8d9c9a16e3 | |||
| 82deccac40 | |||
| 0e540dc3da | |||
| 897f2bc5fd | |||
| 750b0e5c18 | |||
| a65ac105cc | |||
| 6970c80d34 | |||
| dfbc42f833 | |||
| e356daa229 | |||
| 1c27ecb66f | |||
| d01d0bb358 | |||
| 8fcf3658b1 | |||
| b37535bcfb | |||
| ee0a10ec51 | |||
| 39e0333663 | |||
| 3afd2c3597 | |||
| bd19f3baa3 | |||
| 908fa84d49 | |||
| 571f34e0e6 | |||
| 01723025d7 | |||
| 505b5ee565 | |||
| 26ac03a3f6 | |||
| c9aa88b8ef | |||
| 887ac286ff | |||
| b1083d78c7 | |||
| 7863bdb484 | |||
| 5c3363bf25 | |||
| 9404bb8712 | |||
| 20e22d4507 | |||
| 9118e91b72 | |||
| fbbaa5e580 | |||
| 97a2cb4a37 | |||
| 6dee98c3a1 | |||
| 484ccf830a | |||
| 5690204224 | |||
| b203314c2f | |||
| 5ae8fd1f6f | |||
| c80da7eb0d | |||
| 326ca4202e | |||
| 852dde671e | |||
| bf613175e7 | |||
| 4d62cca267 | |||
| a63aa143bb | |||
| ed0fb774b9 | |||
| 9be956f4bc | |||
| 48d741f68a | |||
| 35e6b8a67d | |||
| 8a31ae14a0 | |||
| 26ee3fb93e | |||
| 8872d5ebfc | |||
| faf455283d | |||
| 562b4c5020 | |||
| 7460734522 | |||
| 3be7100e89 | |||
| aedaf7d925 | |||
| 256e84f912 | |||
| 0a87846bd8 | |||
| 073862ee92 | |||
| c820d83ae2 | |||
| 109e0767e7 | |||
| 821f2aeae0 | |||
| 93a11e266f | |||
| e29e583919 | |||
| 310df7acb5 | |||
| b9387febe0 | |||
| 3dce224183 | |||
| 5d8afa25b8 | |||
| 0dca016749 | |||
| 28a911bcce | |||
| eb36120c6e | |||
| dfbb133ea6 | |||
| 79ab77facd | |||
| 2c99692019 | |||
| 0ca0aad7ce | |||
| 3ddba4e620 | |||
| eab3dceb48 | |||
| 663a94fcab | |||
| 373ca88f14 | |||
| be33de1086 | |||
| e5bdfb0962 | |||
| e80c6c48a1 | |||
| 381644a0c0 | |||
| 13bb59c334 | |||
| 632e5f9872 | |||
| 37531930b8 | |||
| 3869f76887 | |||
| 3e76703ff5 | |||
| deb48dd5f0 | |||
| 73fa5cd7bc | |||
| 1be7582106 | |||
| d5b7cdbca3 | |||
| c769262398 | |||
| 64caab1ade | |||
| 78914382c3 | |||
| 15e37ff927 | |||
| 91234060be | |||
| b33b6ce293 | |||
| 8911abbc6d | |||
| bc77922d47 | |||
| 34efc3a381 | |||
| 5c46efb4d1 | |||
| d6343d9d5c | |||
| e368551919 | |||
| 8b4624a420 | |||
| 9fd1b7b1a8 | |||
| a38829c692 | |||
| 1dbc6c83c4 | |||
| 9a6071920f | |||
| 0964176b4d | |||
| 1ffa9a4adf | |||
| 8c5886060f | |||
| 3bb092d4d4 | |||
| 20e62dea6f | |||
| 1b5d312c68 | |||
| 435ddc5f2d | |||
| 1de1f87c6b | |||
| 778164c98e | |||
| b3da00ac1f | |||
| e454ad1b61 | |||
| f845b469b1 | |||
| 35adbffa54 | |||
| 01f3d3eb59 | |||
| 0e43146d32 | |||
| 93f40992c1 | |||
| c6b47fe6a0 | |||
| 536deaa00a | |||
| df1d639deb | |||
| 733a17d470 | |||
| 330f8c7c50 | |||
| e238eb2242 | |||
| 5625f68d51 | |||
| f7d8502572 | |||
| e395c94e5f | |||
| c08b2dfe06 | |||
| 9c3b706533 | |||
| 60f8a62978 | |||
| 0c7c2926f9 | |||
| 3a2188fc7b | |||
| 64a30424da | |||
| 525c41bac9 | |||
| 83de8ff77d | |||
| 170a069132 | |||
| f19b288717 | |||
| 7c54dbc61f | |||
| 1976d9e3fe | |||
| 87153e9a02 | |||
| ba274008d5 | |||
| b059de857c | |||
| 1880e82da6 | |||
| 888d957f60 | |||
| f69c579005 | |||
| 2e043c7fbc | |||
| 3f4f4e0ead | |||
| 5c35bad590 | |||
| c1bff3dad0 | |||
| 3eeffca841 | |||
| cb5f7ad3fd | |||
| 704489ed6d | |||
| 9ca6f72583 | |||
| 584098dfda | |||
| 180662f038 | |||
| 2da9d26e21 | |||
| d840971c42 | |||
| ba3b3e8938 | |||
| c4ef38fbf9 | |||
| edaf877056 | |||
| 45743406d0 | |||
| 5428bfd270 | |||
| 72b19555dc | |||
| 612a38e363 | |||
| 0c2ab895b7 | |||
| 8d5b7710cf | |||
| de532643b1 | |||
| c2765e9eca | |||
| aa63f26f36 | |||
| f542717992 | |||
| 922ba6a4a3 | |||
| febb3906db | |||
| 0f06f59c6e | |||
| 0fdf08d14f | |||
| 133e32c1c4 | |||
| 41e501aaf4 | |||
| abc373dc73 | |||
| 02c5c4213c | |||
| 25b83920b6 | |||
| 3f8a110e4a | |||
| 95ee572b4d | |||
| 120d17c124 | |||
| 193f91b870 | |||
| f9ed15f857 | |||
| 870f00222d | |||
| 5b243f652c | |||
| 789b5c6b78 | |||
| dcf609defb | |||
| 4bce85dc09 | |||
| 8a73502f11 | |||
| 632357ffb9 | |||
| 4c54aa20da | |||
| 8486e07377 | |||
| 91e20a53e0 | |||
| 9e42c8459e | |||
| 627743432a | |||
| 5d92d4dc98 | |||
| 682b3a9523 | |||
| fdf075a7f9 | |||
| 95a7d065ca | |||
| c1c074f170 | |||
| 429c01556b | |||
| ee76822fe6 | |||
| b9bf0aa0da | |||
| fd3976b5bb | |||
| 48f145c4df | |||
| 5a34c64fbe | |||
| 3e60eeb368 | |||
| e5a8d69300 | |||
| 4f8672c9ab | |||
| a7416e7300 | |||
| d63d8e0899 | |||
| 8e009de8a8 | |||
| d6527ece02 | |||
| 211425c3e9 | |||
| 429aea8e74 | |||
| 7755780a49 | |||
| 3a7c25f987 | |||
| a4cbe3326e | |||
| 5282b4efac | |||
| 6a25fd494e | |||
| d95c1b5982 | |||
| 24eed9e376 | |||
| c2af1d183e | |||
| 64651115c2 | |||
| b6c3d142db | |||
| fafe80704f | |||
| a2be2f3330 | |||
| 05fc49a0cf | |||
| 417aa4312e | |||
| bf88cdea90 | |||
| c8d0b87a67 | |||
| 99a618385b | |||
| 9e143efd73 | |||
| d5c70a1cd3 | |||
| bbb2b0c95b | |||
| 4768c7d431 | |||
| 4f7c64cb24 | |||
| ff2a79de7c | |||
| 04c7057ea6 | |||
| f76e8d2812 | |||
| bcdc5e0097 | |||
| 2bee29959b | |||
| cab3834bc9 | |||
| 7f970a45c2 | |||
| ebf70ab18e | |||
| 226744a538 | |||
| dc876c6ed4 | |||
| 5a09084c73 | |||
| 6c9074772c | |||
| e49663809b | |||
| 7d6d93f7aa | |||
| 8c54e3dd88 | |||
| 47c0debe4b | |||
| 9a1cde40a8 | |||
| 20fc814635 | |||
| 64d8a04308 | |||
| b95effbdd7 | |||
| 92c130c7be | |||
| 31c4bc1151 | |||
| 42f5e55ed5 | |||
| 81da7bb4c1 | |||
| 209754493b | |||
| b0aef96340 | |||
| 056394e24b | |||
| 1545659817 | |||
| 38e3d55e64 | |||
| 66da29e89f | |||
| c180d6fe2f | |||
| cdea833b5c | |||
| c2eaa495df | |||
| 3476100dd0 | |||
| 07c7eff3c3 | |||
| a1be8cd9fe | |||
| 52b9fcd57f | |||
| ea53ace270 | |||
| a6cd072fa1 | |||
| 4372dbdd41 | |||
| 17bb1fec44 | |||
| f4fd79f7be | |||
| 811b13a9d3 | |||
| 6e97079367 | |||
| 9c7bbce029 | |||
| 0ec1cb1e19 | |||
| e7118e5bda | |||
| 150d9d1c6b | |||
| 8fe039156b | |||
| 2d17dec7bf | |||
| c1b267a208 | |||
| f02c56c9e0 | |||
| c5879c682c | |||
| 190c20825f | |||
| d04eea7185 | |||
| f24cd31895 | |||
| 2044f6fbcc | |||
| d55a89f96a | |||
| 080a6648e9 | |||
| ec0a142190 | |||
| b7fcf6ad68 | |||
| 7417c14c2d | |||
| 4f39be9c3b | |||
| 7305a44f0c | |||
| 74effd2fa9 | |||
| 9e9768b51d | |||
| 127557d27e | |||
| fd89e9b83a | |||
| 5a1b885024 | |||
| b15adf23f8 | |||
| 29d2719bad | |||
| 194438cdd6 | |||
| 217f677bbe | |||
| 16b0f4cc9a | |||
| ff15b474fd | |||
| 3efc6340ad | |||
| 0426a5f902 | |||
| b898916d52 | |||
| 52983794d7 | |||
| 9d2f52805b | |||
| b18213805a | |||
| eeba90fd5b | |||
| 6507765883 | |||
| a98e35ebab | |||
| c4cfc01ba3 | |||
| f64e70e659 | |||
| 64fa7853a7 | |||
| 37f35f8c91 | |||
| 0d13e61d0c | |||
| 5701bf81f3 | |||
| 6110f0bc1b | |||
| 17302ca762 | |||
| f246599253 | |||
| ec3f387f21 | |||
| 31f7214156 | |||
| 6faf36fc64 | |||
| edb2f2a417 | |||
| 3eee686932 | |||
| 4aac3cd7b1 | |||
| 2588bf7fa0 | |||
| 72f564555e | |||
| b5a8931fb5 | |||
| d3810ecd48 | |||
| 3b8062170c | |||
| 26c36d0a2e | |||
| 09a22ac4b4 | |||
| aed555d858 | |||
| 65b085a5d5 | |||
| 392a709b77 | |||
| ee6fcd76f4 | |||
| ca80c52df1 | |||
| b76ea29520 | |||
| 9172cc15d2 | |||
| 1b83d43cfa | |||
| 7847f4ea8e | |||
| 19736e5be2 | |||
| 51504bfd2e | |||
| 79164dae71 | |||
| 8d04821639 | |||
| f75187392d | |||
| ecc6d16f22 | |||
| 5f252b3e16 | |||
| dd1ebbd31b | |||
| 95671469c7 | |||
| 6601c1b1a6 | |||
| 5ebda89d78 | |||
| b7a3fd8d2f | |||
| fd625f7265 | |||
| d1fe02df23 | |||
| 6593cf22fa | |||
| 548fba82e6 | |||
| 2d26b6cc8d | |||
| 0087d4c6bf | |||
| 289fceb3ea | |||
| e12da15f7d | |||
| c4867c8743 | |||
| 8068419854 | |||
| 9b3eb3fe59 | |||
| 1dcccf87b5 | |||
| d51ef253a9 | |||
| 6d4dfcf72f | |||
| ac9f72be61 | |||
| 258dac2324 | |||
| b7d91e2f33 | |||
| 860697257b | |||
| 91563cff1f | |||
| 4f96a50fd0 | |||
| e98fea0bba | |||
| c728e031df | |||
| ceb7891986 | |||
| 04a7baec3e | |||
| 8ff97bdedd | |||
| a6c4287b4d | |||
| f9f439ad49 | |||
| c02a8141c7 | |||
| 5862b481a4 | |||
| 1648fe8ef2 | |||
| 449eefdb19 | |||
| 058f62ac08 | |||
| 4e49ba597d | |||
| 4b706ae25c | |||
| 1ac26d3d2f | |||
| 0edeaf657f | |||
| 76d5b3d786 | |||
| 915132645e | |||
| 2a76ceb7dd | |||
| 69263e7319 | |||
| 9a6397b325 | |||
| b8420e78bb | |||
| f0d523f778 | |||
| 6e6507bf77 | |||
| b8fe75a9dd | |||
| 0f95b4102d | |||
| 24c11d4ba5 | |||
| 3dcac3b539 | |||
| e615c25319 | |||
| b2fbbeb771 | |||
| bcfe751f62 | |||
| a191326ea8 | |||
| 23d3a31107 | |||
| 0dfa57ea04 | |||
| 2f9e39e21c | |||
| cd2bb8333d | |||
| 5e3c816373 | |||
| 951e50c06f | |||
| 40c9c66c1d | |||
| 01dac8ba8e | |||
| ac5f84e3d7 | |||
| 9583439be4 | |||
| 2690b243ea | |||
| df724f5827 | |||
| 539cc5c47b | |||
| 45cc5b58cd | |||
| c6acaa981d | |||
| 4ef5109a07 | |||
| 6016d0c739 | |||
| 07d23c9d83 | |||
| d5f195387d | |||
| a0d4ecb607 | |||
| 505e68057d | |||
| 87c6e19921 | |||
| 62c1555aa8 | |||
| e62fc43b05 | |||
| 158be02ef7 | |||
| fbb125131d | |||
| c77ed8e50e | |||
| d1b7238a36 | |||
| 453a1d35c3 | |||
| 9da8b5c786 | |||
| 407889864f | |||
| e17daf165d | |||
| 666d2bd501 | |||
| 9d14489547 | |||
| f3dfe1d49e | |||
| b3e1974c30 | |||
| fa12450552 | |||
| b3c040e9f7 | |||
| 52788e5826 | |||
| bcd4c6b658 | |||
| 37460ad60a | |||
| dcca47f1f6 | |||
| 892efaec97 | |||
| 84048f299f | |||
| a9bf981d92 | |||
| d4729f92bd | |||
| 5a014dd6a3 | |||
| d948941d82 | |||
| 5abf1e998d | |||
| b5626e70cb | |||
| c201faab92 | |||
| 2e47a9c5cd | |||
| e13d4ba3d0 | |||
| 9dcb63ca98 | |||
| 78ddc51471 | |||
| c931bc7394 | |||
| 1f3e0004a9 | |||
| d4cb92f27c | |||
| ee5bc65c97 | |||
| 4f1b69827d | |||
| 73f519b589 | |||
| 25abfff632 | |||
| e39358d0ee | |||
| 105fe9b25d | |||
| 753f4b38b4 | |||
| d144b11611 | |||
| 1ef1aa7862 | |||
| 83c98a23ce | |||
| 336b9b33ab | |||
| ef4f46f1d1 | |||
| 16cd500000 | |||
| 16950780d3 | |||
| 7e284a37fc | |||
| b6c53a6f0e | |||
| 8b626e6ea7 | |||
| 1d1f4fc401 | |||
| 250426c1e5 | |||
| e300e90a63 | |||
| e865db232b | |||
| db2a06a9bb | |||
| 560fd0a558 | |||
| b8048112df | |||
| b51cda9721 | |||
| 4d60d679d3 | |||
| 9932f2e126 | |||
| bf8d6460da | |||
| d3e67cf18c | |||
| 5d82a7eee6 | |||
| c82ff057f5 | |||
| 5368dd22c5 | |||
| 194396d715 | |||
| 815784bd96 | |||
| 7fc64a27b4 | |||
| c38bf27091 | |||
| 9a2fab8874 | |||
| f1516ad3ee | |||
| 4dc07d6cc3 | |||
| a35b983a5d |
@@ -1,125 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
commands:
|
||||
submodules:
|
||||
steps:
|
||||
- run:
|
||||
name: "Pull submodules"
|
||||
command: git submodule update --init
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-18-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-18-v1
|
||||
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:
|
||||
steps:
|
||||
- run:
|
||||
name: "Hack Ninja to use less jobs"
|
||||
command: |
|
||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||
chmod +x /go/bin/ninja
|
||||
build-binaryen-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-v3
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-v3
|
||||
paths:
|
||||
- build/wasm-opt
|
||||
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/bullseye/ llvm-toolchain-bullseye-<<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>> \
|
||||
cmake \
|
||||
ninja-build
|
||||
- hack-ninja-jobs
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v4-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v7
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v7
|
||||
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 lint
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-llvm15-go119:
|
||||
docker:
|
||||
- image: golang:1.19-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
# "make lint" fails before go 1.21 because internal/tools/go.mod specifies packages that require go 1.21
|
||||
fmt-check: false
|
||||
resource_class: large
|
||||
test-llvm18-go122:
|
||||
docker:
|
||||
- image: golang:1.22-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "18"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm15-go119
|
||||
# This tests LLVM 18 support when linking against system libraries.
|
||||
- test-llvm18-go122
|
||||
@@ -1,5 +1,4 @@
|
||||
build/
|
||||
llvm-*/
|
||||
.github
|
||||
.circleci
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: tinygo
|
||||
@@ -16,34 +16,37 @@ jobs:
|
||||
name: build-macos
|
||||
strategy:
|
||||
matrix:
|
||||
# macos-12: amd64 (oldest supported version as of 05-02-2024)
|
||||
# macos-14: arm64 (oldest arm64 version)
|
||||
os: [macos-12, macos-14]
|
||||
# macos-14: arm64 (oldest supported version as of 18-11-2025)
|
||||
# macos-15-intel: amd64 (last intel version to be supported by github runners)
|
||||
# See https://github.com/actions/runner-images/issues/13046
|
||||
os: [macos-14, macos-15-intel]
|
||||
include:
|
||||
- os: macos-12
|
||||
goarch: amd64
|
||||
- os: macos-14
|
||||
goarch: arm64
|
||||
- os: macos-15-intel
|
||||
goarch: amd64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-${{ matrix.os }}-v2
|
||||
key: llvm-source-22-${{ matrix.os }}-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -54,7 +57,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -65,73 +68,55 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-${{ matrix.os }}-v3
|
||||
key: llvm-build-22-${{ matrix.os }}-v1
|
||||
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
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install ninja
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
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@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-${{ matrix.os }}-v1
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- name: make gen-device
|
||||
run: make -j3 gen-device
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-short"
|
||||
run: make test GOTESTFLAGS="-only-current-os"
|
||||
- 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-${{ matrix.goarch }}.tar.gz
|
||||
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.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@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: darwin-${{ matrix.goarch }}-double-zipped
|
||||
path: build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
|
||||
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||
archive: false
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||
test-macos-homebrew:
|
||||
name: homebrew-install
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [16, 17, 18]
|
||||
version: [16, 17, 18, 19, 20]
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
uses: Homebrew/actions/setup-homebrew@main
|
||||
- name: Fix Python symlinks
|
||||
run: |
|
||||
# Github runners have broken symlinks, so relink
|
||||
@@ -140,20 +125,21 @@ jobs:
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
brew install llvm@${{ matrix.version }}
|
||||
brew link llvm@${{ matrix.version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
- name: Check binary
|
||||
run: tinygo version
|
||||
- name: Build TinyGo (default LLVM)
|
||||
if: matrix.version == 18
|
||||
if: matrix.version == 20
|
||||
run: go install
|
||||
- name: Check binary
|
||||
if: matrix.version == 18
|
||||
if: matrix.version == 20
|
||||
run: tinygo version
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# This CI job checks whether at least the smoke tests pass for the oldest
|
||||
# Go/LLVM version we claim to support.
|
||||
|
||||
name: Version compatibility test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-compat:
|
||||
runs-on: ubuntu-22.04 # this must be a specific version for the apt install below
|
||||
env:
|
||||
# Oldest versions currently supported by TinyGo
|
||||
LLVM: "15"
|
||||
Go: "1.25" # when updating this, also update minorMin in builder/config.go
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ env.Go }}
|
||||
cache: true
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ env.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 -y \
|
||||
llvm-${{ env.LLVM }}-dev \
|
||||
clang-${{ env.LLVM }} \
|
||||
libclang-${{ env.LLVM }}-dev \
|
||||
lld-${{ env.LLVM }} \
|
||||
binaryen
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-22-linux-compat
|
||||
path: llvm-project/compiler-rt
|
||||
- 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@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: llvm-project/compiler-rt
|
||||
- name: Go cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: go-build-${{ env.Go }}-llvm${{ env.LLVM }}-${{ hashFiles('go.sum') }}
|
||||
- name: Build TinyGo
|
||||
run: go install -tags=llvm${{ env.LLVM }}
|
||||
- run: tinygo version
|
||||
- run: make gen-device -j4
|
||||
- run: go test -tags=llvm${{ env.LLVM }} -short -skip=TestErrors
|
||||
- run: make smoketest XTENSA=0
|
||||
@@ -31,14 +31,14 @@ jobs:
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
@@ -47,18 +47,18 @@ jobs:
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@@ -66,45 +66,3 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
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"}'
|
||||
|
||||
+105
-84
@@ -12,38 +12,61 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
go-mod-tidy:
|
||||
# Check that go.sum is up to date.
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Run go mod tidy
|
||||
run: go mod tidy
|
||||
- name: Check go.mod and go.sum are up to date
|
||||
run: git diff --exit-code
|
||||
|
||||
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.22-alpine
|
||||
image: golang:1.27rc2-alpine
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v4
|
||||
# tar: needed for actions/cache@v5
|
||||
# git+openssh: needed for checkout (I think?)
|
||||
# ruby: needed to install fpm
|
||||
run: apk add tar git openssh make g++ ruby-dev
|
||||
run: apk add tar git openssh make g++ ruby-dev mold
|
||||
- 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@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-linux-alpine-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-linux-alpine-v1
|
||||
key: llvm-source-22-linux-alpine-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -54,7 +77,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -65,10 +88,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-alpine-v2
|
||||
key: llvm-build-22-linux-alpine-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -83,31 +106,22 @@ jobs:
|
||||
# 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@v4
|
||||
uses: actions/cache/save@v5
|
||||
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@v4
|
||||
uses: actions/cache@v5
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-alpine-v1
|
||||
key: binaryen-linux-alpine-v3
|
||||
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@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-alpine-v2
|
||||
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
|
||||
@@ -120,46 +134,52 @@ jobs:
|
||||
- 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@v4
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
- name: "Publish release artifact: tarball"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-amd64.tar.gz
|
||||
/tmp/tinygo_amd64.deb
|
||||
name: tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
path: /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
archive: false
|
||||
- name: "Publish release artifact: Debian package"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
path: /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
archive: false
|
||||
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@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
with:
|
||||
version: "19.0.1"
|
||||
version: "29.0.1"
|
||||
- name: Install wasm-tools
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
- name: Extract release tarball
|
||||
run: |
|
||||
mkdir -p ~/lib
|
||||
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
||||
tar -C ~/lib -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||
- run: make tinygo-test-wasip1-fast
|
||||
- run: make tinygo-test-wasip2-fast
|
||||
- run: make tinygo-test-wasm
|
||||
- run: make smoketest
|
||||
assert-test-linux:
|
||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||
@@ -167,7 +187,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -182,25 +202,25 @@ jobs:
|
||||
simavr \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: '22'
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
with:
|
||||
version: "19.0.1"
|
||||
version: "29.0.1"
|
||||
- name: Setup `wasm-tools`
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-linux-asserts-v1
|
||||
key: llvm-source-22-linux-asserts-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -211,7 +231,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -222,10 +242,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-asserts-v2
|
||||
key: llvm-build-22-linux-asserts-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -238,13 +258,13 @@ jobs:
|
||||
# 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@v4
|
||||
uses: actions/cache/save@v5
|
||||
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@v4
|
||||
uses: actions/cache@v5
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-asserts-v1
|
||||
@@ -252,15 +272,6 @@ jobs:
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: make binaryen
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v6
|
||||
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
|
||||
@@ -272,7 +283,9 @@ jobs:
|
||||
run: make tinygo-test
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-baremetal
|
||||
- run: make tinygo-test-baremetal
|
||||
- name: Check Go code formatting
|
||||
run: make fmt-check lint
|
||||
build-linux-cross:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
@@ -292,11 +305,14 @@ jobs:
|
||||
- goarch: arm
|
||||
toolchain: arm-linux-gnueabihf
|
||||
libc: armhf
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04 # note: use the oldest image available! (see above)
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Get TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -305,15 +321,15 @@ jobs:
|
||||
g++-${{ matrix.toolchain }} \
|
||||
libc6-dev-${{ matrix.libc }}-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-linux-v1
|
||||
key: llvm-source-22-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -324,7 +340,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -335,10 +351,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
|
||||
key: llvm-build-22-linux-${{ matrix.goarch }}-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -353,16 +369,16 @@ jobs:
|
||||
# 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@v4
|
||||
uses: actions/cache/save@v5
|
||||
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@v4
|
||||
uses: actions/cache@v5
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v4
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v5
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
@@ -379,13 +395,13 @@ jobs:
|
||||
run: |
|
||||
make CROSS=${{ matrix.toolchain }}
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||
tar -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz -C build/release tinygo
|
||||
- name: Modify release
|
||||
run: |
|
||||
cp -p build/tinygo build/release/tinygo/bin
|
||||
@@ -393,12 +409,17 @@ jobs:
|
||||
- name: Create ${{ matrix.goarch }} release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||
- name: "Publish release artifact: tarball"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
|
||||
/tmp/tinygo_${{ matrix.libc }}.deb
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
path: /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
archive: false
|
||||
- name: "Publish release artifact: Debian package"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
path: /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||
archive: false
|
||||
|
||||
@@ -25,18 +25,18 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
tinygo/llvm-18
|
||||
ghcr.io/${{ github.repository_owner }}/llvm-18
|
||||
tinygo/llvm-20
|
||||
ghcr.io/${{ github.repository_owner }}/llvm-20
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
@@ -46,13 +46,13 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
|
||||
@@ -15,29 +15,34 @@ jobs:
|
||||
nix-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Uninstall system LLVM
|
||||
# Hack to work around issue where we still include system headers for
|
||||
# some reason.
|
||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||
run: sudo apt-get remove llvm-18
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Pull musl
|
||||
uses: actions/checkout@v6
|
||||
- name: Pull musl, bdwgc
|
||||
run: |
|
||||
git submodule update --init lib/musl
|
||||
git submodule update --init lib/musl lib/bdwgc
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-linux-nix-v1
|
||||
key: llvm-source-22-linux-nix-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- 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@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- name: Test
|
||||
run: |
|
||||
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# still works after checking out the dev branch (that is, when going from LLVM
|
||||
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
||||
|
||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-20 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 -y \
|
||||
llvm-18-dev \
|
||||
clang-18 \
|
||||
libclang-18-dev \
|
||||
lld-18
|
||||
llvm-20-dev \
|
||||
clang-20 \
|
||||
libclang-20-dev \
|
||||
lld-20
|
||||
|
||||
@@ -9,6 +9,8 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
sizediff:
|
||||
# Note: when updating the Ubuntu version, also update the Ubuntu version in
|
||||
# sizediff-install-pkgs.sh
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
@@ -17,25 +19,29 @@ jobs:
|
||||
- name: Add GOBIN to $PATH
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '~1.25'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-sizediff-v1
|
||||
key: llvm-source-22-sizediff-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extract the version string from the source code, to be stored in a variable.
|
||||
grep 'const version' goenv/version.go | sed 's/^const version = "\(.*\)"$/version=\1/g'
|
||||
@@ -14,34 +14,33 @@ concurrency:
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
shell: bash
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-windows-v1
|
||||
key: llvm-source-22-windows-v3
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
@@ -52,7 +51,7 @@ jobs:
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save cached LLVM source
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
@@ -63,10 +62,10 @@ jobs:
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore cached LLVM build
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-windows-v2
|
||||
key: llvm-build-22-windows-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -80,80 +79,64 @@ jobs:
|
||||
# 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@v4
|
||||
uses: actions/cache/save@v5
|
||||
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@v4
|
||||
id: cache-wasi-libc
|
||||
- name: Cache Go cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
key: wasi-libc-sysroot-v5
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
key: go-cache-windows-v3-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
C:/Users/runneradmin/AppData/Local/go-build
|
||||
C:/Users/runneradmin/go/pkg/mod
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop install wasmtime@14.0.4
|
||||
scoop config use_external_7zip true
|
||||
scoop install wasmtime@29.0.1
|
||||
- name: make gen-device
|
||||
run: make -j3 gen-device
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-short"
|
||||
run: make test GOTESTFLAGS="-only-current-os"
|
||||
- name: Build TinyGo release tarball
|
||||
shell: bash
|
||||
run: make build/release -j4
|
||||
- name: Make release artifact
|
||||
shell: bash
|
||||
working-directory: build/release
|
||||
run: 7z -tzip a release.zip tinygo
|
||||
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo
|
||||
- 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 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@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
path: build/release/release.zip
|
||||
name: tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
archive: false
|
||||
|
||||
smoke-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop config use_external_7zip true
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x release.zip -r
|
||||
# This build is already unzipped.
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
@@ -162,28 +145,19 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x release.zip -r
|
||||
# This build is already unzipped.
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
@@ -191,34 +165,24 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- uses: MinoruSekine/setup-scoop@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install binaryen && scoop install wasmtime@14.0.4
|
||||
scoop config use_external_7zip true
|
||||
scoop install binaryen wasmtime@29.0.1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.27.0-rc.2'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: windows-amd64-double-zipped
|
||||
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x release.zip -r
|
||||
# This build is already unzipped.
|
||||
- name: Test stdlib packages on wasip1
|
||||
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
@@ -20,6 +20,8 @@ src/device/stm32/*.go
|
||||
src/device/stm32/*.s
|
||||
src/device/kendryte/*.go
|
||||
src/device/kendryte/*.s
|
||||
src/device/renesas/*.go
|
||||
src/device/renesas/*.s
|
||||
src/device/rp/*.go
|
||||
src/device/rp/*.s
|
||||
./vendor
|
||||
@@ -35,5 +37,9 @@ test.exe
|
||||
test.gba
|
||||
test.hex
|
||||
test.nro
|
||||
test.uf2
|
||||
test.wasm
|
||||
wasm.wasm
|
||||
|
||||
*.uf2
|
||||
*.elf
|
||||
+5
-6
@@ -16,13 +16,13 @@
|
||||
url = https://github.com/WebAssembly/wasi-libc
|
||||
[submodule "lib/picolibc"]
|
||||
path = lib/picolibc
|
||||
url = https://github.com/keith-packard/picolibc.git
|
||||
url = https://github.com/picolibc/picolibc.git
|
||||
[submodule "lib/stm32-svd"]
|
||||
path = lib/stm32-svd
|
||||
url = https://github.com/tinygo-org/stm32-svd
|
||||
[submodule "lib/musl"]
|
||||
path = lib/musl
|
||||
url = git://git.musl-libc.org/musl
|
||||
url = https://github.com/tinygo-org/musl-libc.git
|
||||
[submodule "lib/binaryen"]
|
||||
path = lib/binaryen
|
||||
url = https://github.com/WebAssembly/binaryen.git
|
||||
@@ -32,13 +32,12 @@
|
||||
[submodule "lib/macos-minimal-sdk"]
|
||||
path = lib/macos-minimal-sdk
|
||||
url = https://github.com/aykevl/macos-minimal-sdk.git
|
||||
[submodule "lib/renesas-svd"]
|
||||
path = lib/renesas-svd
|
||||
url = https://github.com/tinygo-org/renesas-svd.git
|
||||
[submodule "src/net"]
|
||||
path = src/net
|
||||
url = https://github.com/tinygo-org/net.git
|
||||
branch = dev
|
||||
[submodule "lib/wasi-cli"]
|
||||
path = lib/wasi-cli
|
||||
url = https://github.com/WebAssembly/wasi-cli
|
||||
[submodule "lib/bdwgc"]
|
||||
path = lib/bdwgc
|
||||
url = https://github.com/ivmai/bdwgc.git
|
||||
|
||||
+8
-2
@@ -85,11 +85,17 @@ Try running TinyGo:
|
||||
|
||||
./build/tinygo help
|
||||
|
||||
Also, make sure the `tinygo` binary really is statically linked. Check this
|
||||
using `ldd` (not to be confused with `lld`):
|
||||
Also, make sure the `tinygo` binary really is statically linked. The command to check for
|
||||
dynamic dependencies differs depending on your operating system.
|
||||
|
||||
On Linux, use `ldd` (not to be confused with `lld`):
|
||||
|
||||
ldd ./build/tinygo
|
||||
|
||||
On macOS, use otool -L:
|
||||
|
||||
otool -L ./build/tinygo
|
||||
|
||||
The result should not contain libclang or libLLVM.
|
||||
|
||||
## Make a release tarball
|
||||
|
||||
+706
-1
@@ -1,3 +1,708 @@
|
||||
0.41.1
|
||||
---
|
||||
* **machine**
|
||||
- esp32c3: correct pin interrupt setup call that was overlooked from #5320
|
||||
* **runtime**
|
||||
- esp32s3: wait for TIMG0 update register to clear before reading timer registers
|
||||
* **net**
|
||||
- update net module to a version that is backwards compatible with Go 1.25.x to fix #5332
|
||||
|
||||
0.41.0
|
||||
---
|
||||
* **general**
|
||||
- go.mod, compiler: bump minimum Go version to 1.23
|
||||
- builder: support Go 1.26 now
|
||||
- feat: add inheritable-only field to filter processor-level targets (#5270)
|
||||
- feature: add esp32flash flash method for esp32s3/esp32c3/esp32/esp8266
|
||||
- flashing: introduce flash method 'esp32jtag' for esp32c3/esp32s3 targets
|
||||
- fashing: add "adb" flash method using Android Debug Bridge
|
||||
- main: auto-use best available flashing options on esp32
|
||||
- espflasher: update to espflasher 0.6.0
|
||||
* **compiler**
|
||||
- implement method-set based AssignableTo and Implements (#5304)
|
||||
- simplify createObjectLayout
|
||||
- implement copy directly
|
||||
- fix min/max on floats by using intrinsics
|
||||
- add element layout to sliceAppend
|
||||
- add intrinsic for crypto/internal/constanttime.boolToUint8
|
||||
- fix SyscallN handling for Windows on Go 1.26+
|
||||
* **core**
|
||||
- reflect: add TypeAssert
|
||||
- reflect: fix TypeAssert for structs
|
||||
- sync: add WaitGroup.Go
|
||||
- os: add UserCacheDir and UserConfigDir
|
||||
- os: implement Lchown function for changing file ownership (#5161)
|
||||
- testing: add b.ReportMetric()
|
||||
- errors: add errors package to passing list
|
||||
- internal/gclayout: use correct lengths
|
||||
- internal/abi: add EscapeNonString, EscapeToResultNonString
|
||||
- internal, runtime: add internal fips bool
|
||||
- internal/itoa: resurrect from Go stdlib
|
||||
- internal/syscall/unix: implement GetRandom for WASI targets
|
||||
- interp: make ptrtoint size mismatch a recoverable error
|
||||
- loader: support "all:" prefix in //go:embed patterns
|
||||
- loader, crypto/internal/entropy: fix 32 MiB RAM overflow on microcontrollers
|
||||
- loader, unicode/utf8: fix hiBits overflow on 16-bit AVR targets
|
||||
- builder: order embedded files deterministically
|
||||
- builder: fix panic in findPackagePath when using -size short
|
||||
- builder: fix SIGSEGV when stripping duplicate function definitions
|
||||
- builder, runtime: fix duplicate symbol error with Go 1.26+ on Windows
|
||||
- builder: use target-specific -march for RISC-V library compilation
|
||||
- transform: modify output format from the -print-allocs flag to match the go coverage tool format
|
||||
- cgo: allow for changes to 'short-enums' flag
|
||||
- wasm: add more stubbed file operations
|
||||
* **machine**
|
||||
- esp32c3: implement BlockDevice for esp32c3 flash
|
||||
- esp32c3: clear GPIO STATUS before dispatching pin callbacks
|
||||
- esp32c3: implement USBDevice using interrupts
|
||||
- esp32c3: add Enable stub for QEMU test target
|
||||
- esp32c3: fix interrupt disable handling
|
||||
- esp32c3: clear MCAUSE after handling interrupt
|
||||
- esp32c3: map missing IRAM sections in linker script
|
||||
- esp32c3: fix to use PROVIDE() for WiFi/BLE ROM function addresses to allow blob overrides
|
||||
- esp32c3: add WiFi/BLE ROM linker support
|
||||
- esp32c3: use TIMG0 alarm interrupt for sleepTicks instead of busy-waiting
|
||||
- esp32s3: implement Pin.SetInterrupt for GPIO pin change interrupts
|
||||
- esp32s3: use edge-triggered CPU interrupt for GPIO pin interrupts
|
||||
- esp32s3: add interrupt support (#5244)
|
||||
- esp32s3: switch USB implementation to use interrupts instead of polling
|
||||
- esp32s3: replace inline ISR with full interrupt vector handler
|
||||
- esp32s3: use TIMG0 alarm interrupt for sleepTicks
|
||||
- esp32s3: improve exception handlers, add procPin/procUnpin, and linker wrap flags
|
||||
- esp32s3: fix Xtensa register window spill using recursive call4 in task switching
|
||||
- esp32s3: update linker script and boot assembly for multi-page flash XIP mapping
|
||||
- esp32s3: add flash XIP boot assembly with cache/MMU init
|
||||
- esp32s3: implement SPI (#5169)
|
||||
- esp32s3: implement I2C interface (#5210)
|
||||
- esp32s3: implement RNG based on onboard hardware random number generator
|
||||
- esp32s3,esp32c3: add USB serial support
|
||||
- esp32s3,esp32c3: add txStalled flag to skip USB serial spin when no host
|
||||
- esp32s3,esp32c3: make USB Serial/JTAG writes non-blocking when FIFO is full
|
||||
- esp32c3/esp32s3: refactor ADC implementation to reduce code duplication
|
||||
- esp32s3,esp32c3: add ADC support (#5231)
|
||||
- esp32s3,esp32c3: add PWM support (#5215)
|
||||
- esp32c3/esp32s3: refactoring and corrections for SPI implementation
|
||||
- esp32xx: add WASM simulator support for ESP32-C3/ESP32-S3 targets
|
||||
- esp32: default SPI CS pin to NoPin when unset
|
||||
- esp: fix tinygo_scanCurrentStack to spill register windows
|
||||
- stm32: fix UART interrupt storm caused by uncleared overrun error
|
||||
- stm32: fix PWM problem due to register shifting
|
||||
- stm32: add STM32U585 target definition and runtime
|
||||
- stm32: add STM32U5 GPIO, pin interrupts, and UART support
|
||||
- stm32: add STM32U5 I2C support
|
||||
- stm32: add STM32U5 SPI support
|
||||
- stm32u585: implement ADC
|
||||
- stm32g0b1: add support (#5150)
|
||||
- stm32g0b1: add Watchdog + ADC support (#5158)
|
||||
- stm32l0: add flash support
|
||||
- stm32l0x1,l0x2: TIM: adapt to 32-bit register access
|
||||
- stm32g0: sync with updated stm32 device files
|
||||
- attiny85: add USI-based SPI support (#5181)
|
||||
- attiny85: add PWM support (#5171)
|
||||
- rp: add Close function to UART to allow for removing all system resources/power usage
|
||||
- rp: use blockReset() and unresetBlockWait() helper functions for peripheral reset/unreset
|
||||
- rp2: prevent PWM Period method overflow
|
||||
- rp2: add per-byte timeout budget for I2C (#5189)
|
||||
- feather-m0: export UART0 and pins
|
||||
- usb/cdc: better ring buffer implementation (#5209)
|
||||
- fix: replace ! with ~ for register mask
|
||||
- atmega328p_simulator: add correct build tag for arduino_uno after renaming
|
||||
* **net**
|
||||
- update to Go 1.26.2 net package with UDP and JS improvements
|
||||
* **runtime**
|
||||
- implement fminimum/fmaximum
|
||||
- make timeoffset atomic
|
||||
- add MemStats.HeapObjects
|
||||
- handle GODEBUG on wasip2 (#5312)
|
||||
- fix Darwin syscall return value truncation and lost arguments
|
||||
- add syscall.runtimeClearenv for Go 1.26
|
||||
- split syscall functions for darwin changes for 1.26
|
||||
- fix: init heap before random number seed on wasm platforms
|
||||
* **targets**
|
||||
- add esp32s3-supermini board target
|
||||
- add support for Seeedstudio Xiao-RP2350 board
|
||||
- add Shrike Lite board (#5170)
|
||||
- rename arduino target to arduino-uno
|
||||
- add pins/setup for Arduino UNO Q board QWIIC connector
|
||||
- correct pin mapping for Arduino UNO Q STM32 MCU
|
||||
- add stm32u585 openocd commands for flashing on Arduino UNO Q
|
||||
- correct name/tag use for esp32s3-wroom1 board
|
||||
- modify esp32, esp32s3, esp32c3, & esp8266 targets to use built-in esp32flash
|
||||
- switch rp2040/rp2350 to default to tasks scheduler
|
||||
- change default stack size for esp32c3 and esp32s3 to 8196
|
||||
- swan: rename group build tag stm32l4x5 => stm32l4y5 to avoid clash with stm32 device name
|
||||
- nucleo-f722ze: add build-tag stm32f722
|
||||
- fix: set stm32u5x clock rate to default
|
||||
- create generic targets for esp32 boards
|
||||
* **libs**
|
||||
- stm32-svd: update submodule
|
||||
- update tools/gen-device-svd for recent changes to stm32-svd file structure (#5212)
|
||||
* **build/test**
|
||||
- build: use Golang 1.26 for all builds
|
||||
- build: actually use the version of llvm we are supposed to be testing by using brew link command
|
||||
- build: let scoop run updates to resolve binaryen install failures on Windows
|
||||
- ci: don't double zip release artifacts
|
||||
- test: increase default timeout from 1 to 2 minutes to prevent spurious CI fails
|
||||
- testdata: fix flaky timers test by draining ticker channel after Stop
|
||||
- testdata: more corpus entries (#5182)
|
||||
- add soypat/lneto to corpus
|
||||
- GNUmakefile: add context and expvar to stdlib tests
|
||||
- GNUmakefile: add encoding/xml to stdlib tests on Linux
|
||||
- main (test): skip mipsle test if the emulator is missing
|
||||
- make: remove machine without board from smoketest
|
||||
- stm32: add Arduino UNO Q to smoketests
|
||||
- flake: bump to nixpkgs 25.11
|
||||
- chore: update all github actions to latest versions
|
||||
- fix: use external 7zip for scoop installs on Windows
|
||||
|
||||
0.40.1
|
||||
---
|
||||
* **machine**
|
||||
- nrf: fix flash writes when SoftDevice is enabled
|
||||
* **runtime**
|
||||
- runtime: avoid fixed math/rand sequence on RP2040/RP2350 (#5124)
|
||||
- runtime: add calls to initRand() during run() for all schedulers
|
||||
- runtime: call initRand() before initHeap() during initialization
|
||||
- runtime: use rand_hwrng hardwareRand for RP2040/RP2350 (#5135)
|
||||
* **libs**
|
||||
- picolibc: use updated location for git repo
|
||||
|
||||
0.40.0
|
||||
---
|
||||
* **general**
|
||||
- all: add full LLVM 20 support
|
||||
- core: feat: enable //go:linkname pragma for globals
|
||||
- core: feature: Add flag to ignore go compatibility matrix (#5078)
|
||||
- chore: update version for 0.40 development cycle
|
||||
* **compiler**
|
||||
- emit an error when the actual arch doesn't match GOARCH
|
||||
- mark string parameters as readonly
|
||||
- use Tarjan's SCC algorithm to detect loops for defer
|
||||
- lower "large stack" limit to 16kb
|
||||
* **core**
|
||||
- shrink bdwgc library
|
||||
- Fix linker errors for runtime.vgetrandom and crypto/internal/sysrand.fatal
|
||||
- fix: add TryLock to sync.RWMutex
|
||||
- fix: correct linter issues exposed by the fix in #4679
|
||||
- fix: don't hardcode success return state
|
||||
- fix: expand RTT debugger compatibility
|
||||
- internal/task (threads): save stack bounds instead of scanning under a lock
|
||||
- internal/task: create detached threads and fix error handling
|
||||
- interp: better errors when debugging interp
|
||||
- transform (gc): create stack slots in callers of external functions
|
||||
- internal/task: prevent semaphore resource leak for threads scheduler
|
||||
* **machine**
|
||||
- cortexm: optimize code size for the HardFault_Handler
|
||||
- fe310: add I2C pins for the HiFive1b
|
||||
- clarify WriteAt semantics of BlockDevice
|
||||
- fix deprecated AsmFull comment (#5005)
|
||||
- make sure DMA buffers do not escape unnecessarily
|
||||
- only enable USB-CDC when needed
|
||||
- use larger SPI MAXCNT on nrf52833 and nrf52840
|
||||
- fix: update m.queuedBytes when clamping output to avoid corrupting sentBytes
|
||||
- fix: use int64 in ReadTemperature to avoid overflow
|
||||
- fix(rp2): disable DBGPAUSE on startup
|
||||
- fix(rp2): possible integer overflow while computing factors for SPI baudrate
|
||||
- fix(rp2): reset spinlocks at startup
|
||||
- fix(rp2): switch spinlock busy loop to wfe
|
||||
- fix(rp2): use side-effect-free spinlocks
|
||||
- nrf: add ADC_VDDH which is an ADC pin for VDDH
|
||||
- nrf: don't block SPI transfer
|
||||
- nrf: don't set PSELN, it's ignored in single ended mode anyway
|
||||
- nrf: fix typo in ADC configuration
|
||||
- nrf: refactor SoftDevice enabled check
|
||||
- nrf: rename pwmPin to adcPin
|
||||
- nrf: support flash operations while the SoftDevice is enabled
|
||||
- rp2040: allow writing to the UART inside interrupts
|
||||
- machine,nrf528: stop the bus only once on I2C bus error and ensures the first error is returned
|
||||
* **net**
|
||||
- update submodule to latest commits
|
||||
* **runtime**
|
||||
- (avr): fix infinite longjmp loop if stack is aligned to 256 bytes
|
||||
- (gc_blocks.go): clear full size of allocation
|
||||
- (gc_blocks.go): make sweep branchless
|
||||
- (gc_blocks.go): simplify scanning logic
|
||||
- (gc_blocks.go): use a linked stack to scan marked objects
|
||||
- (gc_blocks.go): use best-fit allocation
|
||||
- (gc_boehm.go): fix world already stopped check
|
||||
- (wasm): scan the system stack
|
||||
- fix sleep duration for long sleeps
|
||||
- remove copied code for nrf52840
|
||||
- src/syscall: update src buffer after write
|
||||
- wasm: fix C realloc and optimize it a bit
|
||||
* **targets**
|
||||
- add xiao-esp32s3 board target
|
||||
- Add ESP32-S3 support (#5091)
|
||||
- Added Gopher ARCADE board
|
||||
- Create "pico2-ice" target board (#5062)
|
||||
* **build/test**
|
||||
- Add testing.T.Context() and testing.B.Context()
|
||||
- create separate go.mod file for testing dependencies for wasm tests that use Chromium headless browser to avoid use of older incompatible version.
|
||||
- go back to normal scheduler instead of tasks scheduler for macos CI
|
||||
- update CI to use Go 1.25.5
|
||||
- update macOS GH actions builds to handle sunset of macOS 13
|
||||
- use task scheduler on macOS builds to avoid test race condition lockups
|
||||
- update all CI builds to use latest stable Go release. Also update some of the actions to their latest releases.
|
||||
- update GH actions builds to use Go 1.25.4
|
||||
- uninstall cmake before install
|
||||
- fix: point the submodule for musl-lib to a mirror in the TinyGo GitHub org
|
||||
- fix: remove macOS 15 from CI build matrix (conflicts with macOS 14 build)
|
||||
- fix: separate host expected bytes from device intended bytes
|
||||
- fix/typo: makeESPFirmwareImage
|
||||
- make: GNUmakefile: shrink TinyGo binaries on Linux
|
||||
- move the directory list into a variable
|
||||
- several improvements to the macOS GH actions build
|
||||
- Fix for #4678: top-level 'make lint' wasn't working
|
||||
- fix: increase the timeout for chromedp to connect to the headless browser used for running the wasm tests.
|
||||
- testdata: some more packages for the test corpus
|
||||
|
||||
0.39.0
|
||||
---
|
||||
* **general**
|
||||
- all: add Go 1.25 support
|
||||
- net: update to latest tinygo net package
|
||||
- docs: clarify build verification step for macOS users
|
||||
- Add flag to skip Renesas SVD builds
|
||||
* **build**
|
||||
- Makefile: install missing dlmalloc files
|
||||
- flash: add -o flag support to save built binary (Fixes #4937) (#4942)
|
||||
- fix: update version of clang to 17 to accommodate latest Go 1.25 docker base image
|
||||
* **ci**
|
||||
- chore: update all CI builds to test Go 1.25 release
|
||||
- fix: disable test-newest since CircleCI seems unable to download due to rate-limits on Dockerhub
|
||||
- ci: rename some jobs to avoid churn on every Go/LLVM version bump
|
||||
- ci: make the goroutines test less racy
|
||||
- tests: de-flake goroutines test
|
||||
* **compiler**
|
||||
- compiler: implement internal/abi.Escape
|
||||
* **main**
|
||||
- main: show the compiler error (if any) for `tinygo test -c`
|
||||
- chore: correct GOOS=js name in error messages for WASM
|
||||
* **machine**
|
||||
- machine: add international keys
|
||||
- machine: remove some unnecessary "// peripherals:" comments
|
||||
- machine: add I2C pin comments
|
||||
- machine: standardize I2C errors with "i2c:" prefix
|
||||
- machine: make I2C usable in the simulator
|
||||
- fix: add SPI and I2C to teensy 4.1 (#4943)
|
||||
- `rp2`: use the correct channel mask for rp2350 ADC; hold lock during read (#4938)
|
||||
- `rp2`: disable digital input for analog inputs
|
||||
* **runtime**
|
||||
- runtime: ensure time.Sleep(d) sleeps at least d
|
||||
- runtime: stub out weak pointer support
|
||||
- runtime: implement dummy AddCleanup
|
||||
- runtime: enable multi-core scheduler for rp2350
|
||||
- `internal/task`: use -stack-size flag when starting a new thread
|
||||
- `internal/task`: add SA_RESTART flag to GC interrupts
|
||||
- `internal/task`: a few small correctness fixes
|
||||
- `internal/gclayout`: make gclayout values constants
|
||||
- darwin: add threading support and use it by default
|
||||
* **standard library**
|
||||
- `sync`: implement sync.Swap
|
||||
- `reflect`: implement Method.IsExported
|
||||
* **testing**
|
||||
- testing: stub out testing.B.Loop
|
||||
* **targets**
|
||||
- `stm32`: add support for the STM32L031G6U6
|
||||
- add metro-rp2350 board definition (#4989)
|
||||
- `rp2040/rp2350`: set the default stack size to 8k for rp2040/rp2350 based boards where this was not already the case
|
||||
|
||||
|
||||
0.38.0
|
||||
---
|
||||
* **general**
|
||||
- `go.*`: upgrade `golang.org/x/tools` to v0.30.0
|
||||
- `all`: add support for LLVM 20
|
||||
* **build**
|
||||
- go back to using MinoruSekine/setup-scoop for Windows CI builds
|
||||
- `flake.*`: upgrade to nixpkgs 25.05, LLVM 20
|
||||
- `Makefile`: only detect ccache command when needed
|
||||
- `Makefile`: create random filename inside rule
|
||||
- `Makefile`: don't set GOROOT
|
||||
- `Makefile`: call uname at most once
|
||||
- `Makefile`: only read NodeJS version when it is needed
|
||||
* **compiler**
|
||||
- add support for `GODEBUG=gotypesalias=1`
|
||||
- `interp`: fix `copy()` from/to external buffers
|
||||
- add `-nobounds` (similar to `-gcflags=-B`)
|
||||
- `compileopts`: add library version to cached library path
|
||||
- `builder`: build wasi-libc inside TinyGo
|
||||
- `builder`: simplify bdwgc libc dependency
|
||||
- `builder`: don't use precompiled libraries
|
||||
- `compileopts`: enable support for `GOARCH=wasm` in `tinygo test`
|
||||
* **fixes**
|
||||
- `rp2350`: Fix DMA to SPI transmits on RP2350 (#4903)
|
||||
- `microbit v2`: use OpenOCD flash method on microbit v2 when using Nordic Semi SoftDevice
|
||||
- `main`: display all of the current GC options for the `-gc` flag
|
||||
- Remove duplicated error handling
|
||||
- `sync`: fix `TestMutexConcurrent` test
|
||||
- fix race condition in `testdata/goroutines.go`
|
||||
- fix build warnings on Windows ARM
|
||||
* **machine**
|
||||
- `usb`: add USB mass storage class support
|
||||
- implement usb receive message throttling
|
||||
- declare usb endpoints per-platform
|
||||
- `samd21`: implement watchdog
|
||||
- `samd51`: write to flash memory in 512 byte long chunks
|
||||
- `samd21`: write to flash memory in 64 byte long chunks
|
||||
- don't inline RTT `WriteByte` everywhere
|
||||
- `rp2`: unexport machine-specific errors
|
||||
- `rp2`: discount scheduling delays in I2C timeouts (#4876)
|
||||
- use pointer receiver in simulated PWM peripherals
|
||||
- add simulated PWM/timer peripherals
|
||||
- `rp2`: expose usb endpoint stall handling
|
||||
- `arm`: clear pending interrupts before enabling them
|
||||
- `rp2`: merge common usb code (#4856)
|
||||
* **main**
|
||||
- add "cores" and "threads" schedulers to help text
|
||||
- add `StartPos` and `EndPos` to `-json` build output
|
||||
- change `-json` flag to match upstream Go
|
||||
* **runtime**
|
||||
- don't lock the print output inside interrupts
|
||||
- don't try to interrupt other cores before they are started
|
||||
- implement `NumCPU` for the multicore scheduler
|
||||
- add support for multicore scheduler
|
||||
- refactor obtaining the system stack
|
||||
- `interrupt`: add `Checkpoint` type
|
||||
- add `exportedFuncPtr`
|
||||
- avoid an allocation in `(*time.Timer).Reset`
|
||||
- stub runtime signal functions for `os/signal` on wasip1
|
||||
- move `timeUnit` to a single place
|
||||
- implement `NumCPU` for `-scheduler=threads`
|
||||
- move `mainExited` boolean
|
||||
- `internal/task`: rename `tinygo_pause` to `tinygo_task_exit`
|
||||
- map every goroutine to a new OS thread
|
||||
- refactor `timerQueue`
|
||||
- make conservative and precise GC MT-safe
|
||||
- `internal/task`: implement atomic primitives for preemptive scheduling
|
||||
- Use diskutil on macOS to extract volume name and path for FAT mounts #4928
|
||||
* **standard library**
|
||||
- `net`: update submodule to latest commits
|
||||
- `runtime/debug`: add GC related stubs
|
||||
- `metrics`: flesh out some of the metric types
|
||||
- `reflect`: Chan related stubs
|
||||
- `os`: handle relative and abs paths in `Executable()`
|
||||
- `os`: add `os.Executable()` for Darwin
|
||||
- `sync`: implement `RWMutex` using futexes
|
||||
- `reflect`: Add `SliceOf`, `ArrayOf`, `StructOf`, `MapOf`, `FuncOf`
|
||||
* **targets**
|
||||
- `rp2040`: add multicore support
|
||||
- `riscv32`: use `gdb` binary as a fallback
|
||||
- add target for Microbit v2 with SoftDevice S140 support for both peripheral and central
|
||||
- `windows`: use MSVCRT.DLL instead of UCRT on i386
|
||||
- `windows`: add windows/386 support
|
||||
- `arm64`: remove unnecessary `.section` directive
|
||||
- `riscv-qemu`: actually sleep in `time.Sleep()`
|
||||
- `riscv`: define CSR constants and use them where possible
|
||||
- `darwin`: support Boehm GC (and use by default)
|
||||
- `windows`: add support for the Boehm-Demers-Weiser GC
|
||||
- `windows`: fix wrong register for first parameter
|
||||
* **wasm**
|
||||
- add Boehm GC support
|
||||
- refactor/modify stub signal handling
|
||||
- don't block `//go:wasmexport` because of running goroutines
|
||||
- use `int64` instead of `float64` for the `timeUnit`
|
||||
* **boards**
|
||||
- Add board support for BigTreeTech SKR Pico (#4842)
|
||||
|
||||
|
||||
0.37.0
|
||||
---
|
||||
* **general**
|
||||
- add the Boehm-Demers-Weiser GC on Linux
|
||||
* **ci**
|
||||
- add more tests for wasm and baremetal
|
||||
* **compiler**
|
||||
- crypto/internal/sysrand is allowed to use unsafe signatures
|
||||
* **examples**
|
||||
- add goroutine benchmark to examples
|
||||
* **fixes**
|
||||
- ensure use of pointers for SPI interface on atsam21/atsam51 and other machines/boards that were missing implementation (#4798)
|
||||
- replace loop counter with hw timer for USB SetAddressReq on rp2040 (#4796)
|
||||
* **internal**
|
||||
- update to go.bytecodealliance.org@v0.6.2 in GNUmakefile and internal/wasm-tools
|
||||
- exclude certain files when copying package in internal/cm
|
||||
- update to go.bytecodealliance.org/cm@v0.2.2 in internal/cm
|
||||
- remove old reflect.go in internal/reflectlite
|
||||
* **loader**
|
||||
- use build tags for package iter and iter methods on reflect.Value in loader, iter, reflect
|
||||
- add shim for go1.22 and earlier in loader, iter
|
||||
* **machine**
|
||||
- bump rp2040 to 200MHz (#4768)
|
||||
- correct register address for Pin.SetInterrupt for rp2350 (#4782)
|
||||
- don't block the rp2xxx UART interrupt handler
|
||||
- fix RP2040 Pico board on the playground
|
||||
- add flash support for rp2350 (#4803)
|
||||
* **os**
|
||||
- add stub Symlink for wasm
|
||||
* **refactor**
|
||||
- use *SPI everywhere to make consistent for implementations. Fixes #4663 "in reverse" by making SPI a pointer everywhere, as discussed in the comments.
|
||||
* **reflect**
|
||||
- add Value.SetIter{Key,Value} and MapIter.Reset in reflect, internal/reflectlite
|
||||
- embed reflectlite types into reflect types in reflect, internal/reflectlite
|
||||
- add Go 1.24 iter.Seq[2] methods
|
||||
- copy reflect iter tests from upstream Go
|
||||
- panic on Type.CanSeq[2] instead of returning false
|
||||
- remove strconv.go
|
||||
- remove unused go:linkname functions
|
||||
* **riscv-qemu**
|
||||
- add VirtIO RNG device
|
||||
- increase stack size
|
||||
* **runtime**
|
||||
- only allocate heap memory when needed
|
||||
- remove unused file func.go
|
||||
- use package reflectlite
|
||||
* **transform**
|
||||
- cherry-pick from #4774
|
||||
|
||||
|
||||
0.36.0
|
||||
---
|
||||
* **general**
|
||||
- add initial Go 1.24 support
|
||||
- add support for LLVM 19
|
||||
- update license for 2025
|
||||
- make small corrections for README regarding wasm
|
||||
- use GOOS and GOARCH for building wasm simulated boards
|
||||
- only infer target for wasm when GOOS and GOARCH are set correctly, not just based on file extension
|
||||
- add test-corpus-wasip2
|
||||
- use older image for cross-compiling builds
|
||||
- update Linux builds to run on ubuntu-latest since 20.04 is being retired
|
||||
- ensure build output directory is created
|
||||
- add NoSandbox flag to chrome headless that is run during WASM tests, since this is now required for Ubuntu 23+ and we are using Ubuntu 24+ when running Github Actions
|
||||
- update wasmtime used for CI to 29.0.1 to fix issue with install during CI tests
|
||||
- update to use `Get-CimInstance` as `wmic` is being deprecated on WIndows
|
||||
- remove unnecessary executable permissions
|
||||
- `goenv`: update to new v0.36.0 development version
|
||||
* **compiler**
|
||||
- `builder`: fix parsing of external ld.lld error messages
|
||||
- `cgo`: mangle identifier names
|
||||
- `interp`: correctly mark functions as modifying memory
|
||||
- add buildmode=wasi-legacy to support existing base of users who expected the older behavior for wasi modules to not return an exit code as if they were reactors
|
||||
* **standard library**
|
||||
- `crypto/tls`: add Dialer.DialContext() to fix websocket client
|
||||
- `crypto/tls`: add VersionTLS constants and VersionName(version uint16) method that turns it into a string, copied from big go
|
||||
- `internal/syscall/unix`: use our own version of this package
|
||||
- `machine`: replace hard-coded cpu frequencies on rp2xxx
|
||||
- `machine`: bump rp2350 CPUFrequency to 150 MHz
|
||||
- `machine`: compute rp2 clock dividers from crystal and target frequency
|
||||
- `machine`: remove bytes package dependency in flash code
|
||||
- `machine/usb/descriptor`: avoid bytes package
|
||||
- `net`: update to latest submodule with httptest subpackage and ResolveIPAddress implementation
|
||||
- `os`: add File.Chdir support
|
||||
- `os`: implement stub Chdir for non-OS systems
|
||||
- `os/file`: add file.Chmod
|
||||
- `reflect`: implement Value.Equal
|
||||
- `runtime`: add FIPS helper functions
|
||||
- `runtime`: manually initialize xorshift state
|
||||
- `sync`: move Mutex to internal/task
|
||||
- `syscall`: add wasip1 RandomGet
|
||||
- `testing`: add Chdir
|
||||
- `wasip2`: add stubs to get internal/syscall/unix to work
|
||||
* **fixes**
|
||||
- correctly handle calls for GetRNG() when being made from nrf devices with SoftDevice enabled
|
||||
- fix stm32f103 ADC
|
||||
- `wasm`: correctly handle id lookup for finalizeRef call
|
||||
- `wasm`: avoid total failure on wasm finalizer call
|
||||
- `wasm`: convert offset as signed int into unsigned int in syscall/js.stringVal in wasm_exec.js
|
||||
* **targets**
|
||||
- rp2350: add pll generalized solution; fix ADC handles; pwm period fix
|
||||
- rp2350: extending support to include the rp2350b
|
||||
- rp2350: cleanup: unexport internal USB and clock package variable, consts and types
|
||||
- nrf: make ADC resolution changeable
|
||||
- turn on GC for TKey1 device, since it does in fact work
|
||||
- match Pico2 stack size to Pico
|
||||
* **boards**
|
||||
- add support for Pimoroni Pico Plus2
|
||||
- add target for pico2-w board
|
||||
- add comboat_fw tag for elecrow W5 boards with Combo-AT Wifi firmware
|
||||
- add support for Elecrow Pico rp2350 W5 boards
|
||||
- add support for Elecrow Pico rp2040 W5 boards
|
||||
- add support for NRF51 HW-651
|
||||
- add support for esp32c3-supermini
|
||||
- add support for waveshare-rp2040-tiny
|
||||
* **examples**
|
||||
- add naive debouncing for pininterrupt example
|
||||
|
||||
|
||||
0.35.0
|
||||
---
|
||||
* **general**
|
||||
- update cmsis-svd library
|
||||
- use default UART settings in the echo example
|
||||
- `goenv`: also show git hash with custom build of TinyGo
|
||||
- `goenv`: support parsing development versions of Go
|
||||
- `main`: parse extldflags early so we can report the error message
|
||||
* **compiler**
|
||||
- `builder`: whitelist temporary directory env var for Clang invocation to fix Windows bug
|
||||
- `builder`: fix cache paths in `-size=full` output
|
||||
- `builder`: work around incorrectly escaped DWARF paths on Windows (Clang bug)
|
||||
- `builder`: fix wasi-libc path names on Windows with `-size=full`
|
||||
- `builder`: write HTML size report
|
||||
- `cgo`: support C identifiers only referred to from within macros
|
||||
- `cgo`: support function-like macros
|
||||
- `cgo`: support errno value as second return parameter
|
||||
- `cgo`: add support for `#cgo noescape` lines
|
||||
- `compiler`: fix bug in interrupt lowering
|
||||
- `compiler`: allow panic directly in `defer`
|
||||
- `compiler`: fix wasmimport -> wasmexport in error message
|
||||
- `compiler`: support `//go:noescape` pragma
|
||||
- `compiler`: report error instead of crashing when instantiating a generic function without body
|
||||
- `interp`: align created globals
|
||||
* **standard library**
|
||||
- `machine`: modify i2s interface/implementation to better match specification
|
||||
- `os`: implement `StartProcess`
|
||||
- `reflect`: add `Value.Clear`
|
||||
- `reflect`: add interface support to `NumMethods`
|
||||
- `reflect`: fix `AssignableTo` for named + non-named types
|
||||
- `reflect`: implement `CanConvert`
|
||||
- `reflect`: handle more cases in `Convert`
|
||||
- `reflect`: fix Copy of non-pointer array with size > 64bits
|
||||
- `runtime`: don't call sleepTicks with a negative duration
|
||||
- `runtime`: optimize GC scanning (findHead)
|
||||
- `runtime`: move constants into shared package
|
||||
- `runtime`: add `runtime.fcntl` function for internal/syscall/unix
|
||||
- `runtime`: heapptr only needs to be initialized once
|
||||
- `runtime`: refactor scheduler (this fixes a few bugs with `-scheduler=none`)
|
||||
- `runtime`: rewrite channel implementation to be smaller and more flexible
|
||||
- `runtime`: use `SA_RESTART` when registering a signal for os/signal
|
||||
- `runtime`: implement race-free signals using futexes
|
||||
- `runtime`: run deferred functions in `Goexit`
|
||||
- `runtime`: remove `Cond` which seems to be unused
|
||||
- `runtime`: properly handle unix read on directory
|
||||
- `runtime/trace`: stub all public methods
|
||||
- `sync`: don't use volatile in `Mutex`
|
||||
- `sync`: implement `WaitGroup` using a (pseudo)futex
|
||||
- `sync`: make `Cond` parallelism-safe
|
||||
- `syscall`: use wasi-libc tables for wasm/js target
|
||||
* **targets**
|
||||
- `mips`: fix a bug when scanning the stack
|
||||
- `nintendoswitch`: get this target to compile again
|
||||
- `rp2350`: add support for the new RP2350
|
||||
- `rp2040/rp2350` : make I2C implementation shared for rp2040/rp2350
|
||||
- `rp2040/rp2350` : make SPI implementation shared for rp2040/rp2350
|
||||
- `rp2040/rp2350` : make RNG implementation shared for rp2040/rp2350
|
||||
- `wasm`: revise and simplify wasmtime argument handling
|
||||
- `wasm`: support `//go:wasmexport` functions after a call to `time.Sleep`
|
||||
- `wasm`: correctly return from run() in wasm_exec.js
|
||||
- `wasm`: call process.exit() when go.run() returns
|
||||
- `windows`: don't return, exit via exit(0) instead to flush stdout buffer
|
||||
* **boards**
|
||||
- add support for the Tillitis TKey
|
||||
- add support for the Raspberry Pi Pico2 (based on the RP2040)
|
||||
- add support for Pimoroni Tiny2350
|
||||
|
||||
|
||||
0.34.0
|
||||
---
|
||||
* **general**
|
||||
- fix `GOOS=wasip1` for `tinygo test`
|
||||
- add `-C DIR` flag
|
||||
- add initial documentation for project governance
|
||||
- add `-ldflags='-extldflags=...'` support
|
||||
- improve usage message with `tinygo help` and when passing invalid parameters
|
||||
* **compiler**
|
||||
- `builder`: remove environment variables when invoking Clang, to avoid the environment changing the behavior
|
||||
- `builder`: check for the Go toolchain version used to compile TinyGo
|
||||
- `cgo`: add `C.CBytes` implementation
|
||||
- `compiler`: fix passing weirdly-padded structs as parameters to new goroutines
|
||||
- `compiler`: support pragmas on generic functions
|
||||
- `compiler`: do not let the slice buffer escape when casting a `[]byte` or `[]rune` to a string, to help escape analysis
|
||||
- `compiler`: conform to the latest iteration of the wasm types proposal
|
||||
- `loader`: don't panic when main package is not named 'main'
|
||||
- `loader`: make sure we always return type checker errors even without type errors
|
||||
- `transform`: optimize range over `[]byte(string)`
|
||||
* **standard library**
|
||||
- `crypto/x509`: add package stub to build crypto/x509 on macOS
|
||||
- `machine/usb/adc/midi`: fix `PitchBend`
|
||||
- `os`: add `Truncate` stub for baremetal
|
||||
- `os`: add stubs for `os.File` deadlines
|
||||
- `os`: add internal `net.newUnixFile` for the net package
|
||||
- `runtime`: stub runtime_{Before,After}Exec for linkage
|
||||
- `runtime`: randomize map accesses
|
||||
- `runtime`: support `maps.Clone`
|
||||
- `runtime`: add more fields to `MemStats`
|
||||
- `runtime`: implement newcoro, coroswitch to support package iter
|
||||
- `runtime`: disallow defer in interrupts
|
||||
- `runtime`: add support for os/signal on Linux and MacOS
|
||||
- `runtime`: add gc layout info for some basic types to help the precise GC
|
||||
- `runtime`: bump GC mark stack size to avoid excessive heap rescans
|
||||
* **targets**
|
||||
- `darwin`: use Go standard library syscall package instead of a custom one
|
||||
- `fe310`: support GPIO `PinInput`
|
||||
- `mips`: fix compiler crash with GOMIPS=softfloat and defer
|
||||
- `mips`: add big-endian (GOARCH=mips) support
|
||||
- `mips`: use MIPS32 (instead of MIPS32R2) as the instruction set for wider compatibility
|
||||
- `wasi`: add relative and absolute --dir options to wasmtime args
|
||||
- `wasip2`: add wasmtime -S args to support network interfaces
|
||||
- `wasm`: add `//go:wasmexport` support (for all WebAssembly targets)
|
||||
- `wasm`: use precise instead of conservative GC for WebAssembly (including WASI)
|
||||
- `wasm-unknown`: add bulk memory flags since basically every runtime has it now
|
||||
* **boards**
|
||||
- add RAKwireless RAK4631
|
||||
- add WaveShare ESP-C3-32S-Kit
|
||||
|
||||
|
||||
0.33.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- use latest version of x/tools
|
||||
- add chromeos 9p support for flashing
|
||||
- sort compiler error messages by source position in a package
|
||||
- don't include prebuilt libraries in the release to simplify packaging and reduce the release tarball size
|
||||
- show runtime panic addresses for `tinygo run`
|
||||
- support Go 1.23 (including all new language features)
|
||||
- `test`: support GOOS/GOARCH pairs in the `-target` flag
|
||||
- `test`: remove message after test binary built
|
||||
* **compiler**
|
||||
- remove unused registers for x86_64 linux syscalls
|
||||
- remove old atomics workaround for AVR (not necessary in modern LLVM versions)
|
||||
- support `golang.org/x/sys/unix` syscalls
|
||||
- `builder`: remove workaround for generics race condition
|
||||
- `builder`: add package ID to compiler and optimization error messages
|
||||
- `builder`: show better error messages for some common linker errors
|
||||
- `cgo`: support preprocessor macros passed on the command line
|
||||
- `cgo`: use absolute paths for error messages
|
||||
- `cgo`: add support for printf
|
||||
- `loader`: handle `go list` errors inside TinyGo (for better error messages)
|
||||
- `transform`: fix incorrect alignment of heap-to-stack transform
|
||||
- `transform`: use thinlto-pre-link passes (instead of the full pipeline) to speed up compilation speed slightly
|
||||
* **standard library**
|
||||
- `crypto/tls`: add CipherSuiteName and some extra fields to ConnectionSTate
|
||||
- `internal/abi`: implement initial version of this package
|
||||
- `machine`: use new `internal/binary` package
|
||||
- `machine`: rewrite Reply() to fix sending long replies in I2C Target Mode
|
||||
- `machine/usb/descriptor`: Reset joystick physical
|
||||
- `machine/usb/descriptor`: Drop second joystick hat
|
||||
- `machine/usb/descriptor`: Add more HID... functions
|
||||
- `machine/usb/descriptor`: Fix encoding of values
|
||||
- `machine/usb/hid/joystick`: Allow more hat switches
|
||||
- `os`: add `Chown`, `Truncate`
|
||||
- `os/user`: use stdlib version of this package
|
||||
- `reflect`: return correct name for the `unsafe.Pointer` type
|
||||
- `reflect`: implement `Type.Overflow*` functions
|
||||
- `runtime`: implement dummy `getAuxv` to satisfy golang.org/x/sys/
|
||||
- `runtime`: don't zero out new allocations for `-gc=leaking` when they are already zeroed
|
||||
- `runtime`: simplify slice growing/appending code
|
||||
- `runtime`: print a message when a fatal signal like SIGSEGV happens
|
||||
- `runtime/debug`: add `GoVersion` to `debug.BuildInfo`
|
||||
- `sync`: add `Map.Clear()`
|
||||
- `sync/atomic`: add And* and Or* compiler intrinsics needed for Go 1.23
|
||||
- `syscall`: add `Fork` and `Execve`
|
||||
- `syscall`: add all MacOS errno values
|
||||
- `testing`: stub out `T.Deadline`
|
||||
- `unique`: implement custom (naive) version of the unique package
|
||||
* **targets**
|
||||
- `arm`: support `GOARM=*,softfloat` (softfloat support for ARM v5, v6, and v7)
|
||||
- `mips`: add linux/mipsle (and experimental linux/mips) support
|
||||
- `mips`: add `GOMIPS=softfloat` support
|
||||
- `wasip2`: add WASI preview 2 support
|
||||
- `wasm/js`: add `node:` prefix in `require()` call of wasm_exec.js
|
||||
- `wasm-unknown`: make sure the `os` package can be imported
|
||||
- `wasm-unknown`: remove import-memory flag
|
||||
|
||||
|
||||
0.32.0
|
||||
---
|
||||
|
||||
@@ -1896,7 +2601,7 @@
|
||||
- allow packages like github.com/tinygo-org/tinygo/src/\* by aliasing it
|
||||
- remove `//go:volatile` support
|
||||
It has been replaced with the runtime/volatile package.
|
||||
- allow poiners in map keys
|
||||
- allow pointers in map keys
|
||||
- support non-constant syscall numbers
|
||||
- implement non-blocking selects
|
||||
- add support for the `-tags` flag
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.22 AS tinygo-llvm
|
||||
FROM golang:1.27rc2 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-15 ninja-build && \
|
||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||
rm -rf \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/log/* \
|
||||
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
|
||||
|
||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||
# all the build tools etc).
|
||||
FROM golang:1.22 AS tinygo-compiler
|
||||
FROM golang:1.27rc2 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
+389
-96
@@ -8,13 +8,20 @@ LLVM_PROJECTDIR ?= llvm-project
|
||||
CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
|
||||
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# avoid calling uname on Windows
|
||||
uname := Windows_NT
|
||||
else
|
||||
uname := $(shell uname -s)
|
||||
endif
|
||||
|
||||
# Try to autodetect LLVM build tools.
|
||||
# Versions are listed here in descending priority order.
|
||||
LLVM_VERSIONS = 18 17 16 15
|
||||
LLVM_VERSIONS = 19 18 17 16 15
|
||||
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)
|
||||
ifeq ($(uname),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)
|
||||
@@ -27,7 +34,6 @@ 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 ?=
|
||||
@@ -38,14 +44,10 @@ TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||
|
||||
# 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
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(if $(shell command -v ccache 2> /dev/null),ON,OFF)'
|
||||
else
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||
endif
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||
|
||||
# Allow enabling LLVM assertions
|
||||
ifeq (1, $(ASSERT))
|
||||
@@ -77,6 +79,26 @@ ifeq (1, $(STATIC))
|
||||
BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static"
|
||||
endif
|
||||
|
||||
# Optimize the binary size for Linux.
|
||||
# These flags may work on other platforms, but have only been tested on Linux.
|
||||
ifeq ($(uname),Linux)
|
||||
HAS_MOLD := $(shell command -v ld.mold 2> /dev/null)
|
||||
HAS_LLD := $(shell command -v ld.lld 2> /dev/null)
|
||||
LLVM_CFLAGS := -ffunction-sections -fdata-sections -fvisibility=hidden
|
||||
LLVM_LDFLAGS := -Wl,--gc-sections
|
||||
ifneq ($(HAS_MOLD),)
|
||||
# Mold might be slightly faster.
|
||||
LLVM_LDFLAGS += -fuse-ld=mold -Wl,--icf=all
|
||||
else ifneq ($(HAS_LLD),)
|
||||
# LLD is more commonly available.
|
||||
LLVM_LDFLAGS += -fuse-ld=lld -Wl,--icf=all
|
||||
endif
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_C_FLAGS="$(LLVM_CFLAGS)" \
|
||||
-DCMAKE_CXX_FLAGS="$(LLVM_CFLAGS)"
|
||||
CGO_LDFLAGS += $(LLVM_LDFLAGS)
|
||||
endif
|
||||
|
||||
# Cross compiling support.
|
||||
ifneq ($(CROSS),)
|
||||
CC = $(CROSS)-gcc
|
||||
@@ -111,7 +133,7 @@ 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 frontenddriver frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb dtlto executionengine frontenddriver frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE = .exe
|
||||
@@ -120,6 +142,9 @@ ifeq ($(OS),Windows_NT)
|
||||
|
||||
# PIC needs to be disabled for libclang to work.
|
||||
LLVM_OPTION += -DLLVM_ENABLE_PIC=OFF
|
||||
# Statically link the C++ and GCC runtime into LLVM tools so they don't
|
||||
# depend on MinGW DLLs that may not be on PATH when executed during the build.
|
||||
LLVM_OPTION += '-DCMAKE_EXE_LINKER_FLAGS=-static-libgcc -static-libstdc++'
|
||||
|
||||
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
@@ -127,14 +152,14 @@ ifeq ($(OS),Windows_NT)
|
||||
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
else ifeq ($(uname),Darwin)
|
||||
MD5SUM ?= md5
|
||||
|
||||
CGO_LDFLAGS += -lxar
|
||||
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
else ifeq ($(uname),FreeBSD)
|
||||
MD5SUM ?= md5
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
@@ -147,7 +172,7 @@ endif
|
||||
MD5SUM ?= md5sum
|
||||
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAPINotes 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_LIB_NAMES = clangAnalysis clangAnalysisLifetimeSafety clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangInstallAPI clangLex clangOptions 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.
|
||||
@@ -155,7 +180,7 @@ 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 LLVMMCA LLVMRISCVTargetMCA LLVMX86TargetMCA
|
||||
EXTRA_LIB_NAMES = LLVMDTLTO LLVMInterpreter LLVMMCA LLVMRISCVTargetMCA 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)
|
||||
@@ -172,6 +197,12 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm lld $(addprefix lib/lib,
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
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++17
|
||||
ifneq ($(uname),Windows_NT)
|
||||
# Disable GCC DWARF compression: lld built without zlib cannot link
|
||||
# object files with ELFCOMPRESS_ZLIB debug sections.
|
||||
CGO_CFLAGS+=-gz=none
|
||||
CGO_CXXFLAGS+=-gz=none
|
||||
endif
|
||||
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
|
||||
|
||||
@@ -186,6 +217,9 @@ fmt-check: ## Warn if any source needs reformatting
|
||||
|
||||
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp ## Generate microcontroller-specific sources
|
||||
ifneq ($(RENESAS), 0)
|
||||
gen-device: gen-device-renesas
|
||||
endif
|
||||
ifneq ($(STM32), 0)
|
||||
gen-device: gen-device-stm32
|
||||
endif
|
||||
@@ -234,11 +268,11 @@ gen-device-rp: build/gen-device-svd
|
||||
GO111MODULE=off $(GO) fmt ./src/device/rp
|
||||
|
||||
gen-device-renesas: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/tinygo-org/renesas-svd lib/renesas-svd/ src/device/renesas/
|
||||
./build/gen-device-svd -source=https://github.com/cmsis-svd/cmsis-svd-data/tree/master/data/Renesas lib/cmsis-svd/data/Renesas/ src/device/renesas/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/renesas
|
||||
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b tinygo_xtensa_release_18.1.2 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
git clone -b tinygo_22.x --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm ## Get LLVM sources
|
||||
|
||||
# Configure LLVM.
|
||||
@@ -259,41 +293,35 @@ build/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 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC="$(CLANG)" AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||
|
||||
# Generate WASI syscall bindings
|
||||
WASM_TOOLS_MODULE=github.com/ydnar/wasm-tools-go
|
||||
WASM_TOOLS_MODULE=go.bytecodealliance.org
|
||||
.PHONY: wasi-syscall
|
||||
wasi-syscall: wasi-cm
|
||||
go run -modfile ./internal/wasm-tools/go.mod $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
rm -rf ./src/internal/wasi/*
|
||||
go run $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
|
||||
# Copy package cm into src/internal/cm
|
||||
.PHONY: wasi-cm
|
||||
wasi-cm:
|
||||
# rm -rf ./src/internal/cm
|
||||
rsync -rv --delete --exclude '*_test.go' $(shell go list -modfile ./internal/wasm-tools/go.mod -m -f {{.Dir}} $(WASM_TOOLS_MODULE))/cm ./src/internal/
|
||||
rm -rf ./src/internal/cm/*
|
||||
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||
MIN_NODEJS_VERSION=18
|
||||
MIN_NODEJS_VERSION=22
|
||||
|
||||
.PHONY: check-nodejs-version
|
||||
check-nodejs-version:
|
||||
ifeq (, $(shell which node))
|
||||
@echo "Install NodeJS version 18+ to run tests."; exit 1;
|
||||
endif
|
||||
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 18+ to run tests."; exit 1; fi
|
||||
@# Check whether NodeJS is available.
|
||||
@if ! command -v node 2>&1 >/dev/null; then echo "Install NodeJS version ${MIN_NODEJS_VERSION}+ to run tests."; exit 1; fi
|
||||
|
||||
@# Check whether the version is high enough.
|
||||
@if [ "`node -v | sed 's/v\([0-9]\+\).*/\\1/g'`" -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version $(MIN_NODEJS_VERSION)+ to run tests."; exit 1; fi
|
||||
|
||||
tinygo: ## Build the TinyGo compiler
|
||||
@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)" $(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 check-nodejs-version
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm llvm22 osusergo" .
|
||||
test: check-nodejs-version
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22 osusergo" $(GOTESTPKGS)
|
||||
|
||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||
TEST_PACKAGES_SLOW = \
|
||||
@@ -303,26 +331,35 @@ TEST_PACKAGES_SLOW = \
|
||||
|
||||
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
|
||||
TEST_PACKAGES_FAST = \
|
||||
cmp \
|
||||
compress/lzw \
|
||||
compress/zlib \
|
||||
container/heap \
|
||||
container/list \
|
||||
container/ring \
|
||||
crypto/des \
|
||||
crypto/ecdsa \
|
||||
crypto/elliptic \
|
||||
crypto/md5 \
|
||||
crypto/rc4 \
|
||||
crypto/sha1 \
|
||||
crypto/sha256 \
|
||||
crypto/sha512 \
|
||||
database/sql/driver \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
errors \
|
||||
encoding/asn1 \
|
||||
encoding/base32 \
|
||||
encoding/base64 \
|
||||
encoding/csv \
|
||||
encoding/hex \
|
||||
expvar \
|
||||
go/ast \
|
||||
go/format \
|
||||
go/scanner \
|
||||
go/token \
|
||||
go/version \
|
||||
hash \
|
||||
hash/adler32 \
|
||||
hash/crc64 \
|
||||
@@ -334,6 +371,7 @@ TEST_PACKAGES_FAST = \
|
||||
math/cmplx \
|
||||
net/http/internal/ascii \
|
||||
net/mail \
|
||||
net/url \
|
||||
os \
|
||||
path \
|
||||
reflect \
|
||||
@@ -344,40 +382,52 @@ TEST_PACKAGES_FAST = \
|
||||
unicode \
|
||||
unicode/utf16 \
|
||||
unicode/utf8 \
|
||||
unique \
|
||||
$(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
|
||||
# crypto/aes needs reflect.Type.Method(), not yet implemented
|
||||
# crypto/des fails on wasi, needs panic()/recover()
|
||||
# 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
|
||||
# encoding/xml takes a minute on linux and gives a stack overflow on wasi
|
||||
# image fails on wasi, needs panic()/recover()
|
||||
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
# mime: fails on wasi, needs panic()/recover()
|
||||
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# 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
|
||||
# net/mail: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# net/ntextproto: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# regexp/syntax: fails on wasip1, needs panic()/recover()
|
||||
# strconv: fails on wasi, needs panic()/recover()
|
||||
# text/tabwriter: fails on wasi, needs panic()/recover()
|
||||
# text/template/parse: fails on wasi, needs panic()/recover()
|
||||
# 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 \
|
||||
context \
|
||||
crypto/aes \
|
||||
crypto/des \
|
||||
crypto/ecdh \
|
||||
crypto/hmac \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
encoding/xml \
|
||||
image \
|
||||
io/ioutil \
|
||||
mime \
|
||||
mime/multipart \
|
||||
mime/quotedprintable \
|
||||
net \
|
||||
net/mail \
|
||||
net/textproto \
|
||||
os/user \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
testing/fstest \
|
||||
text/tabwriter \
|
||||
@@ -385,16 +435,58 @@ TEST_PACKAGES_LINUX := \
|
||||
|
||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||
|
||||
# os/user requires t.Skip() support
|
||||
TEST_PACKAGES_WINDOWS := \
|
||||
compress/flate \
|
||||
crypto/des \
|
||||
crypto/hmac \
|
||||
image \
|
||||
mime \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
text/tabwriter \
|
||||
text/template/parse \
|
||||
$(nil)
|
||||
|
||||
|
||||
# These packages cannot be tested on wasm, mostly because these tests assume a
|
||||
# working filesystem. This could perhaps be fixed, by supporting filesystem
|
||||
# access when running inside Node.js.
|
||||
TEST_PACKAGES_WASM = $(filter-out $(TEST_PACKAGES_NONWASM), $(TEST_PACKAGES_FAST))
|
||||
TEST_PACKAGES_NONWASM = \
|
||||
compress/lzw \
|
||||
compress/zlib \
|
||||
crypto/ecdsa \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
expvar \
|
||||
go/format \
|
||||
os \
|
||||
testing \
|
||||
$(nil)
|
||||
|
||||
# These packages cannot be tested on baremetal.
|
||||
#
|
||||
# Some reasons why the tests don't pass on baremetal:
|
||||
#
|
||||
# * No filesystem is available, so packages like compress/zlib can't be tested
|
||||
# (just like wasm).
|
||||
# * picolibc math functions apparently are less precise, the math package
|
||||
# fails on baremetal.
|
||||
TEST_PACKAGES_BAREMETAL = $(filter-out $(TEST_PACKAGES_NONBAREMETAL), $(TEST_PACKAGES_FAST))
|
||||
TEST_PACKAGES_NONBAREMETAL = \
|
||||
$(TEST_PACKAGES_NONWASM) \
|
||||
math \
|
||||
$(nil)
|
||||
|
||||
TEST_PACKAGES_FAST_WASI = $(filter-out $(TEST_PACKAGES_NOWASI), $(TEST_PACKAGES_FAST))
|
||||
TEST_PACKAGES_NOWASI = \
|
||||
crypto/ecdsa \
|
||||
$(nil)
|
||||
|
||||
# Report platforms on which each standard library package is known to pass tests
|
||||
jointmp := $(shell echo /tmp/join.$$$$)
|
||||
report-stdlib-tests-pass:
|
||||
$(eval jointmp := $(shell echo /tmp/join.$$$$))
|
||||
@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
|
||||
@@ -403,24 +495,34 @@ report-stdlib-tests-pass:
|
||||
@rm $(jointmp).*
|
||||
|
||||
# Standard library packages that pass tests quickly on the current platform
|
||||
ifeq ($(shell uname),Darwin)
|
||||
ifeq ($(uname),Darwin)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||
TEST_IOFS := true
|
||||
TEST_ENCODING_XML := true
|
||||
endif
|
||||
ifeq ($(shell uname),Linux)
|
||||
ifeq ($(uname),Linux)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||
TEST_IOFS := true
|
||||
TEST_ENCODING_XML := true
|
||||
endif
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation'
|
||||
TEST_ADDITIONAL_FLAGS ?=
|
||||
|
||||
# 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_HOST) $(TEST_PACKAGES_SLOW)
|
||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||
@# TestParseAndBytesRoundTrip/P256/Generic: needs Goexit to run defers on wasm.
|
||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml,$(TEST_PACKAGES_HOST)) $(TEST_PACKAGES_SLOW)
|
||||
ifeq ($(TEST_ENCODING_XML),true)
|
||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -stack-size=16MB encoding/xml
|
||||
endif
|
||||
@# 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.
|
||||
@@ -428,24 +530,26 @@ ifeq ($(TEST_IOFS),true)
|
||||
$(TINYGO) test -stack-size=6MB io/fs
|
||||
endif
|
||||
tinygo-test-fast:
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
||||
$(TINYGO) test $(TEST_SKIP_FLAG) $(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-wasm:
|
||||
$(TINYGO) test -target wasm $(TEST_SKIP_FLAG) $(TEST_PACKAGES_WASM)
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1:
|
||||
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1-fast:
|
||||
$(TINYGO) test -target=wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-slow:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasip2-fast:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST_WASI) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-sum-slow:
|
||||
TINYGO=$(TINYGO) \
|
||||
@@ -469,21 +573,31 @@ tinygo-bench-wasip2:
|
||||
tinygo-bench-wasip2-fast:
|
||||
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST)
|
||||
|
||||
# Run tests on riscv-qemu since that one provides a large amount of memory.
|
||||
tinygo-test-baremetal:
|
||||
$(TINYGO) test -target riscv-qemu $(TEST_SKIP_FLAG) $(TEST_PACKAGES_BAREMETAL)
|
||||
|
||||
# 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
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -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=wasip1
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
||||
test-corpus-wasi:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip1
|
||||
test-corpus-wasip2:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm llvm22" -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip2
|
||||
|
||||
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: testchdir
|
||||
testchdir:
|
||||
# test 'build' command with{,out} -C argument
|
||||
$(TINYGO) build -C tests/testing/chdir chdir.go && rm tests/testing/chdir/chdir
|
||||
$(TINYGO) build ./tests/testing/chdir/chdir.go && rm chdir
|
||||
# test 'run' command with{,out} -C argument
|
||||
EXPECT_DIR=$(PWD)/tests/testing/chdir $(TINYGO) run -C tests/testing/chdir chdir.go
|
||||
EXPECT_DIR=$(PWD) $(TINYGO) run ./tests/testing/chdir/chdir.go
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
smoketest: testchdir
|
||||
$(TINYGO) version
|
||||
$(TINYGO) targets > /dev/null
|
||||
# regression test for #2892
|
||||
@@ -493,6 +607,8 @@ smoketest:
|
||||
# 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=pga2350 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
|
||||
@@ -539,23 +655,31 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.efi -target=uefi-amd64 examples/test
|
||||
@$(MD5SUM) test.efi
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino_uno examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
|
||||
GOOS=js GOARCH=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
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=reelboard examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink
|
||||
GOOS=js GOARCH=wasm $(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
|
||||
GOOS=js GOARCH=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
|
||||
GOOS=js GOARCH=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/machinetest
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=pico examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=xiao_esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
@@ -569,8 +693,12 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s140v7 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=btt-skr-pico examples/uart
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
||||
@@ -641,6 +769,10 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-ble-plus examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=rak4631 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/dac
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pyportal examples/dac
|
||||
@@ -675,6 +807,10 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico-w examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/blinky1
|
||||
@@ -705,10 +841,32 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=gopher-arcade examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2-w examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=tiny2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=badger2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=blinky2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico-plus2 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=vicharak_shrike-lite examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -723,6 +881,10 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/usb-storage
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2 examples/usb-storage
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
@@ -736,6 +898,8 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-h753zi examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l031k6 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||
@@ -762,18 +926,28 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32u031 examples/empty
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno 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
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno -scheduler=tasks examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -785,34 +959,108 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/mcp3008
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-generic examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stamp-s3a examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5stick-c examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target m5paper examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32c6
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c6 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c6 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32s3
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
# esp32s3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-box-3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
# esp32c3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/adc
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-c3-devkit-rust-1 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest
|
||||
@$(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=maixbit examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=tkey examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=elecrow-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=elecrow-rp2350 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=hw-651 examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=hw-651-s110v8 examples/machinetest
|
||||
@$(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
|
||||
@@ -827,7 +1075,7 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/echo2
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -845,15 +1093,17 @@ endif
|
||||
|
||||
|
||||
wasmtest:
|
||||
$(GO) test ./tests/wasm
|
||||
cd ./tests/wasm && $(GO) test .
|
||||
|
||||
build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
|
||||
build/release: tinygo gen-device $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/bdwgc
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||
@mkdir -p build/release/tinygo/lib/musl/arch
|
||||
@mkdir -p build/release/tinygo/lib/musl/crt
|
||||
@@ -861,7 +1111,8 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
|
||||
@mkdir -p build/release/tinygo/lib/nrfx
|
||||
@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/libc-bottom-half/headers
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/dlmalloc
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@mkdir -p build/release/tinygo/lib/wasi-cli/
|
||||
@@ -870,6 +1121,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
|
||||
ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
|
||||
endif
|
||||
@cp -rp lib/bdwgc/* build/release/tinygo/lib/bdwgc
|
||||
@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
|
||||
@@ -883,9 +1135,12 @@ endif
|
||||
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||
@cp -rp lib/musl/include build/release/tinygo/lib/musl
|
||||
@cp -rp lib/musl/src/conf build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/ctype build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/fcntl 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
|
||||
@@ -894,19 +1149,31 @@ endif
|
||||
@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/misc build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/sched 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/stdlib build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/process build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/crt/pseudo-reloc.c build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/gdtoa build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/advapi32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio/ucrt_* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/msvcrt.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/math/x86 build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/misc build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
||||
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||
@cp -rp lib/mingw-w64/mingw-w64-headers/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
||||
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
||||
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@@ -916,18 +1183,40 @@ endif
|
||||
@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/libc-bottom-half/headers/public build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
|
||||
@cp -rp lib/wasi-libc/dlmalloc/src build/release/tinygo/lib/wasi-libc/dlmalloc
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/cloudlibc build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/headers build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/sources build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||
@cp -rp lib/wasi-libc/libc-top-half/headers build/release/tinygo/lib/wasi-libc/libc-top-half
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/generic build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/wasm32 build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/conf build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/dirent build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/env build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/errno build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/exit build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/fcntl build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/fenv build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/internal build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/legacy build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/locale build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/math build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/misc build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/multibyte build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/network build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stat build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stdio build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stdlib build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
|
||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/thread build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/time build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/unistd build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||
@cp -rp lib/wasi-libc/libc-top-half/sources build/release/tinygo/lib/wasi-libc/libc-top-half
|
||||
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
|
||||
@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 ${LLVM_PROJECTDIR}/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp ${LLVM_PROJECTDIR}/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
|
||||
|
||||
@@ -949,8 +1238,9 @@ endif
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
cd internal/tools && go generate -tags tools ./
|
||||
go generate -tags tools ./
|
||||
|
||||
LINTDIRS=src/os/ src/reflect/
|
||||
.PHONY: lint
|
||||
lint: tools ## Lint source tree
|
||||
revive -version
|
||||
@@ -958,16 +1248,19 @@ lint: tools ## Lint source tree
|
||||
# revive.toml isn't flexible enough to filter out just one kind of error from a checker, so do it with grep here.
|
||||
# Can't use grep with friendly formatter. Plain output isn't too bad, though.
|
||||
# Use 'grep .' to get rid of stray blank line
|
||||
revive -config revive.toml compiler/... src/{os,reflect}/*.go | grep -v "should have comment or be unexported" | grep '.' | awk '{print}; END {exit NR>0}'
|
||||
revive -config revive.toml compiler/... $$( find $(LINTDIRS) -type f -name '*.go' ) \
|
||||
| grep -v "should have comment or be unexported" \
|
||||
| grep '.' \
|
||||
| awk '{print}; END {exit NR>0}'
|
||||
|
||||
SPELLDIRSCMD=find . -depth 1 -type d | egrep -wv '.git|lib|llvm|src'; find src -depth 1 | egrep -wv 'device|internal|net|vendor'; find src/internal -depth 1 -type d | egrep -wv src/internal/wasi
|
||||
.PHONY: spell
|
||||
spell: tools ## Spellcheck source tree
|
||||
misspell -error --dict misspell.csv -i 'ackward,devided,extint,rela' $$( $(SPELLDIRSCMD) ) *.md
|
||||
misspell -error --dict misspell.csv -i 'ackward,devided,extint,rela' $$( $(SPELLDIRSCMD) ) *.go *.md
|
||||
|
||||
.PHONY: spellfix
|
||||
spellfix: tools ## Same as spell, but fixes what it finds
|
||||
misspell -w --dict misspell.csv -i 'ackward,devided,extint,rela' $$( $(SPELLDIRSCMD) ) *.md
|
||||
misspell -w --dict misspell.csv -i 'ackward,devided,extint,rela' $$( $(SPELLDIRSCMD) ) *.go *.md
|
||||
|
||||
# https://www.client9.com/self-documenting-makefiles/
|
||||
.PHONY: help
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
TinyGo Team Members
|
||||
===================
|
||||
|
||||
The team of humans who maintain TinyGo.
|
||||
|
||||
* **Purpose**: To maintain the community, code, documentation, and tools for the TinyGo compiler.
|
||||
* **Board**: The group of people who share responsibility for key decisions for the TinyGo organization.
|
||||
* **Majority Voting**: The board makes decisions by majority vote.
|
||||
* **Membership**: The board elects its own members.
|
||||
* **Do-ocracy**: Those who step forward to do a given task propose how it should be done. Then other interested people can make comments.
|
||||
* **Proof of Work**: Power in decision-making is slightly weighted based on a participant's labor for the community.
|
||||
* **Initiation**: We need to establish a procedure for how people join the team of maintainers.
|
||||
* **Transparency**: Important information should be made publicly available, ideally in a way that allows for public comment.
|
||||
* **Code of Conduct**: Participants agree to abide by the current project Code of Conduct.
|
||||
|
||||
## Members
|
||||
|
||||
* Ayke van Laethem (@aykevl)
|
||||
* Daniel Esteban (@conejoninja)
|
||||
* Ron Evans (@deadprogram)
|
||||
* Damian Gryski (@dgryski)
|
||||
* Masaaki Takasago (@sago35)
|
||||
* Patricio Whittingslow (@soypat)
|
||||
* Yurii Soldak (@ysoldak)
|
||||
|
||||
## Experimental
|
||||
|
||||
* **Monthly Meeting**: A monthly meeting for the team and any other interested participants.
|
||||
Duration: 1 hour
|
||||
Facilitation: @deadprogram
|
||||
Schedule: See https://github.com/tinygo-org/tinygo/wiki/Meetings for more information
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2026 The TinyGo Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of the Go standard library.
|
||||
Copyright (c) 2009-2023 The Go Authors. All rights reserved.
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
See https://github.com/golang/go/blob/master/LICENSE for license information.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](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://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/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://github.com/tinygo-org/tinygo/actions/workflows/nix.yml)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
||||
|
||||
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You can help TinyGo with a financial contribution using OpenCollective. Please see https://opencollective.com/tinygo for more information. Thank you!
|
||||
|
||||
## Embedded
|
||||
|
||||
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
||||
@@ -31,10 +34,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
The above program can be compiled and run without modification on an Arduino Uno, an Adafruit ItsyBitsy M0, or any of the supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
The above program can be compiled and run without modification on an [Arduino Uno](https://tinygo.org/docs/reference/microcontrollers/boards/arduino-uno), an [Adafruit Circuit Playground Express](https://tinygo.org/docs/reference/microcontrollers/featured/circuitplay-express), a [Seeed Studio XIAO-ESP32S3](https://tinygo.org/docs/reference/microcontrollers/featured/xiao-esp32s3) or any of the many supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
|
||||
```shell
|
||||
tinygo flash -target arduino examples/blinky1
|
||||
tinygo flash -target arduino-uno examples/blinky1
|
||||
```
|
||||
|
||||
## WebAssembly
|
||||
@@ -48,20 +51,22 @@ Here is a small TinyGo program for use by a WASI host application:
|
||||
```go
|
||||
package main
|
||||
|
||||
//go:wasm-module yourmodulename
|
||||
//export add
|
||||
//go:wasmexport add
|
||||
func add(x, y uint32) uint32 {
|
||||
return x + y
|
||||
}
|
||||
|
||||
// main is required for the `wasip1` target, even if it isn't used.
|
||||
func main() {}
|
||||
```
|
||||
|
||||
This compiles the above TinyGo program for use on any WASI runtime:
|
||||
This compiles the above TinyGo program for use on any WASI Preview 1 runtime:
|
||||
|
||||
```shell
|
||||
tinygo build -o main.wasm -target=wasip1 main.go
|
||||
tinygo build -buildmode=c-shared -o add.wasm -target=wasip1 add.go
|
||||
```
|
||||
|
||||
You can also use the same syntax as Go 1.24+:
|
||||
|
||||
```shell
|
||||
GOOS=wasip1 GOARCH=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
||||
```
|
||||
|
||||
## Installation
|
||||
@@ -72,7 +77,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
### Embedded
|
||||
|
||||
You can compile TinyGo programs for over 94 different microcontroller boards.
|
||||
You can compile TinyGo programs for over 150 different microcontroller boards.
|
||||
|
||||
For more information, please see https://tinygo.org/docs/reference/microcontrollers/
|
||||
|
||||
@@ -137,7 +142,7 @@ Non-goals:
|
||||
|
||||
## Why this project exists
|
||||
|
||||
> We never expected Go to be an embedded language and so its got serious problems...
|
||||
> We never expected Go to be an embedded language, and so it’s got serious problems...
|
||||
|
||||
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
||||
|
||||
|
||||
+17
-2
@@ -3,6 +3,7 @@ package builder
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
@@ -62,6 +63,20 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||
fileIndex int
|
||||
}{symbol.Name, i})
|
||||
}
|
||||
} else if dbg, err := macho.NewFile(objfile); err == nil {
|
||||
for _, symbol := range dbg.Symtab.Syms {
|
||||
// See mach-o/nlist.h
|
||||
if symbol.Type&0x0e != 0xe { // (symbol.Type & N_TYPE) != N_SECT
|
||||
continue // undefined symbol
|
||||
}
|
||||
if symbol.Type&0x01 == 0 { // (symbol.Type & N_EXT) == 0
|
||||
continue // internal symbol (static, etc)
|
||||
}
|
||||
symbolTable = append(symbolTable, struct {
|
||||
name string
|
||||
fileIndex int
|
||||
}{symbol.Name, i})
|
||||
}
|
||||
} else if dbg, err := pe.NewFile(objfile); err == nil {
|
||||
for _, symbol := range dbg.Symbols {
|
||||
if symbol.StorageClass != 2 {
|
||||
@@ -150,7 +165,7 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||
}
|
||||
|
||||
// Keep track of the start of the symbol table.
|
||||
symbolTableStart, err := arfile.Seek(0, os.SEEK_CUR)
|
||||
symbolTableStart, err := arfile.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -172,7 +187,7 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||
|
||||
// Store the start index, for when we'll update the symbol table with
|
||||
// the correct file start indices.
|
||||
offset, err := arfile.Seek(0, os.SEEK_CUR)
|
||||
offset, err := arfile.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package builder
|
||||
|
||||
// The well-known conservative Boehm-Demers-Weiser GC.
|
||||
// This file provides a way to compile this GC for use with TinyGo.
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var BoehmGC = Library{
|
||||
name: "bdwgc",
|
||||
cflags: func(target, headerPath string) []string {
|
||||
libdir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
||||
flags := []string{
|
||||
// use a modern environment
|
||||
"-DUSE_MMAP", // mmap is available
|
||||
"-DUSE_MUNMAP", // return memory to the OS using munmap
|
||||
"-DGC_BUILTIN_ATOMIC", // use compiler intrinsics for atomic operations
|
||||
"-DNO_EXECUTE_PERMISSION", // don't make the heap executable
|
||||
|
||||
// specific flags for TinyGo
|
||||
"-DALL_INTERIOR_POINTERS", // scan interior pointers (needed for Go)
|
||||
"-DIGNORE_DYNAMIC_LOADING", // we don't support dynamic loading at the moment
|
||||
"-DNO_GETCONTEXT", // musl doesn't support getcontext()
|
||||
"-DGC_DISABLE_INCREMENTAL", // don't mess with SIGSEGV and such
|
||||
|
||||
// Use a minimal environment.
|
||||
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
||||
"-DDONT_USE_ATEXIT",
|
||||
"-DNO_GETENV", // smaller binary, more predictable configuration
|
||||
"-DNO_CLOCK", // don't use system clock
|
||||
"-DNO_DEBUGGING", // reduce code size
|
||||
"-DGC_NO_FINALIZATION", // finalization is not used at the moment
|
||||
|
||||
// Special flag to work around the lack of __data_start in ld.lld.
|
||||
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
||||
// work around it anymore.
|
||||
"-DGC_DONT_REGISTER_MAIN_STATIC_DATA",
|
||||
|
||||
// Do not scan the stack. We have our own mechanism to do this.
|
||||
"-DSTACK_NOT_SCANNED",
|
||||
"-DNO_PROC_STAT", // we scan the stack manually (don't read /proc/self/stat on Linux)
|
||||
"-DSTACKBOTTOM=0", // dummy value, we scan the stack manually
|
||||
|
||||
// Assertions can be enabled while debugging GC issues.
|
||||
//"-DGC_ASSERTIONS",
|
||||
|
||||
// We use our own way of dealing with threads (that is a bit hacky).
|
||||
// See src/runtime/gc_boehm.go.
|
||||
//"-DGC_THREADS",
|
||||
//"-DTHREAD_LOCAL_ALLOC",
|
||||
|
||||
"-I" + libdir + "/include",
|
||||
}
|
||||
return flags
|
||||
},
|
||||
needsLibc: true,
|
||||
sourceDir: func() string {
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
||||
},
|
||||
librarySources: func(target string, _ bool) ([]string, error) {
|
||||
sources := []string{
|
||||
"allchblk.c",
|
||||
"alloc.c",
|
||||
"blacklst.c",
|
||||
"dbg_mlc.c",
|
||||
"dyn_load.c",
|
||||
"headers.c",
|
||||
"mach_dep.c",
|
||||
"malloc.c",
|
||||
"mark.c",
|
||||
"mark_rts.c",
|
||||
"misc.c",
|
||||
"new_hblk.c",
|
||||
"os_dep.c",
|
||||
"reclaim.c",
|
||||
}
|
||||
if strings.Split(target, "-")[2] == "windows" {
|
||||
// Due to how the linker on Windows works (that doesn't allow
|
||||
// undefined functions), we need to include these extra files.
|
||||
sources = append(sources,
|
||||
"mallocx.c",
|
||||
"ptr_chck.c",
|
||||
)
|
||||
}
|
||||
return sources, nil
|
||||
},
|
||||
}
|
||||
+222
-98
@@ -14,12 +14,13 @@ import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"hash/crc32"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"math/bits"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -61,6 +62,10 @@ type BuildResult struct {
|
||||
// correctly printing test results: the import path isn't always the same as
|
||||
// the path listed on the command line.
|
||||
ImportPath string
|
||||
|
||||
// Map from path to package name. It is needed to attribute binary size to
|
||||
// the right Go package.
|
||||
PackagePathMap map[string]string
|
||||
}
|
||||
|
||||
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||
@@ -133,9 +138,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if _, ok := globalValues[pkgPath]; !ok {
|
||||
globalValues[pkgPath] = map[string]string{}
|
||||
}
|
||||
for k, v := range vals {
|
||||
globalValues[pkgPath][k] = v
|
||||
}
|
||||
maps.Copy(globalValues[pkgPath], vals)
|
||||
}
|
||||
|
||||
// Check for a libc dependency.
|
||||
@@ -145,16 +148,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
var libcDependencies []*compileJob
|
||||
switch config.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
job := makeDarwinLibSystemJob(config, tmpdir)
|
||||
libcDependencies = append(libcDependencies, job)
|
||||
libcJob := makeDarwinLibSystemJob(config, tmpdir)
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "musl":
|
||||
job, unlock, err := libMusl.load(config, tmpdir)
|
||||
var unlock func()
|
||||
libcJob, unlock, err := libMusl.load(config, tmpdir)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
|
||||
libcDependencies = append(libcDependencies, job)
|
||||
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(libcJob.result), "crt1.o")))
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "picolibc":
|
||||
libcJob, unlock, err := libPicolibc.load(config, tmpdir)
|
||||
if err != nil {
|
||||
@@ -163,11 +167,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "wasi-libc":
|
||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
|
||||
return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||
libcJob, unlock, err := libWasiLibc.load(config, tmpdir)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "wasmbuiltins":
|
||||
libcJob, unlock, err := libWasmBuiltins.load(config, tmpdir)
|
||||
if err != nil {
|
||||
@@ -176,12 +181,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
case "mingw-w64":
|
||||
job, unlock, err := libMinGW.load(config, tmpdir)
|
||||
libcJob, unlock, err := libMinGW.load(config, tmpdir)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
defer unlock()
|
||||
libcDependencies = append(libcDependencies, job)
|
||||
libcDependencies = append(libcDependencies, libcJob)
|
||||
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
|
||||
case "":
|
||||
// no library specified, so nothing to do
|
||||
@@ -197,6 +202,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
ABI: config.ABI(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
BuildMode: config.BuildMode(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
SizeLevel: sizeLevel,
|
||||
@@ -208,6 +214,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
MaxStackAlloc: config.MaxStackAlloc(),
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
|
||||
Nobounds: config.Options.Nobounds,
|
||||
PanicStrategy: config.PanicStrategy(),
|
||||
}
|
||||
|
||||
@@ -241,6 +248,24 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Store which filesystem paths map to which package name.
|
||||
result.PackagePathMap = make(map[string]string, len(lprogram.Packages))
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||
}
|
||||
|
||||
// Strip default initializers for -X globals from the type info before
|
||||
// building SSA. This prevents go/ssa from emitting init stores for them,
|
||||
// so that makeGlobalsModule can supply the correct values at final link
|
||||
// time without any runtime init overwriting them. The -X values themselves
|
||||
// are kept out of the per-package build cache; only the variable names
|
||||
// appear in the cache key.
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
for name := range globalValues[pkg.Pkg.Path()] {
|
||||
pkg.StripVarInitializer(name)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||
// program so it's pretty fast and doesn't need to be parallelized.
|
||||
program := lprogram.LoadSSA()
|
||||
@@ -252,7 +277,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
var embedFileObjects []*compileJob
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
var undefinedGlobals []string
|
||||
for name := range globalValues[pkg.Pkg.Path()] {
|
||||
@@ -268,9 +292,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||
}
|
||||
}
|
||||
for name, files := range allFiles {
|
||||
name := name
|
||||
files := files
|
||||
// Sort embedded files by name to maintain output determinism.
|
||||
embedNames := make([]string, 0, len(allFiles))
|
||||
for _, files := range allFiles {
|
||||
embedNames = append(embedNames, files[0].Name)
|
||||
}
|
||||
slices.Sort(embedNames)
|
||||
for _, name := range embedNames {
|
||||
job := &compileJob{
|
||||
description: "make object file for " + name,
|
||||
run: func(job *compileJob) error {
|
||||
@@ -285,7 +313,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
sum := sha256.Sum256(data)
|
||||
hexSum := hex.EncodeToString(sum[:16])
|
||||
|
||||
for _, file := range files {
|
||||
for _, file := range allFiles[name] {
|
||||
file.Size = uint64(len(data))
|
||||
file.Hash = hexSum
|
||||
if file.NeedsData {
|
||||
@@ -438,8 +466,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if global.IsNil() {
|
||||
return errors.New("global not found: " + globalName)
|
||||
}
|
||||
globalType := global.GlobalValueType()
|
||||
if globalType.TypeKind() != llvm.StructTypeKind || globalType.StructName() != "runtime._string" {
|
||||
// Verify this is indeed a string. This is needed so
|
||||
// that makeGlobalsModule can just create the right
|
||||
// globals of string type without checking.
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
name := global.Name()
|
||||
newGlobal := llvm.AddGlobal(mod, global.GlobalValueType(), name+".tmp")
|
||||
newGlobal := llvm.AddGlobal(mod, globalType, name+".tmp")
|
||||
global.ReplaceAllUsesWith(newGlobal)
|
||||
global.EraseFromParentAsGlobal()
|
||||
newGlobal.SetName(name)
|
||||
@@ -452,7 +487,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if pkgInit.IsNil() {
|
||||
panic("init not found for " + pkg.Pkg.Path())
|
||||
}
|
||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -521,12 +556,38 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||
}
|
||||
// Resolve duplicate function definitions before linking.
|
||||
// This can happen when a newer Go version adds a function
|
||||
// body in a standard library package that was previously
|
||||
// just a declaration provided by //go:linkname from the
|
||||
// runtime. In that case, keep the existing (runtime)
|
||||
// definition by weakening the new one's linkage so the
|
||||
// LLVM linker discards it in favor of the existing one.
|
||||
for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
existing := mod.NamedFunction(fn.Name())
|
||||
if existing.IsNil() || existing.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
err = llvm.LinkModules(mod, pkgMod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link module: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Insert values from -ldflags="-X ..." into the IR.
|
||||
// This is a separate module, so that the "runtime._string" type
|
||||
// doesn't need to match precisely. LLVM tends to rename that type
|
||||
// sometimes, leading to errors. But linking in a separate module
|
||||
// works fine. See:
|
||||
// https://github.com/tinygo-org/tinygo/issues/4810
|
||||
globalsMod := makeGlobalsModule(ctx, globalValues, machine)
|
||||
llvm.LinkModules(mod, globalsMod)
|
||||
|
||||
// Create runtime.initAll function that calls the runtime
|
||||
// initializer of each package.
|
||||
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||
@@ -579,7 +640,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
// Run all optimization passes, which are much more effective now
|
||||
// that the optimizer can see the whole program at once.
|
||||
err := optimizeProgram(mod, config, globalValues)
|
||||
err := optimizeProgram(mod, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -593,6 +654,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
},
|
||||
}
|
||||
|
||||
// Create the output directory, if needed
|
||||
if err := os.MkdirAll(filepath.Dir(outpath), 0777); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Check whether we only need to create an object file.
|
||||
// If so, we don't need to link anything and will be finished quickly.
|
||||
outext := filepath.Ext(outpath)
|
||||
@@ -649,6 +715,23 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
result.Binary = result.Executable // final file
|
||||
ldflags := append(config.LDFlags(), "-o", result.Executable)
|
||||
|
||||
if config.Options.BuildMode == "c-shared" {
|
||||
if !strings.HasPrefix(config.Triple(), "wasm32-") {
|
||||
return result, fmt.Errorf("buildmode c-shared is only supported on wasm at the moment")
|
||||
}
|
||||
ldflags = append(ldflags, "--no-entry")
|
||||
}
|
||||
|
||||
if config.Options.BuildMode == "wasi-legacy" {
|
||||
if !strings.HasPrefix(config.Triple(), "wasm32-") {
|
||||
return result, fmt.Errorf("buildmode wasi-legacy is only supported on wasm")
|
||||
}
|
||||
|
||||
if config.Options.Scheduler != "none" {
|
||||
return result, fmt.Errorf("buildmode wasi-legacy only supports scheduler=none")
|
||||
}
|
||||
}
|
||||
|
||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||
// a cache.
|
||||
if config.Target.RTLib == "compiler-rt" {
|
||||
@@ -660,6 +743,16 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
// The Boehm collector is stored in a separate C library.
|
||||
if config.GC() == "boehm" {
|
||||
job, unlock, err := BoehmGC.load(config, tmpdir)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
defer unlock()
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
// Add jobs to compile extra files. These files are in C or assembly and
|
||||
// contain things like the interrupt vector table and low level operations
|
||||
// such as stack switching.
|
||||
@@ -680,9 +773,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
// TODO: do this as part of building the package to be able to link the
|
||||
// bitcode files together.
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg
|
||||
for _, filename := range pkg.CFiles {
|
||||
abspath := filepath.Join(pkg.Dir, filename)
|
||||
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
||||
job := &compileJob{
|
||||
description: "compile CGo file " + abspath,
|
||||
run: func(job *compileJob) error {
|
||||
@@ -747,22 +839,25 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
|
||||
if config.GOOS() == "windows" {
|
||||
// Options for the MinGW wrapper for the lld COFF linker.
|
||||
switch config.LinkerFlavor() {
|
||||
case "coff":
|
||||
// Options for driving ld.lld in PE/COFF mode.
|
||||
ldflags = append(ldflags,
|
||||
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||
} else if config.GOOS() == "darwin" {
|
||||
case "darwin":
|
||||
// Options for the ld64-compatible lld linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(speedLevel),
|
||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||
} else {
|
||||
case "gnu":
|
||||
// Options for the ELF linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(speedLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||
)
|
||||
default:
|
||||
return fmt.Errorf("unknown linker flavor: %s", config.LinkerFlavor())
|
||||
}
|
||||
if config.CodeModel() != "default" {
|
||||
ldflags = append(ldflags,
|
||||
@@ -804,6 +899,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply patches of bootloader in the order they appear.
|
||||
if len(config.Target.BootPatches) > 0 {
|
||||
err = applyPatches(result.Executable, config.Target.BootPatches)
|
||||
}
|
||||
|
||||
if config.RP2040BootPatch() {
|
||||
// Patch the second stage bootloader CRC into the .boot2 section
|
||||
err = patchRP2040BootCRC(result.Executable)
|
||||
@@ -813,7 +914,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
|
||||
// Run wasm-opt for wasm binaries
|
||||
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
||||
if arch, _, _ := strings.Cut(config.Triple(), "-"); arch == "wasm32" {
|
||||
optLevel, _, _ := config.OptLevel()
|
||||
opt := "-" + optLevel
|
||||
|
||||
@@ -823,19 +924,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
args = append(args, "--asyncify")
|
||||
}
|
||||
|
||||
exeunopt := result.Executable
|
||||
|
||||
if config.Options.Work {
|
||||
// Keep the work direction around => don't overwrite the .wasm binary with the optimized version
|
||||
exeunopt += ".pre-wasm-opt"
|
||||
os.Rename(result.Executable, exeunopt)
|
||||
}
|
||||
|
||||
inputFile := result.Binary
|
||||
result.Binary = result.Executable + ".wasmopt"
|
||||
args = append(args,
|
||||
opt,
|
||||
"-g",
|
||||
exeunopt,
|
||||
"--output", result.Executable,
|
||||
inputFile,
|
||||
"--output", result.Binary,
|
||||
)
|
||||
|
||||
wasmopt := goenv.Get("WASMOPT")
|
||||
@@ -865,13 +960,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
// wasm-tools component embed -w wasi:cli/command
|
||||
// $$(tinygo env TINYGOROOT)/lib/wasi-cli/wit/ main.wasm -o embedded.wasm
|
||||
componentEmbedInputFile := result.Binary
|
||||
result.Binary = result.Executable + ".wasm-component-embed"
|
||||
args := []string{
|
||||
"component",
|
||||
"embed",
|
||||
"-w", witWorld,
|
||||
witPackage,
|
||||
result.Executable,
|
||||
"-o", result.Executable,
|
||||
componentEmbedInputFile,
|
||||
"-o", result.Binary,
|
||||
}
|
||||
|
||||
wasmtools := goenv.Get("WASMTOOLS")
|
||||
@@ -884,15 +981,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("wasm-tools failed: %w", err)
|
||||
return fmt.Errorf("`wasm-tools component embed` failed: %w", err)
|
||||
}
|
||||
|
||||
// wasm-tools component new embedded.wasm -o component.wasm
|
||||
componentNewInputFile := result.Binary
|
||||
result.Binary = result.Executable + ".wasm-component-new"
|
||||
args = []string{
|
||||
"component",
|
||||
"new",
|
||||
result.Executable,
|
||||
"-o", result.Executable,
|
||||
componentNewInputFile,
|
||||
"-o", result.Binary,
|
||||
}
|
||||
|
||||
if config.Options.PrintCommands != nil {
|
||||
@@ -904,24 +1003,21 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("wasm-tools failed: %w", err)
|
||||
return fmt.Errorf("`wasm-tools component new` failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Print code size if requested.
|
||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||
packagePathMap := make(map[string]string, len(lprogram.Packages))
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||
}
|
||||
sizes, err := loadProgramSize(result.Executable, packagePathMap)
|
||||
if config.Options.PrintSizes != "" {
|
||||
sizes, err := loadProgramSize(result.Executable, result.PackagePathMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Options.PrintSizes == "short" {
|
||||
switch config.Options.PrintSizes {
|
||||
case "short":
|
||||
fmt.Printf(" code data bss | flash ram\n")
|
||||
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM())
|
||||
} else {
|
||||
case "full":
|
||||
if !config.Debug() {
|
||||
fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail")
|
||||
}
|
||||
@@ -933,6 +1029,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
fmt.Printf("------------------------------- | --------------- | -------\n")
|
||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS)
|
||||
case "html":
|
||||
const filename = "size-report.html"
|
||||
err := writeSizeReport(sizes, filename, pkgName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Wrote size report to", filename)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,11 +1076,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
||||
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp32c6", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||
err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
|
||||
err := makeESPFirmwareImage(result.Executable, result.Binary, outputBinaryFormat)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@@ -1104,8 +1207,8 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
||||
// optimizeProgram runs a series of optimizations and transformations that are
|
||||
// needed to convert a program to its final form. Some transformations are not
|
||||
// optional and must be run as the compiler expects them to run.
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues map[string]map[string]string) error {
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1122,12 +1225,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
|
||||
}
|
||||
}
|
||||
|
||||
// Insert values from -ldflags="-X ..." into the IR.
|
||||
err = setGlobalValues(mod, globalValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run most of the whole-program optimizations (including the whole
|
||||
// O0/O1/O2/Os/Oz optimization pipeline).
|
||||
errs := transform.Optimize(mod, config)
|
||||
@@ -1141,10 +1238,19 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
|
||||
return nil
|
||||
}
|
||||
|
||||
// setGlobalValues sets the global values from the -ldflags="-X ..." compiler
|
||||
// option in the given module. An error may be returned if the global is not of
|
||||
// the expected type.
|
||||
func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) error {
|
||||
func makeGlobalsModule(ctx llvm.Context, globals map[string]map[string]string, machine llvm.TargetMachine) llvm.Module {
|
||||
mod := ctx.NewModule("cmdline-globals")
|
||||
targetData := machine.CreateTargetData()
|
||||
defer targetData.Dispose()
|
||||
mod.SetDataLayout(targetData.String())
|
||||
|
||||
stringType := ctx.StructCreateNamed("runtime._string")
|
||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||
stringType.StructSetBody([]llvm.Type{
|
||||
llvm.PointerType(ctx.Int8Type(), 0),
|
||||
uintptrType,
|
||||
}, false)
|
||||
|
||||
var pkgPaths []string
|
||||
for pkgPath := range globals {
|
||||
pkgPaths = append(pkgPaths, pkgPath)
|
||||
@@ -1160,24 +1266,6 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
|
||||
for _, name := range names {
|
||||
value := pkg[name]
|
||||
globalName := pkgPath + "." + name
|
||||
global := mod.NamedGlobal(globalName)
|
||||
if global.IsNil() || !global.Initializer().IsNil() {
|
||||
// The global either does not exist (optimized away?) or has
|
||||
// some value, in which case it has already been initialized at
|
||||
// package init time.
|
||||
continue
|
||||
}
|
||||
|
||||
// A strin is a {ptr, len} pair. We need these types to build the
|
||||
// initializer.
|
||||
initializerType := global.GlobalValueType()
|
||||
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
elementTypes := initializerType.StructElementTypes()
|
||||
if len(elementTypes) != 2 {
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
|
||||
// Create a buffer for the string contents.
|
||||
bufInitializer := mod.Context().ConstString(value, false)
|
||||
@@ -1188,22 +1276,20 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
|
||||
buf.SetLinkage(llvm.PrivateLinkage)
|
||||
|
||||
// Create the string value, which is a {ptr, len} pair.
|
||||
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
|
||||
ptr := llvm.ConstGEP(bufInitializer.Type(), buf, []llvm.Value{zero, zero})
|
||||
if ptr.Type() != elementTypes[0] {
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
length := llvm.ConstInt(elementTypes[1], uint64(len(value)), false)
|
||||
initializer := llvm.ConstNamedStruct(initializerType, []llvm.Value{
|
||||
ptr,
|
||||
length := llvm.ConstInt(uintptrType, uint64(len(value)), false)
|
||||
initializer := llvm.ConstNamedStruct(stringType, []llvm.Value{
|
||||
buf,
|
||||
length,
|
||||
})
|
||||
|
||||
// Set the initializer. No initializer should be set at this point.
|
||||
// Create the string global.
|
||||
global := llvm.AddGlobal(mod, stringType, globalName)
|
||||
global.SetInitializer(initializer)
|
||||
global.SetAlignment(targetData.PrefTypeAlignment(stringType))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
// functionStackSizes keeps stack size information about a single function
|
||||
@@ -1259,13 +1345,21 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
}
|
||||
|
||||
// Goroutines need to be started and finished and take up some stack space
|
||||
// that way. This can be measured by measuing the stack size of
|
||||
// that way. This can be measured by measuring the stack size of
|
||||
// tinygo_startTask.
|
||||
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
||||
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
||||
}
|
||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||
|
||||
// Account for the bytes that tinygo_swapTask pushes onto the goroutine stack
|
||||
// on every context switch. The static analysis correctly traces Go calls,
|
||||
// but it cannot see into the assembly-level register push.
|
||||
var contextSwitchOverhead uint64
|
||||
if swapFuncs, ok := functions["tinygo_swapTask"]; ok && len(swapFuncs) == 1 {
|
||||
contextSwitchOverhead = swapFuncs[0].FrameSize
|
||||
}
|
||||
|
||||
sizes := make(map[string]functionStackSize)
|
||||
|
||||
// Add the reset handler function, for convenience. The reset handler runs
|
||||
@@ -1314,6 +1408,12 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
// overflow will occur even before the goroutine is started.
|
||||
stackSize = baseStackSize
|
||||
}
|
||||
if stackSizeType == stacksize.Bounded {
|
||||
// Add the overhead of context switching. This is needed because the
|
||||
// context switch (tinygo_swapTask) pushes callee-saved registers
|
||||
// onto the current stack, which is not seen by the static analysis.
|
||||
stackSize += contextSwitchOverhead
|
||||
}
|
||||
sizes[name] = functionStackSize{
|
||||
stackSize: stackSize,
|
||||
stackSizeType: stackSizeType,
|
||||
@@ -1422,6 +1522,23 @@ func printStacks(calculatedStacks []string, stackSizes map[string]functionStackS
|
||||
}
|
||||
}
|
||||
|
||||
func applyPatches(executable string, bootPatches []string) (err error) {
|
||||
for _, patch := range bootPatches {
|
||||
switch patch {
|
||||
case "rp2040":
|
||||
err = patchRP2040BootCRC(executable)
|
||||
// case "rp2350":
|
||||
// err = patchRP2350BootIMAGE_DEF(executable)
|
||||
default:
|
||||
err = errors.New("undefined boot patch name")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("apply boot patch %q: %w", patch, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RP2040 second stage bootloader CRC32 calculation
|
||||
//
|
||||
// Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
|
||||
@@ -1433,7 +1550,7 @@ func patchRP2040BootCRC(executable string) error {
|
||||
}
|
||||
|
||||
if len(bytes) != 256 {
|
||||
return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes")
|
||||
return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes, got %d", len(bytes))
|
||||
}
|
||||
|
||||
// From the 'official' RP2040 checksum script:
|
||||
@@ -1472,3 +1589,10 @@ func lock(path string) func() {
|
||||
|
||||
return func() { flock.Close() }
|
||||
}
|
||||
|
||||
func b2u8(b bool) uint8 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
+71
-8
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -28,11 +29,15 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32s3",
|
||||
"fe310",
|
||||
"gameboy-advance",
|
||||
"k210",
|
||||
"nintendoswitch",
|
||||
"riscv-qemu",
|
||||
"tkey",
|
||||
"uefi-amd64",
|
||||
"wasip1",
|
||||
"wasip2",
|
||||
"wasm",
|
||||
@@ -44,7 +49,6 @@ func TestClangAttributes(t *testing.T) {
|
||||
targetNames = append(targetNames, "esp32", "esp8266")
|
||||
}
|
||||
for _, targetName := range targetNames {
|
||||
targetName := targetName
|
||||
t.Run(targetName, func(t *testing.T) {
|
||||
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
||||
})
|
||||
@@ -53,9 +57,12 @@ func TestClangAttributes(t *testing.T) {
|
||||
for _, options := range []*compileopts.Options{
|
||||
{GOOS: "linux", GOARCH: "386"},
|
||||
{GOOS: "linux", GOARCH: "amd64"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "5"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "5,softfloat"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "6,softfloat"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "7,softfloat"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "5,hardfloat"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "6,hardfloat"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "7,hardfloat"},
|
||||
{GOOS: "linux", GOARCH: "arm64"},
|
||||
{GOOS: "linux", GOARCH: "mips", GOMIPS: "hardfloat"},
|
||||
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "hardfloat"},
|
||||
@@ -63,9 +70,9 @@ func TestClangAttributes(t *testing.T) {
|
||||
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
|
||||
{GOOS: "darwin", GOARCH: "amd64"},
|
||||
{GOOS: "darwin", GOARCH: "arm64"},
|
||||
{GOOS: "windows", GOARCH: "386"},
|
||||
{GOOS: "windows", GOARCH: "amd64"},
|
||||
{GOOS: "windows", GOARCH: "arm64"},
|
||||
{GOOS: "wasip1", GOARCH: "wasm"},
|
||||
} {
|
||||
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
||||
if options.GOARCH == "arm" {
|
||||
@@ -123,8 +130,10 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
defer mod.Dispose()
|
||||
|
||||
// Check whether the LLVM target matches.
|
||||
if mod.Target() != config.Triple() {
|
||||
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
||||
// Use ClangTriple since LLVM 22 normalizes wasm32-unknown-wasi to wasip1.
|
||||
expectedTriple := compileopts.ClangTriple(config.Triple())
|
||||
if mod.Target() != expectedTriple {
|
||||
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", expectedTriple, mod.Target())
|
||||
}
|
||||
|
||||
// Check the "target-cpu" and "target-features" string attribute of the add
|
||||
@@ -148,11 +157,65 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
// 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)
|
||||
//
|
||||
// Rather than requiring an exact match (which breaks across LLVM
|
||||
// versions as new features are added), check that every feature
|
||||
// TinyGo specifies is consistent with what Clang produces:
|
||||
// - A "+feature" in TinyGo must appear in Clang's output.
|
||||
// - A "-feature" in TinyGo must not be "+feature" in Clang's output.
|
||||
checkFeatureFlags(t, config.Features(), features)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkFeatureFlags verifies that all features specified by TinyGo's target
|
||||
// configuration are consistent with Clang's output. This allows Clang to add
|
||||
// new features across LLVM versions without breaking the test.
|
||||
func checkFeatureFlags(t *testing.T, targetFeatures, clangFeatures string) {
|
||||
t.Helper()
|
||||
|
||||
// Build a set of Clang's features for fast lookup.
|
||||
clangSet := make(map[string]bool) // feature name -> enabled
|
||||
for _, f := range strings.Split(clangFeatures, ",") {
|
||||
f = strings.TrimSpace(f)
|
||||
if len(f) < 2 {
|
||||
continue
|
||||
}
|
||||
enabled := f[0] == '+'
|
||||
name := f[1:]
|
||||
clangSet[name] = enabled
|
||||
}
|
||||
|
||||
// Check each feature that TinyGo specifies.
|
||||
var missing, conflicts []string
|
||||
for _, f := range strings.Split(targetFeatures, ",") {
|
||||
f = strings.TrimSpace(f)
|
||||
if len(f) < 2 {
|
||||
continue
|
||||
}
|
||||
wantEnabled := f[0] == '+'
|
||||
name := f[1:]
|
||||
|
||||
clangEnabled, inClang := clangSet[name]
|
||||
if wantEnabled && (!inClang || !clangEnabled) {
|
||||
// TinyGo requires +feature but Clang doesn't enable it.
|
||||
missing = append(missing, f)
|
||||
} else if !wantEnabled && inClang && clangEnabled {
|
||||
// TinyGo requires -feature but Clang enables it.
|
||||
conflicts = append(conflicts, fmt.Sprintf("target has %q but Clang has %q", f, "+"+name))
|
||||
}
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
t.Errorf("target specifies features not present in Clang output: %s\n\ttarget features: %s\n\tclang features: %s",
|
||||
strings.Join(missing, ", "), targetFeatures, clangFeatures)
|
||||
}
|
||||
if len(conflicts) > 0 {
|
||||
t.Errorf("target disables features that Clang enables: %s",
|
||||
strings.Join(conflicts, "; "))
|
||||
}
|
||||
}
|
||||
|
||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||
// to recursive tests.
|
||||
|
||||
+38
-2
@@ -3,6 +3,7 @@ package builder
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
@@ -201,6 +202,22 @@ var avrBuiltins = []string{
|
||||
"avr/udivmodqi4.S",
|
||||
}
|
||||
|
||||
// Builtins needed specifically for windows/386.
|
||||
var windowsI386Builtins = []string{
|
||||
"i386/chkstk.S", // __chkstk_ms
|
||||
"i386/chkstk2.S", // _alloca (__alloca)
|
||||
}
|
||||
|
||||
// Builtins needed specifically for windows/amd64.
|
||||
var windowsAMD64Builtins = []string{
|
||||
"x86_64/chkstk.S",
|
||||
}
|
||||
|
||||
// Builtins needed specifically for windows/arm64.
|
||||
var windowsARM64Builtins = []string{
|
||||
"aarch64/chkstk.S",
|
||||
}
|
||||
|
||||
// libCompilerRT is a library with symbols required by programs compiled with
|
||||
// LLVM. These symbols are for operations that cannot be emitted with a single
|
||||
// instruction or a short sequence of instructions for that target.
|
||||
@@ -220,16 +237,35 @@ var libCompilerRT = Library{
|
||||
// Development build.
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||
},
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
librarySources: func(target string, _ bool) ([]string, error) {
|
||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||
switch compileopts.CanonicalArchName(target) {
|
||||
case "arm":
|
||||
builtins = append(builtins, aeabiBuiltins...)
|
||||
case "avr":
|
||||
builtins = append(builtins, avrBuiltins...)
|
||||
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
||||
case "x86_64":
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
if isWindowsTriple(target) {
|
||||
builtins = append(builtins, windowsAMD64Builtins...)
|
||||
}
|
||||
case "aarch64":
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
if isWindowsTriple(target) {
|
||||
builtins = append(builtins, windowsARM64Builtins...)
|
||||
}
|
||||
case "riscv64":
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
case "i386":
|
||||
if isWindowsTriple(target) {
|
||||
builtins = append(builtins, windowsI386Builtins...)
|
||||
}
|
||||
}
|
||||
return builtins, nil
|
||||
},
|
||||
}
|
||||
|
||||
func isWindowsTriple(target string) bool {
|
||||
parts := strings.Split(target, "-")
|
||||
return len(parts) > 2 && parts[2] == "windows"
|
||||
}
|
||||
|
||||
+1
-1
@@ -281,7 +281,7 @@ func parseDepFile(s string) ([]string, error) {
|
||||
s = strings.ReplaceAll(s, "\\\n", " ")
|
||||
|
||||
// Only use the first line, which is expected to begin with "deps:".
|
||||
line := strings.SplitN(s, "\n", 2)[0]
|
||||
line, _, _ := strings.Cut(s, "\n")
|
||||
if !strings.HasPrefix(line, "deps:") {
|
||||
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
||||
}
|
||||
|
||||
+40
-35
@@ -23,7 +23,7 @@
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Driver/DriverDiagnostic.h"
|
||||
#include "clang/Driver/Options.h"
|
||||
#include "clang/Options/Options.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCCodeEmitter.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCInstPrinter.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCObjectFileInfo.h"
|
||||
#include "llvm/MC/MCObjectWriter.h"
|
||||
@@ -67,8 +68,7 @@
|
||||
#include <optional>
|
||||
#include <system_error>
|
||||
using namespace clang;
|
||||
using namespace clang::driver;
|
||||
using namespace clang::driver::options;
|
||||
using namespace clang::options;
|
||||
using namespace llvm;
|
||||
using namespace llvm::opt;
|
||||
|
||||
@@ -109,7 +109,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
// Construct the invocation.
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
Opts.Triple = llvm::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());
|
||||
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
|
||||
@@ -125,8 +125,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
// Use the default target triple if unspecified.
|
||||
if (Opts.Triple.empty())
|
||||
Opts.Triple = llvm::sys::getDefaultTargetTriple();
|
||||
if (Opts.Triple.getTriple().empty())
|
||||
Opts.Triple = llvm::Triple(llvm::sys::getDefaultTargetTriple());
|
||||
|
||||
// Language Options
|
||||
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
||||
@@ -144,7 +144,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
|
||||
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
||||
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);
|
||||
@@ -234,6 +233,10 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
Opts.EmitCompactUnwindNonCanonical =
|
||||
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
||||
Opts.Crel = Args.hasArg(OPT_crel);
|
||||
Opts.ImplicitMapsyms = Args.hasArg(OPT_mmapsyms_implicit);
|
||||
Opts.X86RelaxRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
||||
Opts.X86Sse2Avx = Args.hasArg(OPT_msse2avx);
|
||||
|
||||
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||
|
||||
@@ -264,7 +267,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
std::string Error;
|
||||
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||
if (!TheTarget)
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||
@@ -287,8 +290,15 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
assert(MRI && "Unable to create target register info!");
|
||||
|
||||
MCTargetOptions MCOptions;
|
||||
MCOptions.MCRelaxAll = Opts.RelaxAll;
|
||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
||||
MCOptions.MCSaveTempLabels = Opts.SaveTemporaryLabels;
|
||||
MCOptions.Crel = Opts.Crel;
|
||||
MCOptions.ImplicitMapSyms = Opts.ImplicitMapsyms;
|
||||
MCOptions.X86RelaxRelocations = Opts.X86RelaxRelocations;
|
||||
MCOptions.X86Sse2Avx = Opts.X86Sse2Avx;
|
||||
MCOptions.CompressDebugSections = Opts.CompressDebugSections;
|
||||
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||
|
||||
std::unique_ptr<MCAsmInfo> MAI(
|
||||
@@ -297,9 +307,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
|
||||
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
|
||||
// may be created with a combination of default and explicit settings.
|
||||
MAI->setCompressDebugSections(Opts.CompressDebugSections);
|
||||
|
||||
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
|
||||
|
||||
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
|
||||
if (Opts.OutputPath.empty())
|
||||
@@ -319,7 +327,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
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,
|
||||
MCContext Ctx(Opts.Triple, MAI.get(), MRI.get(), STI.get(), &SrcMgr,
|
||||
&MCOptions);
|
||||
|
||||
bool PIC = false;
|
||||
@@ -337,14 +345,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
// 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);
|
||||
if (!Opts.DarwinTargetVariantSDKVersion.empty())
|
||||
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
|
||||
Ctx.setObjectFileInfo(MOFI.get());
|
||||
|
||||
if (Opts.SaveTemporaryLabels)
|
||||
Ctx.setAllowTemporaryLabels(false);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
Ctx.setGenDwarfForAssembly(true);
|
||||
if (!Opts.DwarfDebugFlags.empty())
|
||||
@@ -381,12 +383,15 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
MCOptions.MCNoWarn = Opts.NoWarn;
|
||||
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
|
||||
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
|
||||
MCOptions.ShowMCInst = Opts.ShowInst;
|
||||
MCOptions.AsmVerbose = true;
|
||||
MCOptions.MCUseDwarfDirectory = MCTargetOptions::EnableDwarfDirectory;
|
||||
MCOptions.ABIName = Opts.TargetABI;
|
||||
|
||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
||||
MCInstPrinter *IP = TheTarget->createMCInstPrinter(
|
||||
llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
|
||||
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
|
||||
Opts.Triple, Opts.OutputAsmVariant, *MAI, *MCII, *MRI));
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE;
|
||||
if (Opts.ShowEncoding)
|
||||
@@ -395,10 +400,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
|
||||
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
|
||||
Str.reset(TheTarget->createAsmStreamer(
|
||||
Ctx, std::move(FOut), /*asmverbose*/ true,
|
||||
/*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
|
||||
Opts.ShowInst));
|
||||
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), std::move(IP),
|
||||
std::move(CE), std::move(MAB)));
|
||||
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||
Str.reset(createNullStreamer(Ctx));
|
||||
} else {
|
||||
@@ -419,12 +422,17 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||
: MAB->createObjectWriter(*Out);
|
||||
|
||||
Triple T(Opts.Triple);
|
||||
Triple T = Opts.Triple;
|
||||
Str.reset(TheTarget->createMCObjectStreamer(
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ true));
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI));
|
||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||
if (T.isOSBinFormatMachO() && T.isOSDarwin()) {
|
||||
Triple *TVT = Opts.DarwinTargetVariantTriple
|
||||
? &*Opts.DarwinTargetVariantTriple
|
||||
: nullptr;
|
||||
Str->emitVersionForTarget(T, VersionTuple(), TVT,
|
||||
Opts.DarwinTargetVariantSDKVersion);
|
||||
}
|
||||
}
|
||||
|
||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||
@@ -436,9 +444,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
Str.get()->emitZeros(1);
|
||||
}
|
||||
|
||||
// Assembly to object compilation should leverage assembly info.
|
||||
Str->setUseAssemblerInfoForParsing(true);
|
||||
|
||||
bool Failed = false;
|
||||
|
||||
std::unique_ptr<MCAsmParser> Parser(
|
||||
@@ -448,7 +453,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
std::unique_ptr<MCTargetAsmParser> TAP(
|
||||
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
||||
if (!TAP)
|
||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
|
||||
|
||||
// Set values for symbols, if any.
|
||||
for (auto &S : Opts.SymbolDefs) {
|
||||
@@ -501,12 +506,12 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
InitializeAllAsmParsers();
|
||||
|
||||
// Construct our diagnostic client.
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
DiagnosticOptions DiagOpts;
|
||||
TextDiagnosticPrinter *DiagClient
|
||||
= new TextDiagnosticPrinter(errs(), &*DiagOpts);
|
||||
= new TextDiagnosticPrinter(errs(), DiagOpts);
|
||||
DiagClient->setPrefix("clang -cc1as");
|
||||
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
|
||||
DiagnosticsEngine Diags(DiagID, DiagOpts, DiagClient);
|
||||
|
||||
// Set an error handler, so that any LLVM backend diagnostics go through our
|
||||
// error handler.
|
||||
@@ -523,7 +528,7 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
llvm::outs(), "clang -cc1as [options] file...",
|
||||
"Clang Integrated Assembler", /*ShowHidden=*/false,
|
||||
/*ShowAllAliases=*/false,
|
||||
llvm::opt::Visibility(driver::options::CC1AsOption));
|
||||
llvm::opt::Visibility(clang::options::CC1AsOption));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+32
-3
@@ -20,7 +20,7 @@ struct AssemblerInvocation {
|
||||
/// @{
|
||||
|
||||
/// The name of the target triple to assemble for.
|
||||
std::string Triple;
|
||||
llvm::Triple Triple;
|
||||
|
||||
/// If given, the name of the target CPU to determine which instructions
|
||||
/// are legal.
|
||||
@@ -38,10 +38,13 @@ struct AssemblerInvocation {
|
||||
/// @{
|
||||
|
||||
std::vector<std::string> IncludePaths;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned NoInitialTextSection : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned SaveTemporaryLabels : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned GenDwarfForAssembly : 1;
|
||||
unsigned RelaxELFRelocations : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned Dwarf64 : 1;
|
||||
unsigned DwarfVersion;
|
||||
std::string DwarfDebugFlags;
|
||||
@@ -66,7 +69,9 @@ struct AssemblerInvocation {
|
||||
FT_Obj ///< Object file output.
|
||||
};
|
||||
FileType OutputType;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned ShowHelp : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned ShowVersion : 1;
|
||||
|
||||
/// @}
|
||||
@@ -74,19 +79,28 @@ struct AssemblerInvocation {
|
||||
/// @{
|
||||
|
||||
unsigned OutputAsmVariant;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned ShowEncoding : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned ShowInst : 1;
|
||||
|
||||
/// @}
|
||||
/// @name Assembler Options
|
||||
/// @{
|
||||
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned RelaxAll : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned NoExecStack : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned FatalWarnings : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned NoWarn : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned NoTypeCheck : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned IncrementalLinkerCompatible : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned EmbedBitcode : 1;
|
||||
|
||||
/// Whether to emit DWARF unwind info.
|
||||
@@ -94,8 +108,19 @@ struct AssemblerInvocation {
|
||||
|
||||
// Whether to emit compact-unwind for non-canonical entries.
|
||||
// Note: maybe overridden by other constraints.
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned Crel : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned ImplicitMapsyms : 1;
|
||||
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned X86RelaxRelocations : 1;
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned X86Sse2Avx : 1;
|
||||
|
||||
/// The name of the relocation model to use.
|
||||
std::string RelocationModel;
|
||||
|
||||
@@ -117,7 +142,7 @@ struct AssemblerInvocation {
|
||||
|
||||
public:
|
||||
AssemblerInvocation() {
|
||||
Triple = "";
|
||||
Triple = llvm::Triple();
|
||||
NoInitialTextSection = 0;
|
||||
InputFile = "-";
|
||||
OutputPath = "-";
|
||||
@@ -136,6 +161,10 @@ public:
|
||||
EmbedBitcode = 0;
|
||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||
EmitCompactUnwindNonCanonical = false;
|
||||
Crel = false;
|
||||
ImplicitMapsyms = 0;
|
||||
X86RelaxRelocations = 0;
|
||||
X86Sse2Avx = 0;
|
||||
}
|
||||
|
||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||
|
||||
+3
-3
@@ -27,9 +27,9 @@ bool tinygo_clang_driver(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
|
||||
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false);
|
||||
clang::DiagnosticOptions DiagOpts;
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
|
||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), DiagOpts, &DiagnosticPrinter, false);
|
||||
|
||||
// Create the clang driver
|
||||
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||
|
||||
+1
-13
@@ -3,7 +3,6 @@ package builder
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -18,7 +17,7 @@ import (
|
||||
var commands = map[string][]string{}
|
||||
|
||||
func init() {
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
llvmMajor, _, _ := strings.Cut(llvm.Version, ".")
|
||||
commands["clang"] = []string{"clang-" + llvmMajor}
|
||||
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
||||
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
||||
@@ -76,14 +75,3 @@ func LookupCommand(name string) (string, error) {
|
||||
}
|
||||
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
}
|
||||
|
||||
func execCommand(name string, args ...string) error {
|
||||
name, err := LookupCommand(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
+27
-6
@@ -2,6 +2,7 @@ package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
@@ -23,20 +24,40 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
spec.OpenOCDCommands = options.OpenOCDCommands
|
||||
}
|
||||
|
||||
major, minor, err := goenv.GetGorootVersion()
|
||||
// Version range supported by TinyGo.
|
||||
const minorMin = 25 // when updating the min version, also update .github/workflows/compat.yml
|
||||
const minorMax = 27
|
||||
|
||||
// Check that we support this Go toolchain version.
|
||||
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if major != 1 || minor < 19 || minor > 22 {
|
||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||
return nil, fmt.Errorf("requires go version 1.19 through 1.22, got go%d.%d", major, minor)
|
||||
|
||||
if options.GoCompatibility {
|
||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the Go toolchain version isn't too new, if we haven't been
|
||||
// compiled with the latest Go version.
|
||||
// This may be a bit too aggressive: if the newer version doesn't change the
|
||||
// Go language we will most likely be able to compile it.
|
||||
buildMajor, buildMinor, _, err := goenv.Parse(runtime.Version())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buildMajor != 1 || buildMinor < gorootMinor {
|
||||
return nil, fmt.Errorf("cannot compile with Go toolchain version go%d.%d (TinyGo was built using toolchain version %s)", gorootMajor, gorootMinor, runtime.Version())
|
||||
}
|
||||
|
||||
return &compileopts.Config{
|
||||
Options: options,
|
||||
Target: spec,
|
||||
GoMinorVersion: minor,
|
||||
GoMinorVersion: gorootMinor,
|
||||
TestConfig: options.TestConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
||||
return &compileJob{
|
||||
description: "compile Darwin libSystem.dylib",
|
||||
run: func(job *compileJob) (err error) {
|
||||
arch := strings.Split(config.Triple(), "-")[0]
|
||||
arch, _, _ := strings.Cut(config.Triple(), "-")
|
||||
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")
|
||||
|
||||
+150
-14
@@ -23,7 +23,7 @@ type espImageSegment struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
// makeESPFirmareImage converts an input ELF file to an image file for an ESP32 or
|
||||
// makeESPFirmwareImage converts an input ELF file to an image file for an ESP32 or
|
||||
// ESP8266 chip. This is a special purpose image format just for the ESP chip
|
||||
// family, and is parsed by the on-chip mask ROM bootloader.
|
||||
//
|
||||
@@ -31,7 +31,7 @@ type espImageSegment struct {
|
||||
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
||||
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
|
||||
// https://github.com/espressif/esptool/blob/master/esptool.py
|
||||
func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
inf, err := elf.Open(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -65,15 +65,6 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
// Sort the segments by address. This is what esptool does too.
|
||||
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
|
||||
|
||||
// Calculate checksum over the segment data. This is used in the image
|
||||
// footer.
|
||||
checksum := uint8(0xef)
|
||||
for _, segment := range segments {
|
||||
for _, b := range segment.data {
|
||||
checksum ^= b
|
||||
}
|
||||
}
|
||||
|
||||
// Write first to an in-memory buffer, primarily so that we can easily
|
||||
// calculate a hash over the entire image.
|
||||
// An added benefit is that we don't need to check for errors all the time.
|
||||
@@ -88,6 +79,86 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
chip = format[:len(format)-len("-img")]
|
||||
}
|
||||
|
||||
// For ESP32 (original): separate RAM segments (loadable by ROM bootloader)
|
||||
// from flash-mapped segments (DROM/IROM, require MMU setup by startup code).
|
||||
// The ROM bootloader on ESP32 does NOT handle flash-mapped segments —
|
||||
// it tries to memcpy to the virtual address, which crashes.
|
||||
var flashSegments []*espImageSegment
|
||||
if chip == "esp32" {
|
||||
var ramSegments []*espImageSegment
|
||||
for _, seg := range segments {
|
||||
if (seg.addr >= 0x3F400000 && seg.addr < 0x3F800000) || // DROM
|
||||
(seg.addr >= 0x400D0000 && seg.addr < 0x40400000) { // IROM
|
||||
flashSegments = append(flashSegments, seg)
|
||||
} else {
|
||||
ramSegments = append(ramSegments, seg)
|
||||
}
|
||||
}
|
||||
segments = ramSegments
|
||||
}
|
||||
|
||||
// ESP32 flash XIP: compute where the DROM segment will be placed in flash
|
||||
// (page-aligned, right after the RAM segments) and patch the
|
||||
// _drom_flash_addr variable so the startup code can program the cache MMU.
|
||||
// This must happen before the checksum/hash are computed so the patched
|
||||
// value is covered by both.
|
||||
const esp32FlashBase = 0x1000 // esptool flashes the image at 0x1000
|
||||
// The ESP32 flash cache MMU supports configurable page sizes down to 256 B. 64 KiB is the reset/default size.
|
||||
// If the startup code ever changes the MMU page size, this constant must change too.
|
||||
const esp32PageSize = 0x10000 // 64KB MMU pages
|
||||
var esp32DromFlashAddr uint32
|
||||
if chip == "esp32" && len(flashSegments) > 0 {
|
||||
// Compute the size of the RAM portion of the image (everything the ROM
|
||||
// bootloader loads, up to and including the appended SHA256 hash).
|
||||
ramImageSize := 0
|
||||
if makeImage {
|
||||
ramImageSize += 4096
|
||||
}
|
||||
ramImageSize += 24 // image header (8) + trailer fields (16)
|
||||
for _, seg := range segments {
|
||||
ramImageSize += 8 + len(seg.data) // segment header + data (4-aligned)
|
||||
}
|
||||
ramImageSize += 16 - ramImageSize%16 // footer padding + checksum byte
|
||||
ramImageSize += 32 // appended SHA256 hash
|
||||
|
||||
// DROM flash address must be 64KB page-aligned.
|
||||
esp32DromFlashAddr = uint32(esp32FlashBase+ramImageSize+esp32PageSize-1) &^ (esp32PageSize - 1)
|
||||
|
||||
// Patch _drom_flash_addr in whichever RAM segment contains it.
|
||||
syms, _ := inf.Symbols()
|
||||
var dromSymAddr uint64
|
||||
for _, s := range syms {
|
||||
if s.Name == "_drom_flash_addr" {
|
||||
dromSymAddr = s.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
if dromSymAddr == 0 {
|
||||
return fmt.Errorf("ESP32: _drom_flash_addr symbol not found")
|
||||
}
|
||||
patched := false
|
||||
for _, seg := range segments {
|
||||
if dromSymAddr >= uint64(seg.addr) && dromSymAddr+4 <= uint64(seg.addr)+uint64(len(seg.data)) {
|
||||
off := int(dromSymAddr - uint64(seg.addr))
|
||||
binary.LittleEndian.PutUint32(seg.data[off:], esp32DromFlashAddr)
|
||||
patched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !patched {
|
||||
return fmt.Errorf("ESP32: _drom_flash_addr (0x%x) not in any RAM segment", dromSymAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate checksum over the segment data. This is used in the image
|
||||
// footer.
|
||||
checksum := uint8(0xef)
|
||||
for _, segment := range segments {
|
||||
for _, b := range segment.data {
|
||||
checksum ^= b
|
||||
}
|
||||
}
|
||||
|
||||
if makeImage {
|
||||
// The bootloader starts at 0x1000, or 4096.
|
||||
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||
@@ -100,11 +171,24 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
"esp32c6": 0x000d,
|
||||
"esp32s3": 0x0009,
|
||||
}[chip]
|
||||
|
||||
// SPI flash speed/size byte (byte 3 of header):
|
||||
// Upper nibble = flash size, lower nibble = flash frequency.
|
||||
// The espflasher auto-detects and patches the flash size (upper nibble),
|
||||
// but the frequency (lower nibble) must be correct per chip.
|
||||
spiSpeedSize := map[string]uint8{
|
||||
"esp32": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
"esp32c3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
"esp32c6": 0x10, // 80MHz=0x00, 2MB=0x10 (C6 uses different freq encoding)
|
||||
"esp32s3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||
}[chip]
|
||||
|
||||
// Image header.
|
||||
switch chip {
|
||||
case "esp32", "esp32c3":
|
||||
case "esp32", "esp32c3", "esp32s3", "esp32c6":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
// Note: not adding a SHA256 hash as the binary is modified by
|
||||
@@ -125,8 +209,8 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
}{
|
||||
magic: 0xE9,
|
||||
segment_count: byte(len(segments)),
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||
spi_speed_size: spiSpeedSize,
|
||||
entry_addr: uint32(inf.Entry),
|
||||
wp_pin: 0xEE, // disable WP pin
|
||||
chip_id: chip_id,
|
||||
@@ -178,6 +262,58 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
outf.Write(hash[:])
|
||||
}
|
||||
|
||||
// For ESP32: append flash-mapped segments (DROM/IROM) at page-aligned flash
|
||||
// offsets after the RAM portion. The startup code maps them via the flash
|
||||
// cache MMU (DROM at esp32DromFlashAddr, patched into _drom_flash_addr).
|
||||
if len(flashSegments) > 0 {
|
||||
const flashBase = esp32FlashBase
|
||||
const pageSize = esp32PageSize
|
||||
dromFlashAddr := esp32DromFlashAddr
|
||||
|
||||
// Separate DROM and IROM segments.
|
||||
var dromSegs, iromSegs []*espImageSegment
|
||||
for _, seg := range flashSegments {
|
||||
if seg.addr >= 0x3F400000 && seg.addr < 0x3F800000 {
|
||||
dromSegs = append(dromSegs, seg)
|
||||
} else {
|
||||
iromSegs = append(iromSegs, seg)
|
||||
}
|
||||
}
|
||||
|
||||
// Write DROM segments at the computed page-aligned flash offset.
|
||||
dromSize := 0
|
||||
if len(dromSegs) > 0 {
|
||||
targetImageOffset := int(dromFlashAddr - flashBase)
|
||||
if outf.Len() > targetImageOffset {
|
||||
return fmt.Errorf("ESP32: RAM segments too large (%d bytes), overlap DROM at flash 0x%x", outf.Len(), dromFlashAddr)
|
||||
}
|
||||
outf.Write(make([]byte, targetImageOffset-outf.Len()))
|
||||
for _, seg := range dromSegs {
|
||||
outf.Write(seg.data)
|
||||
dromSize += len(seg.data)
|
||||
}
|
||||
}
|
||||
|
||||
// Write IROM segments immediately after DROM, at the next page boundary.
|
||||
// IROM flash addr = dromFlashAddr + ceil(dromSize/pageSize)*pageSize
|
||||
// (must match the computation in the startup assembly).
|
||||
if len(iromSegs) > 0 {
|
||||
dromPages := (dromSize + pageSize - 1) / pageSize
|
||||
if dromPages == 0 {
|
||||
dromPages = 1
|
||||
}
|
||||
iromFlashAddr := dromFlashAddr + uint32(dromPages)*pageSize
|
||||
targetImageOffset := int(iromFlashAddr - flashBase)
|
||||
if outf.Len() > targetImageOffset {
|
||||
return fmt.Errorf("ESP32: DROM too large, overlaps IROM at flash 0x%x", iromFlashAddr)
|
||||
}
|
||||
outf.Write(make([]byte, targetImageOffset-outf.Len()))
|
||||
for _, seg := range iromSegs {
|
||||
outf.Write(seg.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||
// image to be a certain size.
|
||||
if makeImage {
|
||||
|
||||
+8
-18
@@ -17,14 +17,6 @@ import (
|
||||
// concurrency or performance issues.
|
||||
const jobRunnerDebug = false
|
||||
|
||||
type jobState uint8
|
||||
|
||||
const (
|
||||
jobStateQueued jobState = iota // not yet running
|
||||
jobStateRunning // running
|
||||
jobStateFinished // finished running
|
||||
)
|
||||
|
||||
// compileJob is a single compiler job, comparable to a single Makefile target.
|
||||
// It is used to orchestrate various compiler tasks that can be run in parallel
|
||||
// but that have dependencies and thus have limitations in how they can be run.
|
||||
@@ -55,12 +47,11 @@ func dummyCompileJob(result string) *compileJob {
|
||||
// ordered as such in the job dependencies.
|
||||
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.
|
||||
// Have a default, if the semaphore isn't set. This is useful for tests.
|
||||
sema = make(chan struct{}, runtime.NumCPU())
|
||||
}
|
||||
if cap(sema) == 0 {
|
||||
return errors.New("cannot 0 jobs at a time")
|
||||
return errors.New("cannot run 0 jobs at a time")
|
||||
}
|
||||
|
||||
// Create a slice of jobs to run, where all dependencies are run in order.
|
||||
@@ -81,10 +72,10 @@ func runJobs(job *compileJob, sema chan struct{}) error {
|
||||
|
||||
waiting := make(map[*compileJob]map[*compileJob]struct{}, len(jobs))
|
||||
dependents := make(map[*compileJob][]*compileJob, len(jobs))
|
||||
jidx := make(map[*compileJob]int)
|
||||
compileJobs := make(map[*compileJob]int)
|
||||
var ready intHeap
|
||||
for i, job := range jobs {
|
||||
jidx[job] = i
|
||||
compileJobs[job] = i
|
||||
if len(job.dependencies) == 0 {
|
||||
// This job is ready to run.
|
||||
ready.Push(i)
|
||||
@@ -105,8 +96,7 @@ func runJobs(job *compileJob, sema chan struct{}) error {
|
||||
// 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.
|
||||
// Send each job in the jobs slice to a worker, taking care of job dependencies.
|
||||
numRunningJobs := 0
|
||||
var totalTime time.Duration
|
||||
start := time.Now()
|
||||
@@ -156,7 +146,7 @@ func runJobs(job *compileJob, sema chan struct{}) error {
|
||||
delete(wait, completed)
|
||||
if len(wait) == 0 {
|
||||
// This job is now ready to run.
|
||||
ready.Push(jidx[j])
|
||||
ready.Push(compileJobs[j])
|
||||
delete(waiting, j)
|
||||
}
|
||||
}
|
||||
@@ -205,11 +195,11 @@ type intHeap struct {
|
||||
sort.IntSlice
|
||||
}
|
||||
|
||||
func (h *intHeap) Push(x interface{}) {
|
||||
func (h *intHeap) Push(x any) {
|
||||
h.IntSlice = append(h.IntSlice, x.(int))
|
||||
}
|
||||
|
||||
func (h *intHeap) Pop() interface{} {
|
||||
func (h *intHeap) Pop() any {
|
||||
x := h.IntSlice[len(h.IntSlice)-1]
|
||||
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
||||
return x
|
||||
|
||||
+49
-16
@@ -15,6 +15,9 @@ import (
|
||||
|
||||
// Library is a container for information about a single C library, such as a
|
||||
// compiler runtime or libc.
|
||||
//
|
||||
// Note: whenever a library gets changed, the version in compileopts/config.go
|
||||
// probably also needs to be incremented.
|
||||
type Library struct {
|
||||
// The library name, such as compiler-rt or picolibc.
|
||||
name string
|
||||
@@ -25,11 +28,17 @@ type Library struct {
|
||||
// cflags returns the C flags specific to this library
|
||||
cflags func(target, headerPath string) []string
|
||||
|
||||
// cflagsForFile returns additional C flags for a particular source file.
|
||||
cflagsForFile func(path string) []string
|
||||
|
||||
// needsLibc is set to true if this library needs libc headers.
|
||||
needsLibc bool
|
||||
|
||||
// The source directory.
|
||||
sourceDir func() string
|
||||
|
||||
// The source files, relative to sourceDir.
|
||||
librarySources func(target string) ([]string, error)
|
||||
librarySources func(target string, libcNeedsMalloc bool) ([]string, error)
|
||||
|
||||
// The source code for the crt1.o file, relative to sourceDir.
|
||||
crt1Source string
|
||||
@@ -44,13 +53,8 @@ type Library struct {
|
||||
// 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, abortLock func(), err error) {
|
||||
outdir, precompiled := config.LibcPath(l.name)
|
||||
outdir := config.LibraryPath(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), 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.
|
||||
@@ -129,7 +133,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// 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", "-gdwarf-4", "-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="+compileopts.ClangTriple(target), "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
resourceDir := goenv.ClangResourceDir(false)
|
||||
if resourceDir != "" {
|
||||
args = append(args, "-resource-dir="+resourceDir)
|
||||
@@ -163,9 +167,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
case "riscv32":
|
||||
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
||||
args = append(args, "-march="+riscvMarch(config, "rv32imac"), "-fforce-enable-int128")
|
||||
case "riscv64":
|
||||
args = append(args, "-march=rv64gc")
|
||||
args = append(args, "-march="+riscvMarch(config, "rv64gc"))
|
||||
case "mips":
|
||||
args = append(args, "-fno-pic")
|
||||
}
|
||||
@@ -173,6 +177,16 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// Use softfloat instead of floating point instructions. This is
|
||||
// supported on many architectures.
|
||||
args = append(args, "-msoft-float")
|
||||
} else {
|
||||
if strings.HasPrefix(target, "armv5") {
|
||||
// On ARMv5 we need to explicitly enable hardware floating point
|
||||
// instructions: Clang appears to assume the hardware doesn't have a
|
||||
// FPU otherwise.
|
||||
args = append(args, "-mfpu=vfpv2")
|
||||
}
|
||||
}
|
||||
if l.needsLibc {
|
||||
args = append(args, config.LibcCFlags()...)
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
@@ -204,7 +218,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
return os.Rename(f.Name(), archiveFilePath)
|
||||
return robustRename(f.Name(), archiveFilePath)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -212,7 +226,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
|
||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||
// archive job above, so must be run first.
|
||||
paths, err := l.librarySources(target)
|
||||
paths, err := l.librarySources(target, config.LibcNeedsMalloc())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -226,11 +240,14 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
objpath := filepath.Join(dir, cleanpath+".o")
|
||||
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||
objs = append(objs, objpath)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
objfile := &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
if l.cflagsForFile != nil {
|
||||
compileArgs = append(compileArgs, l.cflagsForFile(path)...)
|
||||
}
|
||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", compileArgs...)
|
||||
@@ -241,7 +258,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
job.dependencies = append(job.dependencies, objfile)
|
||||
}
|
||||
|
||||
// Create crt1.o job, if needed.
|
||||
@@ -250,7 +268,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// won't make much of a difference in speed).
|
||||
if l.crt1Source != "" {
|
||||
srcpath := filepath.Join(sourceDir, l.crt1Source)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
crt1Job := &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
var compileArgs []string
|
||||
@@ -270,7 +288,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
}
|
||||
return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o"))
|
||||
},
|
||||
})
|
||||
}
|
||||
job.dependencies = append(job.dependencies, crt1Job)
|
||||
}
|
||||
|
||||
ok = true
|
||||
@@ -278,3 +297,17 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
once.Do(unlock)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// riscvMarch returns the -march value for RISC-V library compilation.
|
||||
// It extracts the value from the target's cflags if present, otherwise
|
||||
// falls back to the provided default. This ensures libraries are compiled
|
||||
// with the correct ISA extensions for each target (e.g. rv32imc for
|
||||
// ESP32-C3 which lacks the atomic extension).
|
||||
func riscvMarch(config *compileopts.Config, defaultMarch string) string {
|
||||
for _, flag := range config.Target.CFlags {
|
||||
if strings.HasPrefix(flag, "-march=") {
|
||||
return flag[len("-march="):]
|
||||
}
|
||||
}
|
||||
return defaultMarch
|
||||
}
|
||||
|
||||
+87
-33
@@ -30,25 +30,62 @@ var libMinGW = Library{
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
|
||||
cflags: func(target, headerPath string) []string {
|
||||
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
|
||||
return []string{
|
||||
flags := []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", mingwDir + "/mingw-w64-crt/include",
|
||||
"-isystem", mingwDir + "/mingw-w64-headers/crt",
|
||||
"-isystem", mingwDir + "/mingw-w64-headers/include",
|
||||
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
|
||||
"-I" + headerPath,
|
||||
}
|
||||
if strings.Split(target, "-")[0] == "i386" {
|
||||
flags = append(flags,
|
||||
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
|
||||
"-D_WIN32_WINNT=0x0501", // target Windows XP
|
||||
"-D_CRTBLD",
|
||||
"-Wno-pragma-pack",
|
||||
)
|
||||
}
|
||||
return flags
|
||||
},
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
librarySources: func(target string, _ bool) ([]string, error) {
|
||||
// These files are needed so that printf and the like are supported.
|
||||
sources := []string{
|
||||
"mingw-w64-crt/stdio/ucrt_fprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_printf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_snprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_sprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
|
||||
var sources []string
|
||||
if strings.Split(target, "-")[0] == "i386" {
|
||||
// Old 32-bit x86 systems use msvcrt.dll.
|
||||
sources = []string{
|
||||
"mingw-w64-crt/crt/pseudo-reloc.c",
|
||||
"mingw-w64-crt/gdtoa/dmisc.c",
|
||||
"mingw-w64-crt/gdtoa/gdtoa.c",
|
||||
"mingw-w64-crt/gdtoa/gmisc.c",
|
||||
"mingw-w64-crt/gdtoa/misc.c",
|
||||
"mingw-w64-crt/math/x86/exp2.S",
|
||||
"mingw-w64-crt/math/x86/trunc.S",
|
||||
"mingw-w64-crt/misc/___mb_cur_max_func.c",
|
||||
"mingw-w64-crt/misc/lc_locale_func.c",
|
||||
"mingw-w64-crt/misc/mbrtowc.c",
|
||||
"mingw-w64-crt/misc/strnlen.c",
|
||||
"mingw-w64-crt/misc/wcrtomb.c",
|
||||
"mingw-w64-crt/misc/wcsnlen.c",
|
||||
"mingw-w64-crt/stdio/acrt_iob_func.c",
|
||||
"mingw-w64-crt/stdio/mingw_lock.c",
|
||||
"mingw-w64-crt/stdio/mingw_pformat.c",
|
||||
"mingw-w64-crt/stdio/mingw_vfprintf.c",
|
||||
"mingw-w64-crt/stdio/mingw_vsnprintf.c",
|
||||
}
|
||||
} else {
|
||||
// Anything somewhat modern (amd64, arm64) uses UCRT.
|
||||
sources = []string{
|
||||
"mingw-w64-crt/stdio/ucrt_fprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_printf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_snprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_sprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
|
||||
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
|
||||
}
|
||||
}
|
||||
return sources, nil
|
||||
},
|
||||
@@ -63,27 +100,41 @@ var libMinGW = Library{
|
||||
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
|
||||
// .lib file. But to simplify things, we're going to leave them as separate
|
||||
// files.
|
||||
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.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",
|
||||
"api-ms-win-crt-locale-l1-1-0.def",
|
||||
"api-ms-win-crt-math-l1-1-0.def.in",
|
||||
"api-ms-win-crt-multibyte-l1-1-0.def",
|
||||
"api-ms-win-crt-private-l1-1-0.def.in",
|
||||
"api-ms-win-crt-process-l1-1-0.def",
|
||||
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
||||
"api-ms-win-crt-stdio-l1-1-0.def",
|
||||
"api-ms-win-crt-string-l1-1-0.def",
|
||||
"api-ms-win-crt-time-l1-1-0.def",
|
||||
"api-ms-win-crt-utility-l1-1-0.def",
|
||||
} {
|
||||
var libs []string
|
||||
if goarch == "386" {
|
||||
libs = []string{
|
||||
// x86 uses msvcrt.dll instead of UCRT for compatibility with old
|
||||
// Windows versions.
|
||||
"advapi32.def.in",
|
||||
"kernel32.def.in",
|
||||
"msvcrt.def.in",
|
||||
}
|
||||
} else {
|
||||
// Use the modernized UCRT on new systems.
|
||||
// Normally all the api-ms-win-crt-*.def files are all compiled to a
|
||||
// single .lib file. But to simplify things, we're going to leave them
|
||||
// as separate files.
|
||||
libs = []string{
|
||||
"advapi32.def.in",
|
||||
"kernel32.def.in",
|
||||
"api-ms-win-crt-conio-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",
|
||||
"api-ms-win-crt-locale-l1-1-0.def",
|
||||
"api-ms-win-crt-math-l1-1-0.def.in",
|
||||
"api-ms-win-crt-multibyte-l1-1-0.def",
|
||||
"api-ms-win-crt-private-l1-1-0.def.in",
|
||||
"api-ms-win-crt-process-l1-1-0.def",
|
||||
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
||||
"api-ms-win-crt-stdio-l1-1-0.def",
|
||||
"api-ms-win-crt-string-l1-1-0.def",
|
||||
"api-ms-win-crt-time-l1-1-0.def",
|
||||
"api-ms-win-crt-utility-l1-1-0.def",
|
||||
}
|
||||
}
|
||||
for _, name := range libs {
|
||||
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
|
||||
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
|
||||
job := &compileJob{
|
||||
@@ -93,6 +144,9 @@ func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
||||
defpath := inpath
|
||||
var archDef, emulation string
|
||||
switch goarch {
|
||||
case "386":
|
||||
archDef = "-DDEF_I386"
|
||||
emulation = "i386pe"
|
||||
case "amd64":
|
||||
archDef = "-DDEF_X64"
|
||||
emulation = "i386pep"
|
||||
|
||||
+60
-31
@@ -12,6 +12,45 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Create the alltypes.h file from the appropriate alltypes.h.in files.
|
||||
func buildMuslAllTypes(arch, muslDir, outputBitsDir string) error {
|
||||
// Create the file alltypes.h.
|
||||
f, err := os.Create(filepath.Join(outputBitsDir, "alltypes.h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infiles := []string{
|
||||
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
|
||||
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||
}
|
||||
for _, infile := range infiles {
|
||||
data, err := os.ReadFile(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.SplitSeq(string(data), "\n")
|
||||
for line := range lines {
|
||||
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||
value := matches[1]
|
||||
name := matches[2]
|
||||
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
|
||||
}
|
||||
if strings.HasPrefix(line, "STRUCT ") {
|
||||
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
|
||||
name := matches[1]
|
||||
value := matches[2]
|
||||
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
|
||||
}
|
||||
_, err := f.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
var libMusl = Library{
|
||||
name: "musl",
|
||||
makeHeaders: func(target, includeDir string) error {
|
||||
@@ -24,41 +63,13 @@ var libMusl = Library{
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl")
|
||||
|
||||
// Create the file alltypes.h.
|
||||
f, err := os.Create(filepath.Join(bits, "alltypes.h"))
|
||||
err = buildMuslAllTypes(arch, muslDir, bits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infiles := []string{
|
||||
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
|
||||
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||
}
|
||||
for _, infile := range infiles {
|
||||
data, err := os.ReadFile(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||
value := matches[1]
|
||||
name := matches[2]
|
||||
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
|
||||
}
|
||||
if strings.HasPrefix(line, "STRUCT ") {
|
||||
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
|
||||
name := matches[1]
|
||||
value := matches[2]
|
||||
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
|
||||
}
|
||||
f.WriteString(line + "\n")
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// Create the file syscall.h.
|
||||
f, err = os.Create(filepath.Join(bits, "syscall.h"))
|
||||
f, err := os.Create(filepath.Join(bits, "syscall.h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,37 +121,55 @@ var libMusl = Library{
|
||||
return cflags
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
librarySources: func(target string, _ bool) ([]string, error) {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
globs := []string{
|
||||
"conf/*.c",
|
||||
"ctype/*.c",
|
||||
"env/*.c",
|
||||
"errno/*.c",
|
||||
"exit/*.c",
|
||||
"fcntl/*.c",
|
||||
"internal/defsysinfo.c",
|
||||
"internal/intscan.c",
|
||||
"internal/libc.c",
|
||||
"internal/shgetc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"locale/*.c",
|
||||
"linux/*.c",
|
||||
"locale/*.c",
|
||||
"malloc/*.c",
|
||||
"malloc/mallocng/*.c",
|
||||
"mman/*.c",
|
||||
"math/*.c",
|
||||
"misc/*.c",
|
||||
"multibyte/*.c",
|
||||
"sched/*.c",
|
||||
"signal/" + arch + "/*.s",
|
||||
"signal/*.c",
|
||||
"stdio/*.c",
|
||||
"stdlib/*.c",
|
||||
"string/*.c",
|
||||
"thread/" + arch + "/*.s",
|
||||
"thread/*.c",
|
||||
"time/*.c",
|
||||
"unistd/*.c",
|
||||
"process/*.c",
|
||||
}
|
||||
|
||||
if arch == "arm" {
|
||||
// These files need to be added to the start for some reason.
|
||||
globs = append([]string{"thread/arm/*.c"}, globs...)
|
||||
}
|
||||
|
||||
if arch != "aarch64" && arch != "mips" {
|
||||
//aarch64 and mips have no architecture specific code, either they
|
||||
// are not supported or don't need any?
|
||||
globs = append([]string{"process/" + arch + "/*.s"}, globs...)
|
||||
}
|
||||
|
||||
var sources []string
|
||||
seenSources := map[string]struct{}{}
|
||||
basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/"
|
||||
|
||||
+2
-1
@@ -34,6 +34,7 @@ var libPicolibc = Library{
|
||||
"-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",
|
||||
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||
"-nostdlibinc",
|
||||
"-isystem", newlibDir + "/libc/include",
|
||||
"-I" + newlibDir + "/libc/tinystdio",
|
||||
@@ -42,7 +43,7 @@ var libPicolibc = Library{
|
||||
}
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
librarySources: func(target string, _ bool) ([]string, error) {
|
||||
sources := append([]string(nil), picolibcSources...)
|
||||
if !strings.HasPrefix(target, "avr") {
|
||||
// Small chips without long jumps can't compile many files (printf,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !windows
|
||||
|
||||
package builder
|
||||
|
||||
import "os"
|
||||
|
||||
func robustRename(oldpath, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const robustRenameTimeout = 2 * time.Second
|
||||
|
||||
func robustRename(oldpath, newpath string) error {
|
||||
var bestErr error
|
||||
start := time.Now()
|
||||
nextSleep := time.Millisecond
|
||||
for {
|
||||
err := os.Rename(oldpath, newpath)
|
||||
if err == nil || !isEphemeralRenameError(err) {
|
||||
return err
|
||||
}
|
||||
if bestErr == nil {
|
||||
bestErr = err
|
||||
}
|
||||
if d := time.Since(start) + nextSleep; d >= robustRenameTimeout {
|
||||
return bestErr
|
||||
}
|
||||
time.Sleep(nextSleep)
|
||||
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
|
||||
}
|
||||
}
|
||||
|
||||
func isEphemeralRenameError(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
switch errno {
|
||||
case syscall.Errno(2), // ERROR_FILE_NOT_FOUND
|
||||
syscall.Errno(5), // ERROR_ACCESS_DENIED
|
||||
syscall.Errno(32): // ERROR_SHARING_VIOLATION
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
)
|
||||
|
||||
//go:embed size-report.html
|
||||
var sizeReportBase string
|
||||
|
||||
func writeSizeReport(sizes *programSize, filename, pkgName string) error {
|
||||
tmpl, err := template.New("report").Parse(sizeReportBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open report file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Prepare data for the report.
|
||||
type sizeLine struct {
|
||||
Name string
|
||||
Size *packageSize
|
||||
}
|
||||
programData := []sizeLine{}
|
||||
for _, name := range sizes.sortedPackageNames() {
|
||||
pkgSize := sizes.Packages[name]
|
||||
programData = append(programData, sizeLine{
|
||||
Name: name,
|
||||
Size: pkgSize,
|
||||
})
|
||||
}
|
||||
sizeTotal := map[string]uint64{
|
||||
"code": sizes.Code,
|
||||
"rodata": sizes.ROData,
|
||||
"data": sizes.Data,
|
||||
"bss": sizes.BSS,
|
||||
"flash": sizes.Flash(),
|
||||
}
|
||||
|
||||
// Write the report.
|
||||
err = tmpl.Execute(f, map[string]any{
|
||||
"pkgName": pkgName,
|
||||
"sizes": programData,
|
||||
"sizeTotal": sizeTotal,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create report file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Size Report for {{.pkgName}}</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<style>
|
||||
|
||||
.table-vertical-border {
|
||||
border-left: calc(var(--bs-border-width) * 2) solid currentcolor;
|
||||
}
|
||||
|
||||
/* Hover on only the rows that are clickable. */
|
||||
.row-package:hover > * {
|
||||
--bs-table-color-state: var(--bs-table-hover-color);
|
||||
--bs-table-bg-state: var(--bs-table-hover-bg);
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-xxl">
|
||||
<h1>Size Report for {{.pkgName}}</h1>
|
||||
|
||||
<p>How much space is used by Go packages, C libraries, and other bits to set up the program environment.</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Code</strong> is the actual program code (machine code instructions).</li>
|
||||
<li><strong>Read-only data</strong> are read-only global variables. On most microcontrollers, these are stored in flash and do not take up any RAM.</li>
|
||||
<li><strong>Data</strong> are writable global variables with a non-zero initializer. On microcontrollers, they are copied from flash to RAM on reset.</li>
|
||||
<li><strong>BSS</strong> are writable global variables that are zero initialized. They do not take up any space in the binary, but do take up RAM. On microcontrollers, this area is zeroed on reset.</li>
|
||||
</ul>
|
||||
|
||||
<p>The binary size consists of code, read-only data, and data. On microcontrollers, this is exactly the size of the firmware image. On other systems, there is some extra overhead: binary metadata (headers of the ELF/MachO/COFF file), debug information, exception tables, symbol names, etc. Using <code>-no-debug</code> strips most of those.</p>
|
||||
|
||||
<h2>Program breakdown</h2>
|
||||
|
||||
<p>You can click on the rows below to see which files contribute to the binary size.</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table w-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Package</th>
|
||||
<th class="table-vertical-border">Code</th>
|
||||
<th>Read-only data</th>
|
||||
<th>Data</th>
|
||||
<th title="zero-initialized data">BSS</th>
|
||||
<th class="table-vertical-border" style="min-width: 16em">Binary size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider">
|
||||
{{range $i, $pkg := .sizes}}
|
||||
<tr class="row-package" data-collapse=".collapse-row-{{$i}}">
|
||||
<td>{{.Name}}</td>
|
||||
<td class="table-vertical-border">{{.Size.Code}}</td>
|
||||
<td>{{.Size.ROData}}</td>
|
||||
<td>{{.Size.Data}}</td>
|
||||
<td>{{.Size.BSS}}</td>
|
||||
<td class="table-vertical-border" style="background: linear-gradient(to right, var(--bs-info-bg-subtle) {{.Size.FlashPercent}}%, var(--bs-table-bg) {{.Size.FlashPercent}}%)">
|
||||
{{.Size.Flash}}
|
||||
</td>
|
||||
</tr>
|
||||
{{range $filename, $sizes := .Size.Sub}}
|
||||
<tr class="table-secondary collapse collapse-row-{{$i}}">
|
||||
<td class="ps-4">
|
||||
{{if eq $filename ""}}
|
||||
(unknown file)
|
||||
{{else}}
|
||||
{{$filename}}
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="table-vertical-border">{{$sizes.Code}}</td>
|
||||
<td>{{$sizes.ROData}}</td>
|
||||
<td>{{$sizes.Data}}</td>
|
||||
<td>{{$sizes.BSS}}</td>
|
||||
<td class="table-vertical-border" style="background: linear-gradient(to right, var(--bs-info-bg-subtle) {{$sizes.FlashPercent}}%, var(--bs-table-bg) {{$sizes.FlashPercent}}%)">
|
||||
{{$sizes.Flash}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
<tfoot class="table-group-divider">
|
||||
<tr>
|
||||
<th>Total</th>
|
||||
<td class="table-vertical-border">{{.sizeTotal.code}}</td>
|
||||
<td>{{.sizeTotal.rodata}}</td>
|
||||
<td>{{.sizeTotal.data}}</td>
|
||||
<td>{{.sizeTotal.bss}}</td>
|
||||
<td class="table-vertical-border">{{.sizeTotal.flash}}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// Make table rows toggleable to show filenames.
|
||||
for (let clickable of document.querySelectorAll('.row-package')) {
|
||||
clickable.addEventListener('click', e => {
|
||||
for (let row of document.querySelectorAll(clickable.dataset.collapse)) {
|
||||
row.classList.toggle('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+115
-50
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -24,7 +25,7 @@ const sizesDebug = false
|
||||
|
||||
// programSize contains size statistics per package of a compiled program.
|
||||
type programSize struct {
|
||||
Packages map[string]packageSize
|
||||
Packages map[string]*packageSize
|
||||
Code uint64
|
||||
ROData uint64
|
||||
Data uint64
|
||||
@@ -52,13 +53,29 @@ func (ps *programSize) RAM() uint64 {
|
||||
return ps.Data + ps.BSS
|
||||
}
|
||||
|
||||
// Return the package size information for a given package path, creating it if
|
||||
// it doesn't exist yet.
|
||||
func (ps *programSize) getPackage(path string) *packageSize {
|
||||
if field, ok := ps.Packages[path]; ok {
|
||||
return field
|
||||
}
|
||||
field := &packageSize{
|
||||
Program: ps,
|
||||
Sub: map[string]*packageSize{},
|
||||
}
|
||||
ps.Packages[path] = field
|
||||
return field
|
||||
}
|
||||
|
||||
// packageSize contains the size of a package, calculated from the linked object
|
||||
// file.
|
||||
type packageSize struct {
|
||||
Code uint64
|
||||
ROData uint64
|
||||
Data uint64
|
||||
BSS uint64
|
||||
Program *programSize
|
||||
Code uint64
|
||||
ROData uint64
|
||||
Data uint64
|
||||
BSS uint64
|
||||
Sub map[string]*packageSize
|
||||
}
|
||||
|
||||
// Flash usage in regular microcontrollers.
|
||||
@@ -71,6 +88,31 @@ func (ps *packageSize) RAM() uint64 {
|
||||
return ps.Data + ps.BSS
|
||||
}
|
||||
|
||||
// Flash usage in regular microcontrollers, as a percentage of the total flash
|
||||
// usage of the program.
|
||||
func (ps *packageSize) FlashPercent() float64 {
|
||||
return float64(ps.Flash()) / float64(ps.Program.Flash()) * 100
|
||||
}
|
||||
|
||||
// Add a single size data point to this package.
|
||||
// This must only be called while calculating package size, not afterwards.
|
||||
func (ps *packageSize) addSize(getField func(*packageSize, bool) *uint64, filename string, size uint64, isVariable bool) {
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Add size for the package.
|
||||
*getField(ps, isVariable) += size
|
||||
|
||||
// Add size for file inside package.
|
||||
sub, ok := ps.Sub[filename]
|
||||
if !ok {
|
||||
sub = &packageSize{Program: ps.Program}
|
||||
ps.Sub[filename] = sub
|
||||
}
|
||||
*getField(sub, isVariable) += size
|
||||
}
|
||||
|
||||
// A mapping of a single chunk of code or data to a file path.
|
||||
type addressLine struct {
|
||||
Address uint64
|
||||
@@ -194,11 +236,22 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset, codeAlignment uint64
|
||||
if !prevLineEntry.EndSequence {
|
||||
// The chunk describes the code from prevLineEntry to
|
||||
// lineEntry.
|
||||
path := prevLineEntry.File.Name
|
||||
if runtime.GOOS == "windows" {
|
||||
// Work around a Clang bug on Windows:
|
||||
// https://github.com/llvm/llvm-project/issues/117317
|
||||
path = strings.ReplaceAll(path, "\\\\", "\\")
|
||||
|
||||
// wasi-libc likes to use forward slashes, but we
|
||||
// canonicalize everything to use backwards slashes as
|
||||
// is common on Windows.
|
||||
path = strings.ReplaceAll(path, "/", "\\")
|
||||
}
|
||||
line := addressLine{
|
||||
Address: prevLineEntry.Address + codeOffset,
|
||||
Length: lineEntry.Address - prevLineEntry.Address,
|
||||
Align: codeAlignment,
|
||||
File: prevLineEntry.File.Name,
|
||||
File: path,
|
||||
}
|
||||
if line.Length != 0 {
|
||||
addresses = append(addresses, line)
|
||||
@@ -437,7 +490,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
continue
|
||||
}
|
||||
if section.Type == elf.SHT_NOBITS {
|
||||
if section.Name == ".stack" {
|
||||
if strings.HasPrefix(section.Name, ".stack") {
|
||||
// TinyGo emits stack sections on microcontroller using the
|
||||
// ".stack" name.
|
||||
// This is a bit ugly, but I don't think there is a way to
|
||||
@@ -448,8 +501,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
Align: section.Addralign,
|
||||
Type: memoryStack,
|
||||
})
|
||||
} else {
|
||||
// Regular .bss section.
|
||||
} else if section.Flags&elf.SHF_WRITE != 0 {
|
||||
// Regular .bss section. Zero-initialized RAM is always
|
||||
// writable, so require SHF_WRITE here.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
@@ -457,6 +511,11 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
Type: memoryBSS,
|
||||
})
|
||||
}
|
||||
// Other (non-writable) SHT_NOBITS sections are address-space
|
||||
// placeholders that occupy no RAM, such as the ESP linker
|
||||
// script's .irom_dummy / .rodata_dummy sections which reserve
|
||||
// the flash-mapped XIP virtual address ranges. They must not be
|
||||
// counted as bss/RAM usage.
|
||||
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_EXECINSTR != 0 {
|
||||
// .text
|
||||
sections = append(sections, memorySection{
|
||||
@@ -773,49 +832,40 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
|
||||
// Now finally determine the binary/RAM size usage per package by going
|
||||
// through each allocated section.
|
||||
sizes := make(map[string]packageSize)
|
||||
sizes := make(map[string]*packageSize)
|
||||
program := &programSize{
|
||||
Packages: sizes,
|
||||
}
|
||||
for _, section := range sections {
|
||||
switch section.Type {
|
||||
case memoryCode:
|
||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||
field := sizes[path]
|
||||
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||
if isVariable {
|
||||
field.ROData += size
|
||||
} else {
|
||||
field.Code += size
|
||||
return &ps.ROData
|
||||
}
|
||||
sizes[path] = field
|
||||
return &ps.Code
|
||||
}, packagePathMap)
|
||||
case memoryROData:
|
||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||
field := sizes[path]
|
||||
field.ROData += size
|
||||
sizes[path] = field
|
||||
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||
return &ps.ROData
|
||||
}, packagePathMap)
|
||||
case memoryData:
|
||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||
field := sizes[path]
|
||||
field.Data += size
|
||||
sizes[path] = field
|
||||
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||
return &ps.Data
|
||||
}, packagePathMap)
|
||||
case memoryBSS:
|
||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||
field := sizes[path]
|
||||
field.BSS += size
|
||||
sizes[path] = field
|
||||
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||
return &ps.BSS
|
||||
}, packagePathMap)
|
||||
case memoryStack:
|
||||
// We store the C stack as a pseudo-package.
|
||||
sizes["C stack"] = packageSize{
|
||||
BSS: section.Size,
|
||||
}
|
||||
program.getPackage("C stack").addSize(func(ps *packageSize, isVariable bool) *uint64 {
|
||||
return &ps.BSS
|
||||
}, "", section.Size, false)
|
||||
}
|
||||
}
|
||||
|
||||
// ...and summarize the results.
|
||||
program := &programSize{
|
||||
Packages: sizes,
|
||||
}
|
||||
for _, pkg := range sizes {
|
||||
program.Code += pkg.Code
|
||||
program.ROData += pkg.ROData
|
||||
@@ -826,8 +876,8 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
}
|
||||
|
||||
// readSection determines for each byte in this section to which package it
|
||||
// belongs. It reports this usage through the addSize callback.
|
||||
func readSection(section memorySection, addresses []addressLine, addSize func(string, uint64, bool), packagePathMap map[string]string) {
|
||||
// belongs.
|
||||
func readSection(section memorySection, addresses []addressLine, program *programSize, getField func(*packageSize, bool) *uint64, packagePathMap map[string]string) {
|
||||
// The addr variable tracks at which address we are while going through this
|
||||
// section. We start at the beginning.
|
||||
addr := section.Address
|
||||
@@ -849,9 +899,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
addrAligned := (addr + line.Align - 1) &^ (line.Align - 1)
|
||||
if line.Align > 1 && addrAligned >= line.Address {
|
||||
// It is, assume that's what causes the gap.
|
||||
addSize("(padding)", line.Address-addr, true)
|
||||
program.getPackage("(padding)").addSize(getField, "", line.Address-addr, true)
|
||||
} else {
|
||||
addSize("(unknown)", line.Address-addr, false)
|
||||
program.getPackage("(unknown)").addSize(getField, "", line.Address-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: unknown (gap), alignment=%d\n", addr, line.Address, line.Address-addr, line.Align)
|
||||
}
|
||||
@@ -873,7 +923,8 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
length = line.Length - (addr - line.Address)
|
||||
}
|
||||
// Finally, mark this chunk of memory as used by the given package.
|
||||
addSize(findPackagePath(line.File, packagePathMap), length, line.IsVariable)
|
||||
packagePath, filename := findPackagePath(line.File, packagePathMap)
|
||||
program.getPackage(packagePath).addSize(getField, filename, length, line.IsVariable)
|
||||
addr = line.Address + line.Length
|
||||
}
|
||||
if addr < sectionEnd {
|
||||
@@ -882,9 +933,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
if section.Align > 1 && addrAligned >= sectionEnd {
|
||||
// The gap is caused by the section alignment.
|
||||
// For example, if a .rodata section ends with a non-aligned string.
|
||||
addSize("(padding)", sectionEnd-addr, true)
|
||||
program.getPackage("(padding)").addSize(getField, "", sectionEnd-addr, true)
|
||||
} else {
|
||||
addSize("(unknown)", sectionEnd-addr, false)
|
||||
program.getPackage("(unknown)").addSize(getField, "", sectionEnd-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: unknown (end), alignment=%d\n", addr, sectionEnd, sectionEnd-addr, section.Align)
|
||||
}
|
||||
@@ -894,17 +945,29 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
|
||||
// findPackagePath returns the Go package (or a pseudo package) for the given
|
||||
// path. It uses some heuristics, for example for some C libraries.
|
||||
func findPackagePath(path string, packagePathMap map[string]string) string {
|
||||
func findPackagePath(path string, packagePathMap map[string]string) (packagePath, filename string) {
|
||||
// Check whether this path is part of one of the compiled packages.
|
||||
packagePath, ok := packagePathMap[filepath.Dir(path)]
|
||||
if !ok {
|
||||
if ok {
|
||||
// Directory is known as a Go package.
|
||||
// Add the file itself as well.
|
||||
filename = filepath.Base(path)
|
||||
} else {
|
||||
if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")) {
|
||||
// Emit C libraries (in the lib subdirectory of TinyGo) as a single
|
||||
// package, with a "C" prefix. For example: "C compiler-rt" for the
|
||||
// compiler runtime library from LLVM.
|
||||
packagePath = "C " + strings.Split(strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")), string(os.PathSeparator))[1]
|
||||
} else if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project")) {
|
||||
// package, with a "C" prefix. For example: "C picolibc" for the
|
||||
// baremetal libc.
|
||||
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator))
|
||||
parts := strings.SplitN(libPath, string(os.PathSeparator), 2)
|
||||
packagePath = "C " + parts[0]
|
||||
if len(parts) > 1 {
|
||||
filename = parts[1]
|
||||
} else {
|
||||
filename = parts[0]
|
||||
}
|
||||
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
|
||||
packagePath = "C compiler-rt"
|
||||
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
|
||||
} else if packageSymbolRegexp.MatchString(path) {
|
||||
// Parse symbol names like main$alloc or runtime$string.
|
||||
packagePath = path[:strings.LastIndex(path, "$")]
|
||||
@@ -927,9 +990,11 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
|
||||
// fixed in the compiler.
|
||||
packagePath = "-"
|
||||
} else {
|
||||
// This is some other path. Not sure what it is, so just emit its directory.
|
||||
packagePath = filepath.Dir(path) // fallback
|
||||
// This is some other path. Not sure what it is, so just emit its
|
||||
// directory as a fallback.
|
||||
packagePath = filepath.Dir(path)
|
||||
filename = filepath.Base(path)
|
||||
}
|
||||
}
|
||||
return packagePath
|
||||
return
|
||||
}
|
||||
|
||||
+70
-24
@@ -1,6 +1,7 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -41,40 +42,20 @@ func TestBinarySize(t *testing.T) {
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 4484, 280, 0, 2252},
|
||||
{"microbit", "examples/serial", 2732, 388, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 6016, 1484, 116, 6816},
|
||||
{"hifive1b", "examples/echo", 4313, 323, 0, 2260},
|
||||
{"microbit", "examples/serial", 2838, 382, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 8027, 1665, 132, 7488},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
// output varies by binaryen version.
|
||||
}
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.target+"/"+tc.path, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Build the binary.
|
||||
options := compileopts.Options{
|
||||
Target: tc.target,
|
||||
Opt: "z",
|
||||
Semaphore: sema,
|
||||
InterpTimeout: 60 * time.Second,
|
||||
Debug: true,
|
||||
VerifyIR: true,
|
||||
}
|
||||
target, err := compileopts.LoadTarget(&options)
|
||||
if err != nil {
|
||||
t.Fatal("could not load target:", err)
|
||||
}
|
||||
config := &compileopts.Config{
|
||||
Options: &options,
|
||||
Target: target,
|
||||
}
|
||||
result, err := Build(tc.path, "", t.TempDir(), config)
|
||||
if err != nil {
|
||||
t.Fatal("could not build:", err)
|
||||
}
|
||||
result := buildBinary(t, tc.target, tc.path)
|
||||
|
||||
// Check whether the size of the binary matches the expected size.
|
||||
sizes, err := loadProgramSize(result.Executable, nil)
|
||||
@@ -90,3 +71,68 @@ func TestBinarySize(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the -size=full flag attributes binary size to the correct package
|
||||
// without filesystem paths and things like that.
|
||||
func TestSizeFull(t *testing.T) {
|
||||
tests := []string{
|
||||
"microbit",
|
||||
"wasip1",
|
||||
}
|
||||
|
||||
libMatch := regexp.MustCompile(`^C [a-z -]+$`) // example: "C interrupt vector"
|
||||
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
||||
|
||||
for _, target := range tests {
|
||||
t.Run(target, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Build the binary.
|
||||
result := buildBinary(t, target, "examples/serial")
|
||||
|
||||
// Check whether the binary doesn't contain any unexpected package
|
||||
// names.
|
||||
sizes, err := loadProgramSize(result.Executable, result.PackagePathMap)
|
||||
if err != nil {
|
||||
t.Fatal("could not read program size:", err)
|
||||
}
|
||||
for _, pkg := range sizes.sortedPackageNames() {
|
||||
if pkg == "(padding)" || pkg == "(unknown)" || pkg == "Go types" {
|
||||
// TODO: correctly attribute all unknown binary size.
|
||||
continue
|
||||
}
|
||||
if libMatch.MatchString(pkg) {
|
||||
continue
|
||||
}
|
||||
if pkgMatch.MatchString(pkg) {
|
||||
continue
|
||||
}
|
||||
t.Error("unexpected package name in size output:", pkg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildBinary(t *testing.T, targetString, pkgName string) BuildResult {
|
||||
options := compileopts.Options{
|
||||
Target: targetString,
|
||||
Opt: "z",
|
||||
Semaphore: sema,
|
||||
InterpTimeout: 60 * time.Second,
|
||||
Debug: true,
|
||||
VerifyIR: true,
|
||||
}
|
||||
target, err := compileopts.LoadTarget(&options)
|
||||
if err != nil {
|
||||
t.Fatal("could not load target:", err)
|
||||
}
|
||||
config := &compileopts.Config{
|
||||
Options: &options,
|
||||
Target: target,
|
||||
}
|
||||
result, err := Build(pkgName, "", t.TempDir(), config)
|
||||
if err != nil {
|
||||
t.Fatal("could not build:", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func RunTool(tool string, args ...string) error {
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
defer C.free(buf)
|
||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)]
|
||||
cflags := unsafe.Slice((**C.char)(buf), len(args))
|
||||
for i, flag := range args {
|
||||
cflag := C.CString(flag)
|
||||
cflags[i] = cflag
|
||||
|
||||
+49
-14
@@ -14,16 +14,45 @@ import (
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
func runCCompiler(flags ...string) error {
|
||||
// Find the right command to run Clang.
|
||||
var cmd *exec.Cmd
|
||||
if hasBuiltinTools {
|
||||
// Compile this with the internal Clang compiler.
|
||||
cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
cmd = exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
|
||||
} else {
|
||||
// Compile this with an external invocation of the Clang compiler.
|
||||
name, err := LookupCommand("clang")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = exec.Command(name, flags...)
|
||||
}
|
||||
|
||||
// Compile this with an external invocation of the Clang compiler.
|
||||
return execCommand("clang", flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// Make sure the command doesn't use any environmental variables.
|
||||
// Most importantly, it should not use C_INCLUDE_PATH and the like.
|
||||
cmd.Env = []string{}
|
||||
|
||||
// Let some environment variables through. One important one is the
|
||||
// temporary directory, especially on Windows it looks like Clang breaks if
|
||||
// the temporary directory has not been set.
|
||||
// See: https://github.com/tinygo-org/tinygo/issues/4557
|
||||
// Also see: https://github.com/llvm/llvm-project/blob/release/18.x/llvm/lib/Support/Unix/Path.inc#L1435
|
||||
for _, env := range os.Environ() {
|
||||
// We could parse the key and look it up in a map, but since there are
|
||||
// only a few keys iterating through them is easier and maybe even
|
||||
// faster.
|
||||
for _, prefix := range []string{"TMPDIR=", "TMP=", "TEMP=", "TEMPDIR="} {
|
||||
if strings.HasPrefix(env, prefix) {
|
||||
cmd.Env = append(cmd.Env, env)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// link invokes a linker with the given name and flags.
|
||||
@@ -85,29 +114,35 @@ func parseLLDErrors(text string) error {
|
||||
|
||||
// Check for undefined symbols.
|
||||
// This can happen in some cases like with CGo and //go:linkname tricker.
|
||||
if matches := regexp.MustCompile(`^ld.lld: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
||||
symbolName := matches[1]
|
||||
for _, line := range strings.Split(message, "\n") {
|
||||
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
||||
symbolName := matches[2]
|
||||
for line := range strings.SplitSeq(message, "\n") {
|
||||
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
||||
if matches != nil {
|
||||
parsedError = true
|
||||
line, _ := strconv.Atoi(matches[3])
|
||||
// TODO: detect common mistakes like -gc=none?
|
||||
msg := "linker could not find symbol " + symbolName
|
||||
switch symbolName {
|
||||
case "runtime.alloc":
|
||||
msg = "object allocated on the heap with -gc=none"
|
||||
case "runtime.alloc_noheap":
|
||||
msg = "object allocated on the heap in //go:noheap function"
|
||||
}
|
||||
linkErrors = append(linkErrors, scanner.Error{
|
||||
Pos: token.Position{
|
||||
Filename: matches[2],
|
||||
Line: line,
|
||||
},
|
||||
Msg: "linker could not find symbol " + symbolName,
|
||||
Msg: msg,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for flash/RAM overflow.
|
||||
if matches := regexp.MustCompile(`^ld.lld: error: section '(.*?)' will not fit in region '(.*?)': overflowed by ([0-9]+) bytes$`).FindStringSubmatch(message); matches != nil {
|
||||
region := matches[2]
|
||||
n, err := strconv.ParseUint(matches[3], 10, 64)
|
||||
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: section '(.*?)' will not fit in region '(.*?)': overflowed by ([0-9]+) bytes$`).FindStringSubmatch(message); matches != nil {
|
||||
region := matches[3]
|
||||
n, err := strconv.ParseUint(matches[4], 10, 64)
|
||||
if err != nil {
|
||||
// Should not happen at all (unless it overflows an uint64 for some reason).
|
||||
continue
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byt
|
||||
}
|
||||
bl.SetNumBlocks(len(blocks))
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
for i := range blocks {
|
||||
bl.SetBlockNo(i)
|
||||
bl.SetData(blocks[i])
|
||||
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var libWasiLibc = Library{
|
||||
name: "wasi-libc",
|
||||
makeHeaders: func(target, includeDir string) error {
|
||||
bits := filepath.Join(includeDir, "bits")
|
||||
err := os.Mkdir(bits, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "wasi-libc/libc-top-half/musl")
|
||||
err = buildMuslAllTypes("wasm32", muslDir, bits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// See MUSL_OMIT_HEADERS in the Makefile.
|
||||
omitHeaders := map[string]struct{}{
|
||||
"syslog.h": {},
|
||||
"wait.h": {},
|
||||
"ucontext.h": {},
|
||||
"paths.h": {},
|
||||
"utmp.h": {},
|
||||
"utmpx.h": {},
|
||||
"lastlog.h": {},
|
||||
"elf.h": {},
|
||||
"link.h": {},
|
||||
"pwd.h": {},
|
||||
"shadow.h": {},
|
||||
"grp.h": {},
|
||||
"mntent.h": {},
|
||||
"netdb.h": {},
|
||||
"resolv.h": {},
|
||||
"pty.h": {},
|
||||
"dlfcn.h": {},
|
||||
"setjmp.h": {},
|
||||
"ulimit.h": {},
|
||||
"wordexp.h": {},
|
||||
"spawn.h": {},
|
||||
"termios.h": {},
|
||||
"libintl.h": {},
|
||||
"aio.h": {},
|
||||
|
||||
"stdarg.h": {},
|
||||
"stddef.h": {},
|
||||
|
||||
"pthread.h": {},
|
||||
}
|
||||
|
||||
for _, glob := range [][2]string{
|
||||
{"libc-bottom-half/headers/public/*.h", ""},
|
||||
{"libc-bottom-half/headers/public/wasi/*.h", "wasi"},
|
||||
{"libc-top-half/musl/arch/wasm32/bits/*.h", "bits"},
|
||||
{"libc-top-half/musl/include/*.h", ""},
|
||||
{"libc-top-half/musl/include/netinet/*.h", "netinet"},
|
||||
{"libc-top-half/musl/include/sys/*.h", "sys"},
|
||||
} {
|
||||
matches, _ := filepath.Glob(filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc", glob[0]))
|
||||
outDir := filepath.Join(includeDir, glob[1])
|
||||
os.MkdirAll(outDir, 0o777)
|
||||
for _, match := range matches {
|
||||
name := filepath.Base(match)
|
||||
if _, ok := omitHeaders[name]; ok {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(match)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(outDir, name), data, 0o666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
cflags: func(target, headerPath string) []string {
|
||||
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
|
||||
return []string{
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-std=gnu11",
|
||||
"-nostdlibinc",
|
||||
"-mnontrapping-fptoint", "-msign-ext", "-mbulk-memory",
|
||||
"-Wno-null-pointer-arithmetic", "-Wno-unused-parameter", "-Wno-sign-compare", "-Wno-unused-variable", "-Wno-unused-function", "-Wno-ignored-attributes", "-Wno-missing-braces", "-Wno-ignored-pragmas", "-Wno-unused-but-set-variable", "-Wno-unknown-warning-option",
|
||||
"-Wno-parentheses", "-Wno-shift-op-parentheses", "-Wno-bitwise-op-parentheses", "-Wno-logical-op-parentheses", "-Wno-string-plus-int", "-Wno-dangling-else", "-Wno-unknown-pragmas",
|
||||
"-DNDEBUG",
|
||||
"-D__wasilibc_printscan_no_long_double",
|
||||
"-D__wasilibc_printscan_full_support_option=\"long double support is disabled\"",
|
||||
"-DBULK_MEMORY_THRESHOLD=32", // default threshold in wasi-libc
|
||||
"-isystem", headerPath,
|
||||
"-I" + libcDir + "/libc-top-half/musl/src/include",
|
||||
"-I" + libcDir + "/libc-top-half/musl/src/internal",
|
||||
"-I" + libcDir + "/libc-top-half/musl/arch/wasm32",
|
||||
"-I" + libcDir + "/libc-top-half/musl/arch/generic",
|
||||
"-I" + libcDir + "/libc-top-half/headers/private",
|
||||
}
|
||||
},
|
||||
cflagsForFile: func(path string) []string {
|
||||
if strings.HasPrefix(path, "libc-bottom-half"+string(os.PathSeparator)) {
|
||||
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
|
||||
return []string{
|
||||
"-I" + libcDir + "/libc-bottom-half/headers/private",
|
||||
"-I" + libcDir + "/libc-bottom-half/cloudlibc/src/include",
|
||||
"-I" + libcDir + "/libc-bottom-half/cloudlibc/src",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
|
||||
librarySources: func(target string, libcNeedsMalloc bool) ([]string, error) {
|
||||
type filePattern struct {
|
||||
glob string
|
||||
exclude []string
|
||||
}
|
||||
|
||||
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
|
||||
globs := []filePattern{
|
||||
// Top half: mostly musl sources.
|
||||
{glob: "libc-top-half/sources/*.c"},
|
||||
{glob: "libc-top-half/musl/src/conf/*.c"},
|
||||
{glob: "libc-top-half/musl/src/internal/*.c", exclude: []string{
|
||||
"procfdname.c", "syscall.c", "syscall_ret.c", "vdso.c", "version.c",
|
||||
}},
|
||||
{glob: "libc-top-half/musl/src/locale/*.c", exclude: []string{
|
||||
"dcngettext.c", "textdomain.c", "bind_textdomain_codeset.c"}},
|
||||
{glob: "libc-top-half/musl/src/math/*.c", exclude: []string{
|
||||
"__signbit.c", "__signbitf.c", "__signbitl.c",
|
||||
"__fpclassify.c", "__fpclassifyf.c", "__fpclassifyl.c",
|
||||
"ceilf.c", "ceil.c",
|
||||
"floorf.c", "floor.c",
|
||||
"truncf.c", "trunc.c",
|
||||
"rintf.c", "rint.c",
|
||||
"nearbyintf.c", "nearbyint.c",
|
||||
"sqrtf.c", "sqrt.c",
|
||||
"fabsf.c", "fabs.c",
|
||||
"copysignf.c", "copysign.c",
|
||||
"fminf.c", "fmaxf.c",
|
||||
"fmin.c", "fmax.c,",
|
||||
}},
|
||||
{glob: "libc-top-half/musl/src/multibyte/*.c"},
|
||||
{glob: "libc-top-half/musl/src/stdio/*.c", exclude: []string{
|
||||
"vfwscanf.c", "vfwprintf.c", // long double is unsupported
|
||||
"__lockfile.c", "flockfile.c", "funlockfile.c", "ftrylockfile.c",
|
||||
"rename.c",
|
||||
"tmpnam.c", "tmpfile.c", "tempnam.c",
|
||||
"popen.c", "pclose.c",
|
||||
"remove.c",
|
||||
"gets.c"}},
|
||||
{glob: "libc-top-half/musl/src/stdlib/*.c"},
|
||||
{glob: "libc-top-half/musl/src/string/*.c", exclude: []string{
|
||||
"strsignal.c"}},
|
||||
|
||||
// Bottom half: connect top half to WASI equivalents.
|
||||
{glob: "libc-bottom-half/cloudlibc/src/libc/*/*.c"},
|
||||
{glob: "libc-bottom-half/cloudlibc/src/libc/sys/*/*.c"},
|
||||
{glob: "libc-bottom-half/sources/*.c"},
|
||||
}
|
||||
|
||||
// We're using the Boehm GC, so we need a heap implementation in the libc.
|
||||
if libcNeedsMalloc {
|
||||
globs = append(globs, filePattern{glob: "dlmalloc/src/dlmalloc.c"})
|
||||
}
|
||||
|
||||
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
|
||||
sources := []string{
|
||||
"libc-top-half/musl/src/misc/a64l.c",
|
||||
"libc-top-half/musl/src/misc/basename.c",
|
||||
"libc-top-half/musl/src/misc/dirname.c",
|
||||
"libc-top-half/musl/src/misc/ffs.c",
|
||||
"libc-top-half/musl/src/misc/ffsl.c",
|
||||
"libc-top-half/musl/src/misc/ffsll.c",
|
||||
"libc-top-half/musl/src/misc/fmtmsg.c",
|
||||
"libc-top-half/musl/src/misc/getdomainname.c",
|
||||
"libc-top-half/musl/src/misc/gethostid.c",
|
||||
"libc-top-half/musl/src/misc/getopt.c",
|
||||
"libc-top-half/musl/src/misc/getopt_long.c",
|
||||
"libc-top-half/musl/src/misc/getsubopt.c",
|
||||
"libc-top-half/musl/src/misc/uname.c",
|
||||
"libc-top-half/musl/src/misc/nftw.c",
|
||||
"libc-top-half/musl/src/errno/strerror.c",
|
||||
"libc-top-half/musl/src/network/htonl.c",
|
||||
"libc-top-half/musl/src/network/htons.c",
|
||||
"libc-top-half/musl/src/network/ntohl.c",
|
||||
"libc-top-half/musl/src/network/ntohs.c",
|
||||
"libc-top-half/musl/src/network/inet_ntop.c",
|
||||
"libc-top-half/musl/src/network/inet_pton.c",
|
||||
"libc-top-half/musl/src/network/inet_aton.c",
|
||||
"libc-top-half/musl/src/network/in6addr_any.c",
|
||||
"libc-top-half/musl/src/network/in6addr_loopback.c",
|
||||
"libc-top-half/musl/src/fenv/fenv.c",
|
||||
"libc-top-half/musl/src/fenv/fesetround.c",
|
||||
"libc-top-half/musl/src/fenv/feupdateenv.c",
|
||||
"libc-top-half/musl/src/fenv/fesetexceptflag.c",
|
||||
"libc-top-half/musl/src/fenv/fegetexceptflag.c",
|
||||
"libc-top-half/musl/src/fenv/feholdexcept.c",
|
||||
"libc-top-half/musl/src/exit/exit.c",
|
||||
"libc-top-half/musl/src/exit/atexit.c",
|
||||
"libc-top-half/musl/src/exit/assert.c",
|
||||
"libc-top-half/musl/src/exit/quick_exit.c",
|
||||
"libc-top-half/musl/src/exit/at_quick_exit.c",
|
||||
"libc-top-half/musl/src/time/strftime.c",
|
||||
"libc-top-half/musl/src/time/asctime.c",
|
||||
"libc-top-half/musl/src/time/asctime_r.c",
|
||||
"libc-top-half/musl/src/time/ctime.c",
|
||||
"libc-top-half/musl/src/time/ctime_r.c",
|
||||
"libc-top-half/musl/src/time/wcsftime.c",
|
||||
"libc-top-half/musl/src/time/strptime.c",
|
||||
"libc-top-half/musl/src/time/difftime.c",
|
||||
"libc-top-half/musl/src/time/timegm.c",
|
||||
"libc-top-half/musl/src/time/ftime.c",
|
||||
"libc-top-half/musl/src/time/gmtime.c",
|
||||
"libc-top-half/musl/src/time/gmtime_r.c",
|
||||
"libc-top-half/musl/src/time/timespec_get.c",
|
||||
"libc-top-half/musl/src/time/getdate.c",
|
||||
"libc-top-half/musl/src/time/localtime.c",
|
||||
"libc-top-half/musl/src/time/localtime_r.c",
|
||||
"libc-top-half/musl/src/time/mktime.c",
|
||||
"libc-top-half/musl/src/time/__tm_to_secs.c",
|
||||
"libc-top-half/musl/src/time/__month_to_secs.c",
|
||||
"libc-top-half/musl/src/time/__secs_to_tm.c",
|
||||
"libc-top-half/musl/src/time/__year_to_secs.c",
|
||||
"libc-top-half/musl/src/time/__tz.c",
|
||||
"libc-top-half/musl/src/fcntl/creat.c",
|
||||
"libc-top-half/musl/src/dirent/alphasort.c",
|
||||
"libc-top-half/musl/src/dirent/versionsort.c",
|
||||
"libc-top-half/musl/src/env/__stack_chk_fail.c",
|
||||
"libc-top-half/musl/src/env/clearenv.c",
|
||||
"libc-top-half/musl/src/env/getenv.c",
|
||||
"libc-top-half/musl/src/env/putenv.c",
|
||||
"libc-top-half/musl/src/env/setenv.c",
|
||||
"libc-top-half/musl/src/env/unsetenv.c",
|
||||
"libc-top-half/musl/src/unistd/posix_close.c",
|
||||
"libc-top-half/musl/src/stat/futimesat.c",
|
||||
"libc-top-half/musl/src/legacy/getpagesize.c",
|
||||
"libc-top-half/musl/src/thread/thrd_sleep.c",
|
||||
}
|
||||
|
||||
basepath := goenv.Get("TINYGOROOT") + "/lib/wasi-libc/"
|
||||
for _, pattern := range globs {
|
||||
matches, err := filepath.Glob(basepath + pattern.glob)
|
||||
if err != nil {
|
||||
// From the documentation:
|
||||
// > Glob ignores file system errors such as I/O errors reading
|
||||
// > directories. The only possible returned error is
|
||||
// > ErrBadPattern, when pattern is malformed.
|
||||
// So the only possible error is when the (statically defined)
|
||||
// pattern is wrong. In other words, a programming bug.
|
||||
return nil, fmt.Errorf("wasi-libc: could not glob source dirs: %w", err)
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return nil, fmt.Errorf("wasi-libc: did not find any files for pattern %#v", pattern)
|
||||
}
|
||||
excludeSet := map[string]struct{}{}
|
||||
for _, exclude := range pattern.exclude {
|
||||
excludeSet[exclude] = struct{}{}
|
||||
}
|
||||
for _, match := range matches {
|
||||
if _, ok := excludeSet[filepath.Base(match)]; ok {
|
||||
continue
|
||||
}
|
||||
relpath, err := filepath.Rel(basepath, match)
|
||||
if err != nil {
|
||||
// Not sure if this is even possible.
|
||||
return nil, err
|
||||
}
|
||||
sources = append(sources, relpath)
|
||||
}
|
||||
}
|
||||
return sources, nil
|
||||
},
|
||||
}
|
||||
@@ -29,6 +29,8 @@ var libWasmBuiltins = Library{
|
||||
"-Wall",
|
||||
"-std=gnu11",
|
||||
"-nostdlibinc",
|
||||
"-mnontrapping-fptoint", // match wasm-unknown (default on in LLVM 20)
|
||||
"-mno-bulk-memory", // same here
|
||||
"-isystem", libcDir + "/libc-top-half/musl/arch/wasm32",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/arch/generic",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/src/internal",
|
||||
@@ -39,7 +41,7 @@ var libWasmBuiltins = Library{
|
||||
}
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
librarySources: func(target string, _ bool) ([]string, error) {
|
||||
return []string{
|
||||
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
|
||||
// llvm.memset.* LLVM intrinsics.
|
||||
|
||||
+219
-85
@@ -18,6 +18,7 @@ import (
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -25,10 +26,6 @@ import (
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Function that's only defined in Go 1.22.
|
||||
var setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
}
|
||||
|
||||
// cgoPackage holds all CGo-related information of a package.
|
||||
type cgoPackage struct {
|
||||
generated *ast.File
|
||||
@@ -42,7 +39,8 @@ type cgoPackage struct {
|
||||
fset *token.FileSet
|
||||
tokenFiles map[string]*token.File
|
||||
definedGlobally map[string]ast.Node
|
||||
anonDecls map[interface{}]string
|
||||
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
||||
anonDecls map[any]string
|
||||
cflags []string // CFlags from #cgo lines
|
||||
ldflags []string // LDFlags from #cgo lines
|
||||
visitedFiles map[string][]byte
|
||||
@@ -80,21 +78,28 @@ type bitfieldInfo struct {
|
||||
endBit int64 // may be 0 meaning "until the end of the field"
|
||||
}
|
||||
|
||||
// Information about a #cgo noescape line in the source code.
|
||||
type noescapingFunc struct {
|
||||
name string
|
||||
pos token.Pos
|
||||
used bool // true if used somewhere in the source (for proper error reporting)
|
||||
}
|
||||
|
||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||
// in both languages. See addTypeAliases.
|
||||
var cgoAliases = map[string]string{
|
||||
"C.int8_t": "int8",
|
||||
"C.int16_t": "int16",
|
||||
"C.int32_t": "int32",
|
||||
"C.int64_t": "int64",
|
||||
"C.uint8_t": "uint8",
|
||||
"C.uint16_t": "uint16",
|
||||
"C.uint32_t": "uint32",
|
||||
"C.uint64_t": "uint64",
|
||||
"C.uintptr_t": "uintptr",
|
||||
"C.float": "float32",
|
||||
"C.double": "float64",
|
||||
"C._Bool": "bool",
|
||||
"_Cgo_int8_t": "int8",
|
||||
"_Cgo_int16_t": "int16",
|
||||
"_Cgo_int32_t": "int32",
|
||||
"_Cgo_int64_t": "int64",
|
||||
"_Cgo_uint8_t": "uint8",
|
||||
"_Cgo_uint16_t": "uint16",
|
||||
"_Cgo_uint32_t": "uint32",
|
||||
"_Cgo_uint64_t": "uint64",
|
||||
"_Cgo_uintptr_t": "uintptr",
|
||||
"_Cgo_float": "float32",
|
||||
"_Cgo_double": "float64",
|
||||
"_Cgo__Bool": "bool",
|
||||
}
|
||||
|
||||
// builtinAliases are handled specially because they only exist on the Go side
|
||||
@@ -136,31 +141,105 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||
// The string/bytes functions below implement C.CString etc. To make sure the
|
||||
// runtime doesn't need to know the C int type, lengths are converted to uintptr
|
||||
// first.
|
||||
// These functions will be modified to get a "C." prefix, so the source below
|
||||
// doesn't reflect the final AST.
|
||||
const generatedGoFilePrefix = `
|
||||
const generatedGoFilePrefixBase = `
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func __GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func __GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
`
|
||||
|
||||
const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
`
|
||||
|
||||
// Windows uses fake errno values in the syscall package.
|
||||
// See for example: https://github.com/golang/go/issues/23468
|
||||
// TinyGo uses mingw-w64 though, which does have defined errno values. Since the
|
||||
// syscall package is the standard library one we can't change it, but we can
|
||||
// map the errno values to match the values in the syscall package.
|
||||
// Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
|
||||
const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
|
||||
var _Cgo___errno_mapping = [...]syscall.Errno{
|
||||
1: syscall.EPERM,
|
||||
2: syscall.ENOENT,
|
||||
3: syscall.ESRCH,
|
||||
4: syscall.EINTR,
|
||||
5: syscall.EIO,
|
||||
6: syscall.ENXIO,
|
||||
7: syscall.E2BIG,
|
||||
8: syscall.ENOEXEC,
|
||||
9: syscall.EBADF,
|
||||
10: syscall.ECHILD,
|
||||
11: syscall.EAGAIN,
|
||||
12: syscall.ENOMEM,
|
||||
13: syscall.EACCES,
|
||||
14: syscall.EFAULT,
|
||||
16: syscall.EBUSY,
|
||||
17: syscall.EEXIST,
|
||||
18: syscall.EXDEV,
|
||||
19: syscall.ENODEV,
|
||||
20: syscall.ENOTDIR,
|
||||
21: syscall.EISDIR,
|
||||
22: syscall.EINVAL,
|
||||
23: syscall.ENFILE,
|
||||
24: syscall.EMFILE,
|
||||
25: syscall.ENOTTY,
|
||||
27: syscall.EFBIG,
|
||||
28: syscall.ENOSPC,
|
||||
29: syscall.ESPIPE,
|
||||
30: syscall.EROFS,
|
||||
31: syscall.EMLINK,
|
||||
32: syscall.EPIPE,
|
||||
33: syscall.EDOM,
|
||||
34: syscall.ERANGE,
|
||||
36: syscall.EDEADLK,
|
||||
38: syscall.ENAMETOOLONG,
|
||||
39: syscall.ENOLCK,
|
||||
40: syscall.ENOSYS,
|
||||
41: syscall.ENOTEMPTY,
|
||||
42: syscall.EILSEQ,
|
||||
}
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
num := _Cgo___get_errno_num()
|
||||
if num < uintptr(len(_Cgo___errno_mapping)) {
|
||||
if mapped := _Cgo___errno_mapping[num]; mapped != 0 {
|
||||
return mapped
|
||||
}
|
||||
}
|
||||
return syscall.Errno(num)
|
||||
}
|
||||
`
|
||||
|
||||
@@ -171,7 +250,7 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||
// hashes of the accessed C header files. If there is one or more error, it
|
||||
// returns these in the []error slice but still modifies the AST.
|
||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, goos string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||
p := &cgoPackage{
|
||||
packageName: files[0].Name.Name,
|
||||
currentDir: dir,
|
||||
@@ -179,7 +258,8 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
fset: fset,
|
||||
tokenFiles: map[string]*token.File{},
|
||||
definedGlobally: map[string]ast.Node{},
|
||||
anonDecls: map[interface{}]string{},
|
||||
noescapingFuncs: map[string]*noescapingFunc{},
|
||||
anonDecls: map[any]string{},
|
||||
visitedFiles: map[string][]byte{},
|
||||
}
|
||||
|
||||
@@ -203,7 +283,12 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// Construct a new in-memory AST for CGo declarations of this package.
|
||||
// The first part is written as Go code that is then parsed, but more code
|
||||
// is added later to the AST to declare functions, globals, etc.
|
||||
goCode := "package " + files[0].Name.Name + "\n\n" + generatedGoFilePrefix
|
||||
goCode := "package " + files[0].Name.Name + "\n\n"
|
||||
if goos == "windows" {
|
||||
goCode += generatedGoFilePrefixWindows
|
||||
} else {
|
||||
goCode += generatedGoFilePrefixOther
|
||||
}
|
||||
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
// This is always a bug in the cgo package.
|
||||
@@ -213,28 +298,11 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// If the Comments field is not set to nil, the go/format package will get
|
||||
// confused about where comments should go.
|
||||
p.generated.Comments = nil
|
||||
// Adjust some of the functions in there.
|
||||
for _, decl := range p.generated.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
switch decl.Name.Name {
|
||||
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes":
|
||||
// Adjust the name to have a "C." prefix so it is correctly
|
||||
// resolved.
|
||||
decl.Name.Name = "C." + decl.Name.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
// Patch some types, for example *C.char in C.CString.
|
||||
cf := p.newCGoFile(nil, -1) // dummy *cgoFile for the walker
|
||||
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
|
||||
return cf.walker(cursor, nil)
|
||||
}, nil)
|
||||
|
||||
// Find `import "C"` C fragments in the file.
|
||||
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
|
||||
for i, f := range files {
|
||||
var cgoHeader string
|
||||
var cgoHeader strings.Builder
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
@@ -269,7 +337,8 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// Iterate through all parts of the CGo header. Note that every //
|
||||
// line is a new comment.
|
||||
position := fset.Position(genDecl.Doc.Pos())
|
||||
fragment := fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)
|
||||
var fragment strings.Builder
|
||||
fragment.WriteString(fmt.Sprintf("# %d %#v\n", position.Line, position.Filename))
|
||||
for _, comment := range genDecl.Doc.List {
|
||||
// Find all #cgo lines, extract and use their contents, and
|
||||
// replace the lines with spaces (to preserve locations).
|
||||
@@ -286,12 +355,13 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
} else { // comment
|
||||
c = " " + c[2:len(c)-2]
|
||||
}
|
||||
fragment += c + "\n"
|
||||
fragment.WriteString(c)
|
||||
fragment.WriteByte('\n')
|
||||
}
|
||||
cgoHeader += fragment
|
||||
cgoHeader.WriteString(fragment.String())
|
||||
}
|
||||
|
||||
p.cgoHeaders[i] = cgoHeader
|
||||
p.cgoHeaders[i] = cgoHeader.String()
|
||||
}
|
||||
|
||||
// Define CFlags that will be used while parsing the package.
|
||||
@@ -308,7 +378,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
for _, name := range builtinAliases {
|
||||
typeSpec := p.getIntegerType("C."+name, names["_Cgo_"+name])
|
||||
typeSpec := p.getIntegerType("_Cgo_"+name, names["_Cgo_"+name])
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
@@ -337,6 +407,22 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
})
|
||||
}
|
||||
|
||||
// Show an error when a #cgo noescape line isn't used in practice.
|
||||
// This matches upstream Go. I think the goal is to avoid issues with
|
||||
// misspelled function names, which seems very useful.
|
||||
var unusedNoescapeLines []*noescapingFunc
|
||||
for _, value := range p.noescapingFuncs {
|
||||
if !value.used {
|
||||
unusedNoescapeLines = append(unusedNoescapeLines, value)
|
||||
}
|
||||
}
|
||||
sort.SliceStable(unusedNoescapeLines, func(i, j int) bool {
|
||||
return unusedNoescapeLines[i].pos < unusedNoescapeLines[j].pos
|
||||
})
|
||||
for _, value := range unusedNoescapeLines {
|
||||
p.addError(value.pos, fmt.Sprintf("function %#v in #cgo noescape line is not used", value.name))
|
||||
}
|
||||
|
||||
// Print the newly generated in-memory AST, for debugging.
|
||||
//ast.Print(fset, p.generated)
|
||||
|
||||
@@ -402,6 +488,33 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
|
||||
}
|
||||
text = text[:lineStart] + string(spaces) + text[lineEnd:]
|
||||
|
||||
allFields := strings.Fields(line[4:])
|
||||
switch allFields[0] {
|
||||
case "noescape":
|
||||
// The code indicates that pointer parameters will not be captured
|
||||
// by the called C function.
|
||||
if len(allFields) < 2 {
|
||||
p.addErrorAfter(pos, text[:lineStart], "missing function name in #cgo noescape line")
|
||||
continue
|
||||
}
|
||||
if len(allFields) > 2 {
|
||||
p.addErrorAfter(pos, text[:lineStart], "multiple function names in #cgo noescape line")
|
||||
continue
|
||||
}
|
||||
name := allFields[1]
|
||||
p.noescapingFuncs[name] = &noescapingFunc{
|
||||
name: name,
|
||||
pos: pos,
|
||||
used: false,
|
||||
}
|
||||
continue
|
||||
case "nocallback":
|
||||
// We don't do anything special when calling a C function, so there
|
||||
// appears to be no optimization that we can do here.
|
||||
// Accept, but ignore the parameter for compatibility.
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the text before the colon in the #cgo directive.
|
||||
colon := strings.IndexByte(line, ':')
|
||||
if colon < 0 {
|
||||
@@ -539,7 +652,6 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "union",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: pos,
|
||||
@@ -593,7 +705,6 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -649,7 +760,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -696,11 +806,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
@@ -708,7 +813,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -766,7 +870,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -849,11 +952,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
@@ -861,7 +959,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -882,7 +979,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "value",
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
Type: bitfield.field.Type,
|
||||
@@ -900,7 +996,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
@@ -1124,7 +1219,7 @@ func getPos(node ast.Node) token.Pos {
|
||||
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
||||
// declaration. This is used for structs, unions, and enums that are often
|
||||
// defined without a name and used in a typedef.
|
||||
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
||||
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf any) string {
|
||||
if name, ok := p.anonDecls[itf]; ok {
|
||||
return name
|
||||
}
|
||||
@@ -1138,22 +1233,22 @@ func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
||||
func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
|
||||
// Some types are defined in stdint.h and map directly to a particular Go
|
||||
// type.
|
||||
if alias := cgoAliases["C."+name]; alias != "" {
|
||||
if alias := cgoAliases["_Cgo_"+name]; alias != "" {
|
||||
return alias
|
||||
}
|
||||
node := f.getASTDeclNode(name, found, iscall)
|
||||
node := f.getASTDeclNode(name, found)
|
||||
if node, ok := node.(*ast.FuncDecl); ok {
|
||||
if !iscall {
|
||||
return node.Name.Name + "$funcaddr"
|
||||
}
|
||||
return node.Name.Name
|
||||
}
|
||||
return "C." + name
|
||||
return "_Cgo_" + name
|
||||
}
|
||||
|
||||
// getASTDeclNode will declare the given C AST node (if not already defined) and
|
||||
// returns it.
|
||||
func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) ast.Node {
|
||||
func (f *cgoFile) getASTDeclNode(name string, found clangCursor) ast.Node {
|
||||
if node, ok := f.defined[name]; ok {
|
||||
// Declaration was found in the current file, so return it immediately.
|
||||
return node
|
||||
@@ -1248,8 +1343,8 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
|
||||
case *elaboratedTypeInfo:
|
||||
// Add struct bitfields.
|
||||
for _, bitfield := range elaboratedType.bitfields {
|
||||
f.createBitfieldGetter(bitfield, "C."+name)
|
||||
f.createBitfieldSetter(bitfield, "C."+name)
|
||||
f.createBitfieldGetter(bitfield, "_Cgo_"+name)
|
||||
f.createBitfieldSetter(bitfield, "_Cgo_"+name)
|
||||
}
|
||||
if elaboratedType.unionSize != 0 {
|
||||
// Create union getters/setters.
|
||||
@@ -1258,7 +1353,7 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
|
||||
f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
|
||||
continue
|
||||
}
|
||||
f.createUnionAccessor(field, "C."+name)
|
||||
f.createUnionAccessor(field, "_Cgo_"+name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1272,6 +1367,45 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
|
||||
// separate namespace (no _Cgo_ hacks like in gc).
|
||||
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.AssignStmt:
|
||||
// An assign statement could be something like this:
|
||||
//
|
||||
// val, errno := C.some_func()
|
||||
//
|
||||
// Check whether it looks like that, and if so, read the errno value and
|
||||
// return it as the second return value. The call will be transformed
|
||||
// into something like this:
|
||||
//
|
||||
// val, errno := C.some_func(), C.__get_errno()
|
||||
if len(node.Lhs) != 2 || len(node.Rhs) != 1 {
|
||||
return true
|
||||
}
|
||||
rhs, ok := node.Rhs[0].(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
fun, ok := rhs.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
x, ok := fun.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
|
||||
// Replace "C"."some_func" into "C.somefunc".
|
||||
rhs.Fun = &ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: f.getASTDeclName(fun.Sel.Name, found, true),
|
||||
}
|
||||
// Add the errno value as the second value in the statement.
|
||||
node.Rhs = append(node.Rhs, &ast.CallExpr{
|
||||
Fun: &ast.Ident{
|
||||
NamePos: node.Lhs[1].End(),
|
||||
Name: "_Cgo___get_errno",
|
||||
},
|
||||
})
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
@@ -1293,7 +1427,7 @@ func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) b
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
name := "C." + node.Sel.Name
|
||||
name := "_Cgo_" + node.Sel.Name
|
||||
if found, ok := names[node.Sel.Name]; ok {
|
||||
name = f.getASTDeclName(node.Sel.Name, found, false)
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
//go:build go1.22
|
||||
|
||||
package cgo
|
||||
|
||||
// Code specifically for Go 1.22.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
||||
f.FileStart = start
|
||||
f.FileEnd = end
|
||||
}
|
||||
}
|
||||
+23
-5
@@ -45,7 +45,6 @@ func TestCGo(t *testing.T) {
|
||||
"flags",
|
||||
"const",
|
||||
} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
@@ -56,7 +55,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
||||
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "linux")
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
@@ -64,7 +63,7 @@ func TestCGo(t *testing.T) {
|
||||
Error: func(err error) {
|
||||
typecheckErrors = append(typecheckErrors, err)
|
||||
},
|
||||
Importer: simpleImporter{},
|
||||
Importer: newSimpleImporter(),
|
||||
Sizes: types.SizesFor("gccgo", "arm"),
|
||||
}
|
||||
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
||||
@@ -202,14 +201,33 @@ func Test_cgoPackage_isEquivalentAST(t *testing.T) {
|
||||
}
|
||||
|
||||
// simpleImporter implements the types.Importer interface, but only allows
|
||||
// importing the unsafe package.
|
||||
// importing the syscall and unsafe packages.
|
||||
type simpleImporter struct {
|
||||
syscallPkg *types.Package
|
||||
}
|
||||
|
||||
func newSimpleImporter() *simpleImporter {
|
||||
i := &simpleImporter{}
|
||||
|
||||
// Implement a dummy syscall package with the Errno type.
|
||||
i.syscallPkg = types.NewPackage("syscall", "syscall")
|
||||
obj := types.NewTypeName(token.NoPos, i.syscallPkg, "Errno", nil)
|
||||
named := types.NewNamed(obj, nil, nil)
|
||||
i.syscallPkg.Scope().Insert(obj)
|
||||
named.SetUnderlying(types.Typ[types.Uintptr])
|
||||
sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(types.NewParam(token.NoPos, i.syscallPkg, "", types.Typ[types.String])), false)
|
||||
named.AddMethod(types.NewFunc(token.NoPos, i.syscallPkg, "Error", sig))
|
||||
i.syscallPkg.MarkComplete()
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// Import implements the Importer interface. For testing usage only: it only
|
||||
// supports importing the unsafe package.
|
||||
func (i simpleImporter) Import(path string) (*types.Package, error) {
|
||||
func (i *simpleImporter) Import(path string) (*types.Package, error) {
|
||||
switch path {
|
||||
case "syscall":
|
||||
return i.syscallPkg, nil
|
||||
case "unsafe":
|
||||
return types.Unsafe, nil
|
||||
default:
|
||||
|
||||
+136
-4
@@ -54,8 +54,72 @@ func init() {
|
||||
}
|
||||
|
||||
// parseConst parses the given string as a C constant.
|
||||
func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) {
|
||||
t := newTokenizer(pos, fset, value)
|
||||
func parseConst(pos token.Pos, fset *token.FileSet, value string, params []ast.Expr, callerPos token.Pos, f *cgoFile) (ast.Expr, *scanner.Error) {
|
||||
t := newTokenizer(pos, fset, value, f)
|
||||
|
||||
// If params is non-nil (could be a zero length slice), this const is
|
||||
// actually a function-call like expression from another macro.
|
||||
// This means we have to parse a string like "(a, b) (a+b)".
|
||||
// We do this by parsing the parameters at the start and then treating the
|
||||
// following like a normal constant expression.
|
||||
if params != nil {
|
||||
// Parse opening paren.
|
||||
if t.curToken != token.LPAREN {
|
||||
return nil, unexpectedToken(t, token.LPAREN)
|
||||
}
|
||||
t.Next()
|
||||
|
||||
// Parse parameters (identifiers) and closing paren.
|
||||
var paramIdents []string
|
||||
for i := 0; ; i++ {
|
||||
if i == 0 && t.curToken == token.RPAREN {
|
||||
// No parameters, break early.
|
||||
t.Next()
|
||||
break
|
||||
}
|
||||
|
||||
// Read the parameter name.
|
||||
if t.curToken != token.IDENT {
|
||||
return nil, unexpectedToken(t, token.IDENT)
|
||||
}
|
||||
paramIdents = append(paramIdents, t.curValue)
|
||||
t.Next()
|
||||
|
||||
// Read the next token: either a continuation (comma) or end of list
|
||||
// (rparen).
|
||||
if t.curToken == token.RPAREN {
|
||||
// End of parameter list.
|
||||
t.Next()
|
||||
break
|
||||
} else if t.curToken == token.COMMA {
|
||||
// Comma, so there will be another parameter name.
|
||||
t.Next()
|
||||
} else {
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.curPos),
|
||||
Msg: "unexpected token " + t.curToken.String() + " inside macro parameters, expected ',' or ')'",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Report an error if there is a mismatch in parameter length.
|
||||
// The error is reported at the location of the closing paren from the
|
||||
// caller location.
|
||||
if len(params) != len(paramIdents) {
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(callerPos),
|
||||
Msg: fmt.Sprintf("unexpected number of parameters: expected %d, got %d", len(paramIdents), len(params)),
|
||||
}
|
||||
}
|
||||
|
||||
// Assign values to the parameters.
|
||||
// These parameter names are closer in 'scope' than other identifiers so
|
||||
// will be used first when parsing an identifier.
|
||||
for i, name := range paramIdents {
|
||||
t.params[name] = params[i]
|
||||
}
|
||||
}
|
||||
|
||||
expr, err := parseConstExpr(t, precedenceLowest)
|
||||
t.Next()
|
||||
if t.curToken != token.EOF {
|
||||
@@ -96,6 +160,68 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
|
||||
}
|
||||
|
||||
func parseIdent(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
// If the identifier is one of the parameters of this function-like macro,
|
||||
// use the parameter value.
|
||||
if val, ok := t.params[t.curValue]; ok {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
if t.f != nil {
|
||||
// Check whether this identifier is actually a macro "call" with
|
||||
// parameters. In that case, we should parse the parameters and pass it
|
||||
// on to a new invocation of parseConst.
|
||||
if t.peekToken == token.LPAREN {
|
||||
if cursor, ok := t.f.names[t.curValue]; ok && t.f.isFunctionLikeMacro(cursor) {
|
||||
// We know the current and peek tokens (the peek one is the '('
|
||||
// token). So skip ahead until the current token is the first
|
||||
// unknown token.
|
||||
t.Next()
|
||||
t.Next()
|
||||
|
||||
// Parse the list of parameters until ')' (rparen) is found.
|
||||
params := []ast.Expr{}
|
||||
for i := 0; ; i++ {
|
||||
if i == 0 && t.curToken == token.RPAREN {
|
||||
break
|
||||
}
|
||||
x, err := parseConstExpr(t, precedenceLowest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params, x)
|
||||
t.Next()
|
||||
if t.curToken == token.COMMA {
|
||||
t.Next()
|
||||
} else if t.curToken == token.RPAREN {
|
||||
break
|
||||
} else {
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.curPos),
|
||||
Msg: "unexpected token " + t.curToken.String() + ", ',' or ')'",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the macro value and use it as the identifier value.
|
||||
rparen := t.curPos
|
||||
pos, text := t.f.getMacro(cursor)
|
||||
return parseConst(pos, t.fset, text, params, rparen, t.f)
|
||||
}
|
||||
}
|
||||
|
||||
// Normally the name is something defined in the file (like another
|
||||
// macro) which we get the declaration from using getASTDeclName.
|
||||
// This ensures that names that are only referenced inside a macro are
|
||||
// still getting defined.
|
||||
if cursor, ok := t.f.names[t.curValue]; ok {
|
||||
return &ast.Ident{
|
||||
NamePos: t.curPos,
|
||||
Name: t.f.getASTDeclName(t.curValue, cursor, false),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// t.f is nil during testing. This is a fallback.
|
||||
return &ast.Ident{
|
||||
NamePos: t.curPos,
|
||||
Name: "C." + t.curValue,
|
||||
@@ -164,21 +290,25 @@ func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error {
|
||||
|
||||
// tokenizer reads C source code and converts it to Go tokens.
|
||||
type tokenizer struct {
|
||||
f *cgoFile
|
||||
curPos, peekPos token.Pos
|
||||
fset *token.FileSet
|
||||
curToken, peekToken token.Token
|
||||
curValue, peekValue string
|
||||
buf string
|
||||
params map[string]ast.Expr
|
||||
}
|
||||
|
||||
// newTokenizer initializes a new tokenizer, positioned at the first token in
|
||||
// the string.
|
||||
func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer {
|
||||
func newTokenizer(start token.Pos, fset *token.FileSet, buf string, f *cgoFile) *tokenizer {
|
||||
t := &tokenizer{
|
||||
f: f,
|
||||
peekPos: start,
|
||||
fset: fset,
|
||||
buf: buf,
|
||||
peekToken: token.ILLEGAL,
|
||||
params: make(map[string]ast.Expr),
|
||||
}
|
||||
// Parse the first two tokens (cur and peek).
|
||||
t.Next()
|
||||
@@ -230,7 +360,7 @@ func (t *tokenizer) Next() {
|
||||
t.peekValue = t.buf[:2]
|
||||
t.buf = t.buf[2:]
|
||||
return
|
||||
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
|
||||
case c == '(' || c == ')' || c == ',' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
|
||||
// Single-character tokens.
|
||||
// TODO: ++ (increment) and -- (decrement) operators.
|
||||
switch c {
|
||||
@@ -238,6 +368,8 @@ func (t *tokenizer) Next() {
|
||||
t.peekToken = token.LPAREN
|
||||
case ')':
|
||||
t.peekToken = token.RPAREN
|
||||
case ',':
|
||||
t.peekToken = token.COMMA
|
||||
case '+':
|
||||
t.peekToken = token.ADD
|
||||
case '-':
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ func TestParseConst(t *testing.T) {
|
||||
} {
|
||||
fset := token.NewFileSet()
|
||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||
expr, err := parseConst(startPos, fset, tc.C)
|
||||
expr, err := parseConst(startPos, fset, tc.C, nil, token.NoPos, nil)
|
||||
s := "<invalid>"
|
||||
if err != nil {
|
||||
if !strings.HasPrefix(tc.Go, "error: ") {
|
||||
|
||||
+122
-133
@@ -63,10 +63,24 @@ long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
||||
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
|
||||
unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
|
||||
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
||||
unsigned tinygo_clang_Cursor_isMacroFunctionLike(GoCXCursor c);
|
||||
|
||||
// Fix some warnings on Windows ARM. Without the __declspec(dllexport), it gives warnings like this:
|
||||
// In file included from _cgo_export.c:4:
|
||||
// cgo-gcc-export-header-prolog:49:34: warning: redeclaration of 'tinygo_clang_globals_visitor' should not add 'dllexport' attribute [-Wdll-attribute-on-redeclaration]
|
||||
// libclang.go:68:5: note: previous declaration is here
|
||||
// See: https://github.com/golang/go/issues/49721
|
||||
#if defined(_WIN32)
|
||||
#define CGO_DECL __declspec(dllexport)
|
||||
#else
|
||||
#define CGO_DECL
|
||||
#endif
|
||||
|
||||
CGO_DECL
|
||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
CGO_DECL
|
||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
CGO_DECL
|
||||
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
|
||||
*/
|
||||
import "C"
|
||||
@@ -116,7 +130,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
// convert Go slice of strings to C array of strings.
|
||||
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
||||
defer C.free(cmdargsC)
|
||||
cmdargs := (*[1 << 16]*C.char)(cmdargsC)
|
||||
cmdargs := unsafe.Slice((**C.char)(cmdargsC), len(cflags))
|
||||
for i, cflag := range cflags {
|
||||
s := C.CString(cflag)
|
||||
cmdargs[i] = s
|
||||
@@ -146,7 +160,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
pos := f.getClangLocationPosition(location, unit)
|
||||
f.addError(pos, severity+": "+spelling)
|
||||
}
|
||||
for i := 0; i < numDiagnostics; i++ {
|
||||
for i := range numDiagnostics {
|
||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||
addDiagnostic(diagnostic)
|
||||
|
||||
@@ -176,7 +190,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
// Sanity check. This should (hopefully) never trigger.
|
||||
panic("libclang: file contents was not loaded")
|
||||
}
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
data := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size)
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := f.visitedFiles[path]; !ok {
|
||||
@@ -203,10 +217,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "C." + name,
|
||||
}
|
||||
exportName := name
|
||||
localName := name
|
||||
var stringSignature string
|
||||
@@ -243,8 +253,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + localName,
|
||||
Obj: obj,
|
||||
Name: "_Cgo_" + localName,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
@@ -255,13 +264,21 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
},
|
||||
}
|
||||
var doc []string
|
||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||
doc = append(doc, "//go:variadic")
|
||||
}
|
||||
if _, ok := f.noescapingFuncs[name]; ok {
|
||||
doc = append(doc, "//go:noescape")
|
||||
f.noescapingFuncs[name].used = true
|
||||
}
|
||||
if len(doc) != 0 {
|
||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:variadic",
|
||||
Text: strings.Join(doc, "\n"),
|
||||
})
|
||||
}
|
||||
for i := 0; i < numArgs; i++ {
|
||||
for i := range numArgs {
|
||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||
@@ -273,11 +290,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: argName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: argName,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: f.makeDecayingASTType(argType, pos),
|
||||
@@ -293,49 +305,34 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
}
|
||||
}
|
||||
obj.Decl = decl
|
||||
return decl, stringSignature
|
||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||
typ := f.makeASTRecordType(c, pos)
|
||||
typeName := "C." + name
|
||||
typeName := "_Cgo_" + name
|
||||
typeExpr := typ.typeExpr
|
||||
if typ.unionSize != 0 {
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = f.makeUnionField(typ)
|
||||
}
|
||||
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:
|
||||
typeName := "C." + name
|
||||
typeName := "_Cgo_" + name
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
}
|
||||
if underlyingType.kind != C.CXType_Enum {
|
||||
typeSpec.Assign = pos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_VarDecl:
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
@@ -354,61 +351,18 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
},
|
||||
},
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
Name: "_Cgo_" + name,
|
||||
}},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_MacroDefinition:
|
||||
// Extract tokens from the Clang tokenizer.
|
||||
// See: https://stackoverflow.com/a/19074846/559350
|
||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
||||
var rawTokens *C.CXToken
|
||||
var numTokens C.unsigned
|
||||
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
|
||||
tokens := unsafe.Slice(rawTokens, numTokens)
|
||||
// Convert this range of tokens back to source text.
|
||||
// Ugly, but it works well enough.
|
||||
sourceBuf := &bytes.Buffer{}
|
||||
var startOffset int
|
||||
for i, token := range tokens {
|
||||
spelling := getString(C.clang_getTokenSpelling(tu, token))
|
||||
location := C.clang_getTokenLocation(tu, token)
|
||||
var tokenOffset C.unsigned
|
||||
C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset)
|
||||
if i == 0 {
|
||||
// The first token is the macro name itself.
|
||||
// Skip it (after using its location).
|
||||
startOffset = int(tokenOffset) + len(name)
|
||||
} else {
|
||||
// Later tokens are the macro contents.
|
||||
for int(tokenOffset) > (startOffset + sourceBuf.Len()) {
|
||||
// Pad the source text with whitespace (that must have been
|
||||
// present in the original source as well).
|
||||
sourceBuf.WriteByte(' ')
|
||||
}
|
||||
sourceBuf.WriteString(spelling)
|
||||
}
|
||||
}
|
||||
C.clang_disposeTokens(tu, rawTokens, numTokens)
|
||||
value := sourceBuf.String()
|
||||
// Try to convert this #define into a Go constant expression.
|
||||
tokenPos := token.NoPos
|
||||
if pos != token.NoPos {
|
||||
tokenPos = pos + token.Pos(len(name))
|
||||
}
|
||||
expr, scannerError := parseConst(tokenPos, f.fset, value)
|
||||
tokenPos, value := f.getMacro(c)
|
||||
expr, scannerError := parseConst(tokenPos, f.fset, value, nil, token.NoPos, f)
|
||||
if scannerError != nil {
|
||||
f.errors = append(f.errors, *scannerError)
|
||||
return nil, nil
|
||||
@@ -420,39 +374,27 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
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,
|
||||
Name: "_Cgo_" + name,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_EnumDecl:
|
||||
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,
|
||||
Name: "_Cgo_" + name,
|
||||
},
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlying, pos),
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
@@ -467,19 +409,13 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
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,
|
||||
Name: "_Cgo_" + name,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
default:
|
||||
@@ -488,6 +424,62 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether this is a macro that's also function-like, like this:
|
||||
//
|
||||
// #define add(a, b) (a+b)
|
||||
func (f *cgoFile) isFunctionLikeMacro(c clangCursor) bool {
|
||||
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_MacroDefinition {
|
||||
return false
|
||||
}
|
||||
return C.tinygo_clang_Cursor_isMacroFunctionLike(c) != 0
|
||||
}
|
||||
|
||||
// Get the macro value: the position in the source file and the string value of
|
||||
// the macro.
|
||||
func (f *cgoFile) getMacro(c clangCursor) (pos token.Pos, value string) {
|
||||
// Extract tokens from the Clang tokenizer.
|
||||
// See: https://stackoverflow.com/a/19074846/559350
|
||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
||||
var rawTokens *C.CXToken
|
||||
var numTokens C.unsigned
|
||||
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
|
||||
tokens := unsafe.Slice(rawTokens, numTokens)
|
||||
defer C.clang_disposeTokens(tu, rawTokens, numTokens)
|
||||
|
||||
// Convert this range of tokens back to source text.
|
||||
// Ugly, but it works well enough.
|
||||
sourceBuf := &bytes.Buffer{}
|
||||
var startOffset int
|
||||
for i, token := range tokens {
|
||||
spelling := getString(C.clang_getTokenSpelling(tu, token))
|
||||
location := C.clang_getTokenLocation(tu, token)
|
||||
var tokenOffset C.unsigned
|
||||
C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset)
|
||||
if i == 0 {
|
||||
// The first token is the macro name itself.
|
||||
// Skip it (after using its location).
|
||||
startOffset = int(tokenOffset)
|
||||
} else {
|
||||
// Later tokens are the macro contents.
|
||||
for int(tokenOffset) > (startOffset + sourceBuf.Len()) {
|
||||
// Pad the source text with whitespace (that must have been
|
||||
// present in the original source as well).
|
||||
sourceBuf.WriteByte(' ')
|
||||
}
|
||||
sourceBuf.WriteString(spelling)
|
||||
}
|
||||
}
|
||||
value = sourceBuf.String()
|
||||
|
||||
// Obtain the position of this token. This is the position of the first
|
||||
// character in the 'value' string and is used to report errors at the
|
||||
// correct location in the source file.
|
||||
pos = f.getCursorPosition(c)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getString(clangString C.CXString) (s string) {
|
||||
rawString := C.clang_getCString(clangString)
|
||||
s = C.GoString(rawString)
|
||||
@@ -540,7 +532,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
|
||||
// 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{} {
|
||||
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) any {
|
||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||
var file C.CXFile
|
||||
var line C.unsigned
|
||||
@@ -583,7 +575,7 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
// now by reading the file from libclang.
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
|
||||
source := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size)
|
||||
lines := []int{0}
|
||||
for i := 0; i < len(source)-1; i++ {
|
||||
if source[i] == '\n' {
|
||||
@@ -598,7 +590,8 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
||||
Package: f.Pos(0),
|
||||
Name: ast.NewIdent(p.packageName),
|
||||
}
|
||||
setASTFileFields(astFile, f.Pos(0), f.Pos(int(size)))
|
||||
astFile.FileStart = f.Pos(0)
|
||||
astFile.FileEnd = f.Pos(int(size))
|
||||
p.cgoFiles = append(p.cgoFiles, astFile)
|
||||
}
|
||||
positionFile := p.tokenFiles[filename]
|
||||
@@ -704,27 +697,27 @@ 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:
|
||||
typeName = "C.char"
|
||||
typeName = "_Cgo_char"
|
||||
case C.CXType_SChar:
|
||||
typeName = "C.schar"
|
||||
typeName = "_Cgo_schar"
|
||||
case C.CXType_UChar:
|
||||
typeName = "C.uchar"
|
||||
typeName = "_Cgo_uchar"
|
||||
case C.CXType_Short:
|
||||
typeName = "C.short"
|
||||
typeName = "_Cgo_short"
|
||||
case C.CXType_UShort:
|
||||
typeName = "C.ushort"
|
||||
typeName = "_Cgo_ushort"
|
||||
case C.CXType_Int:
|
||||
typeName = "C.int"
|
||||
typeName = "_Cgo_int"
|
||||
case C.CXType_UInt:
|
||||
typeName = "C.uint"
|
||||
typeName = "_Cgo_uint"
|
||||
case C.CXType_Long:
|
||||
typeName = "C.long"
|
||||
typeName = "_Cgo_long"
|
||||
case C.CXType_ULong:
|
||||
typeName = "C.ulong"
|
||||
typeName = "_Cgo_ulong"
|
||||
case C.CXType_LongLong:
|
||||
typeName = "C.longlong"
|
||||
typeName = "_Cgo_longlong"
|
||||
case C.CXType_ULongLong:
|
||||
typeName = "C.ulonglong"
|
||||
typeName = "_Cgo_ulonglong"
|
||||
case C.CXType_Bool:
|
||||
typeName = "bool"
|
||||
case C.CXType_Float, C.CXType_Double, C.CXType_LongDouble:
|
||||
@@ -811,6 +804,14 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
typeName = "<unknown>"
|
||||
}
|
||||
case C.CXType_Unexposed:
|
||||
// LLVM 22+ may report certain builtin type aliases (e.g. __size_t)
|
||||
// as Unexposed. Resolve via the canonical type.
|
||||
canonical := C.clang_getCanonicalType(typ)
|
||||
if canonical.kind != C.CXType_Unexposed && canonical.kind != C.CXType_Invalid {
|
||||
return f.makeASTType(canonical, pos)
|
||||
}
|
||||
// If still unexposed, fall through to the error below.
|
||||
case C.CXType_Record:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
@@ -855,7 +856,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
typeName = "C.<unknown>"
|
||||
typeName = "_Cgo_<unknown>"
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
@@ -872,7 +873,7 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
||||
var goName string
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch name {
|
||||
case "C.char":
|
||||
case "_Cgo_char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
@@ -885,7 +886,7 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
||||
case C.CXType_Char_U:
|
||||
goName = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
case "_Cgo_schar", "_Cgo_short", "_Cgo_int", "_Cgo_long", "_Cgo_longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "int8"
|
||||
@@ -896,7 +897,7 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
||||
case 8:
|
||||
goName = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
case "_Cgo_uchar", "_Cgo_ushort", "_Cgo_uint", "_Cgo_ulong", "_Cgo_ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "uint8"
|
||||
@@ -915,22 +916,16 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -1063,7 +1058,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
pos: prevField.Names[0].NamePos,
|
||||
})
|
||||
prevField.Names[0].Name = bitfieldName
|
||||
prevField.Names[0].Obj.Name = bitfieldName
|
||||
}
|
||||
prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1]
|
||||
prevBitfield.endBit = bitfieldOffset
|
||||
@@ -1080,11 +1074,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: name,
|
||||
Decl: field,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append(fieldList.List, field)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17
|
||||
//go:build !byollvm && llvm18
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm19
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include -I/usr/lib64/llvm19/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-19/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@19/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@19/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm19/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm21 && !llvm22
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include -I/usr/lib64/llvm20/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-20/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@20/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@20/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm20/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm21
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-21 -I/usr/include/llvm-c-21 -I/usr/lib/llvm-21/include -I/usr/lib64/llvm21/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@21/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@21/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm21/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-21/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@21/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@21/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm21/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm22
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-22 -I/usr/include/llvm-c-22 -I/usr/lib/llvm-22/include -I/usr/lib64/llvm22/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@22/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@22/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm22/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-22/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@22/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@22/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm22/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -84,3 +84,7 @@ unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
|
||||
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
|
||||
return clang_Cursor_isBitField(c);
|
||||
}
|
||||
|
||||
unsigned tinygo_clang_Cursor_isMacroFunctionLike(CXCursor c) {
|
||||
return clang_Cursor_isMacroFunctionLike(c);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ var validCompilerFlags = []*regexp.Regexp{
|
||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-f(no-)?plt`),
|
||||
re(`-f(no-)?rtti`),
|
||||
re(`-f(no-)?short-enums`),
|
||||
re(`-f(no-)?split-stack`),
|
||||
re(`-f(no-)?stack-(.+)`),
|
||||
re(`-f(no-)?strict-aliasing`),
|
||||
|
||||
+4
-4
@@ -12,17 +12,17 @@ import "C"
|
||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||
// pointer but only a C pointer.
|
||||
type refMap struct {
|
||||
refs map[unsafe.Pointer]interface{}
|
||||
refs map[unsafe.Pointer]any
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||
// be removed using Remove to avoid memory leaks.
|
||||
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
||||
func (m *refMap) Put(v any) unsafe.Pointer {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
if m.refs == nil {
|
||||
m.refs = make(map[unsafe.Pointer]interface{}, 1)
|
||||
m.refs = make(map[unsafe.Pointer]any, 1)
|
||||
}
|
||||
ref := C.malloc(1)
|
||||
m.refs[ref] = v
|
||||
@@ -31,7 +31,7 @@ func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
||||
|
||||
// Get returns a stored value previously inserted with Put. Use the same
|
||||
// reference as you got from Put.
|
||||
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
||||
func (m *refMap) Get(ref unsafe.Pointer) any {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
return m.refs[ref]
|
||||
|
||||
Vendored
+38
-23
@@ -1,39 +1,54 @@
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
|
||||
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
|
||||
_Cgo_char uint8
|
||||
_Cgo_schar int8
|
||||
_Cgo_uchar uint8
|
||||
_Cgo_short int16
|
||||
_Cgo_ushort uint16
|
||||
_Cgo_int int32
|
||||
_Cgo_uint uint32
|
||||
_Cgo_long int32
|
||||
_Cgo_ulong uint32
|
||||
_Cgo_longlong int64
|
||||
_Cgo_ulonglong uint64
|
||||
)
|
||||
|
||||
Vendored
+16
@@ -3,10 +3,26 @@ package main
|
||||
/*
|
||||
#define foo 3
|
||||
#define bar foo
|
||||
|
||||
#define unreferenced 4
|
||||
#define referenced unreferenced
|
||||
|
||||
#define fnlike() 5
|
||||
#define fnlike_val fnlike()
|
||||
#define square(n) (n*n)
|
||||
#define square_val square(20)
|
||||
#define add(a, b) (a + b)
|
||||
#define add_val add(3, 5)
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
Foo = C.foo
|
||||
Bar = C.bar
|
||||
|
||||
Baz = C.referenced
|
||||
|
||||
fnlike = C.fnlike_val
|
||||
square = C.square_val
|
||||
add = C.add_val
|
||||
)
|
||||
|
||||
Vendored
+45
-25
@@ -1,42 +1,62 @@
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
|
||||
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
|
||||
_Cgo_char uint8
|
||||
_Cgo_schar int8
|
||||
_Cgo_uchar uint8
|
||||
_Cgo_short int16
|
||||
_Cgo_ushort uint16
|
||||
_Cgo_int int32
|
||||
_Cgo_uint uint32
|
||||
_Cgo_long int32
|
||||
_Cgo_ulong uint32
|
||||
_Cgo_longlong int64
|
||||
_Cgo_ulonglong uint64
|
||||
)
|
||||
|
||||
const C.foo = 3
|
||||
const C.bar = C.foo
|
||||
const _Cgo_foo = 3
|
||||
const _Cgo_bar = _Cgo_foo
|
||||
const _Cgo_unreferenced = 4
|
||||
const _Cgo_referenced = _Cgo_unreferenced
|
||||
const _Cgo_fnlike_val = 5
|
||||
const _Cgo_square_val = (20 * 20)
|
||||
const _Cgo_add_val = (3 + 5)
|
||||
|
||||
Vendored
+13
@@ -10,6 +10,11 @@ typedef struct {
|
||||
|
||||
typedef someType noType; // undefined type
|
||||
|
||||
// Some invalid noescape lines
|
||||
#cgo noescape
|
||||
#cgo noescape foo bar
|
||||
#cgo noescape unusedFunction
|
||||
|
||||
#define SOME_CONST_1 5) // invalid const syntax
|
||||
#define SOME_CONST_2 6) // const not used (so no error)
|
||||
#define SOME_CONST_3 1234 // const too large for byte
|
||||
@@ -26,6 +31,11 @@ import "C"
|
||||
// #warning another warning
|
||||
import "C"
|
||||
|
||||
// #define add(a, b) (a+b)
|
||||
// #define add_toomuch add(1, 2, 3)
|
||||
// #define add_toolittle add(1)
|
||||
import "C"
|
||||
|
||||
// Make sure that errors for the following lines won't change with future
|
||||
// additions to the CGo preamble.
|
||||
//
|
||||
@@ -51,4 +61,7 @@ var (
|
||||
// constants passed by a command line parameter
|
||||
_ = C.SOME_PARAM_CONST_invalid
|
||||
_ = C.SOME_PARAM_CONST_valid
|
||||
|
||||
_ = C.add_toomuch
|
||||
_ = C.add_toolittle
|
||||
)
|
||||
|
||||
Vendored
+63
-41
@@ -1,67 +1,89 @@
|
||||
// CGo errors:
|
||||
// testdata/errors.go:14:1: missing function name in #cgo noescape line
|
||||
// testdata/errors.go:15:1: multiple function names in #cgo noescape line
|
||||
// testdata/errors.go:4:2: warning: some warning
|
||||
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
||||
// testdata/errors.go:26:5: warning: another warning
|
||||
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:21:26: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:16:33: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:17:34: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:31:5: warning: another warning
|
||||
// testdata/errors.go:18:23: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:26:26: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:21:33: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:22:34: unexpected token ), expected end of expression
|
||||
// -: unexpected token INT, expected end of expression
|
||||
// testdata/errors.go:35:35: unexpected number of parameters: expected 2, got 3
|
||||
// testdata/errors.go:36:31: unexpected number of parameters: expected 2, got 1
|
||||
// testdata/errors.go:3:1: function "unusedFunction" in #cgo noescape line is not used
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as _Cgo_char value in variable declaration (overflows)
|
||||
// testdata/errors.go:105: unknown field z in struct literal
|
||||
// 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: undefined: C.SOME_CONST_4
|
||||
// testdata/errors.go:114: undefined: C.SOME_CONST_b
|
||||
// testdata/errors.go:116: undefined: C.SOME_CONST_startspace
|
||||
// testdata/errors.go:119: undefined: C.SOME_PARAM_CONST_invalid
|
||||
// testdata/errors.go:108: undefined: _Cgo_SOME_CONST_1
|
||||
// testdata/errors.go:110: cannot use _Cgo_SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
// testdata/errors.go:112: undefined: _Cgo_SOME_CONST_4
|
||||
// testdata/errors.go:114: undefined: _Cgo_SOME_CONST_b
|
||||
// testdata/errors.go:116: undefined: _Cgo_SOME_CONST_startspace
|
||||
// testdata/errors.go:119: undefined: _Cgo_SOME_PARAM_CONST_invalid
|
||||
// testdata/errors.go:122: undefined: _Cgo_add_toomuch
|
||||
// testdata/errors.go:123: undefined: _Cgo_add_toolittle
|
||||
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
|
||||
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
|
||||
_Cgo_char uint8
|
||||
_Cgo_schar int8
|
||||
_Cgo_uchar uint8
|
||||
_Cgo_short int16
|
||||
_Cgo_ushort uint16
|
||||
_Cgo_int int32
|
||||
_Cgo_uint uint32
|
||||
_Cgo_long int32
|
||||
_Cgo_ulong uint32
|
||||
_Cgo_longlong int64
|
||||
_Cgo_ulonglong uint64
|
||||
)
|
||||
type C.struct_point_t struct {
|
||||
x C.int
|
||||
y C.int
|
||||
type _Cgo_struct_point_t struct {
|
||||
x _Cgo_int
|
||||
y _Cgo_int
|
||||
}
|
||||
type C.point_t = C.struct_point_t
|
||||
type _Cgo_point_t = _Cgo_struct_point_t
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
const C.SOME_PARAM_CONST_valid = 3 + 4
|
||||
const _Cgo_SOME_CONST_3 = 1234
|
||||
const _Cgo_SOME_PARAM_CONST_valid = 3 + 4
|
||||
|
||||
Vendored
+40
-25
@@ -5,43 +5,58 @@
|
||||
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
|
||||
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
|
||||
_Cgo_char uint8
|
||||
_Cgo_schar int8
|
||||
_Cgo_uchar uint8
|
||||
_Cgo_short int16
|
||||
_Cgo_ushort uint16
|
||||
_Cgo_int int32
|
||||
_Cgo_uint uint32
|
||||
_Cgo_long int32
|
||||
_Cgo_ulong uint32
|
||||
_Cgo_longlong int64
|
||||
_Cgo_ulonglong uint64
|
||||
)
|
||||
|
||||
const C.BAR = 3
|
||||
const C.FOO_H = 1
|
||||
const _Cgo_BAR = 3
|
||||
const _Cgo_FOO_H = 1
|
||||
|
||||
Vendored
+5
@@ -9,6 +9,10 @@ static void staticfunc(int x);
|
||||
|
||||
// Global variable signatures.
|
||||
extern int someValue;
|
||||
|
||||
void notEscapingFunction(int *a);
|
||||
|
||||
#cgo noescape notEscapingFunction
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -18,6 +22,7 @@ func accessFunctions() {
|
||||
C.variadic0()
|
||||
C.variadic2(3, 5)
|
||||
C.staticfunc(3)
|
||||
C.notEscapingFunction(nil)
|
||||
}
|
||||
|
||||
func accessGlobals() {
|
||||
|
||||
Vendored
+52
-32
@@ -1,64 +1,84 @@
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
|
||||
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
|
||||
_Cgo_char uint8
|
||||
_Cgo_schar int8
|
||||
_Cgo_uchar uint8
|
||||
_Cgo_short int16
|
||||
_Cgo_ushort uint16
|
||||
_Cgo_int int32
|
||||
_Cgo_uint uint32
|
||||
_Cgo_long int32
|
||||
_Cgo_ulong uint32
|
||||
_Cgo_longlong int64
|
||||
_Cgo_ulonglong uint64
|
||||
)
|
||||
|
||||
//export foo
|
||||
func C.foo(a C.int, b C.int) C.int
|
||||
func _Cgo_foo(a _Cgo_int, b _Cgo_int) _Cgo_int
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
var _Cgo_foo$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic0
|
||||
//go:variadic
|
||||
func C.variadic0()
|
||||
func _Cgo_variadic0()
|
||||
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
var _Cgo_variadic0$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic2
|
||||
//go:variadic
|
||||
func C.variadic2(x C.int, y C.int)
|
||||
func _Cgo_variadic2(x _Cgo_int, y _Cgo_int)
|
||||
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
var _Cgo_variadic2$funcaddr unsafe.Pointer
|
||||
|
||||
//export _Cgo_static_173c95a79b6df1980521_staticfunc
|
||||
func C.staticfunc!symbols.go(x C.int)
|
||||
func _Cgo_staticfunc!symbols.go(x _Cgo_int)
|
||||
|
||||
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
|
||||
var _Cgo_staticfunc!symbols.go$funcaddr unsafe.Pointer
|
||||
|
||||
//export notEscapingFunction
|
||||
//go:noescape
|
||||
func _Cgo_notEscapingFunction(a *_Cgo_int)
|
||||
|
||||
var _Cgo_notEscapingFunction$funcaddr unsafe.Pointer
|
||||
//go:extern someValue
|
||||
var C.someValue C.int
|
||||
var _Cgo_someValue _Cgo_int
|
||||
|
||||
Vendored
+110
-91
@@ -1,147 +1,166 @@
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
//go:linkname C.CString runtime.cgo_CString
|
||||
func C.CString(string) *C.char
|
||||
//go:linkname _Cgo_CString runtime.cgo_CString
|
||||
func _Cgo_CString(string) *_Cgo_char
|
||||
|
||||
//go:linkname C.GoString runtime.cgo_GoString
|
||||
func C.GoString(*C.char) string
|
||||
//go:linkname _Cgo_GoString runtime.cgo_GoString
|
||||
func _Cgo_GoString(*_Cgo_char) string
|
||||
|
||||
//go:linkname C.__GoStringN runtime.cgo_GoStringN
|
||||
func C.__GoStringN(*C.char, uintptr) string
|
||||
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
|
||||
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
|
||||
|
||||
func C.GoStringN(cstr *C.char, length C.int) string {
|
||||
return C.__GoStringN(cstr, uintptr(length))
|
||||
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
|
||||
return _Cgo___GoStringN(cstr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__GoBytes runtime.cgo_GoBytes
|
||||
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
|
||||
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
|
||||
|
||||
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
|
||||
return _Cgo___GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
|
||||
func _Cgo___CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func _Cgo_CBytes(b []byte) unsafe.Pointer {
|
||||
return _Cgo___CBytes(b)
|
||||
}
|
||||
|
||||
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
|
||||
func _Cgo___get_errno_num() uintptr
|
||||
|
||||
func _Cgo___get_errno() error {
|
||||
return syscall.Errno(_Cgo___get_errno_num())
|
||||
}
|
||||
|
||||
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
|
||||
_Cgo_char uint8
|
||||
_Cgo_schar int8
|
||||
_Cgo_uchar uint8
|
||||
_Cgo_short int16
|
||||
_Cgo_ushort uint16
|
||||
_Cgo_int int32
|
||||
_Cgo_uint uint32
|
||||
_Cgo_long int32
|
||||
_Cgo_ulong uint32
|
||||
_Cgo_longlong int64
|
||||
_Cgo_ulonglong uint64
|
||||
)
|
||||
type C.myint = C.int
|
||||
type C.struct_point2d_t struct {
|
||||
x C.int
|
||||
y C.int
|
||||
type _Cgo_myint = _Cgo_int
|
||||
type _Cgo_struct_point2d_t struct {
|
||||
x _Cgo_int
|
||||
y _Cgo_int
|
||||
}
|
||||
type C.point2d_t = C.struct_point2d_t
|
||||
type C.struct_point3d struct {
|
||||
x C.int
|
||||
y C.int
|
||||
z C.int
|
||||
type _Cgo_point2d_t = _Cgo_struct_point2d_t
|
||||
type _Cgo_struct_point3d struct {
|
||||
x _Cgo_int
|
||||
y _Cgo_int
|
||||
z _Cgo_int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_type1 struct {
|
||||
_type C.int
|
||||
__type C.int
|
||||
___type C.int
|
||||
type _Cgo_point3d_t = _Cgo_struct_point3d
|
||||
type _Cgo_struct_type1 struct {
|
||||
_type _Cgo_int
|
||||
__type _Cgo_int
|
||||
___type _Cgo_int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
type C.union_union1_t struct{ i C.int }
|
||||
type C.union1_t = C.union_union1_t
|
||||
type C.union_union3_t struct{ $union uint64 }
|
||||
type _Cgo_struct_type2 struct{ _type _Cgo_int }
|
||||
type _Cgo_union_union1_t struct{ i _Cgo_int }
|
||||
type _Cgo_union1_t = _Cgo_union_union1_t
|
||||
type _Cgo_union_union3_t struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_union3_t) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union3_t) unionfield_d() *float64 {
|
||||
func (union *_Cgo_union_union3_t) unionfield_i() *_Cgo_int {
|
||||
return (*_Cgo_int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *_Cgo_union_union3_t) unionfield_d() *float64 {
|
||||
return (*float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_union3_t) unionfield_s() *C.short {
|
||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||
func (union *_Cgo_union_union3_t) unionfield_s() *_Cgo_short {
|
||||
return (*_Cgo_short)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union3_t = C.union_union3_t
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
type _Cgo_union3_t = _Cgo_union_union3_t
|
||||
type _Cgo_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 {
|
||||
func (union *_Cgo_union_union2d) unionfield_i() *_Cgo_int {
|
||||
return (*_Cgo_int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *_Cgo_union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.union_unionarray_t struct{ arr [10]C.uchar }
|
||||
type C.unionarray_t = C.union_unionarray_t
|
||||
type C._Ctype_union___0 struct{ $union [3]uint32 }
|
||||
type _Cgo_union2d_t = _Cgo_union_union2d
|
||||
type _Cgo_union_unionarray_t struct{ arr [10]_Cgo_uchar }
|
||||
type _Cgo_unionarray_t = _Cgo_union_unionarray_t
|
||||
type _Cgo__Ctype_union___0 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C._Ctype_union___0) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *_Cgo__Ctype_union___0) unionfield_area() *_Cgo_point2d_t {
|
||||
return (*_Cgo_point2d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___0) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *_Cgo__Ctype_union___0) unionfield_solid() *_Cgo_point3d_t {
|
||||
return (*_Cgo_point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.struct_struct_nested_t struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
type _Cgo_struct_struct_nested_t struct {
|
||||
begin _Cgo_point2d_t
|
||||
end _Cgo_point2d_t
|
||||
tag _Cgo_int
|
||||
|
||||
coord C._Ctype_union___0
|
||||
coord _Cgo__Ctype_union___0
|
||||
}
|
||||
type C.struct_nested_t = C.struct_struct_nested_t
|
||||
type C.union_union_nested_t struct{ $union [2]uint64 }
|
||||
type _Cgo_struct_nested_t = _Cgo_struct_struct_nested_t
|
||||
type _Cgo_union_union_nested_t struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union_nested_t) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *_Cgo_union_union_nested_t) unionfield_point() *_Cgo_point3d_t {
|
||||
return (*_Cgo_point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_union_nested_t) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *_Cgo_union_union_nested_t) unionfield_array() *_Cgo_unionarray_t {
|
||||
return (*_Cgo_unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_union_nested_t) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *_Cgo_union_union_nested_t) unionfield_thing() *_Cgo_union3_t {
|
||||
return (*_Cgo_union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_nested_t = C.union_union_nested_t
|
||||
type C.enum_option = C.int
|
||||
type C.option_t = C.enum_option
|
||||
type C.enum_option2_t = C.uint
|
||||
type C.option2_t = C.enum_option2_t
|
||||
type C.struct_types_t struct {
|
||||
type _Cgo_union_nested_t = _Cgo_union_union_nested_t
|
||||
type _Cgo_enum_option = _Cgo_int
|
||||
type _Cgo_option_t = _Cgo_enum_option
|
||||
type _Cgo_enum_option2_t = _Cgo_uint
|
||||
type _Cgo_option2_t = _Cgo_enum_option2_t
|
||||
type _Cgo_struct_types_t struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
ptr *_Cgo_int
|
||||
}
|
||||
type C.types_t = C.struct_types_t
|
||||
type C.myIntArray = [10]C.int
|
||||
type C.struct_bitfield_t struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
type _Cgo_types_t = _Cgo_struct_types_t
|
||||
type _Cgo_myIntArray = [10]_Cgo_int
|
||||
type _Cgo_struct_bitfield_t struct {
|
||||
start _Cgo_uchar
|
||||
__bitfield_1 _Cgo_uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
d _Cgo_uchar
|
||||
e _Cgo_uchar
|
||||
}
|
||||
|
||||
func (s *C.struct_bitfield_t) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_bitfield_t) set_bitfield_a(value C.uchar) {
|
||||
func (s *_Cgo_struct_bitfield_t) bitfield_a() _Cgo_uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *_Cgo_struct_bitfield_t) set_bitfield_a(value _Cgo_uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C.struct_bitfield_t) bitfield_b() C.uchar {
|
||||
func (s *_Cgo_struct_bitfield_t) bitfield_b() _Cgo_uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_bitfield_t) set_bitfield_b(value C.uchar) {
|
||||
func (s *_Cgo_struct_bitfield_t) set_bitfield_b(value _Cgo_uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C.struct_bitfield_t) bitfield_c() C.uchar {
|
||||
func (s *_Cgo_struct_bitfield_t) bitfield_c() _Cgo_uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_bitfield_t) set_bitfield_c(value C.uchar,
|
||||
func (s *_Cgo_struct_bitfield_t) set_bitfield_c(value _Cgo_uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.bitfield_t = C.struct_bitfield_t
|
||||
type _Cgo_bitfield_t = _Cgo_struct_bitfield_t
|
||||
|
||||
+162
-80
@@ -8,12 +8,25 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Library versions. Whenever an existing library is changed, this number should
|
||||
// be added/increased so that existing caches are invalidated.
|
||||
//
|
||||
// (This is a bit of a layering violation, this should really be part of the
|
||||
// builder.Library struct but that's hard to do since we want to know the
|
||||
// library path in advance in several places).
|
||||
var libVersions = map[string]int{
|
||||
"musl": 3,
|
||||
"bdwgc": 2,
|
||||
}
|
||||
|
||||
// Config keeps all configuration affecting the build in a single struct.
|
||||
type Config struct {
|
||||
Options *Options
|
||||
@@ -33,17 +46,30 @@ func (c *Config) CPU() string {
|
||||
return c.Target.CPU
|
||||
}
|
||||
|
||||
// The current build mode (like the `-buildmode` command line flag).
|
||||
func (c *Config) BuildMode() string {
|
||||
if c.Options.BuildMode != "" {
|
||||
return c.Options.BuildMode
|
||||
}
|
||||
if c.Target.BuildMode != "" {
|
||||
return c.Target.BuildMode
|
||||
}
|
||||
return "default"
|
||||
}
|
||||
|
||||
// Features returns a list of features this CPU supports. For example, for a
|
||||
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
|
||||
// will be returned.
|
||||
func (c *Config) Features() string {
|
||||
var features string
|
||||
if c.Target.Features == "" {
|
||||
return c.Options.LLVMFeatures
|
||||
features = c.Options.LLVMFeatures
|
||||
} else if c.Options.LLVMFeatures == "" {
|
||||
features = c.Target.Features
|
||||
} else {
|
||||
features = c.Target.Features + "," + c.Options.LLVMFeatures
|
||||
}
|
||||
if c.Options.LLVMFeatures == "" {
|
||||
return c.Target.Features
|
||||
}
|
||||
return c.Target.Features + "," + c.Options.LLVMFeatures
|
||||
return patchFeatures(features)
|
||||
}
|
||||
|
||||
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
||||
@@ -84,9 +110,15 @@ func (c *Config) BuildTags() []string {
|
||||
tags = append(tags, []string{
|
||||
"tinygo", // that's the compiler
|
||||
"purego", // to get various crypto packages to work
|
||||
"osusergo", // to get os/user to work
|
||||
"math_big_pure_go", // to get math/big to work
|
||||
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
|
||||
"serial." + c.Serial()}...) // used inside the machine package
|
||||
switch c.Scheduler() {
|
||||
case "threads", "cores":
|
||||
default:
|
||||
tags = append(tags, "tinygo.unicore")
|
||||
}
|
||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
@@ -110,14 +142,8 @@ func (c *Config) GC() string {
|
||||
// that can be traced by the garbage collector.
|
||||
func (c *Config) NeedsStackObjects() bool {
|
||||
switch c.GC() {
|
||||
case "conservative", "custom", "precise":
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "tinygo.wasm" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
case "conservative", "custom", "precise", "boehm":
|
||||
return slices.Contains(c.BuildTags(), "tinygo.wasm")
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -197,7 +223,7 @@ func (c *Config) StackSize() uint64 {
|
||||
|
||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||
func (c *Config) MaxStackAlloc() uint64 {
|
||||
if c.StackSize() > 32*1024 {
|
||||
if c.StackSize() >= 16*1024 {
|
||||
return 1024
|
||||
}
|
||||
|
||||
@@ -216,7 +242,7 @@ func (c *Config) RP2040BootPatch() bool {
|
||||
// Return a canonicalized architecture name, so we don't have to deal with arm*
|
||||
// vs thumb* vs arm64.
|
||||
func CanonicalArchName(triple string) string {
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
arch, _, _ := strings.Cut(triple, "-")
|
||||
if arch == "arm64" {
|
||||
return "aarch64"
|
||||
}
|
||||
@@ -235,10 +261,18 @@ func MuslArchitecture(triple string) string {
|
||||
return CanonicalArchName(triple)
|
||||
}
|
||||
|
||||
// LibcPath returns the path to the libc directory. The libc path will be either
|
||||
// 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) {
|
||||
// Returns true if the libc needs to include malloc, for the libcs where this
|
||||
// matters.
|
||||
func (c *Config) LibcNeedsMalloc() bool {
|
||||
if c.GC() == "boehm" && c.Target.Libc == "wasi-libc" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LibraryPath returns the path to the library build directory. The path will be
|
||||
// a library path in the cache directory (which might not yet be built).
|
||||
func (c *Config) LibraryPath(name string) string {
|
||||
archname := c.Triple()
|
||||
if c.CPU() != "" {
|
||||
archname += "-" + c.CPU()
|
||||
@@ -249,18 +283,24 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
if c.Target.SoftFloat {
|
||||
archname += "-softfloat"
|
||||
}
|
||||
if name == "bdwgc" {
|
||||
// Boehm GC is compiled against a particular libc.
|
||||
archname += "-" + c.Target.Libc
|
||||
}
|
||||
|
||||
// Try to load a precompiled library.
|
||||
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.
|
||||
return precompiledDir, true
|
||||
// Append a version string, if this library has a version.
|
||||
if v, ok := libVersions[name]; ok {
|
||||
archname += "-v" + strconv.Itoa(v)
|
||||
}
|
||||
|
||||
options := ""
|
||||
if c.LibcNeedsMalloc() {
|
||||
options += "+malloc"
|
||||
}
|
||||
|
||||
// No precompiled library found. Determine the path name that will be used
|
||||
// in the build cache.
|
||||
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
|
||||
return filepath.Join(goenv.Get("GOCACHE"), name+options+"-"+archname)
|
||||
}
|
||||
|
||||
// DefaultBinaryExtension returns the default extension for binaries, such as
|
||||
@@ -303,63 +343,13 @@ func (c *Config) CFlags(libclang bool) []string {
|
||||
"-resource-dir="+resourceDir,
|
||||
)
|
||||
}
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
|
||||
)
|
||||
case "picolibc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||
path, _ := c.LibcPath("picolibc")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-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,
|
||||
"-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")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", root+"/lib/wasi-libc/sysroot/include")
|
||||
case "wasmbuiltins":
|
||||
// nothing to add (library is purely for builtins)
|
||||
case "mingw-w64":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("mingw-w64")
|
||||
cflags = append(cflags,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "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 "":
|
||||
// No libc specified, nothing to add.
|
||||
default:
|
||||
// Incorrect configuration. This could be handled in a better way, but
|
||||
// usually this will be found by developers (not by TinyGo users).
|
||||
panic("unknown libc: " + c.Target.Libc)
|
||||
}
|
||||
cflags = append(cflags, c.LibcCFlags()...)
|
||||
// Always emit debug information. It is optionally stripped at link time.
|
||||
cflags = append(cflags, "-gdwarf-4")
|
||||
// Use the same optimization level as TinyGo.
|
||||
cflags = append(cflags, "-O"+c.Options.Opt)
|
||||
// Set the LLVM target triple.
|
||||
cflags = append(cflags, "--target="+c.Triple())
|
||||
cflags = append(cflags, "--target="+ClangTriple(c.Triple()))
|
||||
// Set the -mcpu (or similar) flag.
|
||||
if c.Target.CPU != "" {
|
||||
if c.GOARCH() == "amd64" || c.GOARCH() == "386" {
|
||||
@@ -380,6 +370,80 @@ func (c *Config) CFlags(libclang bool) []string {
|
||||
return cflags
|
||||
}
|
||||
|
||||
// LibcCFlags returns the C compiler flags for the configured libc.
|
||||
// It only uses flags that are part of the libc path (triple, cpu, abi, libc
|
||||
// name) so it can safely be used to compile another C library.
|
||||
func (c *Config) LibcCFlags() []string {
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
|
||||
}
|
||||
case "picolibc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||
path := c.LibraryPath("picolibc")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||
}
|
||||
case "musl":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path := c.LibraryPath("musl")
|
||||
arch := MuslArchitecture(c.Triple())
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", "generic"),
|
||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||
}
|
||||
case "wasi-libc":
|
||||
path := c.LibraryPath("wasi-libc")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
}
|
||||
case "wasmbuiltins":
|
||||
// nothing to add (library is purely for builtins)
|
||||
return nil
|
||||
case "mingw-w64":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path := c.LibraryPath("mingw-w64")
|
||||
cflags := []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||
}
|
||||
if c.GOARCH() == "386" {
|
||||
cflags = append(cflags,
|
||||
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
|
||||
"-D_WIN32_WINNT=0x0501", // target Windows XP
|
||||
)
|
||||
} else {
|
||||
cflags = append(cflags,
|
||||
"-D_UCRT",
|
||||
"-D_WIN32_WINNT=0x0a00", // target Windows 10
|
||||
)
|
||||
}
|
||||
return cflags
|
||||
case "":
|
||||
// No libc specified, nothing to add.
|
||||
return nil
|
||||
default:
|
||||
// Incorrect configuration. This could be handled in a better way, but
|
||||
// usually this will be found by developers (not by TinyGo users).
|
||||
panic("unknown libc: " + c.Target.Libc)
|
||||
}
|
||||
}
|
||||
|
||||
// LDFlags returns the flags to pass to the linker. A few more flags are needed
|
||||
// (like the one for the compiler runtime), but this represents the majority of
|
||||
// the flags.
|
||||
@@ -394,9 +458,27 @@ func (c *Config) LDFlags() []string {
|
||||
if c.Target.LinkerScript != "" {
|
||||
ldflags = append(ldflags, "-T", c.Target.LinkerScript)
|
||||
}
|
||||
ldflags = append(ldflags, c.Options.ExtLDFlags...)
|
||||
|
||||
return ldflags
|
||||
}
|
||||
|
||||
// LinkerFlavor returns how the configured linker should be driven.
|
||||
// Usually this is derived from GOOS, but targets may override it explicitly.
|
||||
func (c *Config) LinkerFlavor() string {
|
||||
if c.Target.LinkerFlavor != "" {
|
||||
return c.Target.LinkerFlavor
|
||||
}
|
||||
switch c.GOOS() {
|
||||
case "windows":
|
||||
return "coff"
|
||||
case "darwin":
|
||||
return "darwin"
|
||||
default:
|
||||
return "gnu"
|
||||
}
|
||||
}
|
||||
|
||||
// ExtraFiles returns the list of extra files to be built and linked with the
|
||||
// executable. This can include extra C and assembly files.
|
||||
func (c *Config) ExtraFiles() []string {
|
||||
@@ -467,10 +549,10 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
case "":
|
||||
// No configuration supplied.
|
||||
return c.Target.FlashMethod, c.Target.OpenOCDInterface
|
||||
case "openocd", "msd", "command":
|
||||
case "openocd", "msd", "command", "adb":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||
case "bmp":
|
||||
case "bmp", "probe-rs":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, ""
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !llvm22 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
||||
|
||||
package compileopts
|
||||
|
||||
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||
// For LLVM 20/21, features in the target JSON files are already correct.
|
||||
func patchFeatures(features string) string {
|
||||
return features
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build llvm22
|
||||
|
||||
package compileopts
|
||||
|
||||
import "strings"
|
||||
|
||||
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||
// LLVM 22 renamed several Xtensa target features.
|
||||
func patchFeatures(features string) string {
|
||||
// Xtensa feature renames in LLVM 22:
|
||||
// atomctl → (removed, no direct replacement)
|
||||
// memctl → (removed, no direct replacement)
|
||||
// esp32s3 → esp32s3ops
|
||||
// timerint → timers3 (for esp32/esp32s3) or timers1 (for esp8266)
|
||||
// Since we can't distinguish which timer variant at this level,
|
||||
// just remove the obsolete features. The CPU definition already
|
||||
// implies the correct features in LLVM 22.
|
||||
replacer := strings.NewReplacer(
|
||||
"+atomctl,", "",
|
||||
"+memctl,", "",
|
||||
"+esp32s3,", "+esp32s3ops,",
|
||||
"+timerint,", "",
|
||||
",+timerint", "",
|
||||
)
|
||||
return replacer.Replace(features)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//go:build llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19
|
||||
|
||||
package compileopts
|
||||
|
||||
import "strings"
|
||||
|
||||
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||
// LLVM 19 and earlier do not have +bulk-memory-opt or
|
||||
// +call-indirect-overlong for WebAssembly (added in LLVM 20).
|
||||
func patchFeatures(features string) string {
|
||||
features = strings.ReplaceAll(features, ",+bulk-memory-opt", "")
|
||||
features = strings.ReplaceAll(features, "+bulk-memory-opt,", "")
|
||||
features = strings.ReplaceAll(features, ",+call-indirect-overlong", "")
|
||||
features = strings.ReplaceAll(features, "+call-indirect-overlong,", "")
|
||||
return features
|
||||
}
|
||||
+60
-54
@@ -3,15 +3,17 @@ package compileopts
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||
validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"}
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise", "boehm"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify", "threads", "cores"}
|
||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full", "html"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||
)
|
||||
@@ -20,48 +22,61 @@ var (
|
||||
// usually passed from the command line, but can also be passed in environment
|
||||
// variables for example.
|
||||
type Options struct {
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
Opt string
|
||||
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
|
||||
SkipDWARF bool
|
||||
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
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
PrintJSON bool
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
GOOS string // environment variable
|
||||
GOARCH string // environment variable
|
||||
GOARM string // environment variable (only used with GOARCH=arm)
|
||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
||||
Directory string // working dir, leave it unset to use the current working dir
|
||||
Target string
|
||||
BuildMode string // -buildmode flag
|
||||
Opt string
|
||||
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
|
||||
InterpMaxLoopIterations int
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
SkipDWARF bool
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
Debug bool
|
||||
Nobounds bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintAllocsCover bool // emit allocs in go coverage tool format
|
||||
PrintStacks bool
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
ExtLDFlags []string
|
||||
GoCompatibility bool // enable to check for Go version compatibility
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
func (o *Options) Verify() error {
|
||||
if o.BuildMode != "" {
|
||||
valid := slices.Contains(validBuildModeOptions, o.BuildMode)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
|
||||
o.BuildMode,
|
||||
strings.Join(validBuildModeOptions, ", "))
|
||||
}
|
||||
}
|
||||
if o.GC != "" {
|
||||
valid := isInArray(validGCOptions, o.GC)
|
||||
valid := slices.Contains(validGCOptions, o.GC)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
||||
o.GC,
|
||||
@@ -70,7 +85,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.Scheduler != "" {
|
||||
valid := isInArray(validSchedulerOptions, o.Scheduler)
|
||||
valid := slices.Contains(validSchedulerOptions, o.Scheduler)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
||||
o.Scheduler,
|
||||
@@ -79,7 +94,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.Serial != "" {
|
||||
valid := isInArray(validSerialOptions, o.Serial)
|
||||
valid := slices.Contains(validSerialOptions, o.Serial)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
||||
o.Serial,
|
||||
@@ -88,7 +103,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.PrintSizes != "" {
|
||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
||||
valid := slices.Contains(validPrintSizeOptions, o.PrintSizes)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
||||
o.PrintSizes,
|
||||
@@ -97,7 +112,7 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.PanicStrategy != "" {
|
||||
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
|
||||
valid := slices.Contains(validPanicStrategyOptions, o.PanicStrategy)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
||||
o.PanicStrategy,
|
||||
@@ -106,19 +121,10 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
|
||||
if o.Opt != "" {
|
||||
if !isInArray(validOptOptions, o.Opt) {
|
||||
if !slices.Contains(validOptOptions, o.Opt) {
|
||||
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isInArray(arr []string, item string) bool {
|
||||
for _, i := range arr {
|
||||
if i == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
func TestVerifyOptions(t *testing.T) {
|
||||
|
||||
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`)
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise, boehm`)
|
||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify, threads, cores`)
|
||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`)
|
||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||
|
||||
testCases := []struct {
|
||||
|
||||
+145
-48
@@ -24,6 +24,7 @@ import (
|
||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||
type TargetSpec struct {
|
||||
Inherits []string `json:"inherits,omitempty"`
|
||||
InheritableOnly bool `json:"inheritable-only"` // this target is only meant to be inherited from, not used directly
|
||||
Triple string `json:"llvm-target,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
|
||||
@@ -32,11 +33,13 @@ type TargetSpec struct {
|
||||
GOARCH string `json:"goarch,omitempty"`
|
||||
SoftFloat bool // used for non-baremetal systems (GOMIPS=softfloat etc)
|
||||
BuildTags []string `json:"build-tags,omitempty"`
|
||||
BuildMode string `json:"buildmode,omitempty"` // default build mode (if nothing specified)
|
||||
GC string `json:"gc,omitempty"`
|
||||
Scheduler string `json:"scheduler,omitempty"`
|
||||
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker,omitempty"`
|
||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||
LinkerFlavor string `json:"linker-flavor,omitempty"` // how to drive the configured linker (for example: gnu, coff, darwin)
|
||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc,omitempty"`
|
||||
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
||||
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
||||
@@ -45,6 +48,7 @@ type TargetSpec struct {
|
||||
LinkerScript string `json:"linkerscript,omitempty"`
|
||||
ExtraFiles []string `json:"extra-files,omitempty"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
|
||||
BootPatches []string `json:"boot-patches,omitempty"` // Bootloader patches to be applied in the order they appear.
|
||||
Emulator string `json:"emulator,omitempty"`
|
||||
FlashCommand string `json:"flash-command,omitempty"`
|
||||
GDB []string `json:"gdb,omitempty"`
|
||||
@@ -61,6 +65,10 @@ type TargetSpec struct {
|
||||
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||
ADBPreCommands []string `json:"adb-pre-commands,omitempty"`
|
||||
ADBPushRemote string `json:"adb-push-remote,omitempty"`
|
||||
ADBPostCommands []string `json:"adb-post-commands,omitempty"`
|
||||
ProbeRSChip string `json:"probe-rs-chip,omitempty"`
|
||||
CodeModel string `json:"code-model,omitempty"`
|
||||
RelocationModel string `json:"relocation-model,omitempty"`
|
||||
WITPackage string `json:"wit-package,omitempty"`
|
||||
@@ -146,6 +154,11 @@ func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||
|
||||
// resolveInherits loads inherited targets, recursively.
|
||||
func (spec *TargetSpec) resolveInherits() error {
|
||||
// Save InheritableOnly before resolving, since it must not propagate
|
||||
// from parent to child (a board target should not become inheritable-only
|
||||
// just because its parent processor target is).
|
||||
inheritableOnly := spec.InheritableOnly
|
||||
|
||||
// First create a new spec with all the inherited properties.
|
||||
newSpec := &TargetSpec{}
|
||||
for _, name := range spec.Inherits {
|
||||
@@ -171,11 +184,29 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
}
|
||||
*spec = *newSpec
|
||||
|
||||
// Restore InheritableOnly from the original spec, not from parents.
|
||||
spec.InheritableOnly = inheritableOnly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load a target specification.
|
||||
func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
if options.Target == "" && options.GOARCH == "wasm" {
|
||||
// Set a specific target if we're building from a known GOOS/GOARCH
|
||||
// combination that is defined in a target JSON file.
|
||||
switch options.GOOS {
|
||||
case "js":
|
||||
options.Target = "wasm"
|
||||
case "wasip1":
|
||||
options.Target = "wasip1"
|
||||
case "wasip2":
|
||||
options.Target = "wasip2"
|
||||
default:
|
||||
return nil, errors.New("GOARCH=wasm but GOOS is not set correctly. Please set GOOS to js, wasip1, or wasip2.")
|
||||
}
|
||||
}
|
||||
|
||||
if options.Target == "" {
|
||||
return defaultTarget(options)
|
||||
}
|
||||
@@ -222,10 +253,17 @@ func GetTargetSpecs() (map[string]*TargetSpec, error) {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, entry.Name())
|
||||
|
||||
spec, err := LoadTarget(&Options{Target: path})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list target: %w", err)
|
||||
}
|
||||
|
||||
if spec.InheritableOnly {
|
||||
// Skip targets that are only meant to be inherited from, not used directly.
|
||||
continue
|
||||
}
|
||||
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||
// This doesn't look like a regular target file, but rather like
|
||||
// a parent target (such as targets/cortex-m.json).
|
||||
@@ -245,8 +283,6 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
GOOS: options.GOOS,
|
||||
GOARCH: options.GOARCH,
|
||||
BuildTags: []string{options.GOOS, options.GOARCH},
|
||||
GC: "precise",
|
||||
Scheduler: "tasks",
|
||||
Linker: "cc",
|
||||
DefaultStackSize: 1024 * 64, // 64kB
|
||||
GDB: []string{"gdb"},
|
||||
@@ -267,60 +303,94 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
case "arm":
|
||||
spec.CPU = "generic"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
switch options.GOARM {
|
||||
subarch := strings.Split(options.GOARM, ",")
|
||||
if len(subarch) > 2 {
|
||||
return nil, fmt.Errorf("invalid GOARM=%s, must be of form <num>,[hardfloat|softfloat]", options.GOARM)
|
||||
}
|
||||
archLevel := subarch[0]
|
||||
var fpu string
|
||||
if len(subarch) >= 2 {
|
||||
fpu = subarch[1]
|
||||
} else {
|
||||
// Pick the default fpu value: softfloat for armv5 and hardfloat
|
||||
// above that.
|
||||
if archLevel == "5" {
|
||||
fpu = "softfloat"
|
||||
} else {
|
||||
fpu = "hardfloat"
|
||||
}
|
||||
}
|
||||
switch fpu {
|
||||
case "softfloat":
|
||||
spec.CFlags = append(spec.CFlags, "-msoft-float")
|
||||
spec.SoftFloat = true
|
||||
case "hardfloat":
|
||||
// Hardware floating point support is the default everywhere except
|
||||
// on ARMv5 where it needs to be enabled explicitly.
|
||||
if archLevel == "5" {
|
||||
spec.CFlags = append(spec.CFlags, "-mfpu=vfpv2")
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid extension GOARM=%s, must be softfloat or hardfloat", options.GOARM)
|
||||
}
|
||||
switch archLevel {
|
||||
case "5":
|
||||
llvmarch = "armv5"
|
||||
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"
|
||||
if spec.SoftFloat {
|
||||
spec.Features = "+armv5t,+soft-float,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
} else {
|
||||
spec.Features = "+armv5t,+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 "6":
|
||||
llvmarch = "armv6"
|
||||
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"
|
||||
if spec.SoftFloat {
|
||||
spec.Features = "+armv6,+dsp,+soft-float,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
} else {
|
||||
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 "7":
|
||||
llvmarch = "armv7"
|
||||
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"
|
||||
if spec.SoftFloat {
|
||||
spec.Features = "+armv7-a,+dsp,+soft-float,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
} else {
|
||||
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"
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||
return nil, fmt.Errorf("invalid GOARM=%s, must be of form <num>,[hardfloat|softfloat] where num is 5, 6, or 7", options.GOARM)
|
||||
}
|
||||
case "arm64":
|
||||
spec.CPU = "generic"
|
||||
llvmarch = "aarch64"
|
||||
if options.GOOS == "darwin" {
|
||||
spec.Features = "+fp-armv8,+neon"
|
||||
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a"
|
||||
// Looks like Apple prefers to call this architecture ARM64
|
||||
// instead of AArch64.
|
||||
llvmarch = "arm64"
|
||||
} else if options.GOOS == "windows" {
|
||||
spec.Features = "+fp-armv8,+neon,-fmv"
|
||||
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv"
|
||||
} else { // linux
|
||||
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
|
||||
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv,-outline-atomics"
|
||||
}
|
||||
case "mips", "mipsle":
|
||||
spec.CPU = "mips32r2"
|
||||
spec.CPU = "mips32"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-pic")
|
||||
if options.GOOS == "mips" {
|
||||
if options.GOARCH == "mips" {
|
||||
llvmarch = "mips" // big endian
|
||||
} else {
|
||||
llvmarch = "mipsel" // little endian
|
||||
}
|
||||
switch options.GOMIPS {
|
||||
case "hardfloat":
|
||||
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
|
||||
spec.Features = "+fpxx,+mips32,+nooddspreg,-noabicalls"
|
||||
case "softfloat":
|
||||
spec.SoftFloat = true
|
||||
spec.Features = "+mips32r2,+soft-float,-noabicalls"
|
||||
spec.Features = "+mips32,+soft-float,-noabicalls"
|
||||
spec.CFlags = append(spec.CFlags, "-msoft-float")
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid GOMIPS=%s: must be hardfloat or softfloat", options.GOMIPS)
|
||||
}
|
||||
case "wasm":
|
||||
llvmarch = "wasm32"
|
||||
spec.CPU = "generic"
|
||||
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
||||
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
|
||||
spec.CFlags = append(spec.CFlags,
|
||||
"-mbulk-memory",
|
||||
"-mnontrapping-fptoint",
|
||||
"-msign-ext",
|
||||
)
|
||||
return nil, fmt.Errorf("GOARCH=wasm but GOOS is unset. Please set GOOS to js, wasip1, or wasip2.")
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GOARCH=%s", options.GOARCH)
|
||||
}
|
||||
@@ -330,11 +400,13 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
llvmvendor := "unknown"
|
||||
switch options.GOOS {
|
||||
case "darwin":
|
||||
spec.GC = "boehm"
|
||||
platformVersion := "10.12.0"
|
||||
if options.GOARCH == "arm64" {
|
||||
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
spec.Scheduler = "threads"
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "darwin-libSystem"
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
||||
@@ -346,7 +418,15 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"-arch", llvmarch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
)
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/internal/futex/futex_darwin.c",
|
||||
"src/internal/task/task_threads.c",
|
||||
"src/runtime/os_darwin.c",
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
case "linux":
|
||||
spec.GC = "boehm"
|
||||
spec.Scheduler = "threads"
|
||||
spec.Linker = "ld.lld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "musl"
|
||||
@@ -365,25 +445,42 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
// proper threading.
|
||||
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/internal/futex/futex_linux.c",
|
||||
"src/internal/task/task_threads.c",
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
case "windows":
|
||||
spec.GC = "boehm"
|
||||
spec.Scheduler = "tasks"
|
||||
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 options.GOARCH {
|
||||
case "386":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "i386pe",
|
||||
"--major-os-version", "4",
|
||||
"--major-subsystem-version", "4",
|
||||
)
|
||||
// __udivdi3 is not present in ucrt it seems.
|
||||
spec.RTLib = "compiler-rt"
|
||||
case "amd64":
|
||||
// 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
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "i386pep",
|
||||
"--image-base", "0x400000",
|
||||
)
|
||||
spec.RTLib = "compiler-rt"
|
||||
case "arm64":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "arm64pe",
|
||||
)
|
||||
spec.RTLib = "compiler-rt"
|
||||
}
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-Bdynamic",
|
||||
@@ -391,35 +488,35 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
case "wasip1":
|
||||
spec.GC = "" // use default GC
|
||||
spec.Scheduler = "asyncify"
|
||||
spec.Linker = "wasm-ld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "wasi-libc"
|
||||
spec.DefaultStackSize = 1024 * 64 // 64kB
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"--stack-first",
|
||||
"--no-demangle",
|
||||
)
|
||||
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/asm_tinygowasm.S",
|
||||
"src/internal/task/task_asyncify_wasm.S",
|
||||
)
|
||||
llvmos = "wasi"
|
||||
"src/runtime/runtime_windows.c")
|
||||
case "wasm", "wasip1", "wasip2":
|
||||
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GOOS=%s", options.GOOS)
|
||||
}
|
||||
|
||||
if spec.GC == "boehm" {
|
||||
// Add this file only when needed. This fixes a build failure on
|
||||
// Windows.
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_boehm.c")
|
||||
}
|
||||
|
||||
// Target triples (which actually have four components, but are called
|
||||
// triples for historical reasons) have the form:
|
||||
// arch-vendor-os-environment
|
||||
spec.Triple = llvmarch + "-" + llvmvendor + "-" + llvmos
|
||||
if options.GOOS == "windows" {
|
||||
spec.Triple += "-gnu"
|
||||
} else if options.GOARCH == "arm" {
|
||||
spec.Triple += "-gnueabihf"
|
||||
} else if options.GOOS == "linux" {
|
||||
// We use musl on Linux (not glibc) so we should use -musleabi* instead
|
||||
// of -gnueabi*.
|
||||
// The *hf suffix selects between soft/hard floating point ABI.
|
||||
if spec.SoftFloat {
|
||||
spec.Triple += "-musleabi"
|
||||
} else {
|
||||
spec.Triple += "-musleabihf"
|
||||
}
|
||||
}
|
||||
|
||||
// Add extra assembly files (needed for the scheduler etc).
|
||||
|
||||
@@ -23,6 +23,37 @@ func TestLoadTarget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTargetSpecs_InheritableOnlyTargetsExcluded(t *testing.T) {
|
||||
specs, err := GetTargetSpecs()
|
||||
if err != nil {
|
||||
t.Fatal("GetTargetSpecs failed:", err)
|
||||
}
|
||||
|
||||
// Inheritable-only processor-level targets should not appear in the listing.
|
||||
inheritableOnlyTargets := []string{"esp32", "esp32c3", "esp32s3", "esp8266", "rp2040", "rp2350", "rp2350b"}
|
||||
for _, name := range inheritableOnlyTargets {
|
||||
if _, ok := specs[name]; ok {
|
||||
t.Errorf("inheritable-only target %q should not appear in GetTargetSpecs", name)
|
||||
}
|
||||
}
|
||||
|
||||
// Board targets that inherit from inheritable-only targets should still appear.
|
||||
boardTargets := []string{"esp32-coreboard-v2", "pico"}
|
||||
for _, name := range boardTargets {
|
||||
if _, ok := specs[name]; !ok {
|
||||
t.Errorf("board target %q should appear in GetTargetSpecs", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadTarget_InheritableOnlyTargetStillLoadable(t *testing.T) {
|
||||
// Inheritable-only targets should still be loadable directly (for building).
|
||||
_, err := LoadTarget(&Options{Target: "esp32"})
|
||||
if err != nil {
|
||||
t.Errorf("LoadTarget should still load inheritable-only target esp32: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideProperties(t *testing.T) {
|
||||
baseAutoStackSize := true
|
||||
base := &TargetSpec{
|
||||
@@ -81,3 +112,52 @@ func TestOverrideProperties(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConfigLinkerFlavor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target *TargetSpec
|
||||
goos string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "default gnu",
|
||||
target: &TargetSpec{},
|
||||
goos: "linux",
|
||||
want: "gnu",
|
||||
},
|
||||
{
|
||||
name: "default coff",
|
||||
target: &TargetSpec{},
|
||||
goos: "windows",
|
||||
want: "coff",
|
||||
},
|
||||
{
|
||||
name: "default darwin",
|
||||
target: &TargetSpec{},
|
||||
goos: "darwin",
|
||||
want: "darwin",
|
||||
},
|
||||
{
|
||||
name: "target override",
|
||||
target: &TargetSpec{
|
||||
LinkerFlavor: "coff",
|
||||
},
|
||||
goos: "linux",
|
||||
want: "coff",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.target.GOOS = tc.goos
|
||||
config := &Config{
|
||||
Options: &Options{},
|
||||
Target: tc.target,
|
||||
}
|
||||
if got := config.LinkerFlavor(); got != tc.want {
|
||||
t.Fatalf("LinkerFlavor() = %q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build llvm22
|
||||
|
||||
package compileopts
|
||||
|
||||
import "strings"
|
||||
|
||||
// ClangTriple returns the target triple to pass to Clang's --target flag.
|
||||
// LLVM 22 deprecated the "wasm32-unknown-wasi" triple in favor of
|
||||
// "wasm32-unknown-wasip1", so we substitute it here to avoid warnings.
|
||||
func ClangTriple(triple string) string {
|
||||
return strings.Replace(triple, "wasm32-unknown-wasi", "wasm32-unknown-wasip1", 1)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !llvm22
|
||||
|
||||
package compileopts
|
||||
|
||||
// ClangTriple returns the target triple to pass to Clang's --target flag.
|
||||
// For pre-LLVM 22, the triple is used as-is.
|
||||
func ClangTriple(triple string) string {
|
||||
return triple
|
||||
}
|
||||
+6
-5
@@ -18,11 +18,12 @@ var stdlibAliases = map[string]string{
|
||||
// crypto packages
|
||||
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
|
||||
"crypto/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",
|
||||
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||
"internal/chacha8rand.block": "internal/chacha8rand.block_generic",
|
||||
|
||||
// AES
|
||||
"crypto/aes.decryptBlockAsm": "crypto/aes.decryptBlock",
|
||||
|
||||
+22
-9
@@ -241,24 +241,37 @@ 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")
|
||||
faultBlock := b.getRuntimeAssertBlock(blockPrefix, assertFunc)
|
||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||
|
||||
// Fail: the assert triggered so panic.
|
||||
b.SetInsertPointAtEnd(faultBlock)
|
||||
b.createRuntimeCall(assertFunc, nil, "")
|
||||
b.CreateUnreachable()
|
||||
|
||||
// Ok: assert didn't trigger so continue normally.
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
}
|
||||
|
||||
func (b *builder) getRuntimeAssertBlock(blockPrefix, assertFunc string) llvm.BasicBlock {
|
||||
if b.runtimeAssertBlocks == nil {
|
||||
b.runtimeAssertBlocks = make(map[string]llvm.BasicBlock)
|
||||
}
|
||||
if block := b.runtimeAssertBlocks[assertFunc]; !block.IsNil() {
|
||||
return block
|
||||
}
|
||||
savedBlock := b.GetInsertBlock()
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
b.runtimeAssertBlocks[assertFunc] = block
|
||||
b.SetInsertPointAtEnd(block)
|
||||
if b.hasDeferFrame() {
|
||||
b.createFaultCheckpoint()
|
||||
}
|
||||
b.createRuntimeCall(assertFunc, nil, "")
|
||||
b.CreateUnreachable()
|
||||
b.SetInsertPointAtEnd(savedBlock)
|
||||
return block
|
||||
}
|
||||
|
||||
// extendInteger extends the value to at least targetType using a zero or sign
|
||||
// extend. The resulting value is not truncated: it may still be bigger than
|
||||
// targetType.
|
||||
|
||||
@@ -15,6 +15,16 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
|
||||
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, "")
|
||||
case "AndInt32", "AndInt64", "AndUint32", "AndUint64", "AndUintptr":
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAnd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
return oldVal
|
||||
case "OrInt32", "OrInt64", "OrUint32", "OrUint64", "OrUintptr":
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpOr, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
return oldVal
|
||||
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
|
||||
+71
-6
@@ -19,8 +19,9 @@ const maxFieldsPerParam = 3
|
||||
// useful while declaring or defining a function.
|
||||
type paramInfo struct {
|
||||
llvmType llvm.Type
|
||||
name string // name, possibly with suffixes for e.g. struct fields
|
||||
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||
name string // name, possibly with suffixes for e.g. struct fields
|
||||
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||
flags paramFlags // extra flags for this parameter
|
||||
}
|
||||
|
||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||
@@ -28,9 +29,15 @@ type paramInfo struct {
|
||||
type paramFlags uint8
|
||||
|
||||
const (
|
||||
// Parameter may have the deferenceable_or_null attribute. This attribute
|
||||
// cannot be applied to unsafe.Pointer and to the data pointer of slices.
|
||||
paramIsDeferenceableOrNull = 1 << iota
|
||||
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||
// The extra context parameter is not a Go parameter.
|
||||
paramIsGoParam = 1 << iota
|
||||
|
||||
// Whether this is a readonly parameter (for example, a string pointer).
|
||||
paramIsReadonly
|
||||
|
||||
// Whether this parameter is passed through backing storage.
|
||||
paramIsIndirect
|
||||
)
|
||||
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
@@ -75,7 +82,15 @@ func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value,
|
||||
fragments := b.expandFormalParam(arg)
|
||||
expanded = append(expanded, fragments...)
|
||||
}
|
||||
return b.CreateCall(fnType, fn, expanded, name)
|
||||
call := b.CreateCall(fnType, fn, expanded, name)
|
||||
if !fn.IsAFunction().IsNil() {
|
||||
if cc := fn.FunctionCallConv(); cc != llvm.CCallConv {
|
||||
// Set a different calling convention if needed.
|
||||
// This is needed for GetModuleHandleExA on Windows, for example.
|
||||
call.SetInstructionCallConv(cc)
|
||||
}
|
||||
}
|
||||
return call
|
||||
}
|
||||
|
||||
// createInvoke is like createCall but continues execution at the landing pad if
|
||||
@@ -90,6 +105,18 @@ func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Valu
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
// parameter list.
|
||||
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
if c.isIndirectAggregate(t) {
|
||||
return []paramInfo{{
|
||||
llvmType: c.dataPtrType,
|
||||
name: name,
|
||||
elemSize: c.targetData.TypeAllocSize(t),
|
||||
flags: paramIsGoParam | paramIsReadonly | paramIsIndirect,
|
||||
}}
|
||||
}
|
||||
return c.expandDirectFormalParamType(t, name, goType)
|
||||
}
|
||||
|
||||
func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fieldInfos := c.flattenAggregateType(t, name, goType)
|
||||
@@ -103,6 +130,38 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
|
||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||
}
|
||||
|
||||
func (c *compilerContext) storedParamType(t llvm.Type, exported bool) llvm.Type {
|
||||
if c.isIndirectParam(t, exported) {
|
||||
return c.dataPtrType
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (c *compilerContext) isIndirectParam(t llvm.Type, exported bool) bool {
|
||||
return !exported && c.isIndirectAggregate(t)
|
||||
}
|
||||
|
||||
func (b *builder) appendStoredValueTypes(valueTypes []llvm.Type, values []ssa.Value, exported bool) []llvm.Type {
|
||||
for _, value := range values {
|
||||
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(value.Type()), exported))
|
||||
}
|
||||
return valueTypes
|
||||
}
|
||||
|
||||
func (b *builder) appendStoredParamTypes(valueTypes []llvm.Type, params []*types.Var, exported bool) []llvm.Type {
|
||||
for _, param := range params {
|
||||
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(param.Type()), exported))
|
||||
}
|
||||
return valueTypes
|
||||
}
|
||||
|
||||
func (b *builder) prependIndirectResult(sig *types.Signature, exported bool, params []llvm.Value, name string) []llvm.Value {
|
||||
if resultType, indirect := b.hasIndirectResult(sig); !exported && indirect {
|
||||
return append([]llvm.Value{b.createIndirectStorage(resultType, name)}, params...)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// expandFormalParamOffsets returns a list of offsets from the start of an
|
||||
// object of type t after it would have been split up by expandFormalParam. This
|
||||
// is useful for debug information, where it is necessary to know the offset
|
||||
@@ -158,6 +217,7 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
continue
|
||||
}
|
||||
suffix := strconv.Itoa(i)
|
||||
isString := false
|
||||
if goType != nil {
|
||||
// Try to come up with a good suffix for this struct field,
|
||||
// depending on which Go type it's based on.
|
||||
@@ -174,12 +234,16 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
suffix = []string{"r", "i"}[i]
|
||||
case types.String:
|
||||
suffix = []string{"data", "len"}[i]
|
||||
isString = true
|
||||
}
|
||||
case *types.Signature:
|
||||
suffix = []string{"context", "funcptr"}[i]
|
||||
}
|
||||
}
|
||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||
if isString {
|
||||
subInfos[0].flags |= paramIsReadonly
|
||||
}
|
||||
paramInfos = append(paramInfos, subInfos...)
|
||||
}
|
||||
return paramInfos
|
||||
@@ -195,6 +259,7 @@ func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Ty
|
||||
info := paramInfo{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: paramIsGoParam,
|
||||
}
|
||||
if goType != nil {
|
||||
switch underlying := goType.Underlying().(type) {
|
||||
|
||||
+57
-54
@@ -4,7 +4,9 @@ package compiler
|
||||
// or pseudo-operations that are lowered during goroutine lowering.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"math"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
@@ -28,33 +30,29 @@ func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
|
||||
// actual channel send operation during goroutine lowering.
|
||||
func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
ch := b.getValue(instr.Chan, getPos(instr))
|
||||
chanValue := b.getValue(instr.X, getPos(instr))
|
||||
|
||||
// store value-to-send
|
||||
valueType := b.getLLVMType(instr.X.Type())
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var valueAlloca, valueAllocaSize llvm.Value
|
||||
var storage valueStorage
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||
storage.ptr = llvm.ConstNull(b.dataPtrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
b.CreateStore(chanValue, valueAlloca)
|
||||
storage = b.getValueStorage(instr.X, "chan.value")
|
||||
}
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
// Allocate buffer for the channel operation.
|
||||
channelOp := b.getLLVMRuntimeType("channelOp")
|
||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||
|
||||
// Do the send.
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
||||
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, storage.ptr, channelOpAlloca}, "")
|
||||
|
||||
// End the lifetime of the allocas.
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
||||
if !isZeroSize {
|
||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||
}
|
||||
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||
b.endValueStorage(storage)
|
||||
}
|
||||
|
||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||
@@ -64,42 +62,22 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
ch := b.getValue(unop.X, getPos(unop))
|
||||
|
||||
// Allocate memory to receive into.
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var valueAlloca, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
}
|
||||
result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan")
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
// Allocate buffer for the channel operation.
|
||||
channelOp := b.getLLVMRuntimeType("channelOp")
|
||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||
|
||||
// Do the receive.
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
||||
var received llvm.Value
|
||||
if isZeroSize {
|
||||
received = llvm.ConstNull(valueType)
|
||||
} else {
|
||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||
}
|
||||
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
||||
|
||||
if unop.CommaOk {
|
||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||
tuple = b.CreateInsertValue(tuple, received, 0, "")
|
||||
tuple = b.CreateInsertValue(tuple, commaOk, 1, "")
|
||||
return tuple
|
||||
} else {
|
||||
return received
|
||||
}
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, result.valuePtr, channelOpAlloca}, "")
|
||||
received := result.finish(b, commaOk, "chan.received")
|
||||
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||
return received
|
||||
}
|
||||
|
||||
// createChanClose closes the given channel.
|
||||
func (b *builder) createChanClose(ch llvm.Value) {
|
||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||
b.createRuntimeInvoke("chanClose", []llvm.Value{ch}, "")
|
||||
}
|
||||
|
||||
// createSelect emits all IR necessary for a select statements. That's a
|
||||
@@ -124,6 +102,20 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
}
|
||||
}
|
||||
|
||||
const maxSelectStates = math.MaxUint32 >> 2
|
||||
if len(expr.States) > maxSelectStates {
|
||||
// The runtime code assumes that the number of state must fit in 30 bits
|
||||
// (so the select index can be stored in a uint32 with two bits reserved
|
||||
// for other purposes). It seems unlikely that a real program would have
|
||||
// that many states, but we check for this case anyway to be sure.
|
||||
// We use a uint32 (and not a uintptr or uint64) to avoid 64-bit atomic
|
||||
// operations which aren't available everywhere.
|
||||
b.addError(expr.Pos(), fmt.Sprintf("too many select states: got %d but the maximum supported number is %d", len(expr.States), maxSelectStates))
|
||||
|
||||
// Continue as usual (we'll generate broken code but the error will
|
||||
// prevent the compilation to complete).
|
||||
}
|
||||
|
||||
// This code create a (stack-allocated) slice containing all the select
|
||||
// cases and then calls runtime.chanSelect to perform the actual select
|
||||
// statement.
|
||||
@@ -154,9 +146,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
case types.SendOnly:
|
||||
// Store this value in an alloca and put a pointer to this alloca
|
||||
// in the send state.
|
||||
sendValue := b.getValue(state.Send, state.Pos)
|
||||
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
|
||||
b.CreateStore(sendValue, alloca)
|
||||
alloca := b.getSelectSendStorage(state.Send)
|
||||
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
|
||||
default:
|
||||
panic("unreachable")
|
||||
@@ -198,10 +188,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
if expr.Blocking {
|
||||
// Stack-allocate operation structures.
|
||||
// If these were simply created as a slice, they would heap-allocate.
|
||||
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
||||
chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
||||
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
||||
opsAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelOp"), len(selectStates))
|
||||
opsAlloca, opsSize := b.createTemporaryAlloca(opsAllocaType, "select.block.alloca")
|
||||
opsLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||
opsPtr := b.CreateGEP(opsAllocaType, opsAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.block")
|
||||
@@ -209,15 +199,18 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
results = b.createRuntimeCall("chanSelect", []llvm.Value{
|
||||
recvbuf,
|
||||
statesPtr, statesLen, statesLen, // []chanSelectState
|
||||
chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList
|
||||
opsPtr, opsLen, opsLen, // []channelOp
|
||||
}, "select.result")
|
||||
|
||||
// Terminate the lifetime of the operation structures.
|
||||
b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
|
||||
b.emitLifetimeEnd(opsAlloca, opsSize)
|
||||
} else {
|
||||
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
|
||||
opsPtr := llvm.ConstNull(b.dataPtrType)
|
||||
opsLen := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
results = b.createRuntimeCall("chanSelect", []llvm.Value{
|
||||
recvbuf,
|
||||
statesPtr, statesLen, statesLen, // []chanSelectState
|
||||
opsPtr, opsLen, opsLen, // []channelOp (nil slice)
|
||||
}, "select.result")
|
||||
}
|
||||
|
||||
@@ -261,7 +254,17 @@ 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 := b.getLLVMType(expr.Type())
|
||||
return b.CreateLoad(typ, recvbuf, "")
|
||||
return b.loadFromStorage(recvbuf, expr.Type(), "select.received")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) getSelectSendStorage(value ssa.Value) llvm.Value {
|
||||
typ := b.getLLVMType(value.Type())
|
||||
if b.isIndirectAggregate(typ) {
|
||||
return b.getValuePointer(value)
|
||||
}
|
||||
llvmValue := b.getValue(value, getPos(value))
|
||||
ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value")
|
||||
b.CreateStore(llvmValue, ptr)
|
||||
return ptr
|
||||
}
|
||||
|
||||
+548
-115
File diff suppressed because it is too large
Load Diff
+176
-19
@@ -4,6 +4,7 @@ import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -50,6 +51,8 @@ func TestCompiler(t *testing.T) {
|
||||
{"channel.go", "", ""},
|
||||
{"gc.go", "", ""},
|
||||
{"zeromap.go", "", ""},
|
||||
{"generics.go", "", ""},
|
||||
{"large.go", "", ""},
|
||||
}
|
||||
if goMinor >= 20 {
|
||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||
@@ -57,6 +60,9 @@ func TestCompiler(t *testing.T) {
|
||||
if goMinor >= 21 {
|
||||
tests = append(tests, testCase{"go1.21.go", "", ""})
|
||||
}
|
||||
if goMinor >= 27 {
|
||||
tests = append(tests, testCase{"go1.27.go", "", ""})
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
name := tc.file
|
||||
@@ -121,30 +127,138 @@ func TestCompiler(t *testing.T) {
|
||||
t.Fatal("failed to read golden file:", err)
|
||||
}
|
||||
|
||||
if !fuzzyEqualIR(mod.String(), string(expected)) {
|
||||
t.Errorf("output does not match expected output:\n%s", mod.String())
|
||||
if diff := diffIR(string(expected), mod.String()); diff != "" {
|
||||
t.Errorf("output does not match expected output (re-run with -update to regenerate):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||
// equal. That means, only relevant lines are compared (excluding comments
|
||||
// etc.).
|
||||
func fuzzyEqualIR(s1, s2 string) bool {
|
||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
||||
if len(lines1) != len(lines2) {
|
||||
return false
|
||||
}
|
||||
for i, line1 := range lines1 {
|
||||
line2 := lines2[i]
|
||||
if line1 != line2 {
|
||||
return false
|
||||
func TestOptimizedLargeAggregateABI(t *testing.T) {
|
||||
options := &compileopts.Options{Target: "wasm"}
|
||||
mod, errs := testCompilePackage(t, options, "large-optimized.go")
|
||||
if len(errs) != 0 {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer mod.Dispose()
|
||||
|
||||
passOptions := llvm.NewPassBuilderOptions()
|
||||
defer passOptions.Dispose()
|
||||
if err := mod.RunPasses("default<O2>", llvm.TargetMachine{}, passOptions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return true
|
||||
resultFn := mod.NamedFunction("main.makeLargeOptimizedValue")
|
||||
if resultFn.IsNil() {
|
||||
t.Fatal("missing function main.makeLargeOptimizedValue")
|
||||
}
|
||||
if resultType := resultFn.GlobalValueType().ReturnType(); resultType.TypeKind() != llvm.VoidTypeKind {
|
||||
t.Errorf("large aggregate result was promoted to %s", resultType)
|
||||
}
|
||||
|
||||
for _, name := range []string{
|
||||
"main.makeLargeOptimizedValue",
|
||||
"main.readLargeOptimizedValue",
|
||||
"main.readMixedLargeOptimizedValue",
|
||||
} {
|
||||
fn := mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
t.Fatalf("missing function %s", name)
|
||||
}
|
||||
if paramType := fn.GlobalValueType().ParamTypes()[0]; paramType.TypeKind() != llvm.PointerTypeKind {
|
||||
t.Errorf("%s aggregate parameter was promoted to %s", name, paramType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeIR canonicalizes LLVM IR so a single golden file keeps matching
|
||||
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM
|
||||
// prints some attributes differently.
|
||||
func normalizeIR(s string) string {
|
||||
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
||||
// which LLVM printed before any co-occurring attribute such as
|
||||
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
||||
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
||||
// "ptr readonly captures(none)"). Normalize both name and position back
|
||||
// to the old spelling.
|
||||
s = normalizeCapturesAttr(s)
|
||||
|
||||
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
||||
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
||||
// assumed not to create undef/poison before. It's unrelated to the
|
||||
// behavior under test, so ignore it for comparison.
|
||||
s = strings.ReplaceAll(s, "nocreateundeforpoison ", "")
|
||||
|
||||
// LLVM 22 dropped the (redundant) i64 size argument from
|
||||
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
||||
// written against the two-argument form still match.
|
||||
s = lifetimeSizeArgRe.ReplaceAllString(s, "$1")
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// diffIR compares two LLVM IR strings, ignoring irrelevant lines (comments,
|
||||
// empty lines, etc.) and normalizing LLVM-version-specific spellings via
|
||||
// normalizeIR. It returns "" when they are equal. Otherwise it returns a
|
||||
// compact diff of only the region that differs: the common prefix and suffix
|
||||
// are trimmed, then the differing expected lines (prefixed "-") are shown
|
||||
// followed by the differing actual lines (prefixed "+").
|
||||
func diffIR(expected, actual string) string {
|
||||
exp := filterIrrelevantIRLines(strings.Split(normalizeIR(expected), "\n"))
|
||||
act := filterIrrelevantIRLines(strings.Split(normalizeIR(actual), "\n"))
|
||||
|
||||
// Trim the common prefix.
|
||||
start := 0
|
||||
for start < len(exp) && start < len(act) && exp[start] == act[start] {
|
||||
start++
|
||||
}
|
||||
// Trim the common suffix.
|
||||
e, a := len(exp), len(act)
|
||||
for e > start && a > start && exp[e-1] == act[a-1] {
|
||||
e--
|
||||
a--
|
||||
}
|
||||
if start == e && start == a {
|
||||
return "" // equal
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("first difference at relevant line ")
|
||||
b.WriteString(strconv.Itoa(start + 1))
|
||||
b.WriteString(":\n")
|
||||
for _, line := range exp[start:e] {
|
||||
b.WriteString("- ")
|
||||
b.WriteString(line)
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
for _, line := range act[start:a] {
|
||||
b.WriteString("+ ")
|
||||
b.WriteString(line)
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
||||
// 'captures(none)', which is how LLVM 21+ orders these two attributes when
|
||||
// printing IR (the pre-LLVM21 'nocapture' attribute printed the other way
|
||||
// around).
|
||||
var capturesNoneAttrRe = regexp.MustCompile(`\b(readonly|readnone|writeonly|nonnull)\s+captures\(none\)`)
|
||||
|
||||
// lifetimeSizeArgRe matches the i64 size argument of an
|
||||
// llvm.lifetime.start/end call or declaration, which LLVM 22 removed.
|
||||
var lifetimeSizeArgRe = regexp.MustCompile(`(@llvm\.lifetime\.(?:start|end)\.p0\()i64(?: immarg| \d+), `)
|
||||
|
||||
// normalizeCapturesAttr rewrites LLVM 21+'s 'captures(none)' attribute back
|
||||
// to the pre-LLVM21 'nocapture' spelling and position, so golden IR files
|
||||
// written against LLVM <21 keep matching.
|
||||
func normalizeCapturesAttr(s string) string {
|
||||
s = capturesNoneAttrRe.ReplaceAllString(s, "nocapture $1")
|
||||
s = strings.ReplaceAll(s, "captures(none)", "nocapture")
|
||||
return s
|
||||
}
|
||||
|
||||
// filterIrrelevantIRLines removes lines from the input slice of strings that
|
||||
@@ -187,9 +301,9 @@ func TestCompilerErrors(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
errorsFileString := strings.ReplaceAll(string(errorsFile), "\r\n", "\n")
|
||||
for _, line := range strings.Split(errorsFileString, "\n") {
|
||||
if strings.HasPrefix(line, "// ERROR: ") {
|
||||
expectedErrors = append(expectedErrors, strings.TrimPrefix(line, "// ERROR: "))
|
||||
for line := range strings.SplitSeq(errorsFileString, "\n") {
|
||||
if after, ok := strings.CutPrefix(line, "// ERROR: "); ok {
|
||||
expectedErrors = append(expectedErrors, after)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +327,49 @@ func TestCompilerErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateValueCount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
|
||||
byteType := ctx.Int8Type()
|
||||
tests := []struct {
|
||||
name string
|
||||
typ llvm.Type
|
||||
count uint64
|
||||
exceeded bool
|
||||
}{
|
||||
{"empty", llvm.ArrayType(byteType, 0), 0, false},
|
||||
{"limit", llvm.ArrayType(byteType, 1024), 1024, false},
|
||||
{"over limit", llvm.ArrayType(byteType, 1025), 0, true},
|
||||
{"combined limit", ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(byteType, 512),
|
||||
llvm.ArrayType(byteType, 512),
|
||||
}, false), 1024, false},
|
||||
{"combined over limit", ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(byteType, 1000),
|
||||
llvm.ArrayType(byteType, 1000),
|
||||
}, false), 0, true},
|
||||
{"comma-ok over limit", ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(byteType, 1024),
|
||||
ctx.Int1Type(),
|
||||
}, false), 0, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
count, exceeded := aggregateValueCount(test.typ, 0)
|
||||
if exceeded != test.exceeded {
|
||||
t.Errorf("expected exceeded=%t, got %t", test.exceeded, exceeded)
|
||||
}
|
||||
if !exceeded && count != test.count {
|
||||
t.Errorf("expected count=%d, got %d", test.count, count)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Build a package given a number of compiler options and a file.
|
||||
func testCompilePackage(t *testing.T, options *compileopts.Options, file string) (llvm.Module, []error) {
|
||||
target, err := compileopts.LoadTarget(options)
|
||||
|
||||
+240
-130
@@ -16,6 +16,7 @@ package compiler
|
||||
import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
@@ -31,7 +32,7 @@ func (b *builder) supportsRecover() bool {
|
||||
// proposal of WebAssembly:
|
||||
// https://github.com/WebAssembly/exception-handling
|
||||
return false
|
||||
case "riscv64", "xtensa":
|
||||
case "xtensa":
|
||||
// TODO: add support for these architectures
|
||||
return false
|
||||
default:
|
||||
@@ -59,10 +60,6 @@ func (b *builder) deferInitFunc() {
|
||||
b.deferExprFuncs = make(map[ssa.Value]int)
|
||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||
|
||||
// Create defer list pointer.
|
||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), 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
|
||||
@@ -72,12 +69,22 @@ func (b *builder) deferInitFunc() {
|
||||
// in the setjmp-like inline assembly.
|
||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||
// The field index must match the DeferPtr field in runtime.deferFrame,
|
||||
// defined in src/runtime/panic.go.
|
||||
b.deferPtr = b.CreateInBoundsGEP(deferFrameType, b.deferFrame, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 6, false), // DeferPtr field
|
||||
}, "deferPtr")
|
||||
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")
|
||||
} else {
|
||||
// Create defer list pointer.
|
||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,17 +106,18 @@ func (b *builder) createLandingPad() {
|
||||
|
||||
// Continue at the 'recover' block, which returns to the parent in an
|
||||
// appropriate way.
|
||||
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||
b.CreateBr(b.blockInfo[b.fn.Recover.Index].entry)
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// Create a checkpoint (similar to setjmp). This emits inline assembly that
|
||||
// stores the current program counter inside the ptr address (actually
|
||||
// ptr+sizeof(ptr)) and then returns a boolean indicating whether this is the
|
||||
// normal flow (false) or we jumped here from somewhere else (true).
|
||||
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
||||
// 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.
|
||||
// * Registers are either clobbered or, on 386, saved for longjmp to
|
||||
// restore if the ABI requires them to survive calls.
|
||||
// * 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
|
||||
@@ -122,8 +130,12 @@ func (b *builder) createInvokeCheckpoint() {
|
||||
asmString = `
|
||||
xorl %eax, %eax
|
||||
movl $$1f, 4(%ebx)
|
||||
movl %ebx, 8(%ebx)
|
||||
movl %esi, 12(%ebx)
|
||||
movl %edi, 16(%ebx)
|
||||
movl %ebp, 20(%ebx)
|
||||
1:`
|
||||
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
constraints = "={eax},{ebx},~{ecx},~{edx},~{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":
|
||||
@@ -160,7 +172,7 @@ 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}"
|
||||
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},~{memory}"
|
||||
if b.GOOS != "darwin" && b.GOOS != "windows" {
|
||||
// These registers cause the following warning when compiling for
|
||||
// MacOS and Windows:
|
||||
@@ -198,61 +210,199 @@ jal 1f
|
||||
addiu $$ra, 8
|
||||
sw $$ra, 4($$5)
|
||||
.set at`
|
||||
constraints = "={$4},{$5},~{$1},~{$2},~{$3},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$29},~{$30},~{$31},~{$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}"
|
||||
case "riscv32":
|
||||
asmString = `
|
||||
constraints = "={$4},{$5},~{$1},~{$2},~{$3},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$29},~{$30},~{$31},~{memory}"
|
||||
if !strings.Contains(b.Features, "+soft-float") {
|
||||
// Using floating point registers together with GOMIPS=softfloat
|
||||
// results in a crash: "This value type is not natively supported!"
|
||||
// So only add them when using hardfloat.
|
||||
constraints += ",~{$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}"
|
||||
}
|
||||
case "riscv32", "riscv64":
|
||||
if b.archFamily() == "riscv32" {
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sw a2, 4(a1)
|
||||
li a0, 0
|
||||
1:`
|
||||
} else {
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sd a2, 8(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)
|
||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.dataPtrType}, false)
|
||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||
result := b.CreateCall(asmType, asm, []llvm.Value{ptr}, "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")
|
||||
return isZero
|
||||
}
|
||||
|
||||
// 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() {
|
||||
isZero := b.createCheckpoint(b.deferFrame)
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
b.blockExits[b.currentBlock] = continueBB
|
||||
b.currentBlockInfo.exit = continueBB
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
func isInLoop(start *ssa.BasicBlock) bool {
|
||||
// Use a breadth-first search to scan backwards through the block graph.
|
||||
queue := []*ssa.BasicBlock{start}
|
||||
checked := map[*ssa.BasicBlock]struct{}{}
|
||||
// createFaultCheckpoint is like createInvokeCheckpoint but for use in fault
|
||||
// blocks (e.g., bounds check failures). Unlike createInvokeCheckpoint, it does
|
||||
// not update currentBlockInfo.exit because the fault block is a dead-end that
|
||||
// does not participate in phi node resolution.
|
||||
func (b *builder) createFaultCheckpoint() {
|
||||
isZero := b.createCheckpoint(b.deferFrame)
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
}
|
||||
|
||||
for len(queue) > 0 {
|
||||
// pop a block off of the queue
|
||||
block := queue[len(queue)-1]
|
||||
queue = queue[:len(queue)-1]
|
||||
// isInLoop checks if there is a path from the current block to itself.
|
||||
// Use Tarjan's strongly connected components algorithm to search for cycles.
|
||||
// A one-node SCC is a cycle iff there is an edge from the node to itself.
|
||||
// A multi-node SCC is always a cycle.
|
||||
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||
func (b *builder) isInLoop() bool {
|
||||
if b.currentBlockInfo.tarjan.lowLink == 0 {
|
||||
b.strongConnect(b.currentBlock)
|
||||
}
|
||||
return b.currentBlockInfo.tarjan.cyclic
|
||||
}
|
||||
|
||||
// Search through predecessors.
|
||||
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
||||
// Defers are often placed near the start of the function.
|
||||
for _, pred := range block.Preds {
|
||||
if pred == start {
|
||||
// cycle found
|
||||
return true
|
||||
}
|
||||
func (b *builder) strongConnect(block *ssa.BasicBlock) {
|
||||
// Assign a new index.
|
||||
// Indices start from 1 so that 0 can be used as a sentinel.
|
||||
assignedIndex := b.tarjanIndex + 1
|
||||
b.tarjanIndex = assignedIndex
|
||||
|
||||
if _, ok := checked[pred]; ok {
|
||||
// block already checked
|
||||
continue
|
||||
}
|
||||
// Apply the new index.
|
||||
blockIndex := block.Index
|
||||
node := &b.blockInfo[blockIndex].tarjan
|
||||
node.lowLink = assignedIndex
|
||||
|
||||
// add to queue and checked map
|
||||
queue = append(queue, pred)
|
||||
checked[pred] = struct{}{}
|
||||
// Push the node onto the stack.
|
||||
node.onStack = true
|
||||
b.tarjanStack = append(b.tarjanStack, uint(blockIndex))
|
||||
|
||||
// Process the successors.
|
||||
for _, successor := range block.Succs {
|
||||
// Look up the successor's state.
|
||||
successorIndex := successor.Index
|
||||
if successorIndex == blockIndex {
|
||||
// Handle a self-cycle specially.
|
||||
node.cyclic = true
|
||||
continue
|
||||
}
|
||||
successorNode := &b.blockInfo[successorIndex].tarjan
|
||||
|
||||
switch {
|
||||
case successorNode.lowLink == 0:
|
||||
// This node has not yet been visisted.
|
||||
b.strongConnect(successor)
|
||||
|
||||
case !successorNode.onStack:
|
||||
// This node has been visited, but is in a different SCC.
|
||||
// Ignore it, and do not update lowLink.
|
||||
continue
|
||||
}
|
||||
|
||||
// Update the lowLink index.
|
||||
// This always uses the min-of-lowlink instead of using index in the on-stack case.
|
||||
// This is done for two reasons:
|
||||
// 1. The lowLink update can be shared between the new-node and on-stack cases.
|
||||
// 2. The assigned index does not need to be saved - it is only needed for root node detection.
|
||||
if successorNode.lowLink < node.lowLink {
|
||||
node.lowLink = successorNode.lowLink
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
if node.lowLink == assignedIndex {
|
||||
// This is a root node.
|
||||
// Pop the SCC off the stack.
|
||||
stack := b.tarjanStack
|
||||
top := stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
blocks := b.blockInfo
|
||||
topNode := &blocks[top].tarjan
|
||||
topNode.onStack = false
|
||||
|
||||
if top != uint(blockIndex) {
|
||||
// The root node is not the only node in the SCC.
|
||||
// Mark all nodes in this SCC as cyclic.
|
||||
topNode.cyclic = true
|
||||
for top != uint(blockIndex) {
|
||||
top = stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
topNode = &blocks[top].tarjan
|
||||
topNode.onStack = false
|
||||
topNode.cyclic = true
|
||||
}
|
||||
}
|
||||
|
||||
b.tarjanStack = stack
|
||||
}
|
||||
}
|
||||
|
||||
// tarjanNode holds per-block state for isInLoop and strongConnect.
|
||||
type tarjanNode struct {
|
||||
// lowLink is the index of the first visited node that is reachable from this block.
|
||||
// The lowLink indices are assigned by the SCC search, and do not correspond to b.Index.
|
||||
// A lowLink of 0 is used as a sentinel to mark a node which has not yet been visited.
|
||||
lowLink uint
|
||||
|
||||
// onStack tracks whether this node is currently on the SCC search stack.
|
||||
onStack bool
|
||||
|
||||
// cyclic indicates whether this block is in a loop.
|
||||
// If lowLink is 0, strongConnect must be called before reading this field.
|
||||
cyclic bool
|
||||
}
|
||||
|
||||
type llvmValueList struct {
|
||||
values []llvm.Value
|
||||
types []llvm.Type
|
||||
}
|
||||
|
||||
func newLLVMValueList(values ...llvm.Value) llvmValueList {
|
||||
var list llvmValueList
|
||||
list.append(values...)
|
||||
return list
|
||||
}
|
||||
|
||||
func (l *llvmValueList) append(values ...llvm.Value) {
|
||||
for _, value := range values {
|
||||
l.values = append(l.values, value)
|
||||
l.types = append(l.types, value.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func (l *llvmValueList) appendSSAValues(values []ssa.Value, lower func(ssa.Value) llvm.Value) {
|
||||
for _, value := range values {
|
||||
l.append(lower(value))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) loadDeferredCallParams(structType llvm.Type, ptr llvm.Value) []llvm.Value {
|
||||
fieldTypes := structType.StructElementTypes()
|
||||
values := make([]llvm.Value, 0, len(fieldTypes)-2)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 2; i < len(fieldTypes); i++ {
|
||||
fieldPtr := b.CreateInBoundsGEP(structType, ptr, []llvm.Value{
|
||||
zero,
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}, "gep")
|
||||
values = append(values, b.CreateLoad(fieldTypes[i], fieldPtr, "param"))
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// createDefer emits a single defer instruction, to be run when this function
|
||||
@@ -262,31 +412,28 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// make a linked list.
|
||||
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
|
||||
|
||||
var values []llvm.Value
|
||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
||||
var values llvmValueList
|
||||
lowerArgument := func(value ssa.Value) llvm.Value {
|
||||
return b.getCallArgument(value, false)
|
||||
}
|
||||
if instr.Call.IsInvoke() {
|
||||
// Method call on an interface.
|
||||
|
||||
// Get callback type number.
|
||||
methodName := instr.Call.Method.FullName()
|
||||
if _, ok := b.deferInvokeFuncs[methodName]; !ok {
|
||||
b.deferInvokeFuncs[methodName] = len(b.allDeferFuncs)
|
||||
key := b.getInvokeFunctionName(&instr.Call)
|
||||
if _, ok := b.deferInvokeFuncs[key]; !ok {
|
||||
b.deferInvokeFuncs[key] = len(b.allDeferFuncs)
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
|
||||
}
|
||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[methodName]), false)
|
||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[key]), false)
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields, followed by the call parameters).
|
||||
itf := b.getValue(instr.Call.Value, getPos(instr)) // interface
|
||||
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.dataPtrType, b.dataPtrType)
|
||||
for _, arg := range instr.Call.Args {
|
||||
val := b.getValue(arg, getPos(instr))
|
||||
values = append(values, val)
|
||||
valueTypes = append(valueTypes, val.Type())
|
||||
}
|
||||
values = newLLVMValueList(callback, next, typecode, receiverValue)
|
||||
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||
|
||||
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
|
||||
// Regular function call.
|
||||
@@ -298,12 +445,11 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
values = newLLVMValueList(callback, next)
|
||||
exported := b.getFunctionInfo(callee).exported
|
||||
values.appendSSAValues(instr.Call.Args, func(value ssa.Value) llvm.Value {
|
||||
return b.getCallArgument(value, exported)
|
||||
})
|
||||
|
||||
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
|
||||
// Immediately applied function literal with free variables.
|
||||
@@ -326,14 +472,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields, followed by all parameters including the
|
||||
// context pointer).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
values = append(values, context)
|
||||
valueTypes = append(valueTypes, context.Type())
|
||||
values = newLLVMValueList(callback, next)
|
||||
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||
values.append(context)
|
||||
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
var argTypes []types.Type
|
||||
@@ -356,11 +497,8 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range argValues {
|
||||
values = append(values, param)
|
||||
valueTypes = append(valueTypes, param.Type())
|
||||
}
|
||||
values = newLLVMValueList(callback, next)
|
||||
values.append(argValues...)
|
||||
|
||||
} else {
|
||||
funcValue := b.getValue(instr.Call.Value, getPos(instr))
|
||||
@@ -375,34 +513,33 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields, followed by all parameters including the
|
||||
// context pointer).
|
||||
values = []llvm.Value{callback, next, funcValue}
|
||||
valueTypes = append(valueTypes, funcValue.Type())
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
values = newLLVMValueList(callback, next, funcValue)
|
||||
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||
}
|
||||
|
||||
// Make a struct out of the collected values to put in the deferred call
|
||||
// struct.
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCallType := b.ctx.StructType(values.types, false)
|
||||
deferredCall := llvm.ConstNull(deferredCallType)
|
||||
for i, value := range values {
|
||||
for i, value := range values.values {
|
||||
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
if instr.Block() != b.currentBlock {
|
||||
panic("block mismatch")
|
||||
}
|
||||
if !b.isInLoop() {
|
||||
// This can safely use a stack allocation.
|
||||
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(deferredCallType)
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
nilPtr := llvm.ConstNull(b.dataPtrType)
|
||||
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
layoutValue := b.createObjectLayout(deferredCallType, instr.Pos())
|
||||
align := b.targetData.ABITypeAlignment(deferredCallType)
|
||||
alloca = b.createAlloc(sizeValue, layoutValue, align, "defer.alloc.call")
|
||||
}
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
@@ -486,19 +623,11 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||
}
|
||||
|
||||
for _, arg := range callback.Args {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||
}
|
||||
valueTypes = b.appendStoredValueTypes(valueTypes, callback.Args, false)
|
||||
|
||||
// Extract the params from the struct (including receiver).
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
for i := 2; i < len(valueTypes); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
|
||||
var fnPtr llvm.Value
|
||||
var fnType llvm.Type
|
||||
@@ -527,6 +656,7 @@ func (b *builder) createRunDefers() {
|
||||
// with a strict calling convention.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||
}
|
||||
forwardParams = b.prependIndirectResult(callback.Signature(), false, forwardParams, "defer.result")
|
||||
|
||||
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||
|
||||
@@ -535,27 +665,21 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
for _, param := range getParams(callback.Signature) {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||
}
|
||||
exported := b.getFunctionInfo(callback).exported
|
||||
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(callback.Signature), exported)
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// 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(deferredCallType, deferData, []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)
|
||||
}
|
||||
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
|
||||
// Plain TinyGo functions add some extra parameters to implement async functionality and function receivers.
|
||||
// These parameters should not be supplied when calling into an external C/ASM function.
|
||||
if !b.getFunctionInfo(callback).exported {
|
||||
if !exported {
|
||||
// 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.dataPtrType))
|
||||
}
|
||||
forwardParams = b.prependIndirectResult(callback.Signature, exported, forwardParams, "defer.result")
|
||||
|
||||
// Call real function.
|
||||
fnType, fn := b.getFunction(callback)
|
||||
@@ -565,24 +689,16 @@ func (b *builder) createRunDefers() {
|
||||
// Get the real defer struct type and cast to it.
|
||||
fn := callback.Fn.(*ssa.Function)
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
params := fn.Signature.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(fn.Signature), false)
|
||||
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// 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(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
|
||||
// Call deferred function.
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
forwardParams = b.prependIndirectResult(fn.Signature, false, forwardParams, "defer.result")
|
||||
b.createCall(fnType, llvmFn, forwardParams, "")
|
||||
case *ssa.Builtin:
|
||||
db := b.deferBuiltinFuncs[callback]
|
||||
@@ -592,20 +708,14 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
//Get signature from call results
|
||||
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
for v := range params.Variables() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
|
||||
}
|
||||
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// 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(deferredCallType, deferData, []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)
|
||||
}
|
||||
argValues := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||
|
||||
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
|
||||
if err != nil {
|
||||
|
||||
+97
-22
@@ -10,6 +10,91 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// LLVM recursively expands each struct field and array element in parameters
|
||||
// and results into separate values. It gets very slow with too many values, so
|
||||
// pass larger aggregates indirectly before LLVM expands them.
|
||||
const maxDirectAggregateValues = 1024
|
||||
|
||||
func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
|
||||
switch sig.Results().Len() {
|
||||
case 0:
|
||||
return c.ctx.VoidType()
|
||||
case 1:
|
||||
return c.getLLVMType(sig.Results().At(0).Type())
|
||||
default:
|
||||
results := make([]llvm.Type, sig.Results().Len())
|
||||
for i := range results {
|
||||
results[i] = c.getLLVMType(sig.Results().At(i).Type())
|
||||
}
|
||||
return c.ctx.StructType(results, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compilerContext) hasIndirectResult(sig *types.Signature) (llvm.Type, bool) {
|
||||
resultType := c.getLLVMResultType(sig)
|
||||
return resultType, c.isIndirectAggregate(resultType)
|
||||
}
|
||||
|
||||
func (c *compilerContext) isIndirectAggregate(typ llvm.Type) bool {
|
||||
switch typ.TypeKind() {
|
||||
case llvm.ArrayTypeKind, llvm.StructTypeKind:
|
||||
_, exceeded := aggregateValueCount(typ, 0)
|
||||
return exceeded
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func aggregateValueCount(typ llvm.Type, count uint64) (uint64, bool) {
|
||||
switch typ.TypeKind() {
|
||||
case llvm.ArrayTypeKind:
|
||||
length := uint64(typ.ArrayLength())
|
||||
if length == 0 {
|
||||
return count, false
|
||||
}
|
||||
elementCount, exceeded := aggregateValueCount(typ.ElementType(), 0)
|
||||
if exceeded {
|
||||
return count, true
|
||||
}
|
||||
if elementCount != 0 && length > (maxDirectAggregateValues-count)/elementCount {
|
||||
return count, true
|
||||
}
|
||||
return count + length*elementCount, false
|
||||
case llvm.StructTypeKind:
|
||||
for _, field := range typ.StructElementTypes() {
|
||||
var exceeded bool
|
||||
count, exceeded = aggregateValueCount(field, count)
|
||||
if exceeded {
|
||||
return count, true
|
||||
}
|
||||
}
|
||||
return count, false
|
||||
default:
|
||||
count++
|
||||
return count, count > maxDirectAggregateValues
|
||||
}
|
||||
}
|
||||
|
||||
func isLLVMValueType(typ types.Type) bool {
|
||||
switch typ := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return typ.Kind() != types.Invalid
|
||||
case *types.Array:
|
||||
return isLLVMValueType(typ.Elem())
|
||||
case *types.Struct:
|
||||
for field := range typ.Fields() {
|
||||
if !isLLVMValueType(field.Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// createFuncValue creates a function value from a raw function pointer with no
|
||||
// context.
|
||||
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||
@@ -48,28 +133,18 @@ func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||
|
||||
// getLLVMFunctionType returns a LLVM function type for a given signature.
|
||||
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
||||
// Get the return type.
|
||||
var returnType llvm.Type
|
||||
switch typ.Results().Len() {
|
||||
case 0:
|
||||
// No return values.
|
||||
returnType = c.ctx.VoidType()
|
||||
case 1:
|
||||
// Just one return value.
|
||||
returnType = c.getLLVMType(typ.Results().At(0).Type())
|
||||
default:
|
||||
// Multiple return values. Put them together in a struct.
|
||||
// This appears to be the common way to handle multiple return values in
|
||||
// LLVM.
|
||||
members := make([]llvm.Type, typ.Results().Len())
|
||||
for i := 0; i < typ.Results().Len(); i++ {
|
||||
members[i] = c.getLLVMType(typ.Results().At(i).Type())
|
||||
}
|
||||
returnType = c.ctx.StructType(members, false)
|
||||
}
|
||||
returnType, indirectResult := c.hasIndirectResult(typ)
|
||||
|
||||
// Get the parameter types.
|
||||
var paramTypes []llvm.Type
|
||||
if indirectResult {
|
||||
// LLVM expands aggregate returns into scalar leaves before deciding
|
||||
// whether to pass them indirectly, so a large IR return can exhaust
|
||||
// memory. Returning void avoids that expansion and cannot be demoted
|
||||
// again. Keep the result pointer first so the context remains last.
|
||||
paramTypes = append(paramTypes, c.dataPtrType)
|
||||
returnType = c.ctx.VoidType()
|
||||
}
|
||||
if typ.Recv() != nil {
|
||||
recv := c.getLLVMType(typ.Recv().Type())
|
||||
if recv.StructName() == "runtime._interface" {
|
||||
@@ -81,8 +156,8 @@ func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
}
|
||||
for i := 0; i < typ.Params().Len(); i++ {
|
||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||
for v := range typ.Params().Variables() {
|
||||
subType := c.getLLVMType(v.Type())
|
||||
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
@@ -112,7 +187,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
|
||||
// Store the bound variables in a single object, allocating it on the heap
|
||||
// if necessary.
|
||||
context := b.emitPointerPack(boundVars)
|
||||
context := b.emitPointerPack(boundVars, expr.Pos())
|
||||
|
||||
// Create the closure.
|
||||
_, fn := b.getFunction(f)
|
||||
|
||||
+33
-8
@@ -5,11 +5,38 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Heap-allocate a buffer of the given size. This will typically call
|
||||
// runtime.alloc.
|
||||
func (b *builder) createAlloc(sizeValue, layoutValue llvm.Value, align int, comment string) llvm.Value {
|
||||
// Normally allocate using "runtime.alloc", but use "runtime.alloc_noheap"
|
||||
// if the //go:noheap pragma is used.
|
||||
allocFunc := "alloc"
|
||||
if b.info.noheap {
|
||||
allocFunc = "alloc_noheap"
|
||||
}
|
||||
|
||||
// Allocs that don't allocate anything can return an architecture-specific
|
||||
// sentinel value.
|
||||
if !sizeValue.IsAConstantInt().IsNil() && sizeValue.ZExtValue() == 0 {
|
||||
allocFunc = "alloc_zero"
|
||||
}
|
||||
|
||||
// Make the runtime call.
|
||||
call := b.createRuntimeCall(allocFunc, []llvm.Value{sizeValue, layoutValue}, comment)
|
||||
if align == 0 || align&(align-1) != 0 {
|
||||
panic("invalid alignment")
|
||||
}
|
||||
call.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
// trackExpr inserts pointer tracking intrinsics for the GC if the expression is
|
||||
// one of the expressions that need this.
|
||||
func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) {
|
||||
@@ -62,7 +89,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
||||
return
|
||||
}
|
||||
numElements := typ.StructElementTypesCount()
|
||||
for i := 0; i < numElements; i++ {
|
||||
for i := range numElements {
|
||||
subValue := b.CreateExtractValue(value, i, "")
|
||||
b.trackValue(subValue)
|
||||
}
|
||||
@@ -71,7 +98,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
||||
return
|
||||
}
|
||||
numElements := typ.ArrayLength()
|
||||
for i := 0; i < numElements; i++ {
|
||||
for i := range numElements {
|
||||
subValue := b.CreateExtractValue(value, i, "")
|
||||
b.trackValue(subValue)
|
||||
}
|
||||
@@ -92,13 +119,11 @@ func typeHasPointers(t llvm.Type) bool {
|
||||
case llvm.PointerTypeKind:
|
||||
return true
|
||||
case llvm.StructTypeKind:
|
||||
for _, subType := range t.StructElementTypes() {
|
||||
if typeHasPointers(subType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.ContainsFunc(t.StructElementTypes(), typeHasPointers)
|
||||
case llvm.ArrayTypeKind:
|
||||
if t.ArrayLength() == 0 {
|
||||
return false
|
||||
}
|
||||
if typeHasPointers(t.ElementType()) {
|
||||
return true
|
||||
}
|
||||
|
||||
+306
-57
@@ -7,43 +7,14 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createGo emits code to start a new goroutine.
|
||||
func (b *builder) createGo(instr *ssa.Go) {
|
||||
// Get all function parameters to pass to the goroutine.
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.getValue(param, getPos(instr)))
|
||||
}
|
||||
|
||||
var prefix string
|
||||
var funcPtr llvm.Value
|
||||
var funcType llvm.Type
|
||||
hasContext := false
|
||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||
// Static callee is known. This makes it easier to start a new
|
||||
// goroutine.
|
||||
var context llvm.Value
|
||||
switch value := instr.Call.Value.(type) {
|
||||
case *ssa.Function:
|
||||
// Goroutine call is regular function call. No context is necessary.
|
||||
case *ssa.MakeClosure:
|
||||
// A goroutine call on a func value, but the callee is trivial to find. For
|
||||
// example: immediately applied functions.
|
||||
funcValue := b.getValue(value, getPos(instr))
|
||||
context = b.extractFuncContext(funcValue)
|
||||
default:
|
||||
panic("StaticCallee returned an unexpected value")
|
||||
}
|
||||
if !context.IsNil() {
|
||||
params = append(params, context) // context parameter
|
||||
hasContext = true
|
||||
}
|
||||
funcType, funcPtr = b.getFunction(callee)
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
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
|
||||
// noticing the difference.
|
||||
@@ -74,6 +45,34 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
}
|
||||
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
|
||||
return
|
||||
}
|
||||
|
||||
var params []llvm.Value
|
||||
var prefix string
|
||||
var funcPtr llvm.Value
|
||||
var funcType llvm.Type
|
||||
var context llvm.Value
|
||||
hasContext := false
|
||||
exported := false
|
||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||
// Static callee is known. This makes it easier to start a new
|
||||
// goroutine.
|
||||
switch value := instr.Call.Value.(type) {
|
||||
case *ssa.Function:
|
||||
// Goroutine call is regular function call. No context is necessary.
|
||||
case *ssa.MakeClosure:
|
||||
// A goroutine call on a func value, but the callee is trivial to find. For
|
||||
// example: immediately applied functions.
|
||||
funcValue := b.getValue(value, getPos(instr))
|
||||
context = b.extractFuncContext(funcValue)
|
||||
default:
|
||||
panic("StaticCallee returned an unexpected value")
|
||||
}
|
||||
if !context.IsNil() {
|
||||
hasContext = true
|
||||
}
|
||||
funcType, funcPtr = b.getFunction(callee)
|
||||
exported = b.getFunctionInfo(callee).exported
|
||||
} else if instr.Call.IsInvoke() {
|
||||
// This is a method call on an interface value.
|
||||
itf := b.getValue(instr.Call.Value, getPos(instr))
|
||||
@@ -81,25 +80,34 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||
funcType = funcPtr.GlobalValueType()
|
||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||
params = append(params, itfTypeCode) // end with typecode
|
||||
params = append(params, itfValue)
|
||||
context = itfTypeCode
|
||||
} else {
|
||||
// This is a function pointer.
|
||||
// At the moment, two extra params are passed to the newly started
|
||||
// goroutine:
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
var context llvm.Value
|
||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
|
||||
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
params = append(params, context, funcPtr)
|
||||
hasContext = true
|
||||
prefix = b.fn.RelString(nil)
|
||||
prefix = b.getFunctionInfo(b.fn).linkName
|
||||
}
|
||||
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.getGoroutineCallArgument(param, exported)...)
|
||||
}
|
||||
if !context.IsNil() {
|
||||
params = append(params, context)
|
||||
}
|
||||
if hasContext && instr.Call.StaticCallee() == nil {
|
||||
params = append(params, funcPtr)
|
||||
}
|
||||
params = b.prependIndirectResult(instr.Call.Signature(), exported, params, "go.result")
|
||||
|
||||
paramBundle := b.emitPointerPack(params, instr.Pos())
|
||||
var stackSize llvm.Value
|
||||
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos())
|
||||
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, 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
|
||||
@@ -119,6 +127,156 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
||||
}
|
||||
|
||||
func (b *builder) getGoroutineCallArgument(value ssa.Value, exported bool) []llvm.Value {
|
||||
typ := b.getLLVMType(value.Type())
|
||||
arg := b.getCallArgument(value, exported)
|
||||
if b.isIndirectParam(typ, exported) {
|
||||
return []llvm.Value{b.copyToIndirectStorage(arg, typ, "go.param")}
|
||||
}
|
||||
return b.expandFormalParam(arg)
|
||||
}
|
||||
|
||||
// Create an exported wrapper function for functions with the //go:wasmexport
|
||||
// pragma. This wrapper function is quite complex when the scheduler is enabled:
|
||||
// it needs to start a new goroutine each time the exported function is called.
|
||||
func (b *builder) createWasmExport() {
|
||||
pos := b.info.wasmExportPos
|
||||
if b.info.exported {
|
||||
// //export really shouldn't be used anymore when //go:wasmexport is
|
||||
// available, because //go:wasmexport is much better defined.
|
||||
b.addError(pos, "cannot use //export and //go:wasmexport at the same time")
|
||||
return
|
||||
}
|
||||
|
||||
const suffix = "#wasmexport"
|
||||
|
||||
// Declare the exported function.
|
||||
paramTypes := b.llvmFnType.ParamTypes()
|
||||
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
|
||||
exportedFn := llvm.AddFunction(b.mod, b.getFunctionInfo(b.fn).linkName+suffix, exportedFnType)
|
||||
b.addStandardAttributes(exportedFn)
|
||||
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
|
||||
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
|
||||
|
||||
// Create a builder for this wrapper function.
|
||||
builder := newBuilder(b.compilerContext, b.ctx.NewBuilder(), b.fn)
|
||||
defer builder.Dispose()
|
||||
|
||||
// Define this function as a separate function in DWARF
|
||||
if b.Debug {
|
||||
if b.fn.Syntax() != nil {
|
||||
// Create debug info file if needed.
|
||||
pos := b.program.Fset.Position(pos)
|
||||
builder.difunc = builder.attachDebugInfoRaw(b.fn, exportedFn, suffix, pos.Filename, pos.Line)
|
||||
}
|
||||
builder.setDebugLocation(pos)
|
||||
}
|
||||
|
||||
// Create a single basic block inside of it.
|
||||
bb := llvm.AddBasicBlock(exportedFn, "entry")
|
||||
builder.SetInsertPointAtEnd(bb)
|
||||
|
||||
// Insert an assertion to make sure this //go:wasmexport function is not
|
||||
// called at a time when it is not allowed (for example, before the runtime
|
||||
// is initialized).
|
||||
builder.createRuntimeCall("wasmExportCheckRun", nil, "")
|
||||
|
||||
if b.Scheduler == "none" {
|
||||
// When the scheduler has been disabled, this is really trivial: just
|
||||
// call the function.
|
||||
params := exportedFn.Params()
|
||||
params = append(params, llvm.ConstNull(b.dataPtrType)) // context parameter
|
||||
retval := builder.CreateCall(b.llvmFnType, b.llvmFn, params, "")
|
||||
if b.fn.Signature.Results() == nil {
|
||||
builder.CreateRetVoid()
|
||||
} else {
|
||||
builder.CreateRet(retval)
|
||||
}
|
||||
|
||||
} else {
|
||||
// The scheduler is enabled, so we need to start a new goroutine, wait
|
||||
// for it to complete, and read the result value.
|
||||
|
||||
// Build a function that looks like this:
|
||||
//
|
||||
// func foo#wasmexport(param0, param1, ..., paramN) {
|
||||
// var state *stateStruct
|
||||
//
|
||||
// // 'done' must be explicitly initialized ('state' is not zeroed)
|
||||
// state.done = false
|
||||
//
|
||||
// // store the parameters in the state object
|
||||
// state.param0 = param0
|
||||
// state.param1 = param1
|
||||
// ...
|
||||
// state.paramN = paramN
|
||||
//
|
||||
// // create a goroutine and push it to the runqueue
|
||||
// task.start(uintptr(gowrapper), &state)
|
||||
//
|
||||
// // run the scheduler
|
||||
// runtime.wasmExportRun(&state.done)
|
||||
//
|
||||
// // if there is a return value, load it and return
|
||||
// return state.result
|
||||
// }
|
||||
|
||||
hasReturn := b.fn.Signature.Results() != nil
|
||||
|
||||
// Build the state struct type.
|
||||
// It stores the function parameters, the 'done' flag, and reserves
|
||||
// space for a return value if needed.
|
||||
stateFields := exportedFnType.ParamTypes()
|
||||
numParams := len(stateFields)
|
||||
stateFields = append(stateFields, b.ctx.Int1Type()) // 'done' field
|
||||
if hasReturn {
|
||||
stateFields = append(stateFields, b.llvmFnType.ReturnType())
|
||||
}
|
||||
stateStruct := b.ctx.StructType(stateFields, false)
|
||||
|
||||
// Allocate the state struct on the stack.
|
||||
statePtr := builder.CreateAlloca(stateStruct, "status")
|
||||
|
||||
// Initialize the 'done' field.
|
||||
doneGEP := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(numParams), false),
|
||||
}, "done.gep")
|
||||
builder.CreateStore(llvm.ConstNull(b.ctx.Int1Type()), doneGEP)
|
||||
|
||||
// Store all parameters in the state object.
|
||||
for i, param := range exportedFn.Params() {
|
||||
gep := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}, "")
|
||||
builder.CreateStore(param, gep)
|
||||
}
|
||||
|
||||
// Create a new goroutine and add it to the runqueue.
|
||||
wrapper := b.createGoroutineStartWrapper(b.llvmFnType, b.llvmFn, "", false, true, pos)
|
||||
stackSize := llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
taskStartFnType, taskStartFn := builder.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
builder.createCall(taskStartFnType, taskStartFn, []llvm.Value{wrapper, statePtr, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
||||
|
||||
// Run the scheduler.
|
||||
builder.createRuntimeCall("wasmExportRun", []llvm.Value{doneGEP}, "")
|
||||
|
||||
// Read the return value (if any) and return to the caller of the
|
||||
// //go:wasmexport function.
|
||||
if hasReturn {
|
||||
gep := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(numParams)+1, false),
|
||||
}, "")
|
||||
retval := builder.CreateLoad(b.llvmFnType.ReturnType(), gep, "retval")
|
||||
builder.CreateRet(retval)
|
||||
} else {
|
||||
builder.CreateRetVoid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createGoroutineStartWrapper creates a wrapper for the task-based
|
||||
// implementation of goroutines. For example, to call a function like this:
|
||||
//
|
||||
@@ -142,7 +300,7 @@ 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(fnType llvm.Type, 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, isWasmExport bool, pos token.Pos) llvm.Value {
|
||||
var wrapper llvm.Value
|
||||
|
||||
b := &builder{
|
||||
@@ -160,14 +318,18 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
||||
if !fn.IsAFunction().IsNil() {
|
||||
// See whether this wrapper has already been created. If so, return it.
|
||||
name := fn.Name()
|
||||
wrapper = c.mod.NamedFunction(name + "$gowrapper")
|
||||
wrapperName := name + "$gowrapper"
|
||||
if isWasmExport {
|
||||
wrapperName += "-wasmexport"
|
||||
}
|
||||
wrapper = c.mod.NamedFunction(wrapperName)
|
||||
if !wrapper.IsNil() {
|
||||
return llvm.ConstPtrToInt(wrapper, c.uintptrType)
|
||||
}
|
||||
|
||||
// Create the wrapper.
|
||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
|
||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapperType)
|
||||
c.addStandardAttributes(wrapper)
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
@@ -197,23 +359,110 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Create the list of params for the call.
|
||||
paramTypes := fnType.ParamTypes()
|
||||
if !hasContext {
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||
}
|
||||
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||
if !hasContext {
|
||||
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
|
||||
}
|
||||
if !isWasmExport {
|
||||
// Regular 'go' instruction.
|
||||
|
||||
// Create the call.
|
||||
b.CreateCall(fnType, fn, params, "")
|
||||
// Create the list of params for the call.
|
||||
paramTypes := fnType.ParamTypes()
|
||||
if !hasContext {
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||
}
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||
llvm.Undef(c.dataPtrType),
|
||||
}, "")
|
||||
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||
if !hasContext {
|
||||
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
|
||||
}
|
||||
|
||||
// Create the call.
|
||||
b.CreateCall(fnType, fn, params, "")
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||
llvm.Undef(c.dataPtrType),
|
||||
}, "")
|
||||
}
|
||||
} else {
|
||||
// Goroutine started from a //go:wasmexport pragma.
|
||||
// The function looks like this:
|
||||
//
|
||||
// func foo$gowrapper-wasmexport(state *stateStruct) {
|
||||
// // load values
|
||||
// param0 := state.params[0]
|
||||
// param1 := state.params[1]
|
||||
//
|
||||
// // call wrapped functions
|
||||
// result := foo(param0, param1, ...)
|
||||
//
|
||||
// // store result value (if there is any)
|
||||
// state.result = result
|
||||
//
|
||||
// // finish exported function
|
||||
// state.done = true
|
||||
// runtime.wasmExportExit()
|
||||
// }
|
||||
//
|
||||
// The state object here looks like:
|
||||
//
|
||||
// struct state {
|
||||
// param0
|
||||
// param1
|
||||
// param* // etc
|
||||
// done bool
|
||||
// result returnType
|
||||
// }
|
||||
|
||||
returnType := fnType.ReturnType()
|
||||
hasReturn := returnType != b.ctx.VoidType()
|
||||
statePtr := wrapper.Param(0)
|
||||
|
||||
// Create the state struct (it must match the type in createWasmExport).
|
||||
stateFields := fnType.ParamTypes()
|
||||
numParams := len(stateFields) - 1
|
||||
stateFields = stateFields[:numParams:numParams] // strip 'context' parameter
|
||||
stateFields = append(stateFields, c.ctx.Int1Type()) // 'done' bool
|
||||
if hasReturn {
|
||||
stateFields = append(stateFields, returnType)
|
||||
}
|
||||
stateStruct := b.ctx.StructType(stateFields, false)
|
||||
|
||||
// Extract parameters from the state object, and call the function
|
||||
// that's being wrapped.
|
||||
var callParams []llvm.Value
|
||||
for i := range numParams {
|
||||
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}, "")
|
||||
param := b.CreateLoad(stateFields[i], gep, "")
|
||||
callParams = append(callParams, param)
|
||||
}
|
||||
callParams = append(callParams, llvm.ConstNull(c.dataPtrType)) // add 'context' parameter
|
||||
result := b.CreateCall(fnType, fn, callParams, "")
|
||||
|
||||
// Store the return value back into the shared state.
|
||||
// Unlike regular goroutines, these special //go:wasmexport
|
||||
// goroutines can return a value.
|
||||
if hasReturn {
|
||||
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(numParams)+1, false),
|
||||
}, "result.ptr")
|
||||
b.CreateStore(result, gep)
|
||||
}
|
||||
|
||||
// Mark this function as having finished executing.
|
||||
// This is important so the runtime knows the exported function
|
||||
// didn't block.
|
||||
doneGEP := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(numParams), false),
|
||||
}, "done.gep")
|
||||
b.CreateStore(llvm.ConstInt(b.ctx.Int1Type(), 1, false), doneGEP)
|
||||
|
||||
// Call back into the runtime. This will exit the goroutine, switch
|
||||
// back to the scheduler, which will in turn return from the
|
||||
// //go:wasmexport function.
|
||||
b.createRuntimeCall("wasmExportExit", nil, "")
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -295,5 +544,5 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
||||
}
|
||||
|
||||
// Return a ptrtoint of the wrapper, not the function itself.
|
||||
return b.CreatePtrToInt(wrapper, c.uintptrType, "")
|
||||
return llvm.ConstPtrToInt(wrapper, c.uintptrType)
|
||||
}
|
||||
|
||||
+27
-13
@@ -38,10 +38,10 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
// provided immediately. For example:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// "str {value}, [{result}]",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// "value": 1,
|
||||
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||
// })
|
||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
@@ -146,13 +146,14 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||
constraints := "={r0}"
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={r0}")
|
||||
for i, arg := range args[1:] {
|
||||
arg = arg.(*ssa.MakeInterface).X
|
||||
if i == 0 {
|
||||
constraints += ",0"
|
||||
constraints.WriteString(",0")
|
||||
} else {
|
||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
||||
constraints.WriteString(",{r" + strconv.Itoa(i) + "}")
|
||||
}
|
||||
llvmValue := b.getValue(arg, pos)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
@@ -161,9 +162,9 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
||||
// Implement the ARM calling convention by marking r1-r3 as
|
||||
// clobbered. r0 is used as an output register so doesn't have to be
|
||||
// marked as clobbered.
|
||||
constraints += ",~{r1},~{r2},~{r3}"
|
||||
constraints.WriteString(",~{r1},~{r2},~{r3}")
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
@@ -184,13 +185,14 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||
constraints := "={x0}"
|
||||
var constraints strings.Builder
|
||||
constraints.WriteString("={x0}")
|
||||
for i, arg := range args[1:] {
|
||||
arg = arg.(*ssa.MakeInterface).X
|
||||
if i == 0 {
|
||||
constraints += ",0"
|
||||
constraints.WriteString(",0")
|
||||
} else {
|
||||
constraints += ",{x" + strconv.Itoa(i) + "}"
|
||||
constraints.WriteString(",{x" + strconv.Itoa(i) + "}")
|
||||
}
|
||||
llvmValue := b.getValue(arg, pos)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
@@ -199,9 +201,9 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
||||
// Implement the ARM64 calling convention by marking x1-x7 as
|
||||
// clobbered. x0 is used as an output register so doesn't have to be
|
||||
// marked as clobbered.
|
||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
||||
constraints.WriteString(",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}")
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
@@ -249,3 +251,15 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||
}
|
||||
}
|
||||
|
||||
// Implement runtime/interrupt.Checkpoint.Save. It needs to be implemented
|
||||
// directly at the call site. If it isn't implemented directly at the call site
|
||||
// (but instead through a function call), it might result in an overwritten
|
||||
// stack in the non-jump return case.
|
||||
func (b *builder) createInterruptCheckpoint(ptr ssa.Value) llvm.Value {
|
||||
addr := b.getValue(ptr, ptr.Pos())
|
||||
b.createNilCheck(ptr, addr, "deref")
|
||||
stackPointer := b.readStackPointer()
|
||||
b.CreateStore(stackPointer, addr)
|
||||
return b.createCheckpoint(addr)
|
||||
}
|
||||
|
||||
+587
-209
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,8 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
|
||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||
// type are lowered in the interrupt lowering pass.
|
||||
// It must have an alignment of 1, otherwise LLVM thinks a ptrtoint of the
|
||||
// global has the lower bits unset.
|
||||
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||
globalLLVMType := b.getLLVMType(globalType)
|
||||
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||
@@ -48,6 +50,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetAlignment(1)
|
||||
initializer := llvm.ConstNull(globalLLVMType)
|
||||
initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
|
||||
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
|
||||
|
||||
+111
-16
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -27,6 +28,8 @@ func (b *builder) defineIntrinsicFunction() {
|
||||
b.createStackSaveImpl()
|
||||
case name == "runtime.KeepAlive":
|
||||
b.createKeepAliveImpl()
|
||||
case name == "machine.keepAliveNoEscape":
|
||||
b.createMachineKeepAliveImpl()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
b.createVolatileLoad()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
@@ -48,19 +51,24 @@ func (b *builder) defineIntrinsicFunction() {
|
||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||
func (b *builder) createMemoryCopyImpl() {
|
||||
b.createFunctionStart(true)
|
||||
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
params := b.fn.Params[0:3]
|
||||
b.createMemCopy(
|
||||
b.fn.Name(),
|
||||
b.getValue(params[0], getPos(b.fn)),
|
||||
b.getValue(params[1], getPos(b.fn)),
|
||||
b.getValue(params[2], getPos(b.fn)),
|
||||
)
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
func (b *builder) createMemCopy(kind string, dst, src, len llvm.Value) llvm.Value {
|
||||
fnName := "llvm." + kind + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
var params []llvm.Value
|
||||
for _, param := range b.fn.Params {
|
||||
params = append(params, b.getValue(param, getPos(b.fn)))
|
||||
}
|
||||
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
||||
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{dst, src, len, llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "")
|
||||
}
|
||||
|
||||
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||
@@ -121,13 +129,60 @@ func (b *builder) createKeepAliveImpl() {
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createAbiEscapeImpl implements the generic internal/abi.Escape function. It
|
||||
// currently only supports pointer types.
|
||||
func (b *builder) createAbiEscapeImpl() {
|
||||
b.createFunctionStart(true)
|
||||
|
||||
// The first parameter is assumed to be a pointer. This is checked at the
|
||||
// call site of createAbiEscapeImpl.
|
||||
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
|
||||
// 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.dataPtrType, []llvm.Type{b.dataPtrType}, false)
|
||||
asmFn := llvm.InlineAsm(asmType, "", "=r,0", true, false, 0, false)
|
||||
result := b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
||||
|
||||
b.CreateRet(result)
|
||||
}
|
||||
|
||||
// Implement machine.keepAliveNoEscape, which makes sure the compiler keeps the
|
||||
// pointer parameter alive until this point (for GC).
|
||||
func (b *builder) createMachineKeepAliveImpl() {
|
||||
b.createFunctionStart(true)
|
||||
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
|
||||
// See createKeepAliveImpl for details.
|
||||
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, 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.Acos": "llvm.acos.f64",
|
||||
"math.Asin": "llvm.asin.f64",
|
||||
"math.Atan": "llvm.atan.f64",
|
||||
"math.Atan2": "llvm.atan2.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Cos": "llvm.cos.f64",
|
||||
"math.Cosh": "llvm.cosh.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
"math.Exp2": "llvm.exp2.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Log": "llvm.log.f64",
|
||||
"math.Sin": "llvm.sin.f64",
|
||||
"math.Sinh": "llvm.sinh.f64",
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Tan": "llvm.tan.f64",
|
||||
"math.Tanh": "llvm.tanh.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
@@ -141,18 +196,40 @@ var mathToLLVMMapping = map[string]string{
|
||||
// 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
|
||||
func (b *builder) defineMathOp() bool {
|
||||
llvmName, ok := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(b.Triple, "-wasi") || llvmutil.Version() < 19 {
|
||||
// We don't have a real libc for wasip2. Until that is fixed, we need to
|
||||
// limit math intrinsics on WASI to a subset supported natively in
|
||||
// WebAssembly.
|
||||
// Also, since we don't know the specific libc we will target, disallow
|
||||
// these for all WASI targets.
|
||||
//
|
||||
// We also need to limit ourselves to LLVM 19 and above for the extended
|
||||
// set of math intrinsics, see:
|
||||
// https://discourse.llvm.org/t/rfc-all-the-math-intrinsics/78294
|
||||
switch b.fn.Name() {
|
||||
case "Ceil", "Exp", "Exp2", "Floor", "Log", "Sqrt", "Trunc":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
b.createFunctionStart(true)
|
||||
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)
|
||||
var llvmType llvm.Type
|
||||
switch b.fn.Name() {
|
||||
case "Atan2":
|
||||
// double atan2(double %y, double %x)
|
||||
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)
|
||||
default:
|
||||
// double foo(double %x)
|
||||
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
}
|
||||
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
@@ -162,6 +239,24 @@ func (b *builder) defineMathOp() {
|
||||
}
|
||||
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *builder) defineCryptoIntrinsic() bool {
|
||||
if b.fn.Pkg.Pkg.Path() != "crypto/internal/constanttime" {
|
||||
return false
|
||||
}
|
||||
|
||||
switch b.fn.Name() {
|
||||
case "boolToUint8":
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
result := b.CreateZExt(param, b.ctx.Int8Type(), "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Implement most math/bits functions.
|
||||
|
||||
+110
-107
@@ -1,10 +1,10 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -54,7 +54,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||
// 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 {
|
||||
func (b *builder) emitPointerPack(values []llvm.Value, pos token.Pos) llvm.Value {
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
valueTypes[i] = value.Type()
|
||||
@@ -128,14 +128,9 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
layoutValue := b.createObjectLayout(packedType, pos)
|
||||
align := b.targetData.ABITypeAlignment(packedType)
|
||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
||||
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
||||
sizeValue,
|
||||
llvm.ConstNull(b.dataPtrType),
|
||||
llvm.Undef(b.dataPtrType), // unused context parameter
|
||||
}, "")
|
||||
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||
packedAlloc := b.createAlloc(sizeValue, layoutValue, align, "")
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(packedAlloc)
|
||||
}
|
||||
@@ -212,7 +207,7 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
||||
globalType := llvm.ArrayType(elementType, len(buf))
|
||||
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||
value := llvm.Undef(globalType)
|
||||
for i := 0; i < len(buf); i++ {
|
||||
for i := range buf {
|
||||
ch := uint64(buf[i])
|
||||
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||
}
|
||||
@@ -231,6 +226,12 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
||||
//
|
||||
// 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 {
|
||||
if !typeHasPointers(t) {
|
||||
// There are no pointers in this type, so we can simplify the layout.
|
||||
layout := (uint64(1) << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
|
||||
// Use the element type for arrays. This works even for nested arrays.
|
||||
for {
|
||||
kind := t.TypeKind()
|
||||
@@ -248,54 +249,29 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
break
|
||||
}
|
||||
|
||||
// Do a few checks to see whether we need to generate any object layout
|
||||
// information at all.
|
||||
// Create the pointer bitmap.
|
||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||
pointerAlignment := uint64(c.targetData.PrefTypeAlignment(c.dataPtrType))
|
||||
bitmapLen := objectSizeBytes / pointerAlignment
|
||||
bitmapBytes := (bitmapLen + 7) / 8
|
||||
bitmap := make([]byte, bitmapBytes, max(bitmapBytes, 8))
|
||||
c.buildPointerBitmap(bitmap, pointerAlignment, pos, t, 0)
|
||||
|
||||
// Try to encode the layout inline.
|
||||
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||
if objectSizeBytes < pointerSize {
|
||||
// Too small to contain a pointer.
|
||||
layout := (uint64(1) << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
bitmap := c.getPointerBitmap(t, pos)
|
||||
if bitmap.BitLen() == 0 {
|
||||
// There are no pointers in this type, so we can simplify the layout.
|
||||
// TODO: this can be done in many other cases, e.g. when allocating an
|
||||
// array (like [4][]byte, which repeats a slice 4 times).
|
||||
layout := (uint64(1) << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
||||
// This shouldn't happen except for packed structs, which aren't
|
||||
// currently used.
|
||||
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
||||
return llvm.ConstNull(c.dataPtrType)
|
||||
}
|
||||
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
||||
|
||||
pointerBits := pointerSize * 8
|
||||
var sizeFieldBits uint64
|
||||
switch pointerBits {
|
||||
case 16:
|
||||
sizeFieldBits = 4
|
||||
case 32:
|
||||
sizeFieldBits = 5
|
||||
case 64:
|
||||
sizeFieldBits = 6
|
||||
default:
|
||||
panic("unknown pointer size")
|
||||
}
|
||||
layoutFieldBits := pointerBits - 1 - sizeFieldBits
|
||||
if bitmapLen < pointerBits {
|
||||
rawMask := binary.LittleEndian.Uint64(bitmap[0:8])
|
||||
layout := rawMask*pointerBits + bitmapLen
|
||||
layout <<= 1
|
||||
layout |= 1
|
||||
|
||||
// Try to emit the value as an inline integer. This is possible in most
|
||||
// cases.
|
||||
if objectSizeWords < layoutFieldBits {
|
||||
// If it can be stored directly in the pointer value, do so.
|
||||
// The runtime knows that if the least significant bit of the pointer is
|
||||
// set, the pointer contains the value itself.
|
||||
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
// Check if the layout fits.
|
||||
layout &= 1<<pointerBits - 1
|
||||
if (layout>>1)/pointerBits == rawMask {
|
||||
// No set bits were shifted off.
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||
@@ -303,25 +279,24 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
|
||||
// Try first whether the global already exists. All objects with a
|
||||
// particular name have the same type, so this is possible.
|
||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", bitmapLen, (bitmapLen+15)/16, bitmap)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if !global.IsNil() {
|
||||
return global
|
||||
}
|
||||
|
||||
// Create the global initializer.
|
||||
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
||||
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))
|
||||
bitmapByteValues := make([]llvm.Value, bitmapBytes)
|
||||
i8 := c.ctx.Int8Type()
|
||||
for i, b := range bitmap {
|
||||
bitmapByteValues[i] = llvm.ConstInt(i8, uint64(b), false)
|
||||
}
|
||||
initializer := c.ctx.ConstStruct([]llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, objectSizeWords, false),
|
||||
llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
|
||||
llvm.ConstInt(c.uintptrType, bitmapLen, false),
|
||||
llvm.ConstArray(i8, bitmapByteValues),
|
||||
}, false)
|
||||
|
||||
// Create the actual global.
|
||||
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
||||
global.SetInitializer(initializer)
|
||||
global.SetUnnamedAddr(true)
|
||||
@@ -329,6 +304,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
||||
// AVR doesn't have alignment by default.
|
||||
// The lowest bit must be unset to distinguish this from an inline layout.
|
||||
global.SetAlignment(2)
|
||||
}
|
||||
if c.Debug && pos != token.NoPos {
|
||||
@@ -360,59 +336,71 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
return global
|
||||
}
|
||||
|
||||
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||
// bigint at the word offset that contains a pointer. This scan is recursive.
|
||||
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
||||
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||
switch typ.TypeKind() {
|
||||
// buildPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||
// bitmap at the word offset that contains a pointer. This scan is recursive.
|
||||
func (c *compilerContext) buildPointerBitmap(
|
||||
dst []byte,
|
||||
ptrAlign uint64,
|
||||
pos token.Pos,
|
||||
t llvm.Type,
|
||||
offset uint64,
|
||||
) {
|
||||
switch t.TypeKind() {
|
||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
return big.NewInt(0)
|
||||
// These types do not contain pointers.
|
||||
|
||||
case llvm.PointerTypeKind:
|
||||
return big.NewInt(1)
|
||||
// Set the corresponding position in the bitmap.
|
||||
dst[offset/8] |= 1 << (offset % 8)
|
||||
|
||||
case llvm.StructTypeKind:
|
||||
ptrs := big.NewInt(0)
|
||||
if typ.StructName() == "runtime.funcValue" {
|
||||
// Hack: the type runtime.funcValue contains an 'id' field which is
|
||||
// of type uintptr, but before the LowerFuncValues pass it actually
|
||||
// contains a pointer (ptrtoint) to a global. This trips up the
|
||||
// interp package. Therefore, make the id field a pointer for now.
|
||||
typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
|
||||
}
|
||||
for i, subtyp := range typ.StructElementTypes() {
|
||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||
if subptrs.BitLen() == 0 {
|
||||
// Recurse over struct elements.
|
||||
for i, et := range t.StructElementTypes() {
|
||||
eo := c.targetData.ElementOffset(t, i)
|
||||
if eo%uint64(ptrAlign) != 0 {
|
||||
if typeHasPointers(et) {
|
||||
// This error will let the compilation fail, but by continuing
|
||||
// the error can still easily be shown.
|
||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||
}
|
||||
continue
|
||||
}
|
||||
offset := c.targetData.ElementOffset(typ, i)
|
||||
if offset%uint64(alignment) != 0 {
|
||||
// This error will let the compilation fail, but by continuing
|
||||
// the error can still easily be shown.
|
||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||
continue
|
||||
}
|
||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
c.buildPointerBitmap(
|
||||
dst,
|
||||
ptrAlign,
|
||||
pos,
|
||||
et,
|
||||
offset+(eo/ptrAlign),
|
||||
)
|
||||
}
|
||||
return ptrs
|
||||
|
||||
case llvm.ArrayTypeKind:
|
||||
subtyp := typ.ElementType()
|
||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||
ptrs := big.NewInt(0)
|
||||
if subptrs.BitLen() == 0 {
|
||||
return ptrs
|
||||
// Recurse over array elements.
|
||||
len := t.ArrayLength()
|
||||
if len <= 0 {
|
||||
return
|
||||
}
|
||||
elementSize := c.targetData.TypeAllocSize(subtyp)
|
||||
if elementSize%uint64(alignment) != 0 {
|
||||
// This error will let the compilation fail (but continues so that
|
||||
// other errors can be shown).
|
||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||
return ptrs
|
||||
et := t.ElementType()
|
||||
elementSize := c.targetData.TypeAllocSize(et)
|
||||
if elementSize%ptrAlign != 0 {
|
||||
if typeHasPointers(et) {
|
||||
// This error will let the compilation fail (but continues so that
|
||||
// other errors can be shown).
|
||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i < typ.ArrayLength(); i++ {
|
||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
elementSize /= ptrAlign
|
||||
for i := range len {
|
||||
c.buildPointerBitmap(
|
||||
dst,
|
||||
ptrAlign,
|
||||
pos,
|
||||
et,
|
||||
offset+uint64(i)*elementSize,
|
||||
)
|
||||
}
|
||||
return ptrs
|
||||
|
||||
default:
|
||||
// Should not happen.
|
||||
panic("unknown LLVM type")
|
||||
@@ -430,7 +418,7 @@ func (c *compilerContext) archFamily() string {
|
||||
// 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, ",") {
|
||||
for feature := range strings.SplitSeq(c.Features, ",") {
|
||||
if feature == "+thumb-mode" {
|
||||
isThumb = true
|
||||
}
|
||||
@@ -459,6 +447,21 @@ func (b *builder) readStackPointer() llvm.Value {
|
||||
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||
}
|
||||
|
||||
// writeStackPointer emits a LLVM intrinsic call that updates the current stack
|
||||
// pointer.
|
||||
func (b *builder) writeStackPointer(sp llvm.Value) {
|
||||
name := "llvm.stackrestore.p0"
|
||||
if llvmutil.Version() < 18 {
|
||||
name = "llvm.stackrestore" // backwards compatibility with LLVM 17 and below
|
||||
}
|
||||
stackrestore := b.mod.NamedFunction(name)
|
||||
if stackrestore.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
|
||||
stackrestore = llvm.AddFunction(b.mod, name, fnType)
|
||||
}
|
||||
b.CreateCall(stackrestore.GlobalValueType(), stackrestore, []llvm.Value{sp}, "")
|
||||
}
|
||||
|
||||
// createZExtOrTrunc lets the input value fit in the output type bits, by zero
|
||||
// extending or truncating the integer.
|
||||
func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user