mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 07:08:42 +00:00
Compare commits
1463 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2dd06c4378 | |||
| 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 | |||
| a47ff02c82 | |||
| e27b2c4ad6 | |||
| 8135be4e90 | |||
| 835e73237e | |||
| eab1a5d7ec | |||
| f188eaf5f9 | |||
| 6efc6d2bb6 | |||
| d6e73b4c68 | |||
| 2e76cd3687 | |||
| 2eb39785fe | |||
| 3021e16bbf | |||
| 55f7d21ff5 | |||
| 841abb0903 | |||
| fb3d98ce6e | |||
| 020664591a | |||
| 2d6d9eb76d | |||
| a8a532f9d6 | |||
| 417a26d20c | |||
| 88f9fc3ce2 | |||
| 84c376160f | |||
| 6184a6cd35 | |||
| 1fd75ffbda | |||
| 61d36fb3f8 | |||
| b318a941a4 | |||
| 725518f007 | |||
| 04d1261f8a | |||
| 3e2230eadb | |||
| 4f908f4877 | |||
| 319683b863 | |||
| 4af3f618f8 | |||
| 89340f82dc | |||
| 824bf24445 | |||
| 80269b98ba | |||
| 3788b31ba5 | |||
| f4ce11ef37 | |||
| a5f78a3900 | |||
| e409950492 | |||
| 208eb1a817 | |||
| 2920492e0a | |||
| 87a8aafc4f | |||
| f026422b04 | |||
| fb91c7097f | |||
| d7773d3e86 | |||
| b04b690842 | |||
| 8a357af3d8 | |||
| 7ac1ca0ae2 | |||
| 2f3d821b13 | |||
| 6f462fba4c | |||
| ac396708da | |||
| d150badf33 | |||
| 2d85fc6a64 | |||
| 5ca3e4a2da | |||
| e6caa3fe9e | |||
| 571447c7c1 | |||
| 9cb263479c | |||
| f18c6e342f | |||
| 2d5a8d407b | |||
| 36958b2875 | |||
| fae0402fde | |||
| 4517a0c17b | |||
| 6abaee4640 | |||
| 1270a50104 | |||
| 868262f4a7 | |||
| d33ace7b59 | |||
| dd6fa89aa6 | |||
| d513cae5d2 | |||
| e612f7cf3f | |||
| 385a7920e6 | |||
| 077b35e9ad | |||
| 880e940417 | |||
| ad6c89bf64 | |||
| ae6220262a | |||
| e731a31eb9 | |||
| 3023ba584b | |||
| 3a8ef33c72 | |||
| 20b58a0128 | |||
| bfccf3592a | |||
| 272fea13e7 | |||
| b6fd0c818e | |||
| c383a407e3 | |||
| 7b7601d77c | |||
| f7c0466f78 | |||
| 81ce7fb738 | |||
| c2776dcf78 | |||
| ad0340d437 | |||
| 30c4df16f2 | |||
| fe27b674cd | |||
| c78dbcd3f2 | |||
| 8890b57ba0 | |||
| ee3a05f1de | |||
| 9307204111 | |||
| 71878c2c0c | |||
| cef2a82c97 | |||
| 1d9f26cee1 | |||
| 6e58c44390 | |||
| 7d6b667bba | |||
| 72f30ca6ec | |||
| 293b620faf | |||
| ad0af607ef | |||
| da23cdeae4 | |||
| d419cc11bf | |||
| 441dfc98d7 | |||
| 2b3b870bfe | |||
| 7748c4922e | |||
| 50f700ddb5 | |||
| f986ea84ac | |||
| 797a5a2031 | |||
| 3d433fe9f1 | |||
| 1154212c15 | |||
| 22bf045c9a | |||
| 39029cc376 | |||
| 7122755725 | |||
| dcee228c4e | |||
| 9d6e30701b | |||
| 85b59e66da | |||
| 90b0bf646c | |||
| c55ac9f28e | |||
| 2733e376bd | |||
| 331cfb65b7 | |||
| 055950421a | |||
| cfcc894855 | |||
| 8bd0155c51 | |||
| 32a056c32a | |||
| 8e8ad9004f | |||
| 62d8cdb218 | |||
| b6fdbee14e | |||
| a5ceb793be | |||
| 7c546525ea | |||
| d628e3e2cb | |||
| d97e4dbccf | |||
| a7ddb33bea | |||
| 8a93196f1f | |||
| 6714974aba | |||
| d67ec3b266 | |||
| 78775007fa | |||
| ad4d722f54 | |||
| 6384ecace0 | |||
| 38881a2850 | |||
| 0f1cf14743 | |||
| 7c34f7704e | |||
| 377415a6c3 | |||
| 1e13c6d18f | |||
| 14121f4b0e | |||
| 7768195835 | |||
| c095b7e9c4 | |||
| 1f6d34d995 | |||
| 9951eb9990 | |||
| c8f77d26a8 | |||
| 8eaa9bd8d0 | |||
| 6061f3db92 | |||
| 410aa0e0d8 | |||
| c66836cf3a | |||
| ca9211b582 | |||
| cb7d470ba4 | |||
| 5baee9a8ee | |||
| c47f52b546 | |||
| a2588d8db3 | |||
| f529b6071d | |||
| 5557e97888 | |||
| d04b07fa8b | |||
| 7486277012 | |||
| 36d60b8349 | |||
| c55191283b | |||
| ad30085b93 | |||
| e9ca41735a | |||
| df1f83f4e1 | |||
| 5879d785a9 | |||
| 186018abeb | |||
| 828b9614c2 | |||
| f5e933cd4d | |||
| 0952b1b984 | |||
| ed55f56d85 | |||
| edb8766aab | |||
| 413bb55e2c | |||
| 0db1a4bec1 | |||
| 2867da164d | |||
| 7b8ae2d6b6 | |||
| 5b8127fd4e | |||
| cc3e785692 | |||
| 0ea5cfc8fe | |||
| 70f1a5429a | |||
| 2fb2113168 | |||
| cb39611389 | |||
| 45764325b4 | |||
| bb66232164 | |||
| 9679e7f1cc | |||
| 6cdfc298cf | |||
| 9a4bfd0e96 | |||
| ee3106be98 | |||
| 57f49af726 | |||
| 38a80b45d3 | |||
| e9003e2deb | |||
| 204659bdcd | |||
| 08ca1d13d0 | |||
| 53db436a7d | |||
| 0ad15551c8 | |||
| afd65e224a | |||
| 5f50c3b60b | |||
| 8858d49989 | |||
| cf39d8b2c8 | |||
| d92a31b440 | |||
| 9c77a38358 | |||
| d0445d6f83 | |||
| a40b11f535 | |||
| 8c90f4facf | |||
| c7c9a76af5 | |||
| 6984af43a0 | |||
| 81c56c3ab8 | |||
| 3d9a1ca22a | |||
| c2083014b3 | |||
| 603a81f194 | |||
| cfc32794a7 | |||
| 1d9c53d00e | |||
| 8d2a07b927 | |||
| ffe6dfd21b | |||
| cf21380264 | |||
| 534b3b0c0b | |||
| ceeb233ff6 | |||
| de3f0af829 | |||
| a511f18c64 | |||
| 731bd5cd1b | |||
| 4acf59546e | |||
| 2ee4d9aaa1 | |||
| b58b7c59ae | |||
| e14b6a7009 | |||
| a1a2d1ab81 | |||
| 07591178cd | |||
| 43bdc888dd | |||
| 4229e670ce | |||
| 76a7ad2a3e | |||
| e4f551ac7f | |||
| 338590cc75 | |||
| 22d70604d8 | |||
| c6add1e769 | |||
| c6609a02fa | |||
| a449c4813a | |||
| 19fb1bfafc | |||
| f91b6ad0df | |||
| 94459cefe5 | |||
| 803ba4f54d | |||
| 2919fa8b14 | |||
| 6420e90124 | |||
| 2d289addb7 | |||
| 4df145dcb4 | |||
| e065da20cb | |||
| d7c77b6761 | |||
| f51029484a | |||
| 03cfcbc17c | |||
| 649f49e000 | |||
| 3bcd4dc3e0 | |||
| 772c71ec27 | |||
| 9b896dc981 | |||
| d4189feca6 | |||
| 777048cfa9 | |||
| 2b215955ca | |||
| ce25f00769 | |||
| 1a59aecb63 | |||
| cca32e67a9 | |||
| 174d492355 | |||
| 5355473dce | |||
| a531ed614a | |||
| 24ae6fdf29 | |||
| 938ce22307 | |||
| 9fb5a5b9a4 | |||
| 9fd9d9c05a | |||
| fd50227a3d | |||
| a90295430c | |||
| fa4ca63ff2 | |||
| 7019c4e8fc | |||
| b79e0e8528 | |||
| 51bed3afae | |||
| 4d4ccddad8 | |||
| f55f5315cc | |||
| 7468a00ef4 | |||
| 8d77278c6b | |||
| dde9b5ad3a | |||
| 2d4307647e | |||
| 935a293106 | |||
| 72b715fa99 | |||
| d801d0cd53 | |||
| c2f1965e03 | |||
| 75bba42b60 | |||
| 18b50db0dc | |||
| 499fce9cee | |||
| 88b29589d6 | |||
| 8cbfbcae5a | |||
| 5cd8ba2421 | |||
| 2320b18953 | |||
| 3b1913ac57 | |||
| 1da1abe314 | |||
| c9721197d5 | |||
| 738747bd05 | |||
| 0bb4a8abeb | |||
| 6ef8fcd537 | |||
| a896f7f218 | |||
| 081d2e58c6 | |||
| ed2a98c7d0 | |||
| 731532cd2b | |||
| adadbadec3 | |||
| 58fafaeb5c | |||
| 13a8eae0d4 | |||
| 42da7654ec | |||
| 8698a7e496 | |||
| 1d7543e2bf | |||
| ff32fbbb4f | |||
| 3ad2530ee7 | |||
| c4de195f04 | |||
| 75aca0f5ee | |||
| bb9a9beed5 | |||
| 4042c1d618 | |||
| bf73516259 | |||
| 43d282174f | |||
| 0042bf62a5 | |||
| f11731ff35 | |||
| e9d8a9bc0b | |||
| 9d6eb1ff06 | |||
| 4643401a1d | |||
| dc449882ad | |||
| a158d3194f | |||
| 806498f099 | |||
| e3bc6da9e4 | |||
| 3e2471d934 | |||
| a545f17d2e | |||
| f4375d0452 | |||
| 756cdf44ed | |||
| 9037bf8bf0 | |||
| bfe9ee378f | |||
| 37a4fa205c | |||
| 59cc7d4c42 | |||
| ab64e215dd | |||
| 253dbe335a | |||
| 0ef86e1534 | |||
| 72270f9052 | |||
| 67ec722a74 | |||
| 62294feb56 | |||
| f1e25a18d2 | |||
| a2f886a67a | |||
| f791c821ff | |||
| a93f0ed12a | |||
| c25dd0a972 | |||
| 215dd3f0be | |||
| c51f5cea0b | |||
| 395ee2d338 | |||
| 069e4f0d98 | |||
| d1eb642d45 | |||
| 5f2e72f371 | |||
| 5b581d83b3 | |||
| d845f1e1b2 | |||
| 00d46bd25d | |||
| 01d2ef327d | |||
| 4da1f6bcbe | |||
| 14ddba8519 | |||
| a7b205c26c | |||
| c83f712c17 | |||
| fffad84a63 | |||
| e075e0591d | |||
| 46d2696363 | |||
| e98dfd6c46 | |||
| 9126b95737 | |||
| 3871b831af | |||
| 7755f2385c | |||
| 04601a29e8 | |||
| 9aadea930f | |||
| 6efa94035e | |||
| db8d80755f | |||
| dc91c96305 | |||
| 08b3a4576d | |||
| acba0748f1 | |||
| ef72c5bb4e | |||
| 93cc03b15a | |||
| fdc4bbbfad | |||
| dd4e9e86e7 | |||
| ad32d26511 | |||
| 4619896c19 | |||
| cec237917f | |||
| 4d2a6d2bbe | |||
| 6d5f4c4be2 | |||
| 0cb5d336f4 | |||
| 91ee4d0030 | |||
| 785eb93b62 | |||
| e041a8ed88 | |||
| d01d85930d | |||
| ac821d8295 | |||
| 5c2753e54e | |||
| 0212f0c008 | |||
| f5f4751088 | |||
| 62fb386d57 | |||
| a59f92daf8 | |||
| b7508818ee | |||
| 284e1acd87 | |||
| 37849c4897 | |||
| 213e73ad84 | |||
| 4a240827cb | |||
| 92d9f856ab | |||
| ee5890f36f | |||
| 744574193b | |||
| bbe755fb69 | |||
| 635d322703 | |||
| ee90bdebff | |||
| bcdb3fc681 | |||
| b7b23ace3f | |||
| f366cd53b7 | |||
| da81784ee9 | |||
| 481f60c5ea | |||
| cf39db36fe | |||
| 54c07b7de8 | |||
| 2fb866ca86 | |||
| 4d11d552db | |||
| b43bd9e62a | |||
| 572c22f66a | |||
| 1b66da4dff | |||
| b08ff17f6b | |||
| 41e787d504 | |||
| 6dba16f28e | |||
| b336a1561f | |||
| 4c682680ca | |||
| af76c807e2 | |||
| cdbd850f80 | |||
| d256804af7 | |||
| e3c96803c3 | |||
| f55bb8e030 | |||
| 07fb3a0cad | |||
| 02913563df | |||
| 535e64adbc | |||
| af936f3261 | |||
| d1a45f2efc | |||
| b56a263d0d | |||
| 6435f62dcc | |||
| f4c8c37b7b | |||
| 868812717f | |||
| e82f595f37 | |||
| e5af121553 | |||
| 20fdbc1f9d | |||
| 14fed59827 | |||
| d331aca296 | |||
| 7e8a2e8934 | |||
| e7363966a5 | |||
| 373ab34492 | |||
| 602f35a5ca | |||
| 1d5c5ca2ef | |||
| 90935703e6 | |||
| d8ee520bdc | |||
| e4da35486b | |||
| 4e41e9084a | |||
| ee3af40cab | |||
| 666312f63f | |||
| ccfe92a58c | |||
| 4fa6a132e2 | |||
| 1a67795fd3 | |||
| c70daa2497 | |||
| d28b58e4dd | |||
| 25b03414dc | |||
| 2ab7ee6a8a | |||
| 6a2dd35fe6 | |||
| 4c0fbbfc7f | |||
| 36bd66a858 | |||
| 9d7dd3dd4b | |||
| 9bf5d6a4aa | |||
| 839edec64c | |||
| 7e05c92420 | |||
| 0b2aec1164 | |||
| ae381e74a5 | |||
| 3392827c3e | |||
| 59838338ba | |||
| 76303f9db4 | |||
| 4bf8b618e5 | |||
| c89a684ad2 | |||
| 7aac1a1391 | |||
| af9f19615b | |||
| 79b63dd041 | |||
| 06e34caa5f | |||
| 4e9f9e2773 | |||
| 64957c5254 | |||
| 1bba5f5d7b | |||
| 48ef68dd86 | |||
| 8b9bee4cad | |||
| b4c9b579b8 | |||
| 2b1dc4fee5 | |||
| 0244bed033 | |||
| 60b23a7035 | |||
| 4326c8f10e | |||
| 1672610749 | |||
| e00a2395d9 | |||
| 42175496eb | |||
| 3b4e543f4e | |||
| ad3e9e1a77 | |||
| e0385e48d0 | |||
| feadb9c85c | |||
| 4bf7308d26 | |||
| 19e4db45db | |||
| 71b44e79b3 | |||
| 9e97566b5f | |||
| 7d83e76833 | |||
| 9e7882b1b7 | |||
| 0c4f9d1f19 | |||
| a85cb22193 | |||
| 60bb832c89 | |||
| 6eda52a289 | |||
| 8badf79af9 | |||
| 66d3c4edb8 | |||
| 63aaa43072 | |||
| ee81c31884 | |||
| 50d681359d | |||
| d50c54fce0 | |||
| 4a81cac53b | |||
| 84a3273131 | |||
| 9182664845 | |||
| a2f95d6b87 | |||
| 698b1f19c6 | |||
| e6ccdd9d1a | |||
| b39a982067 | |||
| e0bf376068 | |||
| b044d4ff3d | |||
| 0cd93a3a9e | |||
| 5faff2e13a | |||
| 195de23d3b | |||
| d4bdd836bc | |||
| a11f2436e3 | |||
| 017ab4c352 | |||
| 181d2ad2b4 | |||
| 53b95cad08 | |||
| e7bd22edf2 | |||
| 1a60a1f526 | |||
| 3fbd3c4d93 | |||
| 1213a45197 | |||
| 4e4e4eee04 | |||
| bf20c652e2 | |||
| a3afd4e8ac | |||
| cb9c6f0074 | |||
| 5fee3428bc | |||
| 464ebc4fe1 | |||
| 568c2a4363 | |||
| c611c72526 | |||
| dfb8c996a1 | |||
| 85da9a0aac | |||
| 17bc0d6663 | |||
| 31043628d8 | |||
| 2c0f61cad1 | |||
| 97ece754f6 | |||
| 39f76f43fc | |||
| f239e8e2d9 | |||
| 6b73b5e486 | |||
| 855e12df51 | |||
| 0b6bb12e9e | |||
| 72c7adf94a | |||
| 13fb5aa7e7 | |||
| a3fdbec13d | |||
| 360f6904f5 | |||
| 7201b13085 | |||
| 9c0bf8bd2c | |||
| 63c7a41337 | |||
| c0f8f129c0 | |||
| adaa7ca27a | |||
| a5ddc68845 | |||
| f7880e73d8 | |||
| 3aa8c8e0d1 | |||
| 6cb7f29d9b | |||
| 21527353f7 | |||
| bedd27b20e | |||
| 3612b7749e | |||
| 45c916f5c0 | |||
| 688a5dbf8d | |||
| 35dcf135c0 | |||
| c482d65397 | |||
| d73e12db63 | |||
| 4b0e56cbec | |||
| 62e1c3ebb7 | |||
| a4a1001dd3 | |||
| e8f6df928c | |||
| f180339d6b | |||
| 5ed0cecf0d | |||
| 523c6c0e3b | |||
| 17f5fb1071 | |||
| 4d43df75d5 | |||
| 57b0c21492 | |||
| 8fb5877d9e | |||
| 6fbe6fa2ae | |||
| 24b4dc31a4 | |||
| 4f8127d0bf | |||
| e0329b25de | |||
| 229f479a7d | |||
| 876f08979f | |||
| f2cc98caa5 | |||
| 0d65b4dd26 | |||
| 6a685b2a8d | |||
| 569817a514 | |||
| 7a96f0f609 | |||
| 821227a03b | |||
| 5b42871baa | |||
| c5598630c9 | |||
| 6c40ee93fe | |||
| 4acb1a5845 | |||
| e0f3333cc3 | |||
| af247e27ff | |||
| ec3a4da4df | |||
| 905269bf11 | |||
| e4f29ae2f9 | |||
| 7fb23824e2 | |||
| f8a6e662d8 | |||
| 0ddd65658e | |||
| db08b5aaa5 | |||
| 383e7ae14a | |||
| 4f7864b757 | |||
| 833c91fceb | |||
| 344e493ac8 | |||
| 1626b50457 | |||
| 93fb897feb | |||
| 15109a2924 | |||
| d9c6f7c11f | |||
| fa4f361ca7 | |||
| 9f02340a26 | |||
| c6728643e6 | |||
| e849901ad6 | |||
| 91d6ca057c | |||
| 0da97e2427 | |||
| ac36f232bc | |||
| 94a54bc105 | |||
| b044d27dff | |||
| 6768af91e7 | |||
| a366c014c7 | |||
| 584a2718d0 | |||
| 069c397975 | |||
| e0aee1f23c | |||
| ad9f790dfc | |||
| f42d8b3056 | |||
| 04412cba0e | |||
| 3b2763896f | |||
| fb394c7685 | |||
| a52cad3825 | |||
| 0e94553b26 | |||
| e6580bfff4 | |||
| 5db83f11df | |||
| 60366adfa8 | |||
| faa449a9e1 | |||
| 08cf2b25c5 | |||
| d6103222f7 | |||
| 69e5c5088d | |||
| a6084767b3 | |||
| cf4a6d3253 | |||
| 4716298044 | |||
| 7706c41bf6 | |||
| 0bc19735f3 | |||
| 51c1579c3d | |||
| 6a45b73fcb | |||
| 2de64d3f4e | |||
| c192c7376e | |||
| 2a821d2a66 | |||
| 90af41d089 | |||
| 7654d86d2c | |||
| 45f119de34 | |||
| 71be24e4f9 | |||
| 3701e6eac1 | |||
| 0463d34887 | |||
| 0b47b99448 | |||
| ea97c60959 | |||
| de281191e0 | |||
| fc28f513c7 | |||
| 34ba0c1be1 | |||
| 7ca45d61fc | |||
| 871cd66b98 | |||
| 192a55970f | |||
| 8babc47638 | |||
| 1cb702ac4c | |||
| c6ac1cc969 | |||
| 0ce539ad42 | |||
| 930255ff46 | |||
| 57ecf1acdc | |||
| 313c9e31dd | |||
| d87e3ce330 | |||
| 11a6c84ea5 | |||
| 1d86b3f425 | |||
| f9b6f8339b | |||
| 0ff243e5e2 | |||
| 79930a209c | |||
| bbc79ee40a | |||
| 960a0b79bf | |||
| e4ef6f85ac | |||
| 3a3de8a778 | |||
| ca823f9a0d | |||
| 4ec1e58aa6 | |||
| d98c0afbab | |||
| 12e3d1d81d | |||
| a7e3cf0826 | |||
| 43a4b256bd | |||
| 5cc5f11b58 | |||
| 836689fdd2 | |||
| 9b86080c80 | |||
| 517098c468 | |||
| 1cce1ea423 | |||
| a2bb1d3805 | |||
| c4dadbaaab | |||
| 828c3169ab | |||
| f6ee470eda | |||
| d0f4702f8b | |||
| c0a50e9b47 | |||
| 9541525402 | |||
| 60a93e8e2d | |||
| cdf785629a | |||
| ea183e9197 | |||
| 74160c0e32 | |||
| 6e1b8a54aa | |||
| 8bf94b9231 | |||
| 488174767b | |||
| 201592d93b | |||
| 476621736c | |||
| 7b44fcd865 | |||
| cfe971d723 | |||
| bad7bfc4d5 | |||
| ad1da7d26f | |||
| 6df303ff19 | |||
| 96b70fd619 | |||
| 4d0dfbd6fd | |||
| 1065f06e57 | |||
| ec27d9fb48 | |||
| cce9c6d5a1 | |||
| f41b6a3b96 | |||
| e066e67baf | |||
| cacb452aa6 | |||
| 9296332151 | |||
| 1125d42149 | |||
| 7982d26db0 | |||
| f6758d22d8 | |||
| 87cfe6a538 | |||
| c02cc339c5 | |||
| 361ecf9ea4 | |||
| e21ab04fad | |||
| 4e8453167f | |||
| ebb410afd9 | |||
| 012bdfae80 | |||
| f6df276118 | |||
| e0a5fc2555 | |||
| cecb80b8bf | |||
| d899895e32 | |||
| 0d56dee00f | |||
| 1f0bf9ba63 | |||
| 0b0ae19656 | |||
| 9027c50405 | |||
| 4ce54ede15 | |||
| d8b1fac690 | |||
| 1996fad075 | |||
| 7fe996e7ba | |||
| 47ca1c037b | |||
| df0f5ae1da | |||
| 45c8817ddd | |||
| eebd2f648b | |||
| 13698b17f7 | |||
| dd9c2273e4 | |||
| 1adfdf8fa5 | |||
| da362b8a24 | |||
| 7fa13ed0a6 | |||
| eec915170d | |||
| 334a12818d | |||
| 0504e4a201 | |||
| 530ca0838d | |||
| c41a212712 | |||
| 27824e3c80 | |||
| 159317a5fe | |||
| 655075e5e0 | |||
| f9d0ff3bec | |||
| 17176a2cea | |||
| fb73074325 | |||
| 187d9c6aca | |||
| 35f427c8cc | |||
| 26b6d0b06d | |||
| 38252e338f | |||
| e7ba07dd5a | |||
| 0d646d8e95 | |||
| 3177591b31 | |||
| 19db0144ae | |||
| d639e01650 | |||
| c43958972c | |||
| 33489d6344 | |||
| 80077ef276 | |||
| 911ce3a4bc | |||
| 776dabb2c8 | |||
| e11df5c212 | |||
| 656805d91f | |||
| 05ec4e6bd0 | |||
| ae65b37762 | |||
| aa12579864 | |||
| 715b269f78 | |||
| 5f3534fe72 | |||
| a7ff2731b9 | |||
| 0566bbfeb4 | |||
| 70c4e1cf96 | |||
| 9fd0567fb5 | |||
| 481aba6536 | |||
| a700f58581 | |||
| e71e289e8b | |||
| 6cdc718bfb | |||
| 70c94c6d01 | |||
| 762a6f1256 | |||
| 8d4d3c6201 | |||
| 146e2cd376 | |||
| d304e6706b | |||
| c5c6464175 | |||
| c90ff1e1cf | |||
| 0f6dfedcfd | |||
| 69a284bd36 | |||
| 92be61534e | |||
| 675b8e3f3c | |||
| f3d0195d35 | |||
| 398c284480 | |||
| 86f125cf72 | |||
| 5293d3e5f0 | |||
| 7aca814954 | |||
| 1d52e6be29 | |||
| acb2391439 | |||
| 49007891c8 | |||
| 54aec042b7 | |||
| baca8a643e | |||
| b731919f97 | |||
| c759e6fc2d | |||
| a329f56ec2 | |||
| 6e503f5ab9 | |||
| 217449df07 | |||
| a7fc65861d | |||
| f0a271bd21 | |||
| 4d14d3cd54 | |||
| 5c622cfc43 | |||
| 783c6a813a | |||
| 67841207e8 | |||
| d92e16e6dc | |||
| 4daf4fa0a0 | |||
| 0b3a7280fa | |||
| bb5050a50d | |||
| 73b368270c | |||
| 2001c44ed3 | |||
| 666ead3996 | |||
| df888acd5e | |||
| b9bb605257 | |||
| e1281b0128 | |||
| 81dbbea1c8 | |||
| a834359079 | |||
| 29f8d22a2d | |||
| 9e34ca9e5f | |||
| 268140ae40 | |||
| 8906584fb9 | |||
| aaa860f154 | |||
| bce0516394 | |||
| 726d74ad1b | |||
| ad544b5133 | |||
| 580fd490a5 | |||
| c0d505d13a | |||
| 12a41dc791 | |||
| dd3ad57efb | |||
| 89facf834f | |||
| 62594004c6 | |||
| 3dbc4d5210 | |||
| 42e4a75319 | |||
| 2b7f562202 | |||
| 08a51535d4 | |||
| 0ddcf4af96 | |||
| d435fc868b | |||
| 62df1d7490 | |||
| 229746b71e | |||
| 09ec846c9f | |||
| f57cffce2d | |||
| 7b6a9fab42 | |||
| 6bc6de8f82 | |||
| b79bf29c11 | |||
| 65d65c1313 | |||
| 6b46ae261a | |||
| b734b3c7a1 | |||
| 5937f03a07 | |||
| bf9282a3a0 | |||
| cad5b79a2d | |||
| d56c9f5533 | |||
| 3d7f24e26c | |||
| c32490935b | |||
| 2b04006b1e | |||
| 9de757205f | |||
| f866d5cc38 | |||
| c153239682 | |||
| 8e88f3e76a | |||
| a62044d89b | |||
| 5548b5999b | |||
| 8f33721b88 | |||
| 7e9d84777e | |||
| 2072d1bb5c | |||
| 636a151ffe | |||
| e7d02cd51b | |||
| f136eb6adf | |||
| 2688d9d733 | |||
| b5ad81c884 | |||
| c2fb1e776a | |||
| 895c542076 | |||
| e91fae5756 | |||
| f52ecf3054 | |||
| 725864d8dc | |||
| fca2de21b1 | |||
| 107b1c2906 | |||
| 2409bbef69 | |||
| 8b078a9e8f | |||
| 9e24441978 | |||
| 1e6c14b3e4 | |||
| 25f6be1488 | |||
| 9a9106a297 | |||
| a68f7e4ce3 | |||
| 70b3ece6ec | |||
| 86ea216e7d | |||
| 5a92c35536 | |||
| 7513fa2c96 | |||
| bc946f346d | |||
| 6e6666519c | |||
| cecae8084c | |||
| 9720ccacbb | |||
| a5aa777c7b | |||
| aca1ad187d | |||
| 5a41ed6329 | |||
| 91e9c84d85 | |||
| 4af530f238 | |||
| d3863f337d | |||
| 5551ec7a1e | |||
| 91104b2f27 | |||
| 80f38e8449 | |||
| 22893c5180 | |||
| 9e4e182615 | |||
| 5f96d2b784 | |||
| bd1d93b705 | |||
| 03d1c44265 | |||
| dc0346d968 | |||
| dadae95448 | |||
| f5fc2fc072 | |||
| 4ba76a5df9 | |||
| ea1e08f53f | |||
| bba0dc9575 | |||
| 617c7586fe | |||
| 7366d5e185 | |||
| 96e3ecd949 | |||
| 8bbfb1ee68 | |||
| 07cdcc61f7 | |||
| 4ca9b34133 | |||
| d5e4b16911 | |||
| fd05254683 | |||
| c579e7e509 | |||
| d463528a72 | |||
| 49b0086f8f | |||
| d7814ff50b | |||
| 02160aa8d8 | |||
| c199dd03c7 | |||
| 472aecf07a | |||
| 09350e5719 | |||
| 623dd6a815 | |||
| e09bd5abb3 | |||
| e955aa1941 | |||
| edaf13f951 | |||
| 5f6cf665f5 | |||
| c6db89ff05 | |||
| 61d651c947 | |||
| 227a55d891 | |||
| b485e8bfbd | |||
| 9e8739bb47 | |||
| 20a7a6fd54 | |||
| 4695da83b7 | |||
| f094e895c5 | |||
| 55573c6729 | |||
| b8a6a1f62b | |||
| ef912a132d | |||
| fb603a471c | |||
| f439514703 | |||
| 303410d3fc | |||
| aa13b5d83b | |||
| 12d63d9642 | |||
| 24b45555bd | |||
| 80c17c0f32 | |||
| f6e6aca8d9 | |||
| c4d99e5297 | |||
| 697e8c725b | |||
| a87e5cdbf0 | |||
| b56baa7aad | |||
| ee94f92ede | |||
| 0b77e92c50 | |||
| a4ee98e0e1 | |||
| d0808c93f6 | |||
| a0407be7b7 | |||
| e70dfa4dd6 | |||
| f34a0d44ca | |||
| df52b500bf | |||
| c2f437e0b7 | |||
| 2365c7cfec | |||
| a5d28bdcca | |||
| f12ddfe164 | |||
| f9ba99344a | |||
| 1784bcd728 | |||
| a2704f1435 | |||
| edbbca5614 | |||
| 13f21477b1 | |||
| 9d73e6cfe4 | |||
| 9e7667ffae | |||
| b86467f9c5 | |||
| 13a16afc2a | |||
| a107b4d459 | |||
| 5c176f80d5 | |||
| c4392d9472 | |||
| b6d6efde07 | |||
| 8b67282f91 | |||
| c7a23183e8 | |||
| f936125658 | |||
| 3cfaceeb16 | |||
| f0391eac25 | |||
| 05cdde162c | |||
| 25c8d3ec3a | |||
| 7b1e5f6f99 | |||
| 70c52ef1b4 | |||
| 5078ce382d | |||
| 408855da14 | |||
| 58072a5167 | |||
| 5026047cde | |||
| 67aea275c5 | |||
| 99bd4d2c7c | |||
| a4b22bd125 | |||
| 2ed7523001 | |||
| 4aec3d04f9 | |||
| 4f729c323d | |||
| 1f43f32aae | |||
| 6d1cbe6fb9 | |||
| 7cd2890434 | |||
| 13311da7e9 | |||
| 3047d8f321 | |||
| 13ed58950f | |||
| 5271bd8cfa | |||
| 72137d663b | |||
| 740134197e | |||
| 610e7fd16a | |||
| 69a6718b38 | |||
| 7d31d98f0f | |||
| 0a93347e1c | |||
| 411333327e | |||
| 159f7ebbc3 | |||
| 153ff09cc5 | |||
| 57cddf5657 | |||
| 6a35719594 | |||
| 20a46e1b28 | |||
| 926c02b6ff | |||
| f370cd18fc | |||
| a1d7cab080 | |||
| 15a9e2313a | |||
| 58f2533f15 | |||
| 3c2d2a93d3 | |||
| ea36fea5a9 | |||
| 5fdb894760 | |||
| 8fed063820 | |||
| 2f843af286 | |||
| 7afc47d67a | |||
| 0bc7c2a61f | |||
| d434058aef | |||
| 56780c2691 | |||
| 1d99b1ed84 | |||
| e1405640da | |||
| 5996e113ad | |||
| f1e6997018 | |||
| 335a7ad0b7 | |||
| 17deac116f | |||
| b112477e95 | |||
| 2fa24ef752 | |||
| fcefcb191c | |||
| ff7c71c99c | |||
| 7eaad62568 | |||
| 401bd89664 | |||
| 24b1bfcecd | |||
| 27162ebe32 | |||
| b347aea450 | |||
| 1766746c60 | |||
| 07049b87b6 | |||
| f38603530d | |||
| b65447c7d5 | |||
| 0abc909739 | |||
| d984b55311 | |||
| 446fe1f5b3 | |||
| 873412b43a | |||
| 8a5ab5ab12 | |||
| 77cf60ef30 | |||
| a4e2e09457 | |||
| 4262f0ff1f | |||
| 1ceb63d14c | |||
| e1052f921c | |||
| 6dff85c756 | |||
| afae6b3795 | |||
| 2825b4fe74 | |||
| b5c5d95b68 | |||
| 633fe95187 | |||
| 64d7f1e436 | |||
| 9af535bf98 | |||
| 159f0051bb | |||
| 49e22fe678 | |||
| c119721e3b | |||
| 9294141d70 | |||
| bcf58c0840 | |||
| 7a61cb1bc3 | |||
| 11e1b2148f | |||
| a4599ba539 | |||
| 4c64784724 | |||
| 8d6b210c09 | |||
| 79ba6a50c3 | |||
| 2fb5174910 | |||
| b31281a5b6 | |||
| 8754f64f3b | |||
| caf405b01d | |||
| bb65c5ce2b | |||
| 283fed16a5 | |||
| 76bba13963 | |||
| ada11090a2 | |||
| a07287d3c6 | |||
| 1b2e764835 | |||
| 2c93a4085c | |||
| 906757603d | |||
| 5d16811199 | |||
| f2e576decf | |||
| 2d61972475 | |||
| 5c488e3145 | |||
| e45ff9c0e8 | |||
| 39805bca45 | |||
| 97842b367c | |||
| 9246899b30 | |||
| 04ace4de5f | |||
| 87a4676137 | |||
| fd20f63ee3 | |||
| 9dd249a431 | |||
| 777d3f3ea5 | |||
| ea3b5dc689 | |||
| 3dd502a928 | |||
| 48242ba8d6 | |||
| 7ea9eff406 | |||
| 80d94115dc | |||
| 9de76fb42e | |||
| 52c61de19f | |||
| f308d7d28c | |||
| 7729a36782 | |||
| 5404c81ffd | |||
| b52310fed2 | |||
| 046070074d | |||
| c23a5b65ef | |||
| b8e433821a | |||
| dd1a836903 | |||
| 4ed0936c0e | |||
| 7a5d4c9537 | |||
| af6bbaf532 | |||
| 4c7449efe5 | |||
| 995e815b63 | |||
| 109b5298c4 | |||
| a94e03eff2 | |||
| eb3d6261b4 | |||
| 15724848f9 | |||
| 8f40b107d9 | |||
| fa673f0827 | |||
| e3fe6d8f37 | |||
| 5c23f6fb6c | |||
| 5afb63df60 | |||
| 1d2c39c3b9 | |||
| 270a2f51fd | |||
| e87cd23e87 | |||
| db389ba443 | |||
| d1dfa1155c | |||
| cf0b7edc78 | |||
| a32cda7a4c | |||
| b251ce7b33 | |||
| 1abe1203a3 | |||
| 8b360ec911 | |||
| 93654544b3 | |||
| 050d516264 | |||
| 9a8328fcb7 | |||
| 6812a4dc04 | |||
| 8568d4f625 | |||
| bd56636d58 | |||
| 4fe3a379a5 | |||
| fce42fc7fa | |||
| 5d177d1328 | |||
| 9eb4a6268a | |||
| 11a402de95 | |||
| cdd9198888 | |||
| 6055e6eb23 | |||
| 9239c61edb | |||
| 008b373df4 | |||
| c387a09f54 | |||
| 5bb3ca2618 | |||
| a4ea85b303 | |||
| 27d38a9663 | |||
| ee497a296b | |||
| 6b31ee1e93 | |||
| cad6a57077 | |||
| 52233790cc | |||
| 9160f3f16d | |||
| 69d7ed5cda | |||
| 111661658b | |||
| 3000b2d15e | |||
| c89f026532 | |||
| e09b1d06f5 | |||
| 0590ccb260 | |||
| fe0acb1670 | |||
| d58c7d2521 | |||
| 85f5411d60 | |||
| c5de68622e | |||
| 8fb93fbac4 | |||
| ef3b3c0d6a | |||
| 2dc46a851b | |||
| f3afc7bbf3 | |||
| 4858b27120 | |||
| 7f507a7026 | |||
| 83227e68df | |||
| 09a3c6a16b | |||
| f613cb41a3 | |||
| e060e588ab | |||
| 8dfb317d28 | |||
| 9cedd78d9c | |||
| 20a27746b2 | |||
| c78df85015 | |||
| c0d257d682 | |||
| 7af24c7864 | |||
| 3dc1e403ea | |||
| 1deee3ec5a | |||
| 0f9f316db2 | |||
| 0dd521e1d1 | |||
| 1dcdd5f2d2 | |||
| dcd8ee63a8 | |||
| b8bf0ac3f1 | |||
| 73571dd423 | |||
| 8872229a0b | |||
| 7e05efa274 | |||
| b02cea0321 | |||
| 8823a934a3 | |||
| 6ac02cc34c | |||
| e259598b68 | |||
| f159573975 | |||
| 234234af15 | |||
| 3d68804702 | |||
| 38efca1e2d | |||
| 0a75dd82b9 | |||
| fbc748f152 | |||
| 881aba1785 | |||
| aa421bf655 | |||
| 1fb1f08233 | |||
| 0b5d300d94 | |||
| 2fdcabdcce | |||
| d066b5c232 | |||
| 836ab95192 | |||
| cf8f4d65f2 | |||
| 06b19cde2d | |||
| 42ae9a665f | |||
| c55c996c5d | |||
| 6fb90b6fc4 | |||
| 6f31712b7d | |||
| e295770363 | |||
| 486b99961d | |||
| 4a98db4c86 | |||
| c534fa1b6f | |||
| eed78338e6 | |||
| 5141638cbf | |||
| 5d4f795aec | |||
| db8603e378 | |||
| bc098da93f | |||
| e49e93f22c | |||
| 7d4bf09b1a | |||
| 320f21524e | |||
| 4117055611 | |||
| 603fff78d4 | |||
| d4b1467e4c | |||
| 4cf8ad2bee | |||
| 96c60f9692 | |||
| 99ce46669b | |||
| a1b4eafa07 | |||
| ccbe03795a | |||
| 3f69c32a55 | |||
| b176494e44 | |||
| 0243a5b8be | |||
| 1fac41d535 | |||
| 1472fb6032 | |||
| 0fd3d785a3 | |||
| b9c0aa77bf | |||
| 4c28f1b875 | |||
| 0791603c86 | |||
| 095312fa3f | |||
| 7e647a5e81 | |||
| 29c1d7c68d | |||
| ecb7eebcff | |||
| a680bfbb7a | |||
| bf23839931 | |||
| 6b15a72895 | |||
| 08fd49fe60 | |||
| d65e3deccf | |||
| cbfa85be6a | |||
| 6c23f27f73 | |||
| 31a3a7c84b | |||
| a9a225ba26 | |||
| 58d0499e55 | |||
| b24af78327 | |||
| b406422277 | |||
| cfed3f0213 | |||
| d75e14245b | |||
| af8244e868 | |||
| f962e2fd15 | |||
| 5746154cc0 | |||
| e9d9ae8781 | |||
| 52ffd6aa98 | |||
| 644356c220 | |||
| 47a622a903 | |||
| 4417374b53 | |||
| 262291a80a | |||
| cdd267fa10 | |||
| 850a5fdbfb | |||
| 4b2edc9a26 | |||
| f2fef290c9 | |||
| 010cc13e9e | |||
| ac7e370c72 | |||
| 77ec9b6369 | |||
| aa8e0bb509 | |||
| 372d9e0f5e | |||
| d139fa5e0f | |||
| 367fb9d40e | |||
| 98a6ed8059 | |||
| 641a7e5cb9 | |||
| 3fa7d6cc40 | |||
| e497b5c5ba | |||
| 23dc861ddb | |||
| b2ccf12e98 | |||
| 23479c92d3 | |||
| 3458726234 | |||
| a7a69d38a4 | |||
| 4210200070 |
+42
-36
@@ -6,31 +6,20 @@ commands:
|
||||
- run:
|
||||
name: "Pull submodules"
|
||||
command: git submodule update --init
|
||||
install-xtensa-toolchain:
|
||||
parameters:
|
||||
variant:
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: "Install Xtensa toolchain"
|
||||
command: |
|
||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-13-v1
|
||||
- llvm-source-18-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-13-v1
|
||||
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:
|
||||
@@ -44,78 +33,95 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- binaryen-linux-v1
|
||||
- binaryen-linux-v3
|
||||
- run:
|
||||
name: "Build Binaryen"
|
||||
command: |
|
||||
make binaryen
|
||||
- save_cache:
|
||||
key: binaryen-linux-v1
|
||||
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/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
echo 'deb https://apt.llvm.org/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>> \
|
||||
gcc-avr \
|
||||
avr-libc \
|
||||
cmake \
|
||||
ninja-build
|
||||
- hack-ninja-jobs
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- go-cache-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-v3
|
||||
- wasi-libc-sysroot-systemclang-v7
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v3
|
||||
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
|
||||
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
|
||||
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
- run: make fmt-check
|
||||
|
||||
jobs:
|
||||
test-llvm11-go115:
|
||||
test-llvm15-go119:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
- image: golang:1.19-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
test-llvm12-go116:
|
||||
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-go123:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-buster
|
||||
- image: golang:1.23-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "12"
|
||||
llvm: "18"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-llvm11-go115
|
||||
- test-llvm12-go116
|
||||
# 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-go123
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
build/
|
||||
llvm-*/
|
||||
.github
|
||||
.circleci
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: tinygo
|
||||
@@ -7,52 +7,75 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
name: build-macos
|
||||
runs-on: macos-10.15
|
||||
strategy:
|
||||
matrix:
|
||||
# macos-13: amd64 (oldest supported version as of 18-10-2024)
|
||||
# macos-14: arm64 (oldest arm64 version)
|
||||
os: [macos-13, macos-14]
|
||||
include:
|
||||
- os: macos-13
|
||||
goarch: amd64
|
||||
- os: macos-14
|
||||
goarch: arm64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.17'
|
||||
- name: exit early
|
||||
run: command-does-not-exist
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||
- name: Install Xtensa toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
|
||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
|
||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
- 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
|
||||
with:
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-13-macos-v1
|
||||
key: llvm-source-18-${{ matrix.os }}-v2
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-13-macos-v2
|
||||
key: llvm-build-18-${{ matrix.os }}-v3
|
||||
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
|
||||
@@ -62,23 +85,31 @@ jobs:
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v1
|
||||
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="-v -short"
|
||||
run: make test GOTESTFLAGS="-short"
|
||||
- name: Build TinyGo release tarball
|
||||
run: make release -j3
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
- name: Make release artifact
|
||||
shell: bash
|
||||
run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz
|
||||
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
|
||||
@@ -86,10 +117,45 @@ jobs:
|
||||
# - have a double-zipped artifact when downloaded from the UI
|
||||
# - have a very slow artifact upload
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/tinygo.darwin-amd64.tar.gz
|
||||
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||
test-macos-homebrew:
|
||||
name: homebrew-install
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [16, 17, 18]
|
||||
steps:
|
||||
- name: exit early
|
||||
run: command-does-not-exist
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Fix Python symlinks
|
||||
run: |
|
||||
# Github runners have broken symlinks, so relink
|
||||
# see: https://github.com/actions/setup-python/issues/577
|
||||
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
brew install llvm@${{ matrix.version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
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
|
||||
run: go install
|
||||
- name: Check binary
|
||||
if: matrix.version == 18
|
||||
run: tinygo version
|
||||
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
push:
|
||||
branches: [ dev, fix-docker-llvm-build ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: build-push-dev
|
||||
@@ -15,35 +19,46 @@ jobs:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Free Disk space
|
||||
shell: bash
|
||||
run: |
|
||||
df -h
|
||||
sudo rm -rf /opt/hostedtoolcache
|
||||
sudo rm -rf /usr/local/lib/android
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf /usr/local/graalvm
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/tinygo-dev
|
||||
ghcr.io/${{ github.repository }}/tinygo-dev
|
||||
ghcr.io/${{ github.repository_owner }}/tinygo-dev
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@@ -51,9 +66,45 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Trigger Drivers repo build on CircleCI
|
||||
- name: Trigger Drivers repo build on Github Actions
|
||||
run: |
|
||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/drivers/pipeline' \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d '{"branch": "dev"}' \
|
||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
||||
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"}'
|
||||
|
||||
+263
-88
@@ -7,51 +7,75 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
# Build Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
# an older glibc version and therefore are compatible with a wide range of
|
||||
# Linux distributions.
|
||||
runs-on: ubuntu-18.04
|
||||
# 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.23-alpine
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: exit early
|
||||
run: command-does-not-exist
|
||||
- name: Install apk dependencies
|
||||
# tar: needed for actions/cache@v4
|
||||
# git+openssh: needed for checkout (I think?)
|
||||
# ruby: needed to install fpm
|
||||
run: apk add tar git openssh make g++ ruby-dev
|
||||
- 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@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.17'
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: go-cache-linux-v1-${{ hashFiles('go.mod') }}
|
||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-13-linux-v1
|
||||
key: llvm-source-18-linux-alpine-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-13-linux-v1
|
||||
key: llvm-build-18-linux-alpine-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -59,88 +83,100 @@ jobs:
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
apk add cmake samurai python3
|
||||
# build!
|
||||
make llvm-build
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v4
|
||||
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@v2
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-v1
|
||||
key: binaryen-linux-alpine-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: make binaryen
|
||||
run: |
|
||||
apk add cmake samurai python3
|
||||
make binaryen STATIC=1
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v2
|
||||
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: |
|
||||
sudo apt-get install ruby ruby-dev
|
||||
sudo gem install --no-document fpm
|
||||
gem install --version 4.0.7 public_suffix
|
||||
gem install --version 2.7.6 dotenv
|
||||
gem install --no-document fpm
|
||||
- name: Run linter
|
||||
run: make lint
|
||||
- name: Run spellcheck
|
||||
run: make spell
|
||||
- name: Build TinyGo release
|
||||
run: |
|
||||
make release deb -j3
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||
make release deb -j3 STATIC=1
|
||||
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
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-double-zipped
|
||||
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: |
|
||||
/tmp/tinygo.linux-amd64.tar.gz
|
||||
/tmp/tinygo_amd64.deb
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||
test-linux-build:
|
||||
# Test the binaries built in the build-linux job by running the smoke tests.
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.17'
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
with:
|
||||
name: release-double-zipped
|
||||
version: "19.0.1"
|
||||
- name: Install wasm-tools
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
- 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
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends \
|
||||
gcc-avr \
|
||||
avr-libc
|
||||
- name: "Install Xtensa toolchain"
|
||||
run: |
|
||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
- run: make tinygo-test-wasi-fast
|
||||
- run: make tinygo-test-wasip1-fast
|
||||
- run: make tinygo-test-wasip2-fast
|
||||
- run: make smoketest
|
||||
assert-test-linux:
|
||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||
# potential bugs.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: exit early
|
||||
run: command-does-not-exist
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -152,47 +188,53 @@ jobs:
|
||||
qemu-system-arm \
|
||||
qemu-system-riscv32 \
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
avr-libc \
|
||||
simavr \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.17'
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '18'
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
with:
|
||||
key: go-cache-linux-asserts-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
version: "19.0.1"
|
||||
- name: Setup `wasm-tools`
|
||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-13-linux-asserts-v1
|
||||
key: llvm-source-18-linux-asserts-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-13-linux-asserts-v1
|
||||
key: llvm-build-18-linux-asserts-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -204,8 +246,14 @@ jobs:
|
||||
make llvm-build ASSERT=1
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save LLVM build cache
|
||||
uses: actions/cache/save@v4
|
||||
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@v2
|
||||
uses: actions/cache@v4
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-asserts-v1
|
||||
@@ -214,10 +262,10 @@ jobs:
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: make binaryen
|
||||
- name: Cache wasi-libc
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v2
|
||||
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'
|
||||
@@ -231,11 +279,138 @@ jobs:
|
||||
echo "$(pwd)/build" >> $GITHUB_PATH
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
- name: Install Xtensa toolchain
|
||||
run: |
|
||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-baremetal
|
||||
build-linux-cross:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
# an older glibc version and therefore are compatible with a wide range of
|
||||
# Linux distributions.
|
||||
# It is set to "needs: build-linux" because it modifies the release created
|
||||
# in that process to avoid doing lots of duplicate work and to avoid
|
||||
# complications around precompiled libraries such as compiler-rt shipped as
|
||||
# part of the release tarball.
|
||||
strategy:
|
||||
matrix:
|
||||
goarch: [ arm, arm64 ]
|
||||
include:
|
||||
- goarch: arm64
|
||||
toolchain: aarch64-linux-gnu
|
||||
libc: arm64
|
||||
- goarch: arm
|
||||
toolchain: arm-linux-gnueabihf
|
||||
libc: armhf
|
||||
runs-on: ubuntu-20.04
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- 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
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-${{ matrix.toolchain }} \
|
||||
libc6-dev-${{ matrix.libc }}-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore LLVM build cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# Install build dependencies.
|
||||
sudo apt-get install --no-install-recommends ninja-build
|
||||
# build!
|
||||
make llvm-build CROSS=${{ matrix.toolchain }}
|
||||
# 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
|
||||
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
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v4
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends ninja-build
|
||||
git submodule update --init lib/binaryen
|
||||
make CROSS=${{ matrix.toolchain }} binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo gem install --version 4.0.7 public_suffix
|
||||
sudo gem install --version 2.7.6 dotenv
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo binary
|
||||
run: |
|
||||
make CROSS=${{ matrix.toolchain }}
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
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
|
||||
cp -p build/wasm-opt build/release/tinygo/bin
|
||||
- name: Create ${{ matrix.goarch }} release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
|
||||
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: |
|
||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||
/tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# This is the Github action to build and push the LLVM Docker image
|
||||
# used by the tinygo/tinygo-dev Docker image.
|
||||
#
|
||||
# It only needs to be rebuilt when updating the LLVM version.
|
||||
#
|
||||
# To update, make any needed changes to this file,
|
||||
# then push to the "build-llvm-image" branch.
|
||||
#
|
||||
# The needed image will be rebuilt, which will very likely take at least 1-2 hours.
|
||||
name: LLVM
|
||||
on:
|
||||
push:
|
||||
branches: [ build-llvm-image ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-push-llvm:
|
||||
name: build-push-llvm
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
tinygo/llvm-18
|
||||
ghcr.io/${{ github.repository_owner }}/llvm-18
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
- name: Log in to Github Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Nix
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
nix-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: exit early
|
||||
run: command-does-not-exist
|
||||
- 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
|
||||
run: |
|
||||
git submodule update --init lib/musl
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-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
|
||||
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
|
||||
- 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"
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
# Command that's part of sizediff.yml. This is put in a separate file so that it
|
||||
# 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/noble/ llvm-toolchain-noble-18 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
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Binary size difference
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
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
|
||||
steps:
|
||||
- name: exit early
|
||||
run: command-does-not-exist
|
||||
# Prepare, install tools
|
||||
- name: Add GOBIN to $PATH
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-18-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
|
||||
with:
|
||||
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- run: make gen-device -j4
|
||||
- name: Download drivers repo
|
||||
run: git clone https://github.com/tinygo-org/drivers.git
|
||||
- name: Save HEAD
|
||||
run: git branch github-actions-saved-HEAD HEAD
|
||||
|
||||
# Compute sizes for the PR branch
|
||||
- name: Build tinygo binary for the PR branch
|
||||
run: go install
|
||||
- name: Determine binary sizes on the PR branch
|
||||
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-pr.txt)
|
||||
|
||||
# Compute sizes for the dev branch
|
||||
- name: Checkout dev branch
|
||||
run: |
|
||||
git reset --hard origin/dev
|
||||
git checkout --no-recurse-submodules `git merge-base HEAD origin/dev`
|
||||
- name: Install apt dependencies on the dev branch
|
||||
# this is only needed on a PR that changes the LLVM version
|
||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||
- name: Build tinygo binary for the dev branch
|
||||
run: go install
|
||||
- name: Determine binary sizes on the dev branch
|
||||
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-dev.txt)
|
||||
|
||||
# Create comment
|
||||
# TODO: add a summary, something like:
|
||||
# - overall size difference (percent)
|
||||
# - number of binaries that grew / shrank / remained the same
|
||||
# - don't show the full diff when no binaries changed
|
||||
- name: Calculate size diff
|
||||
run: ./tools/sizediff drivers/sizes-dev.txt drivers/sizes-pr.txt | tee sizediff.txt
|
||||
- name: Create comment
|
||||
run: |
|
||||
echo "Size difference with the dev branch:" > comment.txt
|
||||
echo "<details><summary>Binary size difference</summary>" >> comment.txt
|
||||
echo "<pre>" >> comment.txt
|
||||
cat sizediff.txt >> comment.txt
|
||||
echo "</pre></details>" >> comment.txt
|
||||
- name: Comment contents
|
||||
run: cat comment.txt
|
||||
- name: Add comment
|
||||
if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
|
||||
uses: thollander/actions-comment-pull-request@v2.3.1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
filePath: comment.txt
|
||||
comment_tag: sizediff
|
||||
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'
|
||||
+190
-37
@@ -7,48 +7,76 @@ on:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-2022
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
go-version: '1.17'
|
||||
- uses: MinoruSekine/setup-scoop@v1
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install ninja binaryen
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
#- uses: brechtm/setup-scoop@v2
|
||||
# with:
|
||||
# scoop_update: 'false'
|
||||
#- name: Install Dependencies
|
||||
# shell: bash
|
||||
# run: |
|
||||
# scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/AppData/Local/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: submodules
|
||||
shell: bash
|
||||
run: git submodule update --init lib/mingw-w64
|
||||
- name: Extract TinyGo version
|
||||
id: version
|
||||
shell: bash
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: command
|
||||
shell: bash
|
||||
run: go env
|
||||
#- name: Install Go
|
||||
# uses: actions/setup-go@v5
|
||||
# with:
|
||||
# go-version: '1.23'
|
||||
# cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-13-windows-v1
|
||||
key: llvm-source-18-windows-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
- name: Save cached LLVM source
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Restore cached LLVM build
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-13-windows-v1
|
||||
key: llvm-build-18-windows-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -58,27 +86,55 @@ jobs:
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build
|
||||
make llvm-build CCACHE=OFF
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Save cached LLVM build
|
||||
uses: actions/cache/save@v4
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||
path: llvm-build
|
||||
- name: Restore Go cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: go-cache-v2
|
||||
path: |
|
||||
C:/Users/runneradmin/AppData/Local/go-build
|
||||
C:/Users/runneradmin/go/pkg/mod
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-short -run=TestBuild -v"
|
||||
- name: Save Go cache
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
key: go-cache-v2
|
||||
path: |
|
||||
C:/Users/runneradmin/AppData/Local/go-build
|
||||
C:/Users/runneradmin/go/pkg/mod
|
||||
- name: exit
|
||||
run: command-does-not-exist
|
||||
- name: Cache wasi-libc sysroot
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v1
|
||||
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
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-v -short"
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop install wasmtime@14.0.4
|
||||
- name: make gen-device
|
||||
run: make -j3 gen-device
|
||||
- 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
|
||||
@@ -86,12 +142,109 @@ jobs:
|
||||
# - have a dobule-zipped artifact when downloaded from the UI
|
||||
# - have a very slow artifact upload
|
||||
# We're doing the former here, to keep artifact uploads fast.
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-double-zipped
|
||||
path: build/release/release.zip
|
||||
name: windows-amd64-double-zipped-${{ steps.version.outputs.version }}
|
||||
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||
|
||||
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'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Smoke tests
|
||||
shell: bash
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
stdlib-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
stdlib-wasi-test-windows:
|
||||
runs-on: windows-2022
|
||||
needs: build-windows
|
||||
steps:
|
||||
- name: Configure pagefile
|
||||
uses: al-cheb/configure-pagefile-action@v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install binaryen && scoop install wasmtime@14.0.4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
||||
path: build/
|
||||
- name: Unzip TinyGo build
|
||||
shell: bash
|
||||
working-directory: build
|
||||
run: 7z x tinygo*.windows-amd64.zip -r
|
||||
- name: Test stdlib packages on wasip1
|
||||
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||
|
||||
+10
-2
@@ -1,3 +1,8 @@
|
||||
.DS_Store
|
||||
.vscode
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
docs/_build
|
||||
src/device/avr/*.go
|
||||
src/device/avr/*.ld
|
||||
@@ -15,14 +20,17 @@ 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
|
||||
./vendor
|
||||
llvm-build
|
||||
llvm-project
|
||||
build/*
|
||||
|
||||
# Ignore files generated by smoketest
|
||||
test
|
||||
test.bin
|
||||
test.elf
|
||||
test.exe
|
||||
@@ -30,4 +38,4 @@ test.gba
|
||||
test.hex
|
||||
test.nro
|
||||
test.wasm
|
||||
wasm.wasm
|
||||
wasm.wasm
|
||||
|
||||
+13
-6
@@ -9,14 +9,11 @@
|
||||
url = https://github.com/avr-rust/avr-mcu.git
|
||||
[submodule "lib/cmsis-svd"]
|
||||
path = lib/cmsis-svd
|
||||
url = https://github.com/tinygo-org/cmsis-svd
|
||||
[submodule "lib/compiler-rt"]
|
||||
path = lib/compiler-rt
|
||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||
branch = release_80
|
||||
url = https://github.com/cmsis-svd/cmsis-svd-data.git
|
||||
branch = main
|
||||
[submodule "lib/wasi-libc"]
|
||||
path = lib/wasi-libc
|
||||
url = https://github.com/CraneStation/wasi-libc
|
||||
url = https://github.com/WebAssembly/wasi-libc
|
||||
[submodule "lib/picolibc"]
|
||||
path = lib/picolibc
|
||||
url = https://github.com/keith-packard/picolibc.git
|
||||
@@ -32,3 +29,13 @@
|
||||
[submodule "lib/mingw-w64"]
|
||||
path = lib/mingw-w64
|
||||
url = https://github.com/mingw-w64/mingw-w64.git
|
||||
[submodule "lib/macos-minimal-sdk"]
|
||||
path = lib/macos-minimal-sdk
|
||||
url = https://github.com/aykevl/macos-minimal-sdk.git
|
||||
[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
|
||||
|
||||
+17
-1
@@ -18,7 +18,8 @@ tarball. If you want to help with development of TinyGo itself, you should follo
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.15+)
|
||||
* Go (1.19+)
|
||||
* GNU Make
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
@@ -27,6 +28,21 @@ build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
The rest of this guide assumes you're running Linux, but it should be equivalent
|
||||
on a different system like Mac.
|
||||
|
||||
## Using GNU Make
|
||||
|
||||
The static build of TinyGo is driven by GNUmakefile, which provides a help target for quick reference:
|
||||
|
||||
% make help
|
||||
clean Remove build directory
|
||||
fmt Reformat source
|
||||
fmt-check Warn if any source needs reformatting
|
||||
gen-device Generate microcontroller-specific sources
|
||||
llvm-source Get LLVM sources
|
||||
llvm-build Build LLVM
|
||||
tinygo Build the TinyGo compiler
|
||||
lint Lint source tree
|
||||
spell Spellcheck source tree
|
||||
|
||||
## Download the source
|
||||
|
||||
The first step is to download the TinyGo sources (use `--recursive` if you clone
|
||||
|
||||
+890
-4
@@ -1,3 +1,889 @@
|
||||
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
|
||||
---
|
||||
|
||||
* **general**
|
||||
- fix wasi-libc include headers on Nix
|
||||
- apply OpenOCD commands after target configuration
|
||||
- fix a minor race condition when determining the build tags
|
||||
- support UF2 drives with a space in their name on Linux
|
||||
- add LLVM 18 support
|
||||
- drop support for Go 1.18 to be able to stay up to date
|
||||
|
||||
* **compiler**
|
||||
- move `-panic=trap` support to the compiler/runtime
|
||||
- fix symbol table index for WebAssembly archives
|
||||
- fix ed25519 build errors by adjusting the alias names
|
||||
- add aliases to generic AES functions
|
||||
- fix race condition by temporarily applying a proposed patch
|
||||
- `builder`: keep un-wasm-opt'd .wasm if -work was passed
|
||||
- `builder`: make sure wasm-opt command line is printed if asked
|
||||
- `cgo`: implement shift operations in preprocessor macros
|
||||
- `interp`: checking for methodset existence
|
||||
|
||||
* **standard library**
|
||||
- `machine`: add `__tinygo_spi_tx` function to simulator
|
||||
- `machine`: fix simulator I2C support
|
||||
- `machine`: add GetRNG support to simulator
|
||||
- `machine`: add `TxFifoFreeLevel` for CAN
|
||||
- `os`: add `Link`
|
||||
- `os`: add `FindProcess` for posix
|
||||
- `os`: add `Process.Release` for unix
|
||||
- `os`: add `SetReadDeadline` stub
|
||||
- `os`, `os/signal`: add signal stubs
|
||||
- `os/user`: add stubs for `Lookup{,Group}` and `Group`
|
||||
- `reflect`: use int in `StringHeader` and `SliceHeader` on non-AVR platforms
|
||||
- `reflect`: fix `NumMethods` for Interface type
|
||||
- `runtime`: skip negative sleep durations in sleepTicks
|
||||
|
||||
* **targets**
|
||||
- `esp32`: add I2C support
|
||||
- `rp2040`: move UART0 and UART1 to common file
|
||||
- `rp2040`: make all RP2040 boards available for simulation
|
||||
- `rp2040`: fix timeUnit type
|
||||
- `stm32`: add i2c `Frequency` and `SetBaudRate` function for chips that were missing implementation
|
||||
- `wasm-unknown`: add math and memory builtins that LLVM needs
|
||||
- `wasip1`: replace existing `-target=wasi` support with wasip1 as supported in Go 1.21+
|
||||
|
||||
* **boards**
|
||||
- `adafruit-esp32-feather-v2`: add the Adafruit ESP32 Feather V2
|
||||
- `badger2040-w`: add support for the Badger2040 W
|
||||
- `feather-nrf52840-sense`: fix lack of LXFO
|
||||
- `m5paper`: add support for the M5 Paper
|
||||
- `mksnanov3`: limit programming speed to 1800 kHz
|
||||
- `nucleol476rg`: add stm32 nucleol476rg support
|
||||
- `pico-w`: add the Pico W (which is near-idential to the pico target)
|
||||
- `thingplus-rp2040`, `waveshare-rp2040-zero`: add WS2812 definition
|
||||
- `pca10059-s140v7`: add this variant to the PCA10059 board
|
||||
|
||||
|
||||
0.31.2
|
||||
---
|
||||
|
||||
* **general**
|
||||
* update the `net` submodule to updated version with `Buffers` implementation
|
||||
|
||||
* **compiler**
|
||||
* `syscall`: add wasm_unknown tag to some additional files so it can compile more code
|
||||
|
||||
* **standard library**
|
||||
* `runtime`: add Frame.Entry field
|
||||
|
||||
|
||||
0.31.1
|
||||
---
|
||||
|
||||
* **general**
|
||||
* fix Binaryen build in make task
|
||||
* update final build stage of Docker `dev` image to go1.22
|
||||
* only use GHA cache for building Docker `dev` image
|
||||
* update the `net` submodule to latest version
|
||||
|
||||
* **compiler**
|
||||
* `interp`: make getelementptr offsets signed
|
||||
* `interp`: return a proper error message when indexing out of range
|
||||
|
||||
|
||||
0.31.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
* remove LLVM 14 support
|
||||
* add LLVM 17 support, and use it by default
|
||||
* add Nix flake support
|
||||
* update bundled Binaryen to version 116
|
||||
* add `ports` subcommand that lists available serial ports for `-port` and `-monitor`
|
||||
* support wasmtime version 14
|
||||
* add `-serial=rtt` for serial output over SWD
|
||||
* add Go 1.22 support and use it by default
|
||||
* change minimum Node.js version from 16 to 18
|
||||
* **compiler**
|
||||
* use the new LLVM pass manager
|
||||
* allow systems with more stack space to allocate larger values on the stack
|
||||
* `build`: fix a crash due to sharing GlobalValues between build instances
|
||||
* `cgo`: add `C._Bool` type
|
||||
* `cgo`: fix calling CGo callback inside generic function
|
||||
* `compileopts`: set `purego` build tag by default so that more packages can be built
|
||||
* `compileopts`: force-enable CGo to avoid build issues
|
||||
* `compiler`: fix crash on type assert on interfaces with no methods
|
||||
* `interp`: print LLVM instruction in traceback
|
||||
* `interp`: support runtime times by running them at runtime
|
||||
* `loader`: enforce Go language version in the type checker (this may break existing programs with an incorrect Go version in go.mod)
|
||||
* `transform`: fix bug in StringToBytes optimization pass
|
||||
* **standard library**
|
||||
* `crypto/tls`: stub out a lot of functions
|
||||
* `internal/task`, `machine`: make TinyGo code usable with "big Go" CGo
|
||||
* `machine`: implement `I2C.SetBaudRate` consistently across chips
|
||||
* `machine`: implement `SPI.Configure` consistently across chips
|
||||
* `machine`: add `DeviceID` for nrf, rp2040, sam, stm32
|
||||
* `machine`: use smaller UART buffer size on atmega chips
|
||||
* `machine/usb`: allow setting a serial number using a linker flag
|
||||
* `math`: support more math functions on baremetal (picolibc) systems
|
||||
* `net`: replace entire net package with a new one based on the netdev driver
|
||||
* `os/user`: add bare-bones implementation of this package
|
||||
* `reflect`: stub `CallSlice` and `FuncOf`
|
||||
* `reflect`: add `TypeFor[T]`
|
||||
* `reflect`: update `IsZero` to Go 1.22 semantics
|
||||
* `reflect`: move indirect values into interface when setting interfaces
|
||||
* `runtime`: stub `Breakpoint`
|
||||
* `sync`: implement trylock
|
||||
* **targets**
|
||||
* `atmega`: use UART double speed mode for fewer errors and higher throughput
|
||||
* `atmega328pb`: refactor to enable extra uart
|
||||
* `avr`: don't compile large parts of picolibc (math, stdio) for LLVM 17 support
|
||||
* `esp32`: switch over to the official SVD file
|
||||
* `esp32c3`: implement USB_SERIAL for USBCDC communication
|
||||
* `esp32c3`: implement I2C
|
||||
* `esp32c3`: implement RNG
|
||||
* `esp32c3`: add more ROM functions and update linker script for the in-progress wifi support
|
||||
* `esp32c3`: update to newer SVD files
|
||||
* `rp2040`: add support for UART hardware flow control
|
||||
* `rp2040`: add definition for `machine.PinToggle`
|
||||
* `rp2040`: set XOSC startup delay multiplier
|
||||
* `samd21`: add support for UART hardware flow control
|
||||
* `samd51`: add support for UART hardware flow control
|
||||
* `wasm`: increase default stack size to 64k for wasi/wasm targets
|
||||
* `wasm`: bump wasi-libc version to SDK 20
|
||||
* `wasm`: remove line of dead code in wasm_exec.js
|
||||
* **new targets/boards**
|
||||
* `qtpy-esp32c3`: add Adafruit QT Py ESP32-C3 board
|
||||
* `mksnanov3`: add support for the MKS Robin Nano V3.x
|
||||
* `nrf52840-generic`: add generic nrf52840 chip support
|
||||
* `thumby`: add support for Thumby
|
||||
* `wasm`: add new `wasm-unknown` target that doesn't depend on WASI or a browser
|
||||
* **boards**
|
||||
* `arduino-mkrwifi1010`, `arduino-nano33`, `nano-rp2040`, `matrixportal-m4`, `metro-m4-airlift`, `pybadge`, `pyportal`: add `ninafw` build tag and some constants for BLE support
|
||||
* `gopher-badge`: fix typo in USB product name
|
||||
* `nano-rp2040`: add UART1 and correct mappings for NINA via UART
|
||||
* `pico`: bump default stack size from 2kB to 8kB
|
||||
* `wioterminal`: expose UART4
|
||||
|
||||
|
||||
0.30.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- add LLVM 16 support, use it by default
|
||||
* **compiler**
|
||||
- `build`: work around a race condition by building Go SSA serially
|
||||
- `compiler`: fix a crash by not using the LLVM global context types
|
||||
- `interp`: don't copy unknown values in `runtime.sliceCopy` to fix miscompile
|
||||
- `interp`: fix crash in error report by not returning raw LLVM values
|
||||
* **standard library**
|
||||
- `machine/usb/adc/midi`: various improvements and API changes
|
||||
- `reflect`: add support for `[...]T` → `[]T` in reflect
|
||||
* **targets**
|
||||
- `atsamd21`, `atsamd51`: add support for USB INTERRUPT OUT
|
||||
- `rp2040`: always use the USB device enumeration fix, even in chips that supposedly have the HW fix
|
||||
- `wasm`: increase default stack size to 32k for wasi/wasm
|
||||
* **boards**
|
||||
- `gobadge`: add GoBadge target as alias for PyBadge :)
|
||||
- `gemma-m0`: add support for the Adafruit Gemma M0
|
||||
|
||||
|
||||
0.29.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- Go 1.21 support
|
||||
- use https for renesas submodule #3856
|
||||
- ci: rename release-double-zipped to something more useful
|
||||
- ci: update Node.js from version 14 to version 16
|
||||
- ci: switch GH actions builds to use Go 1.21 final release
|
||||
- docker: update clang to version 15
|
||||
- docker: use Go 1.21 for Docker dev container build
|
||||
- `main`: add target JSON file in `tinygo info` output
|
||||
- `main`: improve detection of filesystems
|
||||
- `main`: use `go env` instead of doing all detection manually
|
||||
- make: add make task to generate Renesas device wrappers
|
||||
- make: add task to check NodeJS version before running tests
|
||||
- add submodule for Renesas SVD file mirror repo
|
||||
- update to go-serial package v1.6.0
|
||||
- `testing`: add Testing function
|
||||
- `tools/gen-device-svd`: small changes needed for Renesas MCUs
|
||||
* **compiler**
|
||||
- `builder`: update message for max supported Go version
|
||||
- `compiler,reflect`: NumMethods reports exported methods only
|
||||
- `compiler`: add compiler-rt and wasm symbols to table
|
||||
- `compiler`: add compiler-rt to wasm.json
|
||||
- `compiler`: add min and max builtin support
|
||||
- `compiler`: implement clear builtin for maps
|
||||
- `compiler`: implement clear builtin for slices
|
||||
- `compiler`: improve panic message when a runtime call is unavailable
|
||||
- `compiler`: update .ll test output
|
||||
- `loader`: merge go.env file which is now required starting in Go 1.21 to correctly get required packages
|
||||
* **standard library**
|
||||
- `os`: define ErrNoDeadline
|
||||
- `reflect`: Add FieldByNameFunc
|
||||
- `reflect`: add SetZero
|
||||
- `reflect`: fix iterating over maps with interface{} keys
|
||||
- `reflect`: implement Value.Grow
|
||||
- `reflect`: remove unnecessary heap allocations
|
||||
- `reflect`: use .key() instead of a type assert
|
||||
- `sync`: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
|
||||
* **targets**
|
||||
- `machine`: UART refactor (#3832)
|
||||
- `machine/avr`: pin change interrupt
|
||||
- `machine/macropad_rp2040`: add machine.BUTTON
|
||||
- `machine/nrf`: add I2C timeout
|
||||
- `machine/nrf`: wait for stop condition after reading from the I2C bus
|
||||
- `machine/nRF52`: set SPI TX/RX lengths even data is empty. Fixes #3868 (#3877)
|
||||
- `machine/rp2040`: add missing suffix to CMD_READ_STATUS
|
||||
- `machine/rp2040`: add NoPin support
|
||||
- `machine/rp2040`: move flash related functions into separate file from C imports for correct - LSP. Fixes #3852
|
||||
- `machine/rp2040`: wait for 1000 us after flash reset to avoid issues with busy USB bus
|
||||
- `machine/samd51,rp2040,nrf528xx,stm32`: implement watchdog
|
||||
- `machine/samd51`: fix i2cTimeout was decreasing due to cache activation
|
||||
- `machine/usb`: Add support for HID Keyboard LEDs
|
||||
- `machine/usb`: allow USB Endpoint settings to be changed externally
|
||||
- `machine/usb`: refactor endpoint configuration
|
||||
- `machine/usb`: remove usbDescriptorConfig
|
||||
- `machine/usb/hid,joystick`: fix hidreport (3) (#3802)
|
||||
- `machine/usb/hid`: add RxHandler interface
|
||||
- `machine/usb/hid`: rename Handler() to TxHandler()
|
||||
- `wasi`: allow zero inodes when reading directories
|
||||
- `wasm`: add support for GOOS=wasip1
|
||||
- `wasm`: fix functions exported through //export
|
||||
- `wasm`: remove i64 workaround, use BigInt instead
|
||||
- `example`: adjust time offset
|
||||
- `example`: simplify pininterrupt
|
||||
* **boards**
|
||||
- `targets`: add AKIZUKI DENSHI AE-RP2040
|
||||
- `targets`: adding new uf2 target for PCA10056 (#3765)
|
||||
|
||||
|
||||
0.28.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- fix parallelism in the compiler on Windows by building LLVM with thread support
|
||||
- support qemu-user debugging
|
||||
- make target JSON msd-volume-name an array
|
||||
- print source location when a panic happens in -monitor
|
||||
- `test`: don't print `ok` for a successful compile-only
|
||||
* **compiler**
|
||||
- `builder`: remove non-ThinLTO build mode
|
||||
- `builder`: fail earlier if Go is not available
|
||||
- `builder`: improve `-size=full` in a number of ways
|
||||
- `builder`: implement Nordic DFU file writer in Go
|
||||
- `cgo`: allow `LDFLAGS: --export=...`
|
||||
- `compiler`: support recursive slice types
|
||||
- `compiler`: zero struct padding during map operations
|
||||
- `compiler`: add llvm.ident metadata
|
||||
- `compiler`: remove `unsafe.Pointer(uintptr(v) + idx)` optimization (use `unsafe.Add` instead)
|
||||
- `compiler`: add debug info to `//go:embed` data structures for better `-size` output
|
||||
- `compiler`: add debug info to string constants
|
||||
- `compiler`: fix a minor race condition
|
||||
- `compiler`: emit correct alignment in debug info for global variables
|
||||
- `compiler`: correctly generate reflect data for local named types
|
||||
- `compiler`: add alloc attributes to `runtime.alloc`, reducing flash usage slightly
|
||||
- `compiler`: for interface maps, use the original named type if available
|
||||
- `compiler`: implement most math/bits functions as LLVM intrinsics
|
||||
- `compiler`: ensure all defers have been seen before creating rundefers
|
||||
* **standard library**
|
||||
- `internal/task`: disallow blocking inside an interrupt
|
||||
- `machine`: add `CPUReset`
|
||||
- `machine/usb/hid`: add MediaKey support
|
||||
- `machine/usb/hid/joystick`: move joystick under HID
|
||||
- `machine/usb/hid/joystick`: allow joystick settings override
|
||||
- `machine/usb/hid/joystick`: handle case where we cannot find the correct HID descriptor
|
||||
- `machine/usb/hid/mouse`: add support for mouse back and forward
|
||||
- `machine/usb`: add ability to override default VID, PID, manufacturer name, and product name
|
||||
- `net`: added missing `TCPAddr` and `UDPAddr` implementations
|
||||
- `os`: add IsTimeout function
|
||||
- `os`: fix resource leak in `(*File).Close`
|
||||
- `os`: add `(*File).Sync`
|
||||
- `os`: implement `(*File).ReadDir` for wasi
|
||||
- `os`: implement `(*File).WriteAt`
|
||||
- `reflect`: make sure null bytes are supported in tags
|
||||
- `reflect`: refactor this package to enable many new features
|
||||
- `reflect`: add map type methods: `Elem` and `Key`
|
||||
- `reflect`: add map methods: `MapIndex`, `MapRange`/`MapIter`, `SetMapIndex`, `MakeMap`, `MapKeys`
|
||||
- `reflect`: add slice methods: `Append`, `MakeSlice`, `Slice`, `Slice3`, `Copy`, `Bytes`, `SetLen`
|
||||
- `reflect`: add misc methods: `Zero`, `Addr`, `UnsafeAddr`, `OverflowFloat`, `OverflowInt`, `OverflowUint`, `SetBytes`, `Convert`, `CanInt`, `CanFloat`, `CanComplex`, `Comparable`
|
||||
- `reflect`: add type methods: `String`, `PkgPath`, `FieldByName`, `FieldByIndex`, `NumMethod`
|
||||
- `reflect`: add stubs for `Type.Method`, `CanConvert`, `ArrayOf`, `StructOf`, `MapOf`
|
||||
- `reflect`: add stubs for channel select routines/types
|
||||
- `reflect`: allow nil rawType to call Kind()
|
||||
- `reflect`: ensure all ValueError panics have Kind fields
|
||||
- `reflect`: add support for named types
|
||||
- `reflect`: improve `Value.String()`
|
||||
- `reflect`: set `Index` and `PkgPath` field in `Type.Field`
|
||||
- `reflect`: `Type.AssignableTo`: you can assign anything to `interface{}`
|
||||
- `reflect`: add type check to `Value.Field`
|
||||
- `reflect`: let `TypeOf(nil)` return nil
|
||||
- `reflect`: move `StructField.Anonymous` field to match upstream location
|
||||
- `reflect`: add `UnsafePointer` for Func types
|
||||
- `reflect`: `MapIter.Next` needs to allocate new keys/values every time
|
||||
- `reflect`: fix `IsNil` for interfaces
|
||||
- `reflect`: fix `Type.Name` to return an empty string for non-named types
|
||||
- `reflect`: add `VisibleFields`
|
||||
- `reflect`: properly handle embedded structs
|
||||
- `reflect`: make sure `PointerTo` works for named types
|
||||
- `reflect`: `Set`: convert non-interface to interface
|
||||
- `reflect`: `Set`: fix direction of assignment check
|
||||
- `reflect`: support channel directions
|
||||
- `reflect`: print struct tags in Type.String()
|
||||
- `reflect`: properly handle read-only values
|
||||
- `runtime`: allow custom-gc SetFinalizer and clarify KeepAlive
|
||||
- `runtime`: implement KeepAlive using inline assembly
|
||||
- `runtime`: check for heap allocations inside interrupts
|
||||
- `runtime`: properly turn pointer into empty interface when hashing
|
||||
- `runtime`: improve map size hint usage
|
||||
- `runtime`: zero map key/value on deletion to so GC doesn't see them
|
||||
- `runtime`: print the address where a panic happened
|
||||
- `runtime/debug`: stub `SetGCPercent`, `BuildInfo.Settings`
|
||||
- `runtime/metrics`: add this package as a stub
|
||||
- `syscall`: `Stat_t` timespec fields are Atimespec on darwin
|
||||
- `syscall`: add `Timespec.Unix()` for wasi
|
||||
- `syscall`: add fsync using libc
|
||||
- `testing`: support -test.count
|
||||
- `testing`: make test output unbuffered when verbose
|
||||
- `testing`: add -test.skip
|
||||
- `testing`: move runtime.GC() call to runN to match upstream
|
||||
- `testing`: add -test.shuffle to order randomize test and benchmark order
|
||||
* **targets**
|
||||
- `arm64`: fix register save/restore to include vector registers
|
||||
- `attiny1616`: add support for this chip
|
||||
- `cortexm`: refactor EnableInterrupts and DisableInterrupts to avoid `arm.AsmFull`
|
||||
- `cortexm`: enable functions in RAM for go & cgo
|
||||
- `cortexm`: convert SystemStack from `AsmFull` to C inline assembly
|
||||
- `cortexm`: fix crash due to wrong stack size offset
|
||||
- `nrf`: samd21, stm32: add flash API
|
||||
- `nrf`: fix memory issue in ADC read
|
||||
- `nrf`: new peripheral type for nrf528xx chips
|
||||
- `nrf`: implement target mode
|
||||
- `nrf`: improve ADC and add oversampling, longer sample time, and reference voltage
|
||||
- `rp2040`: change calling order for device enumeration fix to do first
|
||||
- `rp2040`: rtc delayed interrupt
|
||||
- `rp2040`: provide better errors for invalid pins on I2C and SPI
|
||||
- `rp2040`: change uart to allow for a single pin
|
||||
- `rp2040`: implement Flash interface
|
||||
- `rp2040`: remove SPI `DataBits` property
|
||||
- `rp2040`: unify all linker scripts using LDFLAGS
|
||||
- `rp2040`: remove SPI deadline for improved performance
|
||||
- `rp2040`: use 4MHz as default frequency for SPI
|
||||
- `rp2040`: implement target mode
|
||||
- `rp2040`: use DMA for send-only SPI transfers
|
||||
- `samd21`: rearrange switch case for get pin cfg
|
||||
- `samd21`: fix issue with WS2812 driver by making pin accesses faster
|
||||
- `samd51`: enable CMCC cache for greatly improved performance
|
||||
- `samd51`: remove extra BK0RDY clear
|
||||
- `samd51`: implement Flash interface
|
||||
- `samd51`: use correct SPI frequency
|
||||
- `samd51`: remove extra BK0RDY clear
|
||||
- `samd51`: fix ADC multisampling
|
||||
- `wasi`: allow users to set the `runtime_memhash_tsip` or `runtime_memhash_fnv` build tags
|
||||
- `wasi`: set `WASMTIME_BACKTRACE_DETAILS` when running in wasmtime.
|
||||
- `wasm`: implement the `//go:wasmimport` directive
|
||||
* **boards**
|
||||
- `gameboy-advance`: switch to use register definitions in device/gba
|
||||
- `gameboy-advance`: rename display and make pointer receivers
|
||||
- `gopher-badge`: Added Gopher Badge support
|
||||
- `lorae5`: add needed definition for UART2
|
||||
- `lorae5`: correct mapping for I2C bus, add pin mapping to enable power
|
||||
- `pinetime`: update the target file (rename from pinetime-devkit0)
|
||||
- `qtpy`: fix bad pin assignment
|
||||
- `wioterminal`: fix pin definition of BCM13
|
||||
- `xiao`: Pins D4 & D5 are I2C1. Use pins D2 & D3 for I2C0.
|
||||
- `xiao`: add DefaultUART
|
||||
|
||||
|
||||
0.27.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- all: update musl
|
||||
- all: remove "acm:"` prefix for USB vid/pid pair
|
||||
- all: add support for LLVM 15
|
||||
- all: use DWARF version 4
|
||||
- all: add initial (incomplete) support for Go 1.20
|
||||
- all: add `-gc=custom` option
|
||||
- `main`: print ldflags including ThinLTO flags with -x
|
||||
- `main`: fix error message when a serial port can't be accessed
|
||||
- `main`: add `-timeout` flag to allow setting how long TinyGo will try looking for a MSD volume for flashing
|
||||
- `test`: print PASS on pass when running standalone test binaries
|
||||
- `test`: fix printing of benchmark output
|
||||
- `test`: print package name when compilation failed (not just when the test failed)
|
||||
* **compiler**
|
||||
- refactor to support LLVM 15
|
||||
- `builder`: print compiler commands while building a library
|
||||
- `compiler`: fix stack overflow when creating recursive pointer types (fix for LLVM 15+ only)
|
||||
- `compiler`: allow map keys and values of ≥256 bytes
|
||||
- `cgo`: add support for `C.float` and `C.double`
|
||||
- `cgo`: support anonymous enums included in multiple Go files
|
||||
- `cgo`: add support for bitwise operators
|
||||
- `interp`: add support for constant icmp instructions
|
||||
- `transform`: fix memory corruption issues
|
||||
* **standard library**
|
||||
- `machine/usb`: remove allocs in USB ISR
|
||||
- `machine/usb`: add `Port()` and deprecate `New()` to have the API better match the singleton that is actually being returned
|
||||
- `machine/usb`: change HID usage-maximum to 0xFF
|
||||
- `machine/usb`: add USB HID joystick support
|
||||
- `machine/usb`: change to not send before endpoint initialization
|
||||
- `net`: implement `Pipe`
|
||||
- `os`: add stub for `os.Chtimes`
|
||||
- `reflect`: stub out `Type.FieldByIndex`
|
||||
- `reflect`: add `Value.IsZero` method
|
||||
- `reflect`: fix bug in `.Field` method when the field fits in a pointer but the parent doesn't
|
||||
- `runtime`: switch some `panic()` calls in the gc to `runtimePanic()` for consistency
|
||||
- `runtime`: add xorshift-based fastrand64
|
||||
- `runtime`: fix alignment for arm64, arm, xtensa, riscv
|
||||
- `runtime`: implement precise GC
|
||||
- `runtime/debug`: stub `PrintStack`
|
||||
- `sync`: implement simple pooling in `sync.Pool`
|
||||
- `syscall`: stubbed `Setuid`, Exec and friends
|
||||
- `syscall`: add more stubs as needed for Go 1.20 support
|
||||
- `testing`: implement `t.Setenv`
|
||||
- `unsafe`: add support for Go 1.20 slice/string functions
|
||||
* **targets**
|
||||
- `all`: do not set stack size per board
|
||||
- `all`: update picolibc to v1.7.9
|
||||
- `atsame5x`: fix CAN extendedID handling
|
||||
- `atsame5x`: reduce heap allocation
|
||||
- `avr`: drop GNU toolchain dependency
|
||||
- `avr`: fix .data initialization for binaries over 64kB
|
||||
- `avr`: support ThinLTO
|
||||
- `baremetal`: implements calloc
|
||||
- `darwin`: fix `syscall.Open` on darwin/arm64
|
||||
- `darwin`: fix error with `tinygo lldb`
|
||||
- `esp`: use LLVM Xtensa linker instead of Espressif toolchain
|
||||
- `esp`: use ThinLTO for Xtensa
|
||||
- `esp32c3`: add SPI support
|
||||
- `linux`: include musl `getpagesize` function in release
|
||||
- `nrf51`: add ADC implementation
|
||||
- `nrf52840`: add PDM support
|
||||
- `riscv`: add "target-abi" metadata flag
|
||||
- `rp2040`: remove mem allocation in GPIO ISR
|
||||
- `rp2040`: avoid allocating clock on heap
|
||||
- `rp2040`: add basic GPIO support for PIO
|
||||
- `rp2040`: fix USB interrupt issue
|
||||
- `rp2040`: fix RP2040-E5 USB errata
|
||||
- `stm32`: always set ADC pins to pullups floating
|
||||
- `stm32f1`, `stm32f4`: fix ADC by clearing the correct bit for rank after each read
|
||||
- `stm32wl`: Fix incomplete RNG initialisation
|
||||
- `stm32wlx`: change order for init so clock speeds are set before peripheral start
|
||||
- `wasi`: makes wasmtime "run" explicit
|
||||
- `wasm`: fix GC scanning of allocas
|
||||
- `wasm`: allow custom malloc implementation
|
||||
- `wasm`: remove `-wasm-abi=` flag (use `-target` instead)
|
||||
- `wasm`: fix scanning of the stack
|
||||
- `wasm`: fix panic when allocating 0 bytes using malloc
|
||||
- `wasm`: always run wasm-opt even with `-scheduler=none`
|
||||
- `wasm`: avoid miscompile with ThinLTO
|
||||
- `wasm`: allow the emulator to expand `{tmpDir}`
|
||||
- `wasm`: support ThinLTO
|
||||
- `windows`: update mingw-w64 version to avoid linker warning
|
||||
- `windows`: add ARM64 support
|
||||
* **boards**
|
||||
- Add Waveshare RP2040 Zero
|
||||
- Add Arduino Leonardo support
|
||||
- Add Adafruit KB2040
|
||||
- Add Adafruit Feather M0 Express
|
||||
- Add Makerfabs ESP32C3SPI35 TFT Touchscreen board
|
||||
- Add Espressif ESP32-C3-DevKit-RUST-1 board
|
||||
- `lgt92`: fix OpenOCD configuration
|
||||
- `xiao-rp2040`: fix D9 and D10 constants
|
||||
- `xiao-rp2040`: add pin definitions
|
||||
|
||||
0.26.0
|
||||
---
|
||||
|
||||
* **general**
|
||||
- remove support for LLVM 13
|
||||
- remove calls to deprecated ioutil package
|
||||
- move from `os.IsFoo` to `errors.Is(err, ErrFoo)`
|
||||
- fix for builds using an Android host
|
||||
- make interp timeout configurable from command line
|
||||
- ignore ports with VID/PID if there is no candidates
|
||||
- drop support for Go 1.16 and Go 1.17
|
||||
- update serial package to v1.3.5 for latest bugfixes
|
||||
- remove GOARM from `tinygo info`
|
||||
- add flag for setting the goroutine stack size
|
||||
- add serial port monitoring functionality
|
||||
* **compiler**
|
||||
- `cgo`: implement support for static functions
|
||||
- `cgo`: fix panic when FuncType.Results is nil
|
||||
- `compiler`: add aliases for `edwards25519/field.feMul` and `field.feSquare`
|
||||
- `compiler`: fix incorrect DWARF type in some generic parameters
|
||||
- `compiler`: use LLVM math builtins everywhere
|
||||
- `compiler`: replace some math operation bodies with LLVM intrinsics
|
||||
- `compiler`: replace math aliases with intrinsics
|
||||
- `compiler`: fix `unsafe.Sizeof` for chan and map values
|
||||
- `compileopts`: use tags parser from buildutil
|
||||
- `compileopts`: use backticks for regexp to avoid extra escapes
|
||||
- `compileopts`: fail fast on duplicate values in target field slices
|
||||
- `compileopts`: fix windows/arm target triple
|
||||
- `compileopts`: improve error handling when loading target/*.json
|
||||
- `compileopts`: add support for stlink-dap programmer
|
||||
- `compileopts`: do not complain about `-no-debug` on MacOS
|
||||
- `goenv`: support `GOOS=android`
|
||||
- `interp`: fix reading from external global
|
||||
- `loader`: fix link error for `crypto/internal/boring/sig.StandardCrypto`
|
||||
* **standard library**
|
||||
- rename assembly files to .S extension
|
||||
- `machine`: add PWM peripheral comments to pins
|
||||
- `machine`: improve UARTParity slightly
|
||||
- `machine`: do not export DFU_MAGIC_* constants on nrf52840
|
||||
- `machine`: rename `PinInputPullUp`/`PinInputPullDown`
|
||||
- `machine`: add `KHz`, `MHz`, `GHz` constants, deprecate `TWI_FREQ_*` constants
|
||||
- `machine`: remove level triggered pin interrupts
|
||||
- `machine`: do not expose `RESET_MAGIC_VALUE`
|
||||
- `machine`: use `NoPin` constant where appropriate (instead of `0` for example)
|
||||
- `net`: sync net.go with Go 1.18 stdlib
|
||||
- `os`: add `SyscallError.Timeout`
|
||||
- `os`: add `ErrProcessDone` error
|
||||
- `reflect`: implement `CanInterface` and fix string `Index`
|
||||
- `runtime`: make `MemStats` available to leaking collector
|
||||
- `runtime`: add `MemStats.TotalAlloc`
|
||||
- `runtime`: add `MemStats.Mallocs` and `Frees`
|
||||
- `runtime`: add support for `time.NewTimer` and `time.NewTicker`
|
||||
- `runtime`: implement `resetTimer`
|
||||
- `runtime`: ensure some headroom for the GC to run
|
||||
- `runtime`: make gc and scheduler asserts settable with build tags
|
||||
- `runtime/pprof`: add `WriteHeapProfile`
|
||||
- `runtime/pprof`: `runtime/trace`: stub some additional functions
|
||||
- `sync`: implement `Map.LoadAndDelete`
|
||||
- `syscall`: group WASI consts by purpose
|
||||
- `syscall`: add WASI `{D,R}SYNC`, `NONBLOCK` FD flags
|
||||
- `syscall`: add ENOTCONN on darwin
|
||||
- `testing`: add support for -benchmem
|
||||
* **targets**
|
||||
- remove USB vid/pid pair of bootloader
|
||||
- `esp32c3`: remove unused `UARTStopBits` constants
|
||||
- `nrf`: implement `GetRNG` function
|
||||
- `nrf`: `rp2040`: add `machine.ReadTemperature`
|
||||
- `nrf52`: cleanup s140v6 and s140v7 uf2 targets
|
||||
- `rp2040`: implement semi-random RNG based on ROSC based on pico-sdk
|
||||
- `wasm`: add summary of wasm examples and fix callback bug
|
||||
- `wasm`: do not allow undefined symbols (`--allow-undefined`)
|
||||
- `wasm`: make sure buffers returned by `malloc` are kept until `free` is called
|
||||
- `windows`: save and restore xmm registers when switching goroutines
|
||||
* **boards**
|
||||
- add Pimoroni's Tufty2040
|
||||
- add XIAO ESP32C3
|
||||
- add Adafruit QT2040
|
||||
- add Adafruit QT Py RP2040
|
||||
- `esp32c3-12f`: `matrixportal-m4`: `p1am-100`: remove duplicate build tags
|
||||
- `hifive1-qemu`: remove this emulated board
|
||||
- `wioterminal`: add UART3 for RTL8720DN
|
||||
- `xiao-ble`: fix usbpid
|
||||
|
||||
|
||||
0.25.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- change to ignore PortReset failures
|
||||
* **compiler**
|
||||
- `compiler`: darwin/arm64 is aarch64, not arm
|
||||
- `compiler`: don't clobber X18 and FP registers on darwin/arm64
|
||||
- `compiler`: fix issue with methods on generic structs
|
||||
- `compiler`: do not try to build generic functions
|
||||
- `compiler`: fix type names for generic named structs
|
||||
- `compiler`: fix multiple defined function issue for generic functions
|
||||
- `compiler`: implement `unsafe.Alignof` and `unsafe.Sizeof` for generic code
|
||||
* **standard library**
|
||||
- `machine`: add DTR and RTS to Serialer interface
|
||||
- `machine`: reorder pin definitions to improve pin list on tinygo.org
|
||||
- `machine/usb`: add support for MIDI
|
||||
- `machine/usb`: adjust buffer alignment (samd21, samd51, nrf52840)
|
||||
- `machine/usb/midi`: add `NoteOn`, `NoteOff`, and `SendCC` methods
|
||||
- `machine/usb/midi`: add definition of MIDI note number
|
||||
- `runtime`: add benchmarks for memhash
|
||||
- `runtime`: add support for printing slices via print/println
|
||||
* **targets**
|
||||
- `avr`: fix some apparent mistake in atmega1280/atmega2560 pin constants
|
||||
- `esp32`: provide hardware pin constants
|
||||
- `esp32`: fix WDT reset on the MCH2022 badge
|
||||
- `esp32`: optimize SPI transmit
|
||||
- `esp32c3`: provide hardware pin constants
|
||||
- `esp8266`: provide hardware pin constants like `GPIO2`
|
||||
- `nrf51`: define and use `P0_xx` constants
|
||||
- `nrf52840`, `samd21`, `samd51`: unify bootloader entry process
|
||||
- `nrf52840`, `samd21`, `samd51`: change usbSetup and sendZlp to public
|
||||
- `nrf52840`, `samd21`, `samd51`: refactor handleStandardSetup and initEndpoint
|
||||
- `nrf52840`, `samd21`, `samd51`: improve usb-device initialization
|
||||
- `nrf52840`, `samd21`, `samd51`: move usbcdc to machine/usb/cdc
|
||||
- `rp2040`: add usb serial vendor/product ID
|
||||
- `rp2040`: add support for usb
|
||||
- `rp2040`: change default for serial to usb
|
||||
- `rp2040`: add support for `machine.EnterBootloader`
|
||||
- `rp2040`: turn off pullup/down when input type is not specified
|
||||
- `rp2040`: make picoprobe default openocd interface
|
||||
- `samd51`: add support for `DAC1`
|
||||
- `samd51`: improve TRNG
|
||||
- `wasm`: stub `runtime.buffered`, `runtime.getchar`
|
||||
- `wasi`: make leveldb runtime hash the default
|
||||
* **boards**
|
||||
- add Challenger RP2040 LoRa
|
||||
- add MCH2022 badge
|
||||
- add XIAO RP2040
|
||||
- `clue`: remove pins `D21`..`D28`
|
||||
- `feather-rp2040`, `macropad-rp2040`: fix qspi-flash settings
|
||||
- `xiao-ble`: add support for flash-1200-bps-reset
|
||||
- `gopherbot`, `gopherbot2`: add these aliases to simplify for newer users
|
||||
|
||||
|
||||
0.24.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- remove support for go 1.15
|
||||
- remove support for LLVM 11 and LLVM 12
|
||||
- add initial Go 1.19 beta support
|
||||
- `test`: fix package/... syntax
|
||||
* **compiler**
|
||||
- add support for the embed package
|
||||
- `builder`: improve error message for "command not found"
|
||||
- `builder`: add support for ThinLTO on MacOS and Windows
|
||||
- `builder`: free LLVM objects after use, to reduce memory leaking
|
||||
- `builder`: improve `-no-debug` error messages
|
||||
- `cgo`: be more strict: CGo now requires every Go file to import the headers it needs
|
||||
- `compiler`: alignof(func) is 1 pointer, not 2
|
||||
- `compiler`: add support for type parameters (aka generics)
|
||||
- `compiler`: implement `recover()` built-in function
|
||||
- `compiler`: support atomic, volatile, and LLVM memcpy-like functions in defer
|
||||
- `compiler`: drop support for macos syscalls via inline assembly
|
||||
- `interp`: do not try to interpret past task.Pause()
|
||||
- `interp`: fix some buggy localValue handling
|
||||
- `interp`: do not unroll loops
|
||||
- `transform`: fix MakeGCStackSlots that caused a possible GC bug on WebAssembly
|
||||
* **standard library**
|
||||
- `os`: enable os.Stdin for baremetal target
|
||||
- `reflect`: add `Value.UnsafePointer` method
|
||||
- `runtime`: scan GC globals conservatively on Windows, MacOS, Linux and Nintendo Switch
|
||||
- `runtime`: add per-map hash seeds
|
||||
- `runtime`: handle nil map write panics
|
||||
- `runtime`: add stronger hash functions
|
||||
- `syscall`: implement `Getpagesize`
|
||||
* **targets**
|
||||
- `atmega2560`: support UART1-3 + example for uart
|
||||
- `avr`: use compiler-rt for improved float64 support
|
||||
- `avr`: simplify timer-based time
|
||||
- `avr`: fix race condition in stack write
|
||||
- `darwin`: add support for `GOARCH=arm64` (aka Apple Silicon)
|
||||
- `darwin`: support `-size=short` and `-size=full` flag
|
||||
- `rp2040`: replace sleep 'busy loop' with timer alarm
|
||||
- `rp2040`: align api for `PortMaskSet`, `PortMaskClear`
|
||||
- `rp2040`: fix GPIO interrupts
|
||||
- `samd21`, `samd51`, `nrf52840`: add support for USBHID (keyboard / mouse)
|
||||
- `wasm`: update wasi-libc version
|
||||
- `wasm`: use newer WebAssembly features
|
||||
* **boards**
|
||||
- add Badger 2040
|
||||
- `matrixportal-m4`: attach USB DP to the correct pin
|
||||
- `teensy40`: add I2C support
|
||||
- `wioterminal`: fix I2C definition
|
||||
|
||||
|
||||
0.23.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- add `-work` flag
|
||||
- add Go 1.18 support
|
||||
- add LLVM 14 support
|
||||
- `run`: add support for command-line parameters
|
||||
- `build`: calculate default output path if `-o` is not specified
|
||||
- `build`: add JSON output
|
||||
- `test`: support multiple test binaries with `-c`
|
||||
- `test`: support flags like `-v` on all targets (including emulated firmware)
|
||||
* **compiler**
|
||||
- add support for ThinLTO
|
||||
- use compiler-rt from LLVM
|
||||
- `builder`: prefer GNU build ID over Go build ID for caching
|
||||
- `builder`: add support for cross compiling to Darwin
|
||||
- `builder`: support machine outlining pass in stacksize calculation
|
||||
- `builder`: disable asynchronous unwind tables
|
||||
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
|
||||
- `compiler`: move allocations > 256 bytes to the heap
|
||||
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
|
||||
- `compiler`: accept alias for slice `cap` builtin
|
||||
- `compiler`: allow slices of empty structs
|
||||
- `compiler`: fix difference in aliases in interface methods
|
||||
- `compiler`: make `RawSyscall` an alias for `Syscall`
|
||||
- `compiler`: remove support for memory references in `AsmFull`
|
||||
- `loader`: only add Clang header path for CGo
|
||||
- `transform`: fix poison value in heap-to-stack transform
|
||||
* **standard library**
|
||||
- `internal/fuzz`: add this package as a shim
|
||||
- `os`: implement readdir for darwin and linux
|
||||
- `os`: add `DirFS`, which is used by many programs to access readdir.
|
||||
- `os`: isWine: be compatible with older versions of wine, too
|
||||
- `os`: implement `RemoveAll`
|
||||
- `os`: Use a `uintptr` for `NewFile`
|
||||
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
|
||||
- `os`: export correct values for `DevNull` for each OS
|
||||
- `os`: improve support for `Signal` by fixing various bugs
|
||||
- `os`: implement `File.Fd` method
|
||||
- `os`: implement `UserHomeDir`
|
||||
- `os`: add `exec.ProcessState` stub
|
||||
- `os`: implement `Pipe` for darwin
|
||||
- `os`: define stub `ErrDeadlineExceeded`
|
||||
- `reflect`: add stubs for more missing methods
|
||||
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
|
||||
- `reflect`: add `Value.FieldByIndexErr` stub
|
||||
- `runtime`: fix various small GC bugs
|
||||
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
|
||||
- `runtime`: implement `memhash`
|
||||
- `runtime`: implement `fastrand`
|
||||
- `runtime`: add stub for `debug.ReadBuildInfo`
|
||||
- `runtime`: add stub for `NumCPU`
|
||||
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
|
||||
- `runtime`: add `Version`
|
||||
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
|
||||
- `runtime`: stub {Lock,Unlock}OSThread on Windows
|
||||
- `runtime`: be able to deal with a very small heap
|
||||
- `syscall`: make `Environ` return a copy of the environment
|
||||
- `syscall`: implement getpagesize and munmap
|
||||
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
|
||||
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
|
||||
- `syscall`: darwin: more complete list of signals
|
||||
- `syscall`: `wasi`: more complete list of signals
|
||||
- `syscall`: stub `WaitStatus`
|
||||
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
|
||||
- `testing`: implement `TempDir`
|
||||
- `testing`: nudge type TB closer to upstream; should be a no-op change.
|
||||
- `testing`: on baremetal platforms, use simpler test matcher
|
||||
* **targets**
|
||||
- `atsamd`: fix usbcdc initialization when `-serial=uart`
|
||||
- `atsamd51`: allow higher frequency when using SPI
|
||||
- `esp`: support CGo
|
||||
- `esp32c3`: add support for input pin
|
||||
- `esp32c3`: add support for GPIO interrupts
|
||||
- `esp32c3`: add support to receive UART data
|
||||
- `rp2040`: fix PWM bug at high frequency
|
||||
- `rp2040`: fix some minor I2C bugs
|
||||
- `rp2040`: fix incorrect inline assembly
|
||||
- `rp2040`: fix spurious i2c STOP during write+read transaction
|
||||
- `rp2040`: improve ADC support
|
||||
- `wasi`: remove `--export-dynamic` linker flag
|
||||
- `wasm`: remove heap allocator from wasi-libc
|
||||
* **boards**
|
||||
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
|
||||
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
|
||||
- `m5stamp-c3`: add pin setting of UART
|
||||
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
|
||||
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
|
||||
- `teensy41`: add the Teensy 4.1 board
|
||||
- `teensy40`: add ADC support
|
||||
- `teensy40`: add SPI support
|
||||
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
|
||||
- `wioterminal`: add DefaultUART
|
||||
- `wioterminal`: verify written data when flashing through OpenOCD
|
||||
- `xiao-ble`: add XIAO BLE nRF52840 support
|
||||
|
||||
|
||||
0.22.0
|
||||
---
|
||||
|
||||
@@ -30,7 +916,7 @@
|
||||
- `interp`: always run atomic and volatile loads/stores at runtime
|
||||
- `interp`: bump timeout to 180 seconds
|
||||
- `interp`: handle type assertions on nil interfaces
|
||||
- `loader`: elminate goroot cache inconsistency
|
||||
- `loader`: eliminate goroot cache inconsistency
|
||||
- `loader`: respect $GOROOT when running `go list`
|
||||
- `transform`: allocate the correct amount of bytes in an alloca
|
||||
- `transform`: remove switched func lowering
|
||||
@@ -729,7 +1615,7 @@
|
||||
- `sync`: add WaitGroup
|
||||
* **targets**
|
||||
- `arm`: allow nesting in DisableInterrupts and EnableInterrupts
|
||||
- `arm`: make FPU configuraton consistent
|
||||
- `arm`: make FPU configuration consistent
|
||||
- `arm`: do not mask fault handlers in critical sections
|
||||
- `atmega2560`: fix pin mapping for pins D2, D5 and the L port
|
||||
- `atsamd`: return an error when an incorrect PWM pin is used
|
||||
@@ -758,7 +1644,7 @@
|
||||
- `nrf`: add microbit-s110v8 target
|
||||
- `nrf`: fix bug in SPI.Tx
|
||||
- `nrf`: support debugging the PCA10056
|
||||
- `pygamer`: add Adafruit PyGamer suport
|
||||
- `pygamer`: add Adafruit PyGamer support
|
||||
- `riscv`: fix interrupt configuration bug
|
||||
- `riscv`: disable linker relaxations during gp init
|
||||
- `stm32f4disco`: add new target with ST-Link v2.1 debugger
|
||||
@@ -1120,7 +2006,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
|
||||
|
||||
+22
-29
@@ -1,10 +1,15 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.17 AS tinygo-llvm
|
||||
FROM golang:1.23 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
|
||||
apt-get install -y apt-utils make cmake clang-15 ninja-build && \
|
||||
rm -rf \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/log/* \
|
||||
/var/tmp/* \
|
||||
/tmp/*
|
||||
|
||||
COPY ./Makefile /tinygo/Makefile
|
||||
COPY ./GNUmakefile /tinygo/GNUmakefile
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make llvm-source
|
||||
@@ -15,36 +20,24 @@ FROM tinygo-llvm AS tinygo-llvm-build
|
||||
RUN cd /tinygo/ && \
|
||||
make llvm-build
|
||||
|
||||
# tinygo-xtensa stage installs tools needed for ESP32
|
||||
FROM tinygo-llvm-build AS tinygo-xtensa
|
||||
|
||||
ARG xtensa_version="1.22.0-80-g6c4433a-5.2.0"
|
||||
RUN cd /tmp/ && \
|
||||
wget -q https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-${xtensa_version}.tar.gz && \
|
||||
tar xzf xtensa-esp32-elf-linux64-${xtensa_version}.tar.gz && \
|
||||
cp ./xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/ && \
|
||||
rm -rf /tmp/xtensa*
|
||||
|
||||
# tinygo-compiler stage builds the compiler itself
|
||||
FROM tinygo-xtensa AS tinygo-compiler
|
||||
# tinygo-compiler-build stage builds the compiler itself
|
||||
FROM tinygo-llvm-build AS tinygo-compiler-build
|
||||
|
||||
COPY . /tinygo
|
||||
|
||||
# update submodules
|
||||
# build the compiler and tools
|
||||
RUN cd /tinygo/ && \
|
||||
rm -rf ./lib/*/ && \
|
||||
git submodule sync && \
|
||||
git submodule update --init --recursive --force
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make
|
||||
|
||||
# tinygo-tools stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
FROM tinygo-compiler AS tinygo-tools
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
make wasi-libc binaryen && \
|
||||
git submodule update --init && \
|
||||
make gen-device -j4 && \
|
||||
cp build/* $GOPATH/bin/
|
||||
make build/release
|
||||
|
||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||
# all the build tools etc).
|
||||
FROM golang:1.23 AS tinygo-compiler
|
||||
|
||||
# Copy tinygo build.
|
||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||
|
||||
# Configure the container.
|
||||
ENV PATH="${PATH}:/tinygo/bin"
|
||||
CMD ["tinygo"]
|
||||
|
||||
+469
-107
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
||||
|
||||
# Try to autodetect LLVM build tools.
|
||||
# Versions are listed here in descending priority order.
|
||||
LLVM_VERSIONS = 13 12 11
|
||||
LLVM_VERSIONS = 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)
|
||||
@@ -30,18 +30,22 @@ GO ?= go
|
||||
export GOROOT = $(shell $(GO) env GOROOT)
|
||||
|
||||
# Flags to pass to go test.
|
||||
GOTESTFLAGS ?= -v
|
||||
|
||||
# md5sum binary
|
||||
MD5SUM = md5sum
|
||||
GOTESTFLAGS ?=
|
||||
GOTESTPKGS ?= ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
|
||||
# Check for ccache if the user hasn't set it to on or off.
|
||||
ifeq (, $(CCACHE))
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
CCACHE := ON
|
||||
else
|
||||
CCACHE := OFF
|
||||
endif
|
||||
endif
|
||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||
|
||||
# Allow enabling LLVM assertions
|
||||
ifeq (1, $(ASSERT))
|
||||
@@ -50,19 +54,72 @@ else
|
||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||
endif
|
||||
|
||||
# Enable AddressSanitizer
|
||||
ifeq (1, $(ASAN))
|
||||
LLVM_OPTION += -DLLVM_USE_SANITIZER=Address
|
||||
CGO_LDFLAGS += -fsanitize=address
|
||||
endif
|
||||
|
||||
ifeq (1, $(STATIC))
|
||||
# Build TinyGo as a fully statically linked binary (no dynamically loaded
|
||||
# libraries such as a libc). This is not supported with glibc which is used
|
||||
# on most major Linux distributions. However, it is supported in Alpine
|
||||
# Linux with musl.
|
||||
CGO_LDFLAGS += -static
|
||||
# Also set the thread stack size to 1MB. This is necessary on musl as the
|
||||
# default stack size is 128kB and LLVM uses more than that.
|
||||
# For more information, see:
|
||||
# https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size
|
||||
CGO_LDFLAGS += -Wl,-z,stack-size=1048576
|
||||
# Build wasm-opt with static linking.
|
||||
# For details, see:
|
||||
# https://github.com/WebAssembly/binaryen/blob/version_102/.github/workflows/ci.yml#L181
|
||||
BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static"
|
||||
endif
|
||||
|
||||
# Cross compiling support.
|
||||
ifneq ($(CROSS),)
|
||||
CC = $(CROSS)-gcc
|
||||
CXX = $(CROSS)-g++
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_C_COMPILER=$(CC) \
|
||||
-DCMAKE_CXX_COMPILER=$(CXX) \
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
|
||||
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
|
||||
ifeq ($(CROSS), arm-linux-gnueabihf)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=ARM
|
||||
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else ifeq ($(CROSS), aarch64-linux-gnu)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=AArch64
|
||||
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else
|
||||
$(error Unknown cross compilation target: $(CROSS))
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine 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
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
|
||||
# LLVM compiled using MinGW on Windows appears to have problems with threads.
|
||||
# Without this flag, linking results in errors like these:
|
||||
# libLLVMSupport.a(Threading.cpp.obj):Threading.cpp:(.text+0x55): undefined reference to `std::thread::hardware_concurrency()'
|
||||
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF -DLLVM_ENABLE_PIC=OFF
|
||||
# PIC needs to be disabled for libclang to work.
|
||||
LLVM_OPTION += -DLLVM_ENABLE_PIC=OFF
|
||||
|
||||
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
@@ -71,12 +128,14 @@ ifeq ($(OS),Windows_NT)
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
MD5SUM ?= md5
|
||||
|
||||
CGO_LDFLAGS += -lxar
|
||||
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
MD5SUM = md5
|
||||
MD5SUM ?= md5
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
else
|
||||
@@ -84,16 +143,19 @@ else
|
||||
END_GROUP = -Wl,--end-group
|
||||
endif
|
||||
|
||||
# md5sum binary default, can be overridden by an environment variable
|
||||
MD5SUM ?= md5sum
|
||||
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIB_NAMES = clangAnalysis 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_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||
|
||||
# Libraries that should be linked in for the statically linked LLD.
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO lldMinGW lldReaderWriter lldWasm lldYAML
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
|
||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||
|
||||
# Other libraries that are needed to link TinyGo.
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA 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)
|
||||
@@ -104,32 +166,32 @@ LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
||||
# library path (for ninja).
|
||||
# This list also includes a few tools that are necessary as part of the full
|
||||
# TinyGo build.
|
||||
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES)))
|
||||
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm lld $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES)))
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++14
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
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
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
|
||||
clean:
|
||||
clean: ## Remove build directory
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||
fmt:
|
||||
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform
|
||||
fmt: ## Reformat source
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
fmt-check: ## Warn if any source needs reformatting
|
||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||
|
||||
|
||||
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
|
||||
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 gen-device-renesas ## Generate microcontroller-specific sources
|
||||
ifneq ($(STM32), 0)
|
||||
gen-device: gen-device-stm32
|
||||
endif
|
||||
|
||||
gen-device-avr:
|
||||
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
#@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||
@@ -171,18 +233,20 @@ gen-device-rp: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/rp
|
||||
|
||||
# Get LLVM sources.
|
||||
gen-device-renesas: build/gen-device-svd
|
||||
./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 xtensa_release_13.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
git clone -b tinygo_xtensa_release_18.1.2 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm ## Get LLVM sources
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
$(LLVM_BUILDDIR)/build.ninja:
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;AVR;Mips;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja ## Build LLVM
|
||||
cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS)
|
||||
|
||||
ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
@@ -191,7 +255,7 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
binaryen: build/wasm-opt$(EXE)
|
||||
build/wasm-opt$(EXE):
|
||||
mkdir -p build
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF -DENABLE_WERROR=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
||||
endif
|
||||
|
||||
@@ -199,45 +263,74 @@ endif
|
||||
.PHONY: wasi-libc
|
||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG" WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
||||
#@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/bytecodealliance/wasm-tools-go
|
||||
.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
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||
# 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/
|
||||
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
# Check for Node.js used during WASM tests.
|
||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||
MIN_NODEJS_VERSION=18
|
||||
|
||||
# Tests that take over a minute in wasi
|
||||
.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
|
||||
|
||||
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" .
|
||||
test: #wasi-libc check-nodejs-version
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -race -buildmode exe -tags "byollvm osusergo" .
|
||||
|
||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||
TEST_PACKAGES_SLOW = \
|
||||
compress/bzip2 \
|
||||
compress/flate \
|
||||
crypto/dsa \
|
||||
index/suffixarray \
|
||||
|
||||
TEST_PACKAGES_BASE = \
|
||||
# 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/elliptic/internal/fiat \
|
||||
crypto/internal/subtle \
|
||||
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 \
|
||||
encoding/asn1 \
|
||||
encoding/base32 \
|
||||
encoding/base64 \
|
||||
encoding/csv \
|
||||
encoding/hex \
|
||||
go/ast \
|
||||
go/format \
|
||||
go/scanner \
|
||||
go/version \
|
||||
hash \
|
||||
hash/adler32 \
|
||||
hash/crc64 \
|
||||
@@ -259,41 +352,143 @@ TEST_PACKAGES_BASE = \
|
||||
unicode \
|
||||
unicode/utf16 \
|
||||
unicode/utf8 \
|
||||
unique \
|
||||
$(nil)
|
||||
|
||||
# Standard library packages that pass tests natively
|
||||
TEST_PACKAGES := \
|
||||
$(TEST_PACKAGES_BASE)
|
||||
|
||||
# archive/zip requires ReadAt, which is not yet supported on windows
|
||||
ifneq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES := \
|
||||
$(TEST_PACKAGES) \
|
||||
archive/zip
|
||||
# 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
|
||||
|
||||
# Standard library packages that pass tests on wasi
|
||||
TEST_PACKAGES_WASI = \
|
||||
$(TEST_PACKAGES_BASE)
|
||||
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
||||
# bytes requires mmap
|
||||
# compress/flate appears to hang on wasi
|
||||
# crypto/aes 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
|
||||
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
# mime: fail on wasi; neds panic()/recover()
|
||||
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# mime/quotedprintable requires syscall.Faccessat
|
||||
# net/mail: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# net/ntextproto: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||
# regexp/syntax: fails on wasip1; needs panic()/recover()
|
||||
# strconv requires recover() which is not yet supported on wasi
|
||||
# text/tabwriter requires recover(), which is not yet supported on wasi
|
||||
# text/template/parse requires recover(), which is not yet supported on wasi
|
||||
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
||||
|
||||
# Additional standard library packages that pass tests on individual platforms
|
||||
TEST_PACKAGES_LINUX := \
|
||||
archive/zip \
|
||||
compress/flate \
|
||||
crypto/aes \
|
||||
crypto/hmac \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
image \
|
||||
io/ioutil \
|
||||
mime \
|
||||
mime/multipart \
|
||||
mime/quotedprintable \
|
||||
net \
|
||||
net/mail \
|
||||
net/textproto \
|
||||
os/user \
|
||||
regexp/syntax \
|
||||
strconv \
|
||||
text/tabwriter \
|
||||
text/template/parse
|
||||
|
||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||
|
||||
TEST_PACKAGES_WINDOWS := \
|
||||
compress/flate \
|
||||
crypto/hmac \
|
||||
os/user \
|
||||
strconv \
|
||||
text/template/parse \
|
||||
$(nil)
|
||||
|
||||
# Report platforms on which each standard library package is known to pass tests
|
||||
jointmp := $(shell echo /tmp/join.$$$$)
|
||||
report-stdlib-tests-pass:
|
||||
@for t in $(TEST_PACKAGES_DARWIN); do echo "$$t darwin"; done | sort > $(jointmp).darwin
|
||||
@for t in $(TEST_PACKAGES_LINUX); do echo "$$t linux"; done | sort > $(jointmp).linux
|
||||
@for t in $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW); do echo "$$t darwin linux wasi windows"; done | sort > $(jointmp).portable
|
||||
@join -a1 -a2 $(jointmp).darwin $(jointmp).linux | \
|
||||
join -a1 -a2 - $(jointmp).portable
|
||||
@rm $(jointmp).*
|
||||
|
||||
# Standard library packages that pass tests quickly on the current platform
|
||||
ifeq ($(shell uname),Darwin)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||
TEST_IOFS := true
|
||||
endif
|
||||
ifeq ($(shell uname),Linux)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||
TEST_IOFS := true
|
||||
endif
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test $(TEST_PACKAGES) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||
@# For more details, see the comments on issue #3143.
|
||||
ifeq ($(TEST_IOFS),true)
|
||||
$(TINYGO) test -stack-size=6MB io/fs
|
||||
endif
|
||||
tinygo-test-fast:
|
||||
$(TINYGO) test $(TEST_PACKAGES)
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
||||
tinygo-bench:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-fast:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES)
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST)
|
||||
|
||||
# Same thing, except for wasi rather than the current platform.
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_WASI) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasi-fast:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_WASI)
|
||||
tinygo-bench-wasi:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_WASI) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-wasi-fast:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_WASI)
|
||||
$(TINYGO) test -target wasip1 $(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
|
||||
tinygo-test-wasip1-fast:
|
||||
$(TINYGO) test -target=wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-slow:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasip2-fast:
|
||||
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||
|
||||
tinygo-test-wasip2-sum-slow:
|
||||
TINYGO=$(TINYGO) \
|
||||
TARGET=wasip2 \
|
||||
TESTOPTS="-x -work" \
|
||||
PACKAGES="$(TEST_PACKAGES_SLOW)" \
|
||||
gotestsum --raw-command -- ./tools/tgtestjson.sh
|
||||
tinygo-test-wasip2-sum-fast:
|
||||
TINYGO=$(TINYGO) \
|
||||
TARGET=wasip2 \
|
||||
TESTOPTS="-x -work" \
|
||||
PACKAGES="$(TEST_PACKAGES_FAST)" \
|
||||
gotestsum --raw-command -- ./tools/tgtestjson.sh
|
||||
tinygo-bench-wasip1:
|
||||
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-wasip1-fast:
|
||||
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST)
|
||||
|
||||
tinygo-bench-wasip2:
|
||||
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-wasip2-fast:
|
||||
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST)
|
||||
|
||||
# Test external packages in a large corpus.
|
||||
test-corpus:
|
||||
@@ -301,12 +496,30 @@ test-corpus:
|
||||
test-corpus-fast:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
||||
test-corpus-wasi: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasi
|
||||
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
|
||||
|
||||
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
|
||||
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||
# compile-only platform-independent examples
|
||||
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
||||
# regression test for #2563
|
||||
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
|
||||
# test all examples (except pwm)
|
||||
@@ -324,6 +537,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@@ -334,12 +549,26 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/time-offset
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@@ -348,12 +577,16 @@ ifneq ($(WASM), 0)
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=reelboard examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=pca10040 examples/blinky2
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=pca10056 examples/blinky2
|
||||
$(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@@ -382,12 +615,16 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10059 examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=bluemicro840 examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=gemma-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
||||
@@ -404,6 +641,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=matrixportal-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||
@@ -416,12 +655,14 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=particle-xenon examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=pinetime examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10059-s140v7 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard-s140v7 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/blinky1
|
||||
@@ -430,6 +671,8 @@ 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=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
|
||||
@@ -440,7 +683,9 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy examples/serial
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy41 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -468,6 +713,34 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=kb2040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=badger2040-w examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=tufty2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-zero examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=gopher-badge 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
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -475,6 +748,15 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
# test usb
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -490,6 +772,8 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l476rg examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-wl55jc examples/blinky1
|
||||
@@ -508,12 +792,17 @@ ifneq ($(STM32), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||
$(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
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@@ -524,34 +813,50 @@ ifneq ($(AVR), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=attiny1616 examples/empty
|
||||
@$(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 -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(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/serial
|
||||
$(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/serial
|
||||
$(TINYGO) build -size short -o test.bin -target m5stack 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
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||
$(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=m5stamp-c3 examples/serial
|
||||
$(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=hifive1-qemu examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm-unknown examples/hello-wasm-unknown
|
||||
endif
|
||||
# test various compiler flags
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
||||
@@ -560,12 +865,18 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||
@$(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
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=linux GOARCH=mips $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
ifneq ($(OS),Windows_NT)
|
||||
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||
# not garbage collected.
|
||||
@@ -580,8 +891,9 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-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
|
||||
@@ -589,10 +901,10 @@ 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
|
||||
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0
|
||||
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus
|
||||
@mkdir -p build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
|
||||
@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/
|
||||
@echo copying source files
|
||||
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
||||
ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
@@ -601,13 +913,12 @@ endif
|
||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
||||
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
|
||||
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||
@@ -615,19 +926,27 @@ endif
|
||||
@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
|
||||
@cp -rp lib/musl/src/locale build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
||||
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
||||
@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/def-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/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-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/nrfx/* build/release/tinygo/lib/nrfx
|
||||
@@ -637,23 +956,66 @@ endif
|
||||
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
|
||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||
@cp -rp lib/wasi-libc/libc-bottom-half/headers/public build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
|
||||
@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/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/math 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-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 src build/release/tinygo/src
|
||||
@cp -rp targets build/release/tinygo/targets
|
||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||
|
||||
release: build/release
|
||||
release:
|
||||
tar -czf build/release.tar.gz -C build/release tinygo
|
||||
|
||||
deb: build/release
|
||||
DEB_ARCH ?= native
|
||||
deb:
|
||||
@mkdir -p build/release-deb/usr/local/bin
|
||||
@mkdir -p build/release-deb/usr/local/lib
|
||||
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
||||
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
||||
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
|
||||
ifneq ($(RELEASEONLY), 1)
|
||||
release: build/release
|
||||
deb: build/release
|
||||
endif
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
cd internal/tools && go generate -tags tools ./
|
||||
|
||||
.PHONY: lint
|
||||
lint: tools ## Lint source tree
|
||||
revive -version
|
||||
# TODO: lint more directories!
|
||||
# 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}'
|
||||
|
||||
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) ) *.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) ) *.go *.md
|
||||
|
||||
# https://www.client9.com/self-documenting-makefiles/
|
||||
.PHONY: help
|
||||
help:
|
||||
@awk -F ':|##' '/^[^\t].+?:.*?##/ {\
|
||||
gsub(/\$$\(LLVM_BUILDDIR\)/, "$(LLVM_BUILDDIR)"); \
|
||||
printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \
|
||||
}' $(MAKEFILE_LIST)
|
||||
#.DEFAULT_GOAL=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,7 @@
|
||||
Copyright (c) 2018-2022 The TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of the Go standard library.
|
||||
Copyright (c) 2009-2022 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2009-2023 The Go Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# 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://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) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (Wasm), and command-line tools.
|
||||
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.
|
||||
|
||||
## Embedded
|
||||
|
||||
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
||||
|
||||
```go
|
||||
@@ -35,98 +37,62 @@ The above program can be compiled and run without modification on an Arduino Uno
|
||||
tinygo flash -target arduino examples/blinky1
|
||||
```
|
||||
|
||||
## WebAssembly
|
||||
|
||||
TinyGo is very useful for compiling programs both for use in browsers (WASM) as well as for use on servers and other edge devices (WASI).
|
||||
|
||||
TinyGo programs can run in [Fastly Compute](https://www.fastly.com/documentation/guides/compute/go/), [Fermyon Spin](https://developer.fermyon.com/spin/go-components), [wazero](https://wazero.io/languages/tinygo/) and many other WebAssembly runtimes.
|
||||
|
||||
Here is a small TinyGo program for use by a WASI host application:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
//go:wasm-module yourmodulename
|
||||
//export 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:
|
||||
|
||||
```shell
|
||||
tinygo build -o main.wasm -target=wasip1 main.go
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
See the [getting started instructions](https://tinygo.org/getting-started/) for information on how to install TinyGo, as well as how to run the TinyGo compiler using our Docker container.
|
||||
|
||||
## Supported boards/targets
|
||||
## Supported targets
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
### Embedded
|
||||
|
||||
The following 80 microcontroller boards are currently supported:
|
||||
You can compile TinyGo programs for over 94 different microcontroller boards.
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||
* [Adafruit Feather nRF52840 Sense](https://www.adafruit.com/product/4516)
|
||||
* [Adafruit Feather RP2040](https://www.adafruit.com/product/4884)
|
||||
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
|
||||
* [Adafruit Grand Central M4](https://www.adafruit.com/product/4064)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
|
||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
|
||||
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||
* [Arduino MKR WiFi 1010](https://store.arduino.cc/usa/mkr-wifi-1010)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||
* [Arduino Nano 33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Nano RP2040 Connect](https://store.arduino.cc/nano-rp2040-connect)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
* [BBC micro:bit v2](https://microbit.org/new-microbit/)
|
||||
* [blues wireless Swan](https://blues.io/products/swan/)
|
||||
* [Digispark](http://digistump.com/products/1)
|
||||
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
|
||||
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [M5Stack](https://docs.m5stack.com/en/core/basic)
|
||||
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
|
||||
* [M5Stamp C3](https://docs.m5stack.com/en/core/stamp_c3)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
|
||||
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
|
||||
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
|
||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Nordic Semiconductor pca10059](https://www.nordicsemi.com/Software-and-tools/Development-Kits/nRF52840-Dongle)
|
||||
* [Particle Argon](https://docs.particle.io/datasheets/wi-fi/argon-datasheet/)
|
||||
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
|
||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
||||
* [ST Micro "Nucleo" L432KC](https://www.st.com/ja/evaluation-tools/nucleo-l432kc.html)
|
||||
* [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
|
||||
* [ST Micro "Nucleo" WL55JC](https://www.st.com/en/evaluation-tools/nucleo-wl55jc.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
* [ST Micro STM32F469 "Discovery"](https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html)
|
||||
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
|
||||
* [The Things Industries Generic Node Sensor Edition](https://www.genericnode.com/docs/sensor-edition/)
|
||||
For more information, please see https://tinygo.org/docs/reference/microcontrollers/
|
||||
|
||||
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
|
||||
### WebAssembly
|
||||
|
||||
TinyGo programs can be compiled for both WASM and WASI targets.
|
||||
|
||||
For more information, see https://tinygo.org/docs/guides/webassembly/
|
||||
|
||||
### Operating Systems
|
||||
|
||||
You can also compile programs for Linux, macOS, and Windows targets.
|
||||
|
||||
For more information:
|
||||
|
||||
- Linux https://tinygo.org/docs/guides/linux/
|
||||
|
||||
- macOS https://tinygo.org/docs/guides/macos/
|
||||
|
||||
- Windows https://tinygo.org/docs/guides/windows/
|
||||
|
||||
## Currently supported features:
|
||||
|
||||
|
||||
+33
-5
@@ -12,13 +12,14 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
wasm "github.com/aykevl/go-wasm"
|
||||
"github.com/blakesmith/ar"
|
||||
)
|
||||
|
||||
// makeArchive creates an arcive for static linking from a list of object files
|
||||
// makeArchive creates an archive for static linking from a list of object files
|
||||
// given as a parameter. It is equivalent to the following command:
|
||||
//
|
||||
// ar -rcs <archivePath> <objs...>
|
||||
// ar -rcs <archivePath> <objs...>
|
||||
func makeArchive(arfile *os.File, objs []string) error {
|
||||
// Open the archive file.
|
||||
arwriter := ar.NewWriter(arfile)
|
||||
@@ -74,8 +75,35 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||
fileIndex int
|
||||
}{symbol.Name, i})
|
||||
}
|
||||
} else if dbg, err := wasm.Parse(objfile); err == nil {
|
||||
for _, s := range dbg.Sections {
|
||||
switch section := s.(type) {
|
||||
case *wasm.SectionLinking:
|
||||
for _, symbol := range section.Symbols {
|
||||
if symbol.Flags&wasm.LinkingSymbolFlagUndefined != 0 {
|
||||
// Don't list undefined functions.
|
||||
continue
|
||||
}
|
||||
if symbol.Flags&wasm.LinkingSymbolFlagBindingLocal != 0 {
|
||||
// Don't include local symbols.
|
||||
continue
|
||||
}
|
||||
if symbol.Kind != wasm.LinkingSymbolKindFunction && symbol.Kind != wasm.LinkingSymbolKindData {
|
||||
// Link functions and data symbols.
|
||||
// Some data symbols need to be included, such as
|
||||
// __log_data.
|
||||
continue
|
||||
}
|
||||
// Include in the archive.
|
||||
symbolTable = append(symbolTable, struct {
|
||||
name string
|
||||
fileIndex int
|
||||
}{symbol.Name, i})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
|
||||
return fmt.Errorf("failed to open file %s as WASM, ELF or PE/COFF: %w", objpath, err)
|
||||
}
|
||||
|
||||
// Close file, to avoid issues with too many open files (especially on
|
||||
@@ -122,7 +150,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
|
||||
}
|
||||
@@ -144,7 +172,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
|
||||
}
|
||||
|
||||
+514
-253
File diff suppressed because it is too large
Load Diff
+23
-13
@@ -2,14 +2,12 @@ package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -26,7 +24,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m0",
|
||||
"cortex-m0plus",
|
||||
"cortex-m3",
|
||||
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
||||
"cortex-m33",
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
@@ -35,8 +33,10 @@ func TestClangAttributes(t *testing.T) {
|
||||
"k210",
|
||||
"nintendoswitch",
|
||||
"riscv-qemu",
|
||||
"wasi",
|
||||
"wasip1",
|
||||
"wasip2",
|
||||
"wasm",
|
||||
"wasm-unknown",
|
||||
}
|
||||
if hasBuiltinTools {
|
||||
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
|
||||
@@ -53,18 +53,30 @@ 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"},
|
||||
{GOOS: "linux", GOARCH: "mips", GOMIPS: "softfloat"},
|
||||
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
|
||||
{GOOS: "darwin", GOARCH: "amd64"},
|
||||
{GOOS: "darwin", GOARCH: "arm64"},
|
||||
{GOOS: "windows", GOARCH: "amd64"},
|
||||
{GOOS: "windows", GOARCH: "arm64"},
|
||||
{GOOS: "wasip1", GOARCH: "wasm"},
|
||||
} {
|
||||
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
||||
if options.GOARCH == "arm" {
|
||||
name += ",GOARM=" + options.GOARM
|
||||
}
|
||||
if options.GOARCH == "mips" || options.GOARCH == "mipsle" {
|
||||
name += ",GOMIPS=" + options.GOMIPS
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
testClangAttributes(t, options)
|
||||
})
|
||||
@@ -73,7 +85,6 @@ func TestClangAttributes(t *testing.T) {
|
||||
|
||||
func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
testDir := t.TempDir()
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
@@ -83,21 +94,20 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
t.Fatalf("could not load target: %s", err)
|
||||
}
|
||||
config := compileopts.Config{
|
||||
Options: options,
|
||||
Target: target,
|
||||
ClangHeaders: clangHeaderPath,
|
||||
Options: options,
|
||||
Target: target,
|
||||
}
|
||||
|
||||
// Create a very simple C input file.
|
||||
srcpath := filepath.Join(testDir, "test.c")
|
||||
err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
||||
err = os.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
||||
if err != nil {
|
||||
t.Fatalf("could not write target file %s: %s", srcpath, err)
|
||||
}
|
||||
|
||||
// Compile this file using Clang.
|
||||
outpath := filepath.Join(testDir, "test.bc")
|
||||
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
|
||||
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags(false)...)
|
||||
if config.GOOS() == "darwin" {
|
||||
// Silence some warnings that happen when testing GOOS=darwin on
|
||||
// something other than MacOS.
|
||||
|
||||
+46
-12
@@ -24,12 +24,13 @@ func ReadBuildID() ([]byte, error) {
|
||||
defer f.Close()
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
case "linux", "freebsd", "android":
|
||||
// Read the GNU build id section. (Not sure about FreeBSD though...)
|
||||
file, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var gnuID, goID []byte
|
||||
for _, section := range file.Sections {
|
||||
if section.Type != elf.SHT_NOTE ||
|
||||
(section.Name != ".note.gnu.build-id" && section.Name != ".note.go.buildid") {
|
||||
@@ -40,7 +41,16 @@ func ReadBuildID() ([]byte, error) {
|
||||
if uint64(n) != section.Size || err != nil {
|
||||
return nil, fmt.Errorf("could not read build id: %w", err)
|
||||
}
|
||||
return buf, nil
|
||||
if section.Name == ".note.gnu.build-id" {
|
||||
gnuID = buf
|
||||
} else {
|
||||
goID = buf
|
||||
}
|
||||
}
|
||||
if gnuID != nil {
|
||||
return gnuID, nil
|
||||
} else if goID != nil {
|
||||
return goID, nil
|
||||
}
|
||||
case "darwin":
|
||||
// Read the LC_UUID load command, which contains the equivalent of a
|
||||
@@ -67,6 +77,15 @@ func ReadBuildID() ([]byte, error) {
|
||||
}
|
||||
return raw[4:], nil
|
||||
}
|
||||
|
||||
// Normally we would have found a build ID by now. But not on Nix,
|
||||
// unfortunately, because Nix adds -no_uuid for some reason:
|
||||
// https://github.com/NixOS/nixpkgs/issues/178366
|
||||
// Fall back to the same implementation that we use for Windows.
|
||||
id, err := readRawGoBuildID(f, 32*1024)
|
||||
if len(id) != 0 || err != nil {
|
||||
return id, err
|
||||
}
|
||||
default:
|
||||
// On other platforms (such as Windows) there isn't such a convenient
|
||||
// build ID. Luckily, Go does have an equivalent of the build ID, which
|
||||
@@ -78,16 +97,31 @@ func ReadBuildID() ([]byte, error) {
|
||||
// directly. Luckily the build ID is always at the start of the file.
|
||||
// For details, see:
|
||||
// https://github.com/golang/go/blob/master/src/cmd/internal/buildid/buildid.go
|
||||
fileStart := make([]byte, 4096)
|
||||
_, err := io.ReadFull(f, fileStart)
|
||||
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
|
||||
if index < 0 || index > len(fileStart)-103 {
|
||||
return nil, fmt.Errorf("could not find build id in %s", err)
|
||||
}
|
||||
buf := fileStart[index : index+103]
|
||||
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
|
||||
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
|
||||
id, err := readRawGoBuildID(f, 4096)
|
||||
if len(id) != 0 || err != nil {
|
||||
return id, err
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("could not find build ID in %s", executable)
|
||||
return nil, fmt.Errorf("could not find build ID in %v", executable)
|
||||
}
|
||||
|
||||
// The Go toolchain stores a build ID in the binary that we can use, as a
|
||||
// fallback if binary file specific build IDs can't be obtained.
|
||||
// This function reads that build ID from the binary.
|
||||
func readRawGoBuildID(f *os.File, prefixSize int) ([]byte, error) {
|
||||
fileStart := make([]byte, prefixSize)
|
||||
_, err := io.ReadFull(f, fileStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read build id from %s: %v", f.Name(), err)
|
||||
}
|
||||
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
|
||||
if index < 0 || index > len(fileStart)-103 {
|
||||
return nil, fmt.Errorf("could not find build id in %s", f.Name())
|
||||
}
|
||||
buf := fileStart[index : index+103]
|
||||
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
|
||||
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
+82
-19
@@ -1,17 +1,21 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// These are the GENERIC_SOURCES according to CMakeList.txt.
|
||||
// These are the GENERIC_SOURCES according to CMakeList.txt except for
|
||||
// divmodsi4.c and udivmodsi4.c.
|
||||
var genericBuiltins = []string{
|
||||
"absvdi2.c",
|
||||
"absvsi2.c",
|
||||
"absvti2.c",
|
||||
"adddf3.c",
|
||||
"addsf3.c",
|
||||
"addtf3.c",
|
||||
"addvdi3.c",
|
||||
"addvsi3.c",
|
||||
"addvti3.c",
|
||||
@@ -36,13 +40,12 @@ var genericBuiltins = []string{
|
||||
"divdf3.c",
|
||||
"divdi3.c",
|
||||
"divmoddi4.c",
|
||||
"divmodsi4.c",
|
||||
//"divmodsi4.c",
|
||||
"divmodti4.c",
|
||||
"divsc3.c",
|
||||
"divsf3.c",
|
||||
"divsi3.c",
|
||||
"divtc3.c",
|
||||
"divti3.c",
|
||||
"divtf3.c",
|
||||
"extendsfdf2.c",
|
||||
"extendhfsf2.c",
|
||||
"ffsdi2.c",
|
||||
@@ -72,6 +75,7 @@ var genericBuiltins = []string{
|
||||
"floatunsisf.c",
|
||||
"floatuntidf.c",
|
||||
"floatuntisf.c",
|
||||
"fp_mode.c",
|
||||
//"int_util.c",
|
||||
"lshrdi3.c",
|
||||
"lshrti3.c",
|
||||
@@ -87,7 +91,6 @@ var genericBuiltins = []string{
|
||||
"mulsc3.c",
|
||||
"mulsf3.c",
|
||||
"multi3.c",
|
||||
"multf3.c",
|
||||
"mulvdi3.c",
|
||||
"mulvsi3.c",
|
||||
"mulvti3.c",
|
||||
@@ -107,13 +110,11 @@ var genericBuiltins = []string{
|
||||
"popcountti2.c",
|
||||
"powidf2.c",
|
||||
"powisf2.c",
|
||||
"powitf2.c",
|
||||
"subdf3.c",
|
||||
"subsf3.c",
|
||||
"subvdi3.c",
|
||||
"subvsi3.c",
|
||||
"subvti3.c",
|
||||
"subtf3.c",
|
||||
"trampoline_setup.c",
|
||||
"truncdfhf2.c",
|
||||
"truncdfsf2.c",
|
||||
@@ -122,7 +123,7 @@ var genericBuiltins = []string{
|
||||
"ucmpti2.c",
|
||||
"udivdi3.c",
|
||||
"udivmoddi4.c",
|
||||
"udivmodsi4.c",
|
||||
//"udivmodsi4.c",
|
||||
"udivmodti4.c",
|
||||
"udivsi3.c",
|
||||
"udivti3.c",
|
||||
@@ -131,6 +132,38 @@ var genericBuiltins = []string{
|
||||
"umodti3.c",
|
||||
}
|
||||
|
||||
// These are the GENERIC_TF_SOURCES as of LLVM 18.
|
||||
// They are not needed on all platforms (32-bit platforms usually don't need
|
||||
// these) but they seem to compile fine so it's easier to include them.
|
||||
var genericBuiltins128 = []string{
|
||||
"addtf3.c",
|
||||
"comparetf2.c",
|
||||
"divtc3.c",
|
||||
"divtf3.c",
|
||||
"extenddftf2.c",
|
||||
"extendhftf2.c",
|
||||
"extendsftf2.c",
|
||||
"fixtfdi.c",
|
||||
"fixtfsi.c",
|
||||
"fixtfti.c",
|
||||
"fixunstfdi.c",
|
||||
"fixunstfsi.c",
|
||||
"fixunstfti.c",
|
||||
"floatditf.c",
|
||||
"floatsitf.c",
|
||||
"floattitf.c",
|
||||
"floatunditf.c",
|
||||
"floatunsitf.c",
|
||||
"floatuntitf.c",
|
||||
"multc3.c",
|
||||
"multf3.c",
|
||||
"powitf2.c",
|
||||
"subtf3.c",
|
||||
"trunctfdf2.c",
|
||||
"trunctfhf2.c",
|
||||
"trunctfsf2.c",
|
||||
}
|
||||
|
||||
var aeabiBuiltins = []string{
|
||||
"arm/aeabi_cdcmp.S",
|
||||
"arm/aeabi_cdcmpeq_check_nan.c",
|
||||
@@ -149,24 +182,54 @@ var aeabiBuiltins = []string{
|
||||
"arm/aeabi_memset.S",
|
||||
"arm/aeabi_uidivmod.S",
|
||||
"arm/aeabi_uldivmod.S",
|
||||
|
||||
// These two are not technically EABI builtins but are used by them and only
|
||||
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||
// other architectures.
|
||||
// Most importantly, they have a different calling convention on AVR so
|
||||
// should not be used on AVR.
|
||||
"divmodsi4.c",
|
||||
"udivmodsi4.c",
|
||||
}
|
||||
|
||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||
// These symbols are for operations that cannot be emitted with a single
|
||||
var avrBuiltins = []string{
|
||||
"avr/divmodhi4.S",
|
||||
"avr/divmodqi4.S",
|
||||
"avr/mulhi3.S",
|
||||
"avr/mulqi3.S",
|
||||
"avr/udivmodhi4.S",
|
||||
"avr/udivmodqi4.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.
|
||||
//
|
||||
// For more information, see: https://compiler-rt.llvm.org/
|
||||
var CompilerRT = Library{
|
||||
var libCompilerRT = Library{
|
||||
name: "compiler-rt",
|
||||
cflags: func(target, headerPath string) []string {
|
||||
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
|
||||
},
|
||||
sourceDir: "lib/compiler-rt/lib/builtins",
|
||||
librarySources: func(target string) []string {
|
||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
builtins = append(builtins, aeabiBuiltins...)
|
||||
sourceDir: func() string {
|
||||
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
|
||||
if _, err := os.Stat(llvmDir); err == nil {
|
||||
// Release build.
|
||||
return llvmDir
|
||||
}
|
||||
return builtins
|
||||
// Development build.
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||
},
|
||||
librarySources: func(target string) ([]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
|
||||
builtins = append(builtins, genericBuiltins128...)
|
||||
}
|
||||
return builtins, nil
|
||||
},
|
||||
}
|
||||
|
||||
+40
-39
@@ -10,7 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -33,29 +33,29 @@ import (
|
||||
// Because of this complexity, every file has in fact two cached build outputs:
|
||||
// the file itself, and the list of dependencies. Its operation is as follows:
|
||||
//
|
||||
// depfile = hash(path, compiler, cflags, ...)
|
||||
// if depfile exists:
|
||||
// outfile = hash of all files and depfile name
|
||||
// if outfile exists:
|
||||
// # cache hit
|
||||
// return outfile
|
||||
// # cache miss
|
||||
// tmpfile = compile file
|
||||
// read dependencies (side effect of compile)
|
||||
// write depfile
|
||||
// outfile = hash of all files and depfile name
|
||||
// rename tmpfile to outfile
|
||||
// depfile = hash(path, compiler, cflags, ...)
|
||||
// if depfile exists:
|
||||
// outfile = hash of all files and depfile name
|
||||
// if outfile exists:
|
||||
// # cache hit
|
||||
// return outfile
|
||||
// # cache miss
|
||||
// tmpfile = compile file
|
||||
// read dependencies (side effect of compile)
|
||||
// write depfile
|
||||
// outfile = hash of all files and depfile name
|
||||
// rename tmpfile to outfile
|
||||
//
|
||||
// There are a few edge cases that are not handled:
|
||||
// - If a file is added to an include path, that file may be included instead of
|
||||
// some other file. This would be fixed by also including lookup failures in the
|
||||
// dependencies file, but I'm not aware of a compiler which does that.
|
||||
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||
// details.
|
||||
// - A header file may be changed to add/remove an include. This invalidates the
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
// - If a file is added to an include path, that file may be included instead of
|
||||
// some other file. This would be fixed by also including lookup failures in the
|
||||
// dependencies file, but I'm not aware of a compiler which does that.
|
||||
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||
// details.
|
||||
// - A header file may be changed to add/remove an include. This invalidates the
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
@@ -88,7 +88,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
// Load dependencies file, if possible.
|
||||
depfileName := "dep-" + depfileNameHash + ".json"
|
||||
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
|
||||
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
|
||||
depfileBuf, err := os.ReadFile(depfileCachePath)
|
||||
var dependencies []string // sorted list of dependency paths
|
||||
if err == nil {
|
||||
// There is a dependency file, that's great!
|
||||
@@ -103,27 +103,27 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
if err == nil {
|
||||
if _, err := os.Stat(outpath); err == nil {
|
||||
return outpath, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||
// expected either nil or IsNotExist
|
||||
return "", err
|
||||
}
|
||||
|
||||
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
|
||||
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*.bc")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
objTmpFile.Close()
|
||||
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
|
||||
depTmpFile, err := os.CreateTemp(tmpdir, "dep-*.d")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
depTmpFile.Close()
|
||||
flags := append([]string{}, cflags...) // copy cflags
|
||||
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||
flags := append([]string{}, cflags...) // copy cflags
|
||||
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name(), "-flto=thin") // autogenerate dependencies
|
||||
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
||||
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
||||
@@ -158,7 +158,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
sort.Strings(dependencySlice)
|
||||
|
||||
// Write dependencies file.
|
||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||
f, err := os.CreateTemp(filepath.Dir(depfileCachePath), depfileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func makeCFileCachePath(paths []string, depfileNameHash string) (string, error)
|
||||
outFileNameBuf := sha512.Sum512_224(buf)
|
||||
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
||||
|
||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
|
||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".bc")
|
||||
return outpath, nil
|
||||
}
|
||||
|
||||
@@ -244,13 +244,14 @@ func hashFile(path string) (string, error) {
|
||||
// file is assumed to have a single target named deps.
|
||||
//
|
||||
// There are roughly three make syntax variants:
|
||||
// - BSD make, which doesn't support any escaping. This means that many special
|
||||
// characters are not supported in file names.
|
||||
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||
// find a file it tries to fall back with the literal path name (to match BSD
|
||||
// make).
|
||||
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||
// any weird characters.
|
||||
// - BSD make, which doesn't support any escaping. This means that many special
|
||||
// characters are not supported in file names.
|
||||
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||
// find a file it tries to fall back with the literal path name (to match BSD
|
||||
// make).
|
||||
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||
// any weird characters.
|
||||
//
|
||||
// Clang supports two variants: a format that's a compromise between BSD and GNU
|
||||
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
|
||||
// is at least somewhat sane. This last format isn't perfect either: it does not
|
||||
@@ -258,7 +259,7 @@ func hashFile(path string) (string, error) {
|
||||
// allowed on Windows, but of course can be used on POSIX like systems. Still,
|
||||
// it's the most sane of any of the formats so readDepFile will use that format.
|
||||
func readDepFile(filename string) ([]string, error) {
|
||||
buf, err := ioutil.ReadFile(filename)
|
||||
buf, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+66
-23
@@ -1,4 +1,11 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
|
||||
// This file needs to be updated each LLVM release.
|
||||
// There are a few small modifications to make, like:
|
||||
// * ExecuteAssembler is made non-static.
|
||||
// * The struct AssemblerImplementation is moved to cc1as.h so it can be
|
||||
// included elsewhere.
|
||||
|
||||
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||
//
|
||||
@@ -21,8 +28,8 @@
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/MC/MCAsmBackend.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
@@ -38,6 +45,7 @@
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/OptTable.h"
|
||||
@@ -45,17 +53,18 @@
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/TargetParser/Triple.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <system_error>
|
||||
using namespace clang;
|
||||
using namespace clang::driver;
|
||||
@@ -73,10 +82,10 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
// Parse the arguments.
|
||||
const OptTable &OptTbl = getDriverOptTable();
|
||||
|
||||
const unsigned IncludedFlagsBitmask = options::CC1AsOption;
|
||||
llvm::opt::Visibility VisibilityMask(options::CC1AsOption);
|
||||
unsigned MissingArgIndex, MissingArgCount;
|
||||
InputArgList Args = OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount,
|
||||
IncludedFlagsBitmask);
|
||||
InputArgList Args =
|
||||
OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount, VisibilityMask);
|
||||
|
||||
// Check for missing argument error.
|
||||
if (MissingArgCount) {
|
||||
@@ -89,7 +98,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
|
||||
auto ArgString = A->getAsString(Args);
|
||||
std::string Nearest;
|
||||
if (OptTbl.findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
|
||||
if (OptTbl.findNearest(ArgString, Nearest, VisibilityMask) > 1)
|
||||
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
|
||||
else
|
||||
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
|
||||
@@ -101,6 +110,17 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
||||
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
||||
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
|
||||
VersionTuple Version;
|
||||
if (Version.tryParse(A->getValue()))
|
||||
Diags.Report(diag::err_drv_invalid_value)
|
||||
<< A->getAsString(Args) << A->getValue();
|
||||
else
|
||||
Opts.DarwinTargetVariantSDKVersion = Version;
|
||||
}
|
||||
|
||||
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
@@ -119,12 +139,12 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
Opts.CompressDebugSections =
|
||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||
.Case("none", llvm::DebugCompressionType::None)
|
||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
||||
.Case("zlib", llvm::DebugCompressionType::Zlib)
|
||||
.Case("zstd", llvm::DebugCompressionType::Zstd)
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
|
||||
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
||||
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);
|
||||
@@ -139,8 +159,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
|
||||
auto Split = StringRef(Arg).split('=');
|
||||
Opts.DebugPrefixMap.insert(
|
||||
{std::string(Split.first), std::string(Split.second)});
|
||||
Opts.DebugPrefixMap.emplace_back(Split.first, Split.second);
|
||||
}
|
||||
|
||||
// Frontend Options
|
||||
@@ -187,6 +206,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
||||
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
||||
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
|
||||
Opts.NoTypeCheck = Args.hasArg(OPT_mno_type_check);
|
||||
Opts.RelocationModel =
|
||||
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
|
||||
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
|
||||
@@ -204,6 +224,19 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
.Default(0);
|
||||
}
|
||||
|
||||
if (auto *A = Args.getLastArg(OPT_femit_dwarf_unwind_EQ)) {
|
||||
Opts.EmitDwarfUnwind =
|
||||
llvm::StringSwitch<EmitDwarfUnwindType>(A->getValue())
|
||||
.Case("always", EmitDwarfUnwindType::Always)
|
||||
.Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind)
|
||||
.Case("default", EmitDwarfUnwindType::Default);
|
||||
}
|
||||
|
||||
Opts.EmitCompactUnwindNonCanonical =
|
||||
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
||||
|
||||
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
@@ -237,8 +270,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||
|
||||
if (std::error_code EC = Buffer.getError()) {
|
||||
Error = EC.message();
|
||||
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
|
||||
return Diags.Report(diag::err_fe_error_reading)
|
||||
<< Opts.InputFile << EC.message();
|
||||
}
|
||||
|
||||
SourceMgr SrcMgr;
|
||||
@@ -254,6 +287,10 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
assert(MRI && "Unable to create target register info!");
|
||||
|
||||
MCTargetOptions MCOptions;
|
||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
||||
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||
|
||||
std::unique_ptr<MCAsmInfo> MAI(
|
||||
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
|
||||
assert(MAI && "Unable to create target asm info!");
|
||||
@@ -300,6 +337,10 @@ 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)
|
||||
@@ -339,6 +380,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
|
||||
MCOptions.MCNoWarn = Opts.NoWarn;
|
||||
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
|
||||
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
|
||||
MCOptions.ABIName = Opts.TargetABI;
|
||||
|
||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||
@@ -348,7 +390,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE;
|
||||
if (Opts.ShowEncoding)
|
||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
|
||||
@@ -368,7 +410,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
}
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE(
|
||||
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
TheTarget->createMCCodeEmitter(*MCII, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
assert(MAB && "Unable to create asm backend!");
|
||||
@@ -382,7 +424,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ true));
|
||||
Str.get()->InitSections(Opts.NoExecStack);
|
||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||
}
|
||||
|
||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||
@@ -390,7 +432,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
|
||||
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||
Str.get()->SwitchSection(AsmLabel);
|
||||
Str.get()->switchSection(AsmLabel);
|
||||
Str.get()->emitZeros(1);
|
||||
}
|
||||
|
||||
@@ -442,7 +484,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts,
|
||||
return Failed;
|
||||
}
|
||||
|
||||
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
||||
static void LLVMErrorHandler(void *UserData, const char *Message,
|
||||
bool GenCrashDiag) {
|
||||
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||
|
||||
@@ -479,9 +521,10 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
if (Asm.ShowHelp) {
|
||||
getDriverOptTable().printHelp(
|
||||
llvm::outs(), "clang -cc1as [options] file...",
|
||||
"Clang Integrated Assembler",
|
||||
/*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
|
||||
/*ShowAllAliases=*/false);
|
||||
"Clang Integrated Assembler", /*ShowHidden=*/false,
|
||||
/*ShowAllAliases=*/false,
|
||||
llvm::opt::Visibility(driver::options::CC1AsOption));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+25
-1
@@ -1,3 +1,6 @@
|
||||
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
|
||||
// See cc1as.cpp for details.
|
||||
|
||||
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
@@ -44,7 +47,7 @@ struct AssemblerInvocation {
|
||||
std::string DwarfDebugFlags;
|
||||
std::string DwarfDebugProducer;
|
||||
std::string DebugCompilationDir;
|
||||
std::map<const std::string, const std::string> DebugPrefixMap;
|
||||
llvm::SmallVector<std::pair<std::string, std::string>, 0> DebugPrefixMap;
|
||||
llvm::DebugCompressionType CompressDebugSections =
|
||||
llvm::DebugCompressionType::None;
|
||||
std::string MainFileName;
|
||||
@@ -82,9 +85,17 @@ struct AssemblerInvocation {
|
||||
unsigned NoExecStack : 1;
|
||||
unsigned FatalWarnings : 1;
|
||||
unsigned NoWarn : 1;
|
||||
unsigned NoTypeCheck : 1;
|
||||
unsigned IncrementalLinkerCompatible : 1;
|
||||
unsigned EmbedBitcode : 1;
|
||||
|
||||
/// Whether to emit DWARF unwind info.
|
||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||
|
||||
// Whether to emit compact-unwind for non-canonical entries.
|
||||
// Note: maybe overridden by other constraints.
|
||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||
|
||||
/// The name of the relocation model to use.
|
||||
std::string RelocationModel;
|
||||
|
||||
@@ -92,6 +103,16 @@ struct AssemblerInvocation {
|
||||
/// otherwise.
|
||||
std::string TargetABI;
|
||||
|
||||
/// Darwin target variant triple, the variant of the deployment target
|
||||
/// for which the code is being compiled.
|
||||
std::optional<llvm::Triple> DarwinTargetVariantTriple;
|
||||
|
||||
/// The version of the darwin target variant SDK which was used during the
|
||||
/// compilation
|
||||
llvm::VersionTuple DarwinTargetVariantSDKVersion;
|
||||
|
||||
/// The name of a file to use with \c .secure_log_unique directives.
|
||||
std::string AsSecureLogFile;
|
||||
/// @}
|
||||
|
||||
public:
|
||||
@@ -108,10 +129,13 @@ public:
|
||||
NoExecStack = 0;
|
||||
FatalWarnings = 0;
|
||||
NoWarn = 0;
|
||||
NoTypeCheck = 0;
|
||||
IncrementalLinkerCompatible = 0;
|
||||
Dwarf64 = 0;
|
||||
DwarfVersion = 0;
|
||||
EmbedBitcode = 0;
|
||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||
EmitCompactUnwindNonCanonical = false;
|
||||
}
|
||||
|
||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
#include <clang/Basic/DiagnosticOptions.h>
|
||||
#include <clang/CodeGen/CodeGenAction.h>
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <clang/FrontendTool/Utils.h>
|
||||
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||
#include <llvm/Option/Option.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/TargetParser/Host.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
+1
-13
@@ -3,7 +3,6 @@ package builder
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -74,16 +73,5 @@ func LookupCommand(name string) (string, error) {
|
||||
}
|
||||
return cmdName, nil
|
||||
}
|
||||
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
}
|
||||
|
||||
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()
|
||||
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
}
|
||||
|
||||
+23
-12
@@ -1,8 +1,8 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
@@ -24,26 +24,37 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
spec.OpenOCDCommands = options.OpenOCDCommands
|
||||
}
|
||||
|
||||
goroot := goenv.Get("GOROOT")
|
||||
if goroot == "" {
|
||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
// Version range supported by TinyGo.
|
||||
const minorMin = 19
|
||||
const minorMax = 23
|
||||
|
||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
||||
// Check that we support this Go toolchain version.
|
||||
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
return nil, err
|
||||
}
|
||||
if major != 1 || minor < 15 || minor > 17 {
|
||||
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||
if 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.19 through 1.23, got go%d.%d", gorootMajor, gorootMinor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
// 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,
|
||||
ClangHeaders: clangHeaderPath,
|
||||
GoMinorVersion: gorootMinor,
|
||||
TestConfig: options.TestConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Create a job that builds a Darwin libSystem.dylib stub library. This library
|
||||
// contains all the symbols needed so that we can link against it, but it
|
||||
// doesn't contain any real symbol implementations.
|
||||
func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJob {
|
||||
return &compileJob{
|
||||
description: "compile Darwin libSystem.dylib",
|
||||
run: func(job *compileJob) (err error) {
|
||||
arch := strings.Split(config.Triple(), "-")[0]
|
||||
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
||||
objpath := filepath.Join(tmpdir, "libSystem.o")
|
||||
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
||||
|
||||
// Compile assembly file to object file.
|
||||
flags := []string{
|
||||
"-nostdlib",
|
||||
"--target=" + config.Triple(),
|
||||
"-c",
|
||||
"-o", objpath,
|
||||
inpath,
|
||||
}
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", flags...)
|
||||
}
|
||||
err = runCCompiler(flags...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Link object file to dynamic library.
|
||||
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
||||
flags = []string{
|
||||
"-flavor", "darwin",
|
||||
"-demangle",
|
||||
"-dynamic",
|
||||
"-dylib",
|
||||
"-arch", arch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
"-install_name", "/usr/lib/libSystem.B.dylib",
|
||||
"-o", job.result,
|
||||
objpath,
|
||||
}
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("ld.lld", flags...)
|
||||
}
|
||||
return link("ld.lld", flags...)
|
||||
},
|
||||
}
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
|
||||
// multiple locations, which should make it find the directory when installed in
|
||||
// various ways.
|
||||
func getClangHeaderPath(TINYGOROOT string) string {
|
||||
// Check whether we're running from the source directory.
|
||||
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
return path
|
||||
}
|
||||
|
||||
// Check whether we're running from the installation directory.
|
||||
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
return path
|
||||
}
|
||||
|
||||
// It looks like we are built with a system-installed LLVM. Do a last
|
||||
// attempt: try to use Clang headers relative to the clang binary.
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
for _, cmdName := range commands["clang"] {
|
||||
binpath, err := exec.LookPath(cmdName)
|
||||
if err == nil {
|
||||
// This should be the command that will also be used by
|
||||
// execCommand. To avoid inconsistencies, make sure we use the
|
||||
// headers relative to this command.
|
||||
binpath, err = filepath.EvalSymlinks(binpath)
|
||||
if err != nil {
|
||||
// Unexpected.
|
||||
return ""
|
||||
}
|
||||
// Example executable:
|
||||
// /usr/lib/llvm-9/bin/clang
|
||||
// Example include path:
|
||||
// /usr/lib/llvm-9/lib64/clang/9.0.1/include/
|
||||
llvmRoot := filepath.Dir(filepath.Dir(binpath))
|
||||
clangVersionRoot := filepath.Join(llvmRoot, "lib64", "clang")
|
||||
dirs64, err64 := ioutil.ReadDir(clangVersionRoot)
|
||||
// Example include path:
|
||||
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
|
||||
clangVersionRoot = filepath.Join(llvmRoot, "lib", "clang")
|
||||
dirs32, err32 := ioutil.ReadDir(clangVersionRoot)
|
||||
if err64 != nil && err32 != nil {
|
||||
// Unexpected.
|
||||
continue
|
||||
}
|
||||
dirnames := make([]string, len(dirs64)+len(dirs32))
|
||||
dirCount := 0
|
||||
for _, d := range dirs32 {
|
||||
name := d.Name()
|
||||
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
|
||||
dirnames[dirCount] = filepath.Join(llvmRoot, "lib", "clang", name)
|
||||
dirCount++
|
||||
}
|
||||
}
|
||||
for _, d := range dirs64 {
|
||||
name := d.Name()
|
||||
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
|
||||
dirnames[dirCount] = filepath.Join(llvmRoot, "lib64", "clang", name)
|
||||
dirCount++
|
||||
}
|
||||
}
|
||||
sort.Strings(dirnames)
|
||||
// Check for the highest version first.
|
||||
for i := dirCount - 1; i >= 0; i-- {
|
||||
path := filepath.Join(dirnames[i], "include")
|
||||
_, err := os.Stat(filepath.Join(path, "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On Arch Linux, the clang executable is stored in /usr/bin rather than being symlinked from there.
|
||||
// Search directly in /usr/lib for clang.
|
||||
if matches, err := filepath.Glob("/usr/lib/clang/" + llvmMajor + ".*.*"); err == nil {
|
||||
// Check for the highest version first.
|
||||
sort.Strings(matches)
|
||||
for i := len(matches) - 1; i >= 0; i-- {
|
||||
path := filepath.Join(matches[i], "include")
|
||||
_, err := os.Stat(filepath.Join(path, "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find it.
|
||||
return ""
|
||||
}
|
||||
+7
-5
@@ -3,7 +3,8 @@ package builder
|
||||
// MultiError is a list of multiple errors (actually: diagnostics) returned
|
||||
// during LLVM IR generation.
|
||||
type MultiError struct {
|
||||
Errs []error
|
||||
ImportPath string
|
||||
Errs []error
|
||||
}
|
||||
|
||||
func (e *MultiError) Error() string {
|
||||
@@ -14,15 +15,16 @@ func (e *MultiError) Error() string {
|
||||
|
||||
// newMultiError returns a *MultiError if there is more than one error, or
|
||||
// returns that error directly when there is only one. Passing an empty slice
|
||||
// will lead to a panic.
|
||||
func newMultiError(errs []error) error {
|
||||
// will return nil (because there is no error).
|
||||
// The importPath may be passed if this error is for a single package.
|
||||
func newMultiError(errs []error, importPath string) error {
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
panic("attempted to create empty MultiError")
|
||||
return nil
|
||||
case 1:
|
||||
return errs[0]
|
||||
default:
|
||||
return &MultiError{errs}
|
||||
return &MultiError{importPath, errs}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-6
@@ -13,8 +13,9 @@ import (
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type espImageSegment struct {
|
||||
@@ -22,7 +23,7 @@ type espImageSegment struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
// makeESPFirmare converts an input ELF file to an image file for an ESP32 or
|
||||
// makeESPFirmareImage 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.
|
||||
//
|
||||
@@ -78,15 +79,31 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
// An added benefit is that we don't need to check for errors all the time.
|
||||
outf := &bytes.Buffer{}
|
||||
|
||||
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
|
||||
// image, not just a binary to be flashed at 0x1000 for example.
|
||||
chip := format
|
||||
makeImage := false
|
||||
if strings.HasSuffix(format, "-img") {
|
||||
makeImage = true
|
||||
chip = format[:len(format)-len("-img")]
|
||||
}
|
||||
|
||||
if makeImage {
|
||||
// The bootloader starts at 0x1000, or 4096.
|
||||
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||
// application in the bootloader location.
|
||||
outf.Write(make([]byte, 4096))
|
||||
}
|
||||
|
||||
// Chip IDs. Source:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
}[format]
|
||||
}[chip]
|
||||
|
||||
// Image header.
|
||||
switch format {
|
||||
switch chip {
|
||||
case "esp32", "esp32c3":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
@@ -155,12 +172,22 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||
outf.WriteByte(checksum)
|
||||
|
||||
if format != "esp8266" {
|
||||
if chip != "esp8266" {
|
||||
// SHA256 hash (to protect against image corruption, not for security).
|
||||
hash := sha256.Sum256(outf.Bytes())
|
||||
outf.Write(hash[:])
|
||||
}
|
||||
|
||||
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||
// image to be a certain size.
|
||||
if makeImage {
|
||||
// Use a default image size of 4MB.
|
||||
grow := 4096*1024 - outf.Len()
|
||||
if grow > 0 {
|
||||
outf.Write(make([]byte, grow))
|
||||
}
|
||||
}
|
||||
|
||||
// Write the image to the output file.
|
||||
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
||||
return os.WriteFile(outfile, outf.Bytes(), 0666)
|
||||
}
|
||||
|
||||
+7
-17
@@ -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()
|
||||
@@ -134,7 +124,7 @@ func runJobs(job *compileJob, sema chan struct{}) error {
|
||||
numRunningJobs--
|
||||
<-sema
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## finished:", job.description, "(time "+job.duration.String()+")")
|
||||
fmt.Println("## finished:", completed.description, "(time "+completed.duration.String()+")")
|
||||
}
|
||||
if completed.err != nil {
|
||||
// Wait for any current jobs to finish.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+72
-32
@@ -1,7 +1,8 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -24,29 +25,16 @@ type Library struct {
|
||||
// cflags returns the C flags specific to this library
|
||||
cflags func(target, headerPath string) []string
|
||||
|
||||
// The source directory, relative to TINYGOROOT.
|
||||
sourceDir string
|
||||
// The source directory.
|
||||
sourceDir func() string
|
||||
|
||||
// The source files, relative to sourceDir.
|
||||
librarySources func(target string) []string
|
||||
librarySources func(target string) ([]string, error)
|
||||
|
||||
// The source code for the crt1.o file, relative to sourceDir.
|
||||
crt1Source string
|
||||
}
|
||||
|
||||
// Load the library archive, possibly generating and caching it if needed.
|
||||
// The resulting directory may be stored in the provided tmpdir, which is
|
||||
// expected to be removed after the Load call.
|
||||
func (l *Library) Load(config *compileopts.Config, tmpdir string) (dir string, err error) {
|
||||
job, unlock, err := l.load(config, tmpdir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer unlock()
|
||||
err = runJobs(job, config.Options.Semaphore)
|
||||
return filepath.Dir(job.result), err
|
||||
}
|
||||
|
||||
// load returns a compile job to build this library file for the given target
|
||||
// and CPU. It may return a dummy compileJob if the library build is already
|
||||
// cached. The path is stored as job.result but is only valid after the job has
|
||||
@@ -94,7 +82,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
target := config.Triple()
|
||||
if l.makeHeaders != nil {
|
||||
if _, err = os.Stat(headerPath); err != nil {
|
||||
temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*")
|
||||
temporaryHeaderPath, err := os.MkdirTemp(outdir, "include.tmp*")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -103,13 +91,17 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = os.Chmod(temporaryHeaderPath, 0o755) // TempDir uses 0o700 by default
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = os.Rename(temporaryHeaderPath, headerPath)
|
||||
if err != nil {
|
||||
switch {
|
||||
case os.IsExist(err):
|
||||
case errors.Is(err, fs.ErrExist):
|
||||
// Another invocation of TinyGo also seems to have already created the headers.
|
||||
|
||||
case runtime.GOOS == "windows" && os.IsPermission(err):
|
||||
case runtime.GOOS == "windows" && errors.Is(err, fs.ErrPermission):
|
||||
// On Windows, a rename with a destination directory that already
|
||||
// exists does not result in an IsExist error, but rather in an
|
||||
// access denied error. To be sure, check for this case by checking
|
||||
@@ -137,25 +129,57 @@ 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", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
resourceDir := goenv.ClangResourceDir(false)
|
||||
if resourceDir != "" {
|
||||
args = append(args, "-resource-dir="+resourceDir)
|
||||
}
|
||||
cpu := config.CPU()
|
||||
if cpu != "" {
|
||||
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
||||
// However, ARM has not done this.
|
||||
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||
args = append(args, "-march="+cpu)
|
||||
} else if strings.HasPrefix(target, "avr") {
|
||||
args = append(args, "-mmcu="+cpu)
|
||||
} else {
|
||||
args = append(args, "-mcpu="+cpu)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables")
|
||||
if config.ABI() != "" {
|
||||
args = append(args, "-mabi="+config.ABI())
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
switch compileopts.CanonicalArchName(target) {
|
||||
case "arm":
|
||||
if strings.Split(target, "-")[2] == "linux" {
|
||||
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
} else {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
}
|
||||
case "avr":
|
||||
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||
// from what most code (and certainly compiler-rt) expects. So we need
|
||||
// to force the compiler to use 64-bit floating point numbers for
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
case "riscv32":
|
||||
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
||||
case "riscv64":
|
||||
args = append(args, "-march=rv64gc")
|
||||
case "mips":
|
||||
args = append(args, "-fno-pic")
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv64-") {
|
||||
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
||||
if config.Target.SoftFloat {
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
@@ -170,7 +194,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
defer once.Do(unlock)
|
||||
|
||||
// Create an archive of all object files.
|
||||
f, err := ioutil.TempFile(outdir, "libc.a.tmp*")
|
||||
f, err := os.CreateTemp(outdir, "libc.a.tmp*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -182,20 +206,30 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(f.Name(), 0o644) // TempFile uses 0o600 by default
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
return os.Rename(f.Name(), archiveFilePath)
|
||||
},
|
||||
}
|
||||
|
||||
sourceDir := l.sourceDir()
|
||||
|
||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||
// archive job above, so must be run first.
|
||||
for _, path := range l.librarySources(target) {
|
||||
paths, err := l.librarySources(target)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, path := range paths {
|
||||
// Strip leading "../" parts off the path.
|
||||
cleanpath := path
|
||||
for strings.HasPrefix(cleanpath, "../") {
|
||||
cleanpath = cleanpath[3:]
|
||||
}
|
||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
|
||||
srcpath := filepath.Join(sourceDir, path)
|
||||
objpath := filepath.Join(dir, cleanpath+".o")
|
||||
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||
objs = append(objs, objpath)
|
||||
@@ -205,6 +239,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", compileArgs...)
|
||||
}
|
||||
err := runCCompiler(compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
@@ -219,18 +256,21 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// (It could be done in parallel with creating the ar file, but it probably
|
||||
// won't make much of a difference in speed).
|
||||
if l.crt1Source != "" {
|
||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
|
||||
srcpath := filepath.Join(sourceDir, l.crt1Source)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*")
|
||||
tmpfile, err := os.CreateTemp(outdir, "crt1.o.tmp*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpfile.Close()
|
||||
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands("clang", compileArgs...)
|
||||
}
|
||||
err = runCCompiler(compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
|
||||
+21
-13
@@ -1,24 +1,32 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
// This file provides C wrappers for liblld.
|
||||
|
||||
#include <lld/Common/Driver.h>
|
||||
#include <llvm/Support/Parallel.h>
|
||||
|
||||
LLD_HAS_DRIVER(coff)
|
||||
LLD_HAS_DRIVER(elf)
|
||||
LLD_HAS_DRIVER(mingw)
|
||||
LLD_HAS_DRIVER(macho)
|
||||
LLD_HAS_DRIVER(wasm)
|
||||
|
||||
static void configure() {
|
||||
#if _WIN64
|
||||
// This is a hack to work around a hang in the LLD linker on Windows, with
|
||||
// -DLLVM_ENABLE_THREADS=ON. It has a similar effect as the -threads=1
|
||||
// linker flag, but with support for the COFF linker.
|
||||
llvm::parallel::strategy = llvm::hardware_concurrency(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool tinygo_link_elf(int argc, char **argv) {
|
||||
bool tinygo_link(int argc, char **argv) {
|
||||
configure();
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
bool tinygo_link_mingw(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
bool tinygo_link_wasm(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
||||
lld::Result r = lld::lldMain(args, llvm::outs(), llvm::errs(), LLD_ALL_DRIVERS);
|
||||
return !r.retCode;
|
||||
}
|
||||
|
||||
} // external "C"
|
||||
|
||||
+39
-10
@@ -1,6 +1,7 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -9,7 +10,7 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var MinGW = Library{
|
||||
var libMinGW = Library{
|
||||
name: "mingw-w64",
|
||||
makeHeaders: func(target, includeDir string) error {
|
||||
// copy _mingw.h
|
||||
@@ -26,13 +27,30 @@ var MinGW = Library{
|
||||
_, err = io.Copy(outf, inf)
|
||||
return err
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
|
||||
cflags: func(target, headerPath string) []string {
|
||||
// No flags necessary because there are no files to compile.
|
||||
return nil
|
||||
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
|
||||
return []string{
|
||||
"-nostdlibinc",
|
||||
"-isystem", mingwDir + "/mingw-w64-headers/crt",
|
||||
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
|
||||
"-I" + headerPath,
|
||||
}
|
||||
},
|
||||
librarySources: func(target string) []string {
|
||||
// We only use the UCRT DLL file. No source files necessary.
|
||||
return nil
|
||||
librarySources: func(target string) ([]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",
|
||||
}
|
||||
return sources, nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -42,7 +60,7 @@ var MinGW = Library{
|
||||
//
|
||||
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
|
||||
// compile these files.
|
||||
func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
||||
var jobs []*compileJob
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
||||
@@ -51,7 +69,7 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||
for _, name := range []string{
|
||||
"kernel32.def.in",
|
||||
"api-ms-win-crt-conio-l1-1-0.def",
|
||||
"api-ms-win-crt-convert-l1-1-0.def",
|
||||
"api-ms-win-crt-convert-l1-1-0.def.in",
|
||||
"api-ms-win-crt-environment-l1-1-0.def",
|
||||
"api-ms-win-crt-filesystem-l1-1-0.def",
|
||||
"api-ms-win-crt-heap-l1-1-0.def",
|
||||
@@ -73,16 +91,27 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
||||
result: outpath,
|
||||
run: func(job *compileJob) error {
|
||||
defpath := inpath
|
||||
var archDef, emulation string
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
archDef = "-DDEF_X64"
|
||||
emulation = "i386pep"
|
||||
case "arm64":
|
||||
archDef = "-DDEF_ARM64"
|
||||
emulation = "arm64pe"
|
||||
default:
|
||||
return fmt.Errorf("unsupported architecture for mingw-w64: %s", goarch)
|
||||
}
|
||||
if strings.HasSuffix(inpath, ".in") {
|
||||
// .in files need to be preprocessed by a preprocessor (-E)
|
||||
// first.
|
||||
defpath = outpath + ".def"
|
||||
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
||||
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", archDef, "-DDATA", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
|
||||
return link("ld.lld", "-m", emulation, "-o", outpath, defpath)
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
|
||||
+38
-11
@@ -3,7 +3,6 @@ package builder
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var Musl = Library{
|
||||
var libMusl = Library{
|
||||
name: "musl",
|
||||
makeHeaders: func(target, includeDir string) error {
|
||||
bits := filepath.Join(includeDir, "bits")
|
||||
@@ -35,7 +34,7 @@ var Musl = Library{
|
||||
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||
}
|
||||
for _, infile := range infiles {
|
||||
data, err := ioutil.ReadFile(infile)
|
||||
data, err := os.ReadFile(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,7 +62,7 @@ var Musl = Library{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
||||
data, err := os.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -78,7 +77,7 @@ var Musl = Library{
|
||||
cflags: func(target, headerPath string) []string {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
|
||||
return []string{
|
||||
cflags := []string{
|
||||
"-std=c99", // same as in musl
|
||||
"-D_XOPEN_SOURCE=700", // same as in musl
|
||||
// Musl triggers some warnings and we don't want to show any
|
||||
@@ -90,6 +89,11 @@ var Musl = Library{
|
||||
"-Wno-shift-op-parentheses",
|
||||
"-Wno-ignored-attributes",
|
||||
"-Wno-string-plus-int",
|
||||
"-Wno-ignored-pragmas",
|
||||
"-Wno-tautological-constant-out-of-range-compare",
|
||||
"-Wno-deprecated-non-prototype",
|
||||
"-Wno-format",
|
||||
"-Wno-parentheses",
|
||||
"-Qunused-arguments",
|
||||
// Select include dirs. Don't include standard library includes
|
||||
// (that would introduce host dependencies and other complications),
|
||||
@@ -103,28 +107,48 @@ var Musl = Library{
|
||||
"-I" + muslDir + "/include",
|
||||
"-fno-stack-protector",
|
||||
}
|
||||
return cflags
|
||||
},
|
||||
sourceDir: "lib/musl/src",
|
||||
librarySources: func(target string) []string {
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
globs := []string{
|
||||
"env/*.c",
|
||||
"errno/*.c",
|
||||
"exit/*.c",
|
||||
"fcntl/*.c",
|
||||
"internal/defsysinfo.c",
|
||||
"internal/libc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"locale/*.c",
|
||||
"linux/*.c",
|
||||
"malloc/*.c",
|
||||
"malloc/mallocng/*.c",
|
||||
"mman/*.c",
|
||||
"math/*.c",
|
||||
"multibyte/*.c",
|
||||
"signal/" + arch + "/*.s",
|
||||
"signal/*.c",
|
||||
"stdio/*.c",
|
||||
"string/*.c",
|
||||
"thread/" + arch + "/*.s",
|
||||
"thread/" + arch + "/*.c",
|
||||
"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
|
||||
@@ -139,13 +163,16 @@ var Musl = Library{
|
||||
// > ErrBadPattern, when pattern is malformed.
|
||||
// So the only possible error is when the (statically defined)
|
||||
// pattern is wrong. In other words, a programming bug.
|
||||
panic("could not glob source dirs: " + err.Error())
|
||||
return nil, fmt.Errorf("musl: could not glob source dirs: %w", err)
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return nil, fmt.Errorf("musl: did not find any files for pattern %#v", pattern)
|
||||
}
|
||||
for _, match := range matches {
|
||||
relpath, err := filepath.Rel(basepath, match)
|
||||
if err != nil {
|
||||
// Not sure if this is even possible.
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
// Make sure architecture specific files override generic files.
|
||||
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
|
||||
@@ -157,7 +184,7 @@ var Musl = Library{
|
||||
sources = append(sources, relpath)
|
||||
}
|
||||
}
|
||||
return sources
|
||||
return sources, nil
|
||||
},
|
||||
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
|
||||
}
|
||||
|
||||
+109
-19
@@ -1,27 +1,117 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/sigurn/crc16"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
)
|
||||
|
||||
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrfutil%2FUG%2Fnrfutil%2Fnrfutil_intro.html
|
||||
|
||||
func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string) error {
|
||||
cmdLine := []string{"nrfutil", "pkg", "generate", "--hw-version", "52", "--sd-req", "0x0", "--debug-mode", "--application", infile, outfile}
|
||||
|
||||
if options.PrintCommands != nil {
|
||||
options.PrintCommands(cmdLine[0], cmdLine[1:]...)
|
||||
}
|
||||
|
||||
cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
|
||||
cmd.Stdout = ioutil.Discard
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
|
||||
}
|
||||
return nil
|
||||
// Structure of the manifest.json file.
|
||||
type jsonManifest struct {
|
||||
Manifest struct {
|
||||
Application struct {
|
||||
BinaryFile string `json:"bin_file"`
|
||||
DataFile string `json:"dat_file"`
|
||||
InitPacketData nrfInitPacket `json:"init_packet_data"`
|
||||
} `json:"application"`
|
||||
DFUVersion float64 `json:"dfu_version"` // yes, this is a JSON number, not a string
|
||||
} `json:"manifest"`
|
||||
}
|
||||
|
||||
// Structure of the init packet.
|
||||
// Source:
|
||||
// https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/master/lib/sdk11/components/libraries/bootloader_dfu/dfu_init.h#L47-L57
|
||||
type nrfInitPacket struct {
|
||||
ApplicationVersion uint32 `json:"application_version"`
|
||||
DeviceRevision uint16 `json:"device_revision"`
|
||||
DeviceType uint16 `json:"device_type"`
|
||||
FirmwareCRC16 uint16 `json:"firmware_crc16"`
|
||||
SoftDeviceRequired []uint16 `json:"softdevice_req"` // this is actually a variable length array
|
||||
}
|
||||
|
||||
// Create the init packet (the contents of application.dat).
|
||||
func (p nrfInitPacket) createInitPacket() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
binary.Write(buf, binary.LittleEndian, p.DeviceType) // uint16_t device_type;
|
||||
binary.Write(buf, binary.LittleEndian, p.DeviceRevision) // uint16_t device_rev;
|
||||
binary.Write(buf, binary.LittleEndian, p.ApplicationVersion) // uint32_t app_version;
|
||||
binary.Write(buf, binary.LittleEndian, uint16(len(p.SoftDeviceRequired))) // uint16_t softdevice_len;
|
||||
binary.Write(buf, binary.LittleEndian, p.SoftDeviceRequired) // uint16_t softdevice[1];
|
||||
binary.Write(buf, binary.LittleEndian, p.FirmwareCRC16)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Make a Nordic DFU firmware image from an ELF file.
|
||||
func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string) error {
|
||||
// Read ELF file as input and convert it to a binary image file.
|
||||
_, data, err := extractROM(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the zip file in memory.
|
||||
// It won't be very large anyway.
|
||||
buf := &bytes.Buffer{}
|
||||
w := zip.NewWriter(buf)
|
||||
|
||||
// Write the application binary to the zip file.
|
||||
binw, err := w.Create("application.bin")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = binw.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the init packet.
|
||||
initPacket := nrfInitPacket{
|
||||
ApplicationVersion: 0xffff_ffff, // appears to be unused by the Adafruit bootloader
|
||||
DeviceRevision: 0xffff, // DFU_DEVICE_REVISION_EMPTY
|
||||
DeviceType: 0x0052, // ADAFRUIT_DEVICE_TYPE
|
||||
FirmwareCRC16: crc16.Checksum(data, crc16.MakeTable(crc16.CRC16_CCITT_FALSE)),
|
||||
SoftDeviceRequired: []uint16{0xfffe}, // DFU_SOFTDEVICE_ANY
|
||||
}
|
||||
|
||||
// Write the init packet to the zip file.
|
||||
datw, err := w.Create("application.dat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = datw.Write(initPacket.createInitPacket())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the JSON manifest.
|
||||
manifest := &jsonManifest{}
|
||||
manifest.Manifest.Application.BinaryFile = "application.bin"
|
||||
manifest.Manifest.Application.DataFile = "application.dat"
|
||||
manifest.Manifest.Application.InitPacketData = initPacket
|
||||
manifest.Manifest.DFUVersion = 0.5
|
||||
|
||||
// Write the JSON manifest to the file.
|
||||
jsonw, err := w.Create("manifest.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enc := json.NewEncoder(jsonw)
|
||||
enc.SetIndent("", " ")
|
||||
err = enc.Encode(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finish the zip file.
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(outfile, buf.Bytes(), 0o666)
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package builder
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
@@ -87,7 +87,7 @@ func extractROM(path string) (uint64, []byte, error) {
|
||||
// Pad the difference
|
||||
rom = append(rom, make([]byte, diff)...)
|
||||
}
|
||||
data, err := ioutil.ReadAll(prog.Open())
|
||||
data, err := io.ReadAll(prog.Open())
|
||||
if err != nil {
|
||||
return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err}
|
||||
}
|
||||
|
||||
+408
-210
@@ -3,13 +3,14 @@ package builder
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// Picolibc is a C library for bare metal embedded devices. It was originally
|
||||
// libPicolibc is a C library for bare metal embedded devices. It was originally
|
||||
// based on newlib.
|
||||
var Picolibc = Library{
|
||||
var libPicolibc = Library{
|
||||
name: "picolibc",
|
||||
makeHeaders: func(target, includeDir string) error {
|
||||
f, err := os.Create(filepath.Join(includeDir, "picolibc.h"))
|
||||
@@ -19,227 +20,424 @@ var Picolibc = Library{
|
||||
return f.Close()
|
||||
},
|
||||
cflags: func(target, headerPath string) []string {
|
||||
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
||||
newlibDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib")
|
||||
return []string{
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-std=gnu11",
|
||||
"-D_COMPILING_NEWLIB",
|
||||
"-DHAVE_ALIAS_ATTRIBUTE",
|
||||
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||
"-DTINY_STDIO",
|
||||
"-DPOSIX_IO",
|
||||
"-DFORMAT_DEFAULT_INTEGER", // use __i_vfprintf and __i_vfscanf by default
|
||||
"-D_IEEE_LIBM",
|
||||
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||
"-D_WANT_IO_C99_FORMATS",
|
||||
"-nostdlibinc",
|
||||
"-isystem", picolibcDir + "/include",
|
||||
"-I" + picolibcDir + "/tinystdio",
|
||||
"-isystem", newlibDir + "/libc/include",
|
||||
"-I" + newlibDir + "/libc/tinystdio",
|
||||
"-I" + newlibDir + "/libm/common",
|
||||
"-I" + headerPath,
|
||||
}
|
||||
},
|
||||
sourceDir: "lib/picolibc/newlib/libc",
|
||||
librarySources: func(target string) []string {
|
||||
return picolibcSources
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
sources := append([]string(nil), picolibcSources...)
|
||||
if !strings.HasPrefix(target, "avr") {
|
||||
// Small chips without long jumps can't compile many files (printf,
|
||||
// pow, etc). Therefore exclude those source files for those chips.
|
||||
// Unfortunately it's difficult to exclude only some chips, so this
|
||||
// excludes those files on all AVR chips for now.
|
||||
// More information:
|
||||
// https://github.com/llvm/llvm-project/issues/67042
|
||||
sources = append(sources, picolibcSourcesLarge...)
|
||||
}
|
||||
return sources, nil
|
||||
},
|
||||
}
|
||||
|
||||
var picolibcSources = []string{
|
||||
"../../../picolibc-stdio.c",
|
||||
"../../picolibc-stdio.c",
|
||||
|
||||
"tinystdio/asprintf.c",
|
||||
"tinystdio/atod_engine.c",
|
||||
"tinystdio/atod_ryu.c",
|
||||
"tinystdio/atof_engine.c",
|
||||
"tinystdio/atof_ryu.c",
|
||||
//"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
|
||||
"tinystdio/clearerr.c",
|
||||
"tinystdio/compare_exchange.c",
|
||||
"tinystdio/dtoa_data.c",
|
||||
"tinystdio/dtoa_engine.c",
|
||||
"tinystdio/dtoa_ryu.c",
|
||||
"tinystdio/ecvtbuf.c",
|
||||
"tinystdio/ecvt.c",
|
||||
"tinystdio/ecvt_data.c",
|
||||
"tinystdio/ecvtfbuf.c",
|
||||
"tinystdio/ecvtf.c",
|
||||
"tinystdio/ecvtf_data.c",
|
||||
"tinystdio/exchange.c",
|
||||
//"tinystdio/fclose.c", // posix-io
|
||||
"tinystdio/fcvtbuf.c",
|
||||
"tinystdio/fcvt.c",
|
||||
"tinystdio/fcvtfbuf.c",
|
||||
"tinystdio/fcvtf.c",
|
||||
"tinystdio/fdevopen.c",
|
||||
//"tinystdio/fdopen.c", // posix-io
|
||||
"tinystdio/feof.c",
|
||||
"tinystdio/ferror.c",
|
||||
"tinystdio/fflush.c",
|
||||
"tinystdio/fgetc.c",
|
||||
"tinystdio/fgets.c",
|
||||
"tinystdio/fileno.c",
|
||||
"tinystdio/filestrget.c",
|
||||
"tinystdio/filestrputalloc.c",
|
||||
"tinystdio/filestrput.c",
|
||||
//"tinystdio/fopen.c", // posix-io
|
||||
"tinystdio/fprintf.c",
|
||||
"tinystdio/fputc.c",
|
||||
"tinystdio/fputs.c",
|
||||
"tinystdio/fread.c",
|
||||
"tinystdio/fscanf.c",
|
||||
"tinystdio/fseek.c",
|
||||
"tinystdio/ftell.c",
|
||||
"tinystdio/ftoa_data.c",
|
||||
"tinystdio/ftoa_engine.c",
|
||||
"tinystdio/ftoa_ryu.c",
|
||||
"tinystdio/fwrite.c",
|
||||
"tinystdio/gcvtbuf.c",
|
||||
"tinystdio/gcvt.c",
|
||||
"tinystdio/gcvtfbuf.c",
|
||||
"tinystdio/gcvtf.c",
|
||||
"tinystdio/getchar.c",
|
||||
"tinystdio/gets.c",
|
||||
"tinystdio/matchcaseprefix.c",
|
||||
"tinystdio/perror.c",
|
||||
//"tinystdio/posixiob.c", // posix-io
|
||||
//"tinystdio/posixio.c", // posix-io
|
||||
"tinystdio/printf.c",
|
||||
"tinystdio/putchar.c",
|
||||
"tinystdio/puts.c",
|
||||
"tinystdio/ryu_divpow2.c",
|
||||
"tinystdio/ryu_log10.c",
|
||||
"tinystdio/ryu_log2pow5.c",
|
||||
"tinystdio/ryu_pow5bits.c",
|
||||
"tinystdio/ryu_table.c",
|
||||
"tinystdio/ryu_umul128.c",
|
||||
"tinystdio/scanf.c",
|
||||
"tinystdio/setbuf.c",
|
||||
"tinystdio/setvbuf.c",
|
||||
//"tinystdio/sflags.c", // posix-io
|
||||
"tinystdio/snprintf.c",
|
||||
"tinystdio/snprintfd.c",
|
||||
"tinystdio/snprintff.c",
|
||||
"tinystdio/sprintf.c",
|
||||
"tinystdio/sprintfd.c",
|
||||
"tinystdio/sprintff.c",
|
||||
"tinystdio/sscanf.c",
|
||||
"tinystdio/strfromd.c",
|
||||
"tinystdio/strfromf.c",
|
||||
"tinystdio/strtod.c",
|
||||
"tinystdio/strtod_l.c",
|
||||
"tinystdio/strtof.c",
|
||||
//"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
|
||||
//"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
|
||||
"tinystdio/ungetc.c",
|
||||
"tinystdio/vasprintf.c",
|
||||
"tinystdio/vfiprintf.c",
|
||||
"tinystdio/vfiscanf.c",
|
||||
"tinystdio/vfprintf.c",
|
||||
"tinystdio/vfprintff.c",
|
||||
"tinystdio/vfscanf.c",
|
||||
"tinystdio/vfscanff.c",
|
||||
"tinystdio/vprintf.c",
|
||||
"tinystdio/vscanf.c",
|
||||
"tinystdio/vsnprintf.c",
|
||||
"tinystdio/vsprintf.c",
|
||||
"tinystdio/vsscanf.c",
|
||||
|
||||
"string/bcmp.c",
|
||||
"string/bcopy.c",
|
||||
"string/bzero.c",
|
||||
"string/explicit_bzero.c",
|
||||
"string/ffsl.c",
|
||||
"string/ffsll.c",
|
||||
"string/fls.c",
|
||||
"string/flsl.c",
|
||||
"string/flsll.c",
|
||||
"string/gnu_basename.c",
|
||||
"string/index.c",
|
||||
"string/memccpy.c",
|
||||
"string/memchr.c",
|
||||
"string/memcmp.c",
|
||||
"string/memcpy.c",
|
||||
"string/memmem.c",
|
||||
"string/memmove.c",
|
||||
"string/mempcpy.c",
|
||||
"string/memrchr.c",
|
||||
"string/memset.c",
|
||||
"string/rawmemchr.c",
|
||||
"string/rindex.c",
|
||||
"string/stpcpy.c",
|
||||
"string/stpncpy.c",
|
||||
"string/strcasecmp.c",
|
||||
"string/strcasecmp_l.c",
|
||||
"string/strcasestr.c",
|
||||
"string/strcat.c",
|
||||
"string/strchr.c",
|
||||
"string/strchrnul.c",
|
||||
"string/strcmp.c",
|
||||
"string/strcoll.c",
|
||||
"string/strcoll_l.c",
|
||||
"string/strcpy.c",
|
||||
"string/strcspn.c",
|
||||
"string/strdup.c",
|
||||
"string/strerror.c",
|
||||
"string/strerror_r.c",
|
||||
"string/strlcat.c",
|
||||
"string/strlcpy.c",
|
||||
"string/strlen.c",
|
||||
"string/strlwr.c",
|
||||
"string/strncasecmp.c",
|
||||
"string/strncasecmp_l.c",
|
||||
"string/strncat.c",
|
||||
"string/strncmp.c",
|
||||
"string/strncpy.c",
|
||||
"string/strndup.c",
|
||||
"string/strnlen.c",
|
||||
"string/strnstr.c",
|
||||
"string/strpbrk.c",
|
||||
"string/strrchr.c",
|
||||
"string/strsep.c",
|
||||
"string/strsignal.c",
|
||||
"string/strspn.c",
|
||||
"string/strstr.c",
|
||||
"string/strtok.c",
|
||||
"string/strtok_r.c",
|
||||
"string/strupr.c",
|
||||
"string/strverscmp.c",
|
||||
"string/strxfrm.c",
|
||||
"string/strxfrm_l.c",
|
||||
"string/swab.c",
|
||||
"string/timingsafe_bcmp.c",
|
||||
"string/timingsafe_memcmp.c",
|
||||
"string/u_strerr.c",
|
||||
"string/wcpcpy.c",
|
||||
"string/wcpncpy.c",
|
||||
"string/wcscasecmp.c",
|
||||
"string/wcscasecmp_l.c",
|
||||
"string/wcscat.c",
|
||||
"string/wcschr.c",
|
||||
"string/wcscmp.c",
|
||||
"string/wcscoll.c",
|
||||
"string/wcscoll_l.c",
|
||||
"string/wcscpy.c",
|
||||
"string/wcscspn.c",
|
||||
"string/wcsdup.c",
|
||||
"string/wcslcat.c",
|
||||
"string/wcslcpy.c",
|
||||
"string/wcslen.c",
|
||||
"string/wcsncasecmp.c",
|
||||
"string/wcsncasecmp_l.c",
|
||||
"string/wcsncat.c",
|
||||
"string/wcsncmp.c",
|
||||
"string/wcsncpy.c",
|
||||
"string/wcsnlen.c",
|
||||
"string/wcspbrk.c",
|
||||
"string/wcsrchr.c",
|
||||
"string/wcsspn.c",
|
||||
"string/wcsstr.c",
|
||||
"string/wcstok.c",
|
||||
"string/wcswidth.c",
|
||||
"string/wcsxfrm.c",
|
||||
"string/wcsxfrm_l.c",
|
||||
"string/wcwidth.c",
|
||||
"string/wmemchr.c",
|
||||
"string/wmemcmp.c",
|
||||
"string/wmemcpy.c",
|
||||
"string/wmemmove.c",
|
||||
"string/wmempcpy.c",
|
||||
"string/wmemset.c",
|
||||
"string/xpg_strerror_r.c",
|
||||
"libc/string/bcmp.c",
|
||||
"libc/string/bcopy.c",
|
||||
"libc/string/bzero.c",
|
||||
"libc/string/explicit_bzero.c",
|
||||
"libc/string/ffsl.c",
|
||||
"libc/string/ffsll.c",
|
||||
"libc/string/fls.c",
|
||||
"libc/string/flsl.c",
|
||||
"libc/string/flsll.c",
|
||||
"libc/string/gnu_basename.c",
|
||||
"libc/string/index.c",
|
||||
"libc/string/memccpy.c",
|
||||
"libc/string/memchr.c",
|
||||
"libc/string/memcmp.c",
|
||||
"libc/string/memcpy.c",
|
||||
"libc/string/memmem.c",
|
||||
"libc/string/memmove.c",
|
||||
"libc/string/mempcpy.c",
|
||||
"libc/string/memrchr.c",
|
||||
"libc/string/memset.c",
|
||||
"libc/string/rawmemchr.c",
|
||||
"libc/string/rindex.c",
|
||||
"libc/string/stpcpy.c",
|
||||
"libc/string/stpncpy.c",
|
||||
"libc/string/strcasecmp.c",
|
||||
"libc/string/strcasecmp_l.c",
|
||||
"libc/string/strcasestr.c",
|
||||
"libc/string/strcat.c",
|
||||
"libc/string/strchr.c",
|
||||
"libc/string/strchrnul.c",
|
||||
"libc/string/strcmp.c",
|
||||
"libc/string/strcoll.c",
|
||||
"libc/string/strcoll_l.c",
|
||||
"libc/string/strcpy.c",
|
||||
"libc/string/strcspn.c",
|
||||
"libc/string/strdup.c",
|
||||
"libc/string/strerror.c",
|
||||
"libc/string/strerror_r.c",
|
||||
"libc/string/strlcat.c",
|
||||
"libc/string/strlcpy.c",
|
||||
"libc/string/strlen.c",
|
||||
"libc/string/strlwr.c",
|
||||
"libc/string/strncasecmp.c",
|
||||
"libc/string/strncasecmp_l.c",
|
||||
"libc/string/strncat.c",
|
||||
"libc/string/strncmp.c",
|
||||
"libc/string/strncpy.c",
|
||||
"libc/string/strndup.c",
|
||||
"libc/string/strnlen.c",
|
||||
"libc/string/strnstr.c",
|
||||
"libc/string/strpbrk.c",
|
||||
"libc/string/strrchr.c",
|
||||
"libc/string/strsep.c",
|
||||
"libc/string/strsignal.c",
|
||||
"libc/string/strspn.c",
|
||||
"libc/string/strstr.c",
|
||||
"libc/string/strtok.c",
|
||||
"libc/string/strtok_r.c",
|
||||
"libc/string/strupr.c",
|
||||
"libc/string/strverscmp.c",
|
||||
"libc/string/strxfrm.c",
|
||||
"libc/string/strxfrm_l.c",
|
||||
"libc/string/swab.c",
|
||||
"libc/string/timingsafe_bcmp.c",
|
||||
"libc/string/timingsafe_memcmp.c",
|
||||
"libc/string/u_strerr.c",
|
||||
"libc/string/wcpcpy.c",
|
||||
"libc/string/wcpncpy.c",
|
||||
"libc/string/wcscasecmp.c",
|
||||
"libc/string/wcscasecmp_l.c",
|
||||
"libc/string/wcscat.c",
|
||||
"libc/string/wcschr.c",
|
||||
"libc/string/wcscmp.c",
|
||||
"libc/string/wcscoll.c",
|
||||
"libc/string/wcscoll_l.c",
|
||||
"libc/string/wcscpy.c",
|
||||
"libc/string/wcscspn.c",
|
||||
"libc/string/wcsdup.c",
|
||||
"libc/string/wcslcat.c",
|
||||
"libc/string/wcslcpy.c",
|
||||
"libc/string/wcslen.c",
|
||||
"libc/string/wcsncasecmp.c",
|
||||
"libc/string/wcsncasecmp_l.c",
|
||||
"libc/string/wcsncat.c",
|
||||
"libc/string/wcsncmp.c",
|
||||
"libc/string/wcsncpy.c",
|
||||
"libc/string/wcsnlen.c",
|
||||
"libc/string/wcspbrk.c",
|
||||
"libc/string/wcsrchr.c",
|
||||
"libc/string/wcsspn.c",
|
||||
"libc/string/wcsstr.c",
|
||||
"libc/string/wcstok.c",
|
||||
"libc/string/wcswidth.c",
|
||||
"libc/string/wcsxfrm.c",
|
||||
"libc/string/wcsxfrm_l.c",
|
||||
"libc/string/wcwidth.c",
|
||||
"libc/string/wmemchr.c",
|
||||
"libc/string/wmemcmp.c",
|
||||
"libc/string/wmemcpy.c",
|
||||
"libc/string/wmemmove.c",
|
||||
"libc/string/wmempcpy.c",
|
||||
"libc/string/wmemset.c",
|
||||
"libc/string/xpg_strerror_r.c",
|
||||
}
|
||||
|
||||
// Parts of picolibc that are too large for small AVRs.
|
||||
var picolibcSourcesLarge = []string{
|
||||
// srcs_tinystdio
|
||||
"libc/tinystdio/asprintf.c",
|
||||
"libc/tinystdio/bufio.c",
|
||||
"libc/tinystdio/clearerr.c",
|
||||
"libc/tinystdio/ecvt_r.c",
|
||||
"libc/tinystdio/ecvt.c",
|
||||
"libc/tinystdio/ecvtf_r.c",
|
||||
"libc/tinystdio/ecvtf.c",
|
||||
"libc/tinystdio/fcvt.c",
|
||||
"libc/tinystdio/fcvt_r.c",
|
||||
"libc/tinystdio/fcvtf.c",
|
||||
"libc/tinystdio/fcvtf_r.c",
|
||||
"libc/tinystdio/gcvt.c",
|
||||
"libc/tinystdio/gcvtf.c",
|
||||
"libc/tinystdio/fclose.c",
|
||||
"libc/tinystdio/fdevopen.c",
|
||||
"libc/tinystdio/feof.c",
|
||||
"libc/tinystdio/ferror.c",
|
||||
"libc/tinystdio/fflush.c",
|
||||
"libc/tinystdio/fgetc.c",
|
||||
"libc/tinystdio/fgets.c",
|
||||
"libc/tinystdio/fileno.c",
|
||||
"libc/tinystdio/filestrget.c",
|
||||
"libc/tinystdio/filestrput.c",
|
||||
"libc/tinystdio/filestrputalloc.c",
|
||||
"libc/tinystdio/fmemopen.c",
|
||||
"libc/tinystdio/fprintf.c",
|
||||
"libc/tinystdio/fputc.c",
|
||||
"libc/tinystdio/fputs.c",
|
||||
"libc/tinystdio/fread.c",
|
||||
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
|
||||
"libc/tinystdio/fscanf.c",
|
||||
"libc/tinystdio/fseek.c",
|
||||
"libc/tinystdio/fseeko.c",
|
||||
"libc/tinystdio/ftell.c",
|
||||
"libc/tinystdio/ftello.c",
|
||||
"libc/tinystdio/fwrite.c",
|
||||
"libc/tinystdio/getchar.c",
|
||||
"libc/tinystdio/gets.c",
|
||||
"libc/tinystdio/matchcaseprefix.c",
|
||||
"libc/tinystdio/mktemp.c",
|
||||
"libc/tinystdio/perror.c",
|
||||
"libc/tinystdio/printf.c",
|
||||
"libc/tinystdio/putchar.c",
|
||||
"libc/tinystdio/puts.c",
|
||||
"libc/tinystdio/rewind.c",
|
||||
"libc/tinystdio/scanf.c",
|
||||
"libc/tinystdio/setbuf.c",
|
||||
"libc/tinystdio/setbuffer.c",
|
||||
"libc/tinystdio/setlinebuf.c",
|
||||
"libc/tinystdio/setvbuf.c",
|
||||
"libc/tinystdio/snprintf.c",
|
||||
"libc/tinystdio/sprintf.c",
|
||||
"libc/tinystdio/snprintfd.c",
|
||||
"libc/tinystdio/snprintff.c",
|
||||
"libc/tinystdio/sprintff.c",
|
||||
"libc/tinystdio/sprintfd.c",
|
||||
"libc/tinystdio/sscanf.c",
|
||||
"libc/tinystdio/strfromf.c",
|
||||
"libc/tinystdio/strfromd.c",
|
||||
"libc/tinystdio/strtof.c",
|
||||
"libc/tinystdio/strtof_l.c",
|
||||
"libc/tinystdio/strtod.c",
|
||||
"libc/tinystdio/strtod_l.c",
|
||||
"libc/tinystdio/ungetc.c",
|
||||
"libc/tinystdio/vasprintf.c",
|
||||
"libc/tinystdio/vfiprintf.c",
|
||||
"libc/tinystdio/vfprintf.c",
|
||||
"libc/tinystdio/vfprintff.c",
|
||||
"libc/tinystdio/vfscanf.c",
|
||||
"libc/tinystdio/vfiscanf.c",
|
||||
"libc/tinystdio/vfscanff.c",
|
||||
"libc/tinystdio/vprintf.c",
|
||||
"libc/tinystdio/vscanf.c",
|
||||
"libc/tinystdio/vsscanf.c",
|
||||
"libc/tinystdio/vsnprintf.c",
|
||||
"libc/tinystdio/vsprintf.c",
|
||||
|
||||
"libm/common/sf_finite.c",
|
||||
"libm/common/sf_copysign.c",
|
||||
"libm/common/sf_modf.c",
|
||||
"libm/common/sf_scalbn.c",
|
||||
"libm/common/sf_cbrt.c",
|
||||
"libm/common/sf_exp10.c",
|
||||
"libm/common/sf_expm1.c",
|
||||
"libm/common/sf_ilogb.c",
|
||||
"libm/common/sf_infinity.c",
|
||||
"libm/common/sf_isinf.c",
|
||||
"libm/common/sf_isinff.c",
|
||||
"libm/common/sf_isnan.c",
|
||||
"libm/common/sf_isnanf.c",
|
||||
"libm/common/sf_issignaling.c",
|
||||
"libm/common/sf_log1p.c",
|
||||
"libm/common/sf_nan.c",
|
||||
"libm/common/sf_nextafter.c",
|
||||
"libm/common/sf_pow10.c",
|
||||
"libm/common/sf_rint.c",
|
||||
"libm/common/sf_logb.c",
|
||||
"libm/common/sf_fdim.c",
|
||||
"libm/common/sf_fma.c",
|
||||
"libm/common/sf_fmax.c",
|
||||
"libm/common/sf_fmin.c",
|
||||
"libm/common/sf_fpclassify.c",
|
||||
"libm/common/sf_lrint.c",
|
||||
"libm/common/sf_llrint.c",
|
||||
"libm/common/sf_lround.c",
|
||||
"libm/common/sf_llround.c",
|
||||
"libm/common/sf_nearbyint.c",
|
||||
"libm/common/sf_remquo.c",
|
||||
"libm/common/sf_round.c",
|
||||
"libm/common/sf_scalbln.c",
|
||||
"libm/common/sf_trunc.c",
|
||||
"libm/common/sf_exp.c",
|
||||
"libm/common/sf_exp2.c",
|
||||
"libm/common/sf_exp2_data.c",
|
||||
"libm/common/sf_log.c",
|
||||
"libm/common/sf_log_data.c",
|
||||
"libm/common/sf_log2.c",
|
||||
"libm/common/sf_log2_data.c",
|
||||
"libm/common/sf_pow_log2_data.c",
|
||||
"libm/common/sf_pow.c",
|
||||
|
||||
"libm/common/s_finite.c",
|
||||
"libm/common/s_copysign.c",
|
||||
"libm/common/s_modf.c",
|
||||
"libm/common/s_scalbn.c",
|
||||
"libm/common/s_cbrt.c",
|
||||
"libm/common/s_exp10.c",
|
||||
"libm/common/s_expm1.c",
|
||||
"libm/common/s_ilogb.c",
|
||||
"libm/common/s_infinity.c",
|
||||
"libm/common/s_iseqsig.c",
|
||||
"libm/common/s_isinf.c",
|
||||
"libm/common/s_isinfd.c",
|
||||
"libm/common/s_isnan.c",
|
||||
"libm/common/s_isnand.c",
|
||||
"libm/common/s_issignaling.c",
|
||||
"libm/common/s_log1p.c",
|
||||
"libm/common/s_nan.c",
|
||||
"libm/common/s_nextafter.c",
|
||||
"libm/common/s_pow10.c",
|
||||
"libm/common/s_rint.c",
|
||||
"libm/common/s_logb.c",
|
||||
"libm/common/s_log2.c",
|
||||
"libm/common/s_fdim.c",
|
||||
"libm/common/s_fma.c",
|
||||
"libm/common/s_fmax.c",
|
||||
"libm/common/s_fmin.c",
|
||||
"libm/common/s_fpclassify.c",
|
||||
"libm/common/s_getpayload.c",
|
||||
"libm/common/s_lrint.c",
|
||||
"libm/common/s_llrint.c",
|
||||
"libm/common/s_lround.c",
|
||||
"libm/common/s_llround.c",
|
||||
"libm/common/s_nearbyint.c",
|
||||
"libm/common/s_remquo.c",
|
||||
"libm/common/s_round.c",
|
||||
"libm/common/s_scalbln.c",
|
||||
"libm/common/s_signbit.c",
|
||||
"libm/common/s_trunc.c",
|
||||
"libm/common/exp.c",
|
||||
"libm/common/exp2.c",
|
||||
"libm/common/exp_data.c",
|
||||
"libm/common/math_err_with_errno.c",
|
||||
"libm/common/math_err_uflow.c",
|
||||
"libm/common/math_err_oflow.c",
|
||||
"libm/common/math_err_divzero.c",
|
||||
"libm/common/math_err_invalid.c",
|
||||
"libm/common/math_err_may_uflow.c",
|
||||
"libm/common/math_err_check_uflow.c",
|
||||
"libm/common/math_err_check_oflow.c",
|
||||
"libm/common/math_errf_divzerof.c",
|
||||
"libm/common/math_errf_invalidf.c",
|
||||
"libm/common/math_errf_may_uflowf.c",
|
||||
"libm/common/math_errf_oflowf.c",
|
||||
"libm/common/math_errf_uflowf.c",
|
||||
"libm/common/math_errf_with_errnof.c",
|
||||
"libm/common/math_inexact.c",
|
||||
"libm/common/math_inexactf.c",
|
||||
"libm/common/log.c",
|
||||
"libm/common/log_data.c",
|
||||
"libm/common/log2.c",
|
||||
"libm/common/log2_data.c",
|
||||
"libm/common/pow.c",
|
||||
"libm/common/pow_log_data.c",
|
||||
|
||||
"libm/math/k_cos.c",
|
||||
"libm/math/k_rem_pio2.c",
|
||||
"libm/math/k_sin.c",
|
||||
"libm/math/k_tan.c",
|
||||
"libm/math/kf_cos.c",
|
||||
"libm/math/kf_rem_pio2.c",
|
||||
"libm/math/kf_sin.c",
|
||||
"libm/math/kf_tan.c",
|
||||
"libm/math/s_acos.c",
|
||||
"libm/math/s_acosh.c",
|
||||
"libm/math/s_asin.c",
|
||||
"libm/math/s_asinh.c",
|
||||
"libm/math/s_atan.c",
|
||||
"libm/math/s_atan2.c",
|
||||
"libm/math/s_atanh.c",
|
||||
"libm/math/s_ceil.c",
|
||||
"libm/math/s_cos.c",
|
||||
"libm/math/s_cosh.c",
|
||||
"libm/math/s_drem.c",
|
||||
"libm/math/s_erf.c",
|
||||
"libm/math/s_exp.c",
|
||||
"libm/math/s_exp2.c",
|
||||
"libm/math/s_fabs.c",
|
||||
"libm/math/s_floor.c",
|
||||
"libm/math/s_fmod.c",
|
||||
"libm/math/s_frexp.c",
|
||||
"libm/math/s_gamma.c",
|
||||
"libm/math/s_hypot.c",
|
||||
"libm/math/s_j0.c",
|
||||
"libm/math/s_j1.c",
|
||||
"libm/math/s_jn.c",
|
||||
"libm/math/s_lgamma.c",
|
||||
"libm/math/s_log.c",
|
||||
"libm/math/s_log10.c",
|
||||
"libm/math/s_pow.c",
|
||||
"libm/math/s_rem_pio2.c",
|
||||
"libm/math/s_remainder.c",
|
||||
"libm/math/s_scalb.c",
|
||||
"libm/math/s_signif.c",
|
||||
"libm/math/s_sin.c",
|
||||
"libm/math/s_sincos.c",
|
||||
"libm/math/s_sinh.c",
|
||||
"libm/math/s_sqrt.c",
|
||||
"libm/math/s_tan.c",
|
||||
"libm/math/s_tanh.c",
|
||||
"libm/math/s_tgamma.c",
|
||||
"libm/math/sf_acos.c",
|
||||
"libm/math/sf_acosh.c",
|
||||
"libm/math/sf_asin.c",
|
||||
"libm/math/sf_asinh.c",
|
||||
"libm/math/sf_atan.c",
|
||||
"libm/math/sf_atan2.c",
|
||||
"libm/math/sf_atanh.c",
|
||||
"libm/math/sf_ceil.c",
|
||||
"libm/math/sf_cos.c",
|
||||
"libm/math/sf_cosh.c",
|
||||
"libm/math/sf_drem.c",
|
||||
"libm/math/sf_erf.c",
|
||||
"libm/math/sf_exp.c",
|
||||
"libm/math/sf_exp2.c",
|
||||
"libm/math/sf_fabs.c",
|
||||
"libm/math/sf_floor.c",
|
||||
"libm/math/sf_fmod.c",
|
||||
"libm/math/sf_frexp.c",
|
||||
"libm/math/sf_gamma.c",
|
||||
"libm/math/sf_hypot.c",
|
||||
"libm/math/sf_j0.c",
|
||||
"libm/math/sf_j1.c",
|
||||
"libm/math/sf_jn.c",
|
||||
"libm/math/sf_lgamma.c",
|
||||
"libm/math/sf_log.c",
|
||||
"libm/math/sf_log10.c",
|
||||
"libm/math/sf_log2.c",
|
||||
"libm/math/sf_pow.c",
|
||||
"libm/math/sf_rem_pio2.c",
|
||||
"libm/math/sf_remainder.c",
|
||||
"libm/math/sf_scalb.c",
|
||||
"libm/math/sf_signif.c",
|
||||
"libm/math/sf_sin.c",
|
||||
"libm/math/sf_sincos.c",
|
||||
"libm/math/sf_sinh.c",
|
||||
"libm/math/sf_sqrt.c",
|
||||
"libm/math/sf_tan.c",
|
||||
"libm/math/sf_tanh.c",
|
||||
"libm/math/sf_tgamma.c",
|
||||
"libm/math/sr_lgamma.c",
|
||||
"libm/math/srf_lgamma.c",
|
||||
}
|
||||
|
||||
+293
-34
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@@ -74,6 +75,7 @@ func (ps *packageSize) RAM() uint64 {
|
||||
type addressLine struct {
|
||||
Address uint64
|
||||
Length uint64 // length of this chunk
|
||||
Align uint64 // (maximum) alignment of this line
|
||||
File string // file path as stored in DWARF
|
||||
IsVariable bool // true if this is a variable (or constant), false if it is code
|
||||
}
|
||||
@@ -85,6 +87,7 @@ type memorySection struct {
|
||||
Type memoryType
|
||||
Address uint64
|
||||
Size uint64
|
||||
Align uint64
|
||||
}
|
||||
|
||||
type memoryType int
|
||||
@@ -117,16 +120,12 @@ var (
|
||||
// pack: data created when storing a constant in an interface for example
|
||||
// string: buffer behind strings
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
||||
|
||||
// Reflect sidetables. Created by the reflect lowering pass.
|
||||
// See src/reflect/sidetables.go.
|
||||
reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
|
||||
)
|
||||
|
||||
// readProgramSizeFromDWARF reads the source location for each line of code and
|
||||
// each variable in the program, as far as this is stored in the DWARF debug
|
||||
// information.
|
||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
|
||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset, codeAlignment uint64, skipTombstone bool) ([]addressLine, error) {
|
||||
r := data.Reader()
|
||||
var lines []*dwarf.LineFile
|
||||
var addresses []addressLine
|
||||
@@ -168,7 +167,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 {
|
||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 && skipTombstone {
|
||||
// Tombstone value. This symbol has been removed, for
|
||||
// example by the --gc-sections linker flag. It is still
|
||||
// here in the debug information because the linker can't
|
||||
@@ -177,6 +176,10 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
// skipped.
|
||||
// For more details, see (among others):
|
||||
// https://reviews.llvm.org/D84825
|
||||
// The value 0 can however really occur in object files,
|
||||
// that typically start at address 0. So don't skip
|
||||
// tombstone values in object files (like when parsing MachO
|
||||
// files).
|
||||
for {
|
||||
err := lr.Next(&lineEntry)
|
||||
if err != nil {
|
||||
@@ -194,6 +197,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
line := addressLine{
|
||||
Address: prevLineEntry.Address + codeOffset,
|
||||
Length: lineEntry.Address - prevLineEntry.Address,
|
||||
Align: codeAlignment,
|
||||
File: prevLineEntry.File.Name,
|
||||
}
|
||||
if line.Length != 0 {
|
||||
@@ -218,20 +222,9 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
// Try to parse the location. While this could in theory be a very
|
||||
// complex expression, usually it's just a DW_OP_addr opcode
|
||||
// followed by an address.
|
||||
locationCode := location.Val.([]uint8)
|
||||
if locationCode[0] != 3 { // DW_OP_addr
|
||||
continue
|
||||
}
|
||||
var addr uint64
|
||||
switch len(locationCode) {
|
||||
case 1 + 2:
|
||||
addr = uint64(binary.LittleEndian.Uint16(locationCode[1:]))
|
||||
case 1 + 4:
|
||||
addr = uint64(binary.LittleEndian.Uint32(locationCode[1:]))
|
||||
case 1 + 8:
|
||||
addr = binary.LittleEndian.Uint64(locationCode[1:])
|
||||
default:
|
||||
continue // unknown address
|
||||
addr, err := readDWARFConstant(r.AddressSize(), location.Val.([]uint8))
|
||||
if err != nil {
|
||||
continue // ignore the error, we don't know what to do with it
|
||||
}
|
||||
|
||||
// Parse the type of the global variable, which (importantly)
|
||||
@@ -242,9 +235,16 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read alignment, if it's stored as part of the debug information.
|
||||
var alignment uint64
|
||||
if attr := e.AttrField(dwarf.AttrAlignment); attr != nil {
|
||||
alignment = uint64(attr.Val.(int64))
|
||||
}
|
||||
|
||||
addresses = append(addresses, addressLine{
|
||||
Address: addr,
|
||||
Length: uint64(typ.Size()),
|
||||
Align: alignment,
|
||||
File: lines[file.Val.(int64)].Name,
|
||||
IsVariable: true,
|
||||
})
|
||||
@@ -255,6 +255,111 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// Parse a DWARF constant. For addresses, this is usually a very simple
|
||||
// expression.
|
||||
func readDWARFConstant(addressSize int, bytecode []byte) (uint64, error) {
|
||||
var addr uint64
|
||||
for len(bytecode) != 0 {
|
||||
op := bytecode[0]
|
||||
bytecode = bytecode[1:]
|
||||
switch op {
|
||||
case 0x03: // DW_OP_addr
|
||||
switch addressSize {
|
||||
case 2:
|
||||
addr = uint64(binary.LittleEndian.Uint16(bytecode))
|
||||
case 4:
|
||||
addr = uint64(binary.LittleEndian.Uint32(bytecode))
|
||||
case 8:
|
||||
addr = binary.LittleEndian.Uint64(bytecode)
|
||||
default:
|
||||
panic("unexpected address size")
|
||||
}
|
||||
bytecode = bytecode[addressSize:]
|
||||
case 0x23: // DW_OP_plus_uconst
|
||||
offset, n := readULEB128(bytecode)
|
||||
addr += offset
|
||||
bytecode = bytecode[n:]
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown DWARF opcode: 0x%x", op)
|
||||
}
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// Source: https://en.wikipedia.org/wiki/LEB128#Decode_unsigned_integer
|
||||
func readULEB128(buf []byte) (result uint64, n int) {
|
||||
var shift uint8
|
||||
for {
|
||||
b := buf[n]
|
||||
n++
|
||||
result |= uint64(b&0x7f) << shift
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
shift += 7
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Read a MachO object file and return a line table.
|
||||
// Also return an index from symbol name to start address in the line table.
|
||||
func readMachOSymbolAddresses(path string) (map[string]int, []addressLine, error) {
|
||||
// Some constants from mach-o/nlist.h
|
||||
// See: https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
|
||||
const (
|
||||
N_STAB = 0xe0
|
||||
N_TYPE = 0x0e // bitmask for N_TYPE field
|
||||
N_SECT = 0xe // one of the possible type in the N_TYPE field
|
||||
)
|
||||
|
||||
// Read DWARF from the given object file.
|
||||
file, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
dwarf, err := file.DWARF()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
lines, err := readProgramSizeFromDWARF(dwarf, 0, 0, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Make a map from start addresses to indices in the line table (because the
|
||||
// line table is a slice, not a map).
|
||||
addressToLine := make(map[uint64]int, len(lines))
|
||||
for i, line := range lines {
|
||||
if _, ok := addressToLine[line.Address]; ok {
|
||||
addressToLine[line.Address] = -1
|
||||
continue
|
||||
}
|
||||
addressToLine[line.Address] = i
|
||||
}
|
||||
|
||||
// Make a map that for each symbol gives the start index in the line table.
|
||||
addresses := make(map[string]int, len(addressToLine))
|
||||
for _, symbol := range file.Symtab.Syms {
|
||||
if symbol.Type&N_STAB != 0 {
|
||||
continue // STABS entry, ignore
|
||||
}
|
||||
if symbol.Type&0x0e != N_SECT {
|
||||
continue // undefined symbol
|
||||
}
|
||||
if index, ok := addressToLine[symbol.Value]; ok && index >= 0 {
|
||||
if _, ok := addresses[symbol.Name]; ok {
|
||||
// There is a duplicate. Mark it as unavailable.
|
||||
addresses[symbol.Name] = -1
|
||||
continue
|
||||
}
|
||||
addresses[symbol.Name] = index
|
||||
}
|
||||
}
|
||||
|
||||
return addresses, lines, nil
|
||||
}
|
||||
|
||||
// loadProgramSize calculate a program/data size breakdown of each package for a
|
||||
// given ELF file.
|
||||
// If the file doesn't contain DWARF debug information, the returned program
|
||||
@@ -274,10 +379,15 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
// Load the binary file, which could be in a number of file formats.
|
||||
var sections []memorySection
|
||||
if file, err := elf.NewFile(f); err == nil {
|
||||
var codeAlignment uint64
|
||||
switch file.Machine {
|
||||
case elf.EM_ARM:
|
||||
codeAlignment = 4 // usually 2, but can be 4
|
||||
}
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0, codeAlignment, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -311,7 +421,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
if section.Flags&elf.SHF_ALLOC == 0 {
|
||||
continue
|
||||
}
|
||||
if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
|
||||
if packageSymbolRegexp.MatchString(symbol.Name) || symbol.Name == "__isr_vector" {
|
||||
addresses = append(addresses, addressLine{
|
||||
Address: symbol.Value,
|
||||
Length: symbol.Size,
|
||||
@@ -335,6 +445,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
Align: section.Addralign,
|
||||
Type: memoryStack,
|
||||
})
|
||||
} else {
|
||||
@@ -342,6 +453,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
Align: section.Addralign,
|
||||
Type: memoryBSS,
|
||||
})
|
||||
}
|
||||
@@ -350,6 +462,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
Align: section.Addralign,
|
||||
Type: memoryCode,
|
||||
})
|
||||
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_WRITE != 0 {
|
||||
@@ -357,6 +470,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
Align: section.Addralign,
|
||||
Type: memoryData,
|
||||
})
|
||||
} else if section.Type == elf.SHT_PROGBITS {
|
||||
@@ -364,15 +478,142 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: section.Size,
|
||||
Align: section.Addralign,
|
||||
Type: memoryROData,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if file, err := macho.NewFile(f); err == nil {
|
||||
// Read segments, for use while reading through sections.
|
||||
segments := map[string]*macho.Segment{}
|
||||
for _, load := range file.Loads {
|
||||
switch load := load.(type) {
|
||||
case *macho.Segment:
|
||||
segments[load.Name] = load
|
||||
}
|
||||
}
|
||||
|
||||
// Read MachO sections.
|
||||
for _, section := range file.Sections {
|
||||
sectionType := section.Flags & 0xff
|
||||
sectionFlags := section.Flags >> 8
|
||||
segment := segments[section.Seg]
|
||||
// For the constants used here, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
|
||||
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
|
||||
// Section containing only instructions.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Align: uint64(section.Align),
|
||||
Type: memoryCode,
|
||||
})
|
||||
} else if sectionType == 1 { // S_ZEROFILL
|
||||
// Section filled with zeroes on demand.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Align: uint64(section.Align),
|
||||
Type: memoryBSS,
|
||||
})
|
||||
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
|
||||
// Protection doesn't allow writes, so mark this section read-only.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Align: uint64(section.Align),
|
||||
Type: memoryROData,
|
||||
})
|
||||
} else {
|
||||
// The rest is assumed to be regular data.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Align: uint64(section.Align),
|
||||
Type: memoryData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Read DWARF information.
|
||||
// The data isn't stored directly in the binary as in most executable
|
||||
// formats. Instead, it is left in the object files that were used as a
|
||||
// basis for linking. The executable does however contain STABS debug
|
||||
// information that points to the source object file and is used by
|
||||
// debuggers.
|
||||
// For more information:
|
||||
// http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme
|
||||
var objSymbolNames map[string]int
|
||||
var objAddresses []addressLine
|
||||
var previousSymbol macho.Symbol
|
||||
for _, symbol := range file.Symtab.Syms {
|
||||
// STABS constants, from mach-o/stab.h:
|
||||
// https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/stab.h.auto.html
|
||||
const (
|
||||
N_GSYM = 0x20
|
||||
N_FUN = 0x24
|
||||
N_STSYM = 0x26
|
||||
N_SO = 0x64
|
||||
N_OSO = 0x66
|
||||
)
|
||||
if symbol.Type == N_OSO {
|
||||
// Found an object file. Now try to parse it.
|
||||
objSymbolNames, objAddresses, err = readMachOSymbolAddresses(symbol.Name)
|
||||
if err != nil && sizesDebug {
|
||||
// Errors are normally ignored. If there is an error, it's
|
||||
// simply treated as that the DWARF is not available.
|
||||
fmt.Fprintf(os.Stderr, "could not read DWARF from file %s: %s\n", symbol.Name, err)
|
||||
}
|
||||
} else if symbol.Type == N_FUN {
|
||||
// Found a function.
|
||||
// The way this is encoded is a bit weird. MachO symbols don't
|
||||
// have a length. What I've found is that the length is encoded
|
||||
// by first having a N_FUN symbol as usual, and then having a
|
||||
// symbol with a zero-length name that has the value not set to
|
||||
// the address of the symbol but to the length. So in order to
|
||||
// get both the address and the length, we look for a symbol
|
||||
// with a name followed by a symbol without a name.
|
||||
if symbol.Name == "" && previousSymbol.Type == N_FUN && previousSymbol.Name != "" {
|
||||
// Functions are encoded as many small chunks in the line
|
||||
// table (one or a few instructions per source line). But
|
||||
// the symbol length covers the whole symbols, over many
|
||||
// lines and possibly including inlined functions. So we
|
||||
// continue to iterate through the objAddresses slice until
|
||||
// we've found all the source lines that are part of this
|
||||
// symbol.
|
||||
address := previousSymbol.Value
|
||||
length := symbol.Value
|
||||
if index, ok := objSymbolNames[previousSymbol.Name]; ok && index >= 0 {
|
||||
for length > 0 {
|
||||
line := objAddresses[index]
|
||||
line.Address = address
|
||||
if line.Length > length {
|
||||
// Line extends beyond the end of te symbol?
|
||||
// Weird, shouldn't happen.
|
||||
break
|
||||
}
|
||||
addresses = append(addresses, line)
|
||||
index++
|
||||
length -= line.Length
|
||||
address += line.Length
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if symbol.Type == N_GSYM || symbol.Type == N_STSYM {
|
||||
// Global variables.
|
||||
if index, ok := objSymbolNames[symbol.Name]; ok {
|
||||
address := objAddresses[index]
|
||||
address.Address = symbol.Value
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
}
|
||||
previousSymbol = symbol
|
||||
}
|
||||
} else if file, err := pe.NewFile(f); err == nil {
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
||||
addresses, err = readProgramSizeFromDWARF(data, 0, 0, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -442,9 +683,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
const codeOffset = 0x8000_0000_0000_0000
|
||||
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, err := file.DWARF()
|
||||
data, _ := file.DWARF()
|
||||
if data != nil {
|
||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset)
|
||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset, 0, true)
|
||||
if err != nil {
|
||||
// However, _do_ report an error here. Something must have gone
|
||||
// wrong while trying to parse DWARF data.
|
||||
@@ -604,10 +845,18 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
if addr < line.Address {
|
||||
// There is a gap: there is a space between the current and the
|
||||
// previous line entry.
|
||||
addSize("(unknown)", line.Address-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: unknown (gap)\n", addr, line.Address, line.Address-addr)
|
||||
// Check whether this is caused by alignment requirements.
|
||||
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)
|
||||
} else {
|
||||
addSize("(unknown)", line.Address-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: unknown (gap), alignment=%d\n", addr, line.Address, line.Address-addr, line.Align)
|
||||
}
|
||||
}
|
||||
addr = line.Address
|
||||
}
|
||||
if addr > line.Address+line.Length {
|
||||
// The current line is already covered by a previous line entry.
|
||||
@@ -629,9 +878,16 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
||||
}
|
||||
if addr < sectionEnd {
|
||||
// There is a gap at the end of the section.
|
||||
addSize("(unknown)", sectionEnd-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
|
||||
addrAligned := (addr + section.Align - 1) &^ (section.Align - 1)
|
||||
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)
|
||||
} else {
|
||||
addSize("(unknown)", sectionEnd-addr, false)
|
||||
if sizesDebug {
|
||||
fmt.Printf("%08x..%08x %5d: unknown (end), alignment=%d\n", addr, sectionEnd, sectionEnd-addr, section.Align)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -647,12 +903,15 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
|
||||
// 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")) {
|
||||
packagePath = "C compiler-rt"
|
||||
} else if packageSymbolRegexp.MatchString(path) {
|
||||
// Parse symbol names like main$alloc or runtime$string.
|
||||
packagePath = path[:strings.LastIndex(path, "$")]
|
||||
} else if reflectDataRegexp.MatchString(path) {
|
||||
// Parse symbol names like reflect.structTypesSidetable.
|
||||
packagePath = "Go reflect data"
|
||||
} else if path == "__isr_vector" {
|
||||
packagePath = "C interrupt vector"
|
||||
} else if path == "<Go type>" {
|
||||
packagePath = "Go types"
|
||||
} else if path == "<Go interface assert>" {
|
||||
// Interface type assert, generated by the interface lowering pass.
|
||||
packagePath = "Go interface assert"
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
)
|
||||
|
||||
var sema = make(chan struct{}, runtime.NumCPU())
|
||||
|
||||
type sizeTest struct {
|
||||
target string
|
||||
path string
|
||||
codeSize uint64
|
||||
rodataSize uint64
|
||||
dataSize uint64
|
||||
bssSize uint64
|
||||
}
|
||||
|
||||
// Test whether code and data size is as expected for the given targets.
|
||||
// This tests both the logic of loadProgramSize and checks that code size
|
||||
// doesn't change unintentionally.
|
||||
//
|
||||
// If you find that code or data size is reduced, then great! You can reduce the
|
||||
// number in this test.
|
||||
// If you find that the code or data size is increased, take a look as to why
|
||||
// this is. It could be due to an update (LLVM version, Go version, etc) which
|
||||
// is fine, but it could also mean that a recent change introduced this size
|
||||
// increase. If so, please consider whether this new feature is indeed worth the
|
||||
// size increase for all users.
|
||||
func TestBinarySize(t *testing.T) {
|
||||
if runtime.GOOS == "linux" && !hasBuiltinTools {
|
||||
// Debian LLVM packages are modified a bit and tend to produce
|
||||
// different machine code. Ideally we'd fix this (with some attributes
|
||||
// or something?), but for now skip it.
|
||||
t.Skip("Skip: using external LLVM version so binary size might differ")
|
||||
}
|
||||
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 4600, 280, 0, 2268},
|
||||
{"microbit", "examples/serial", 2908, 388, 8, 2272},
|
||||
{"wioterminal", "examples/pininterrupt", 6140, 1484, 116, 6832},
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Check whether the size of the binary matches the expected size.
|
||||
sizes, err := loadProgramSize(result.Executable, nil)
|
||||
if err != nil {
|
||||
t.Fatal("could not read program size:", err)
|
||||
}
|
||||
if sizes.Code != tc.codeSize || sizes.ROData != tc.rodataSize || sizes.Data != tc.dataSize || sizes.BSS != tc.bssSize {
|
||||
t.Errorf("Unexpected code size when compiling: -target=%s %s", tc.target, tc.path)
|
||||
t.Errorf(" code rodata data bss")
|
||||
t.Errorf("expected: %6d %6d %6d %6d", tc.codeSize, tc.rodataSize, tc.dataSize, tc.bssSize)
|
||||
t.Errorf("actual: %6d %6d %6d %6d", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build byollvm
|
||||
//go:build byollvm
|
||||
|
||||
package builder
|
||||
|
||||
@@ -12,9 +12,7 @@ import (
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
bool tinygo_clang_driver(int argc, char **argv);
|
||||
bool tinygo_link_elf(int argc, char **argv);
|
||||
bool tinygo_link_mingw(int argc, char **argv);
|
||||
bool tinygo_link_wasm(int argc, char **argv);
|
||||
bool tinygo_link(int argc, char **argv);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -25,11 +23,7 @@ const hasBuiltinTools = true
|
||||
// This version actually runs the tools because TinyGo was compiled while
|
||||
// linking statically with LLVM (with the byollvm build tag).
|
||||
func RunTool(tool string, args ...string) error {
|
||||
linker := "elf"
|
||||
if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" {
|
||||
linker = "mingw"
|
||||
}
|
||||
args = append([]string{"tinygo:" + tool}, args...)
|
||||
args = append([]string{tool}, args...)
|
||||
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
@@ -45,17 +39,8 @@ func RunTool(tool string, args ...string) error {
|
||||
switch tool {
|
||||
case "clang":
|
||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld":
|
||||
switch linker {
|
||||
case "elf":
|
||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||
case "mingw":
|
||||
ok = C.tinygo_link_mingw(C.int(len(args)), (**C.char)(buf))
|
||||
default:
|
||||
return errors.New("unknown linker: " + linker)
|
||||
}
|
||||
case "wasm-ld":
|
||||
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld", "wasm-ld":
|
||||
ok = C.tinygo_link(C.int(len(args)), (**C.char)(buf))
|
||||
default:
|
||||
return errors.New("unknown tool: " + tool)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !byollvm
|
||||
//go:build !byollvm
|
||||
|
||||
package builder
|
||||
|
||||
|
||||
+168
-27
@@ -1,50 +1,191 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
return errors.New("could not locate Clang headers")
|
||||
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
|
||||
}
|
||||
flags = append(flags, "-I"+headerPath)
|
||||
cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
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.
|
||||
func link(linker string, flags ...string) error {
|
||||
if hasBuiltinTools && (linker == "ld.lld" || linker == "wasm-ld") {
|
||||
// Run command with internal linker.
|
||||
cmd := exec.Command(os.Args[0], append([]string{linker}, flags...)...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
// We only support LLD.
|
||||
if linker != "ld.lld" && linker != "wasm-ld" {
|
||||
return fmt.Errorf("unexpected: linker %s should be ld.lld or wasm-ld", linker)
|
||||
}
|
||||
|
||||
// Fall back to external command.
|
||||
if _, ok := commands[linker]; ok {
|
||||
return execCommand(linker, flags...)
|
||||
var cmd *exec.Cmd
|
||||
if hasBuiltinTools {
|
||||
cmd = exec.Command(os.Args[0], append([]string{linker}, flags...)...)
|
||||
} else {
|
||||
name, err := LookupCommand(linker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = exec.Command(name, flags...)
|
||||
}
|
||||
|
||||
cmd := exec.Command(linker, flags...)
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
return cmd.Run()
|
||||
cmd.Stderr = &buf
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if buf.Len() == 0 {
|
||||
// The linker failed but there was no output.
|
||||
// Therefore, show some output anyway.
|
||||
return fmt.Errorf("failed to run linker: %w", err)
|
||||
}
|
||||
return parseLLDErrors(buf.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Split LLD errors into individual erros (including errors that continue on the
|
||||
// next line, using a ">>>" prefix). If possible, replace the raw errors with a
|
||||
// more user-friendly version (and one that's more in a Go style).
|
||||
func parseLLDErrors(text string) error {
|
||||
// Split linker output in separate error messages.
|
||||
lines := strings.Split(text, "\n")
|
||||
var errorLines []string // one or more line (belonging to a single error) per line
|
||||
for _, line := range lines {
|
||||
line = strings.TrimRight(line, "\r") // needed for Windows
|
||||
if len(errorLines) != 0 && strings.HasPrefix(line, ">>> ") {
|
||||
errorLines[len(errorLines)-1] += "\n" + line
|
||||
continue
|
||||
}
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
errorLines = append(errorLines, line)
|
||||
}
|
||||
|
||||
// Parse error messages.
|
||||
var linkErrors []error
|
||||
var flashOverflow, ramOverflow uint64
|
||||
for _, message := range errorLines {
|
||||
parsedError := false
|
||||
|
||||
// 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") {
|
||||
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?
|
||||
linkErrors = append(linkErrors, scanner.Error{
|
||||
Pos: token.Position{
|
||||
Filename: matches[2],
|
||||
Line: line,
|
||||
},
|
||||
Msg: "linker could not find symbol " + symbolName,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 err != nil {
|
||||
// Should not happen at all (unless it overflows an uint64 for some reason).
|
||||
continue
|
||||
}
|
||||
|
||||
// Check which area overflowed.
|
||||
// Some chips use differently named memory areas, but these are by
|
||||
// far the most common.
|
||||
switch region {
|
||||
case "FLASH_TEXT":
|
||||
if n > flashOverflow {
|
||||
flashOverflow = n
|
||||
}
|
||||
parsedError = true
|
||||
case "RAM":
|
||||
if n > ramOverflow {
|
||||
ramOverflow = n
|
||||
}
|
||||
parsedError = true
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't parse the linker error: show the error as-is to
|
||||
// the user.
|
||||
if !parsedError {
|
||||
linkErrors = append(linkErrors, LinkerError{message})
|
||||
}
|
||||
}
|
||||
|
||||
if flashOverflow > 0 {
|
||||
linkErrors = append(linkErrors, LinkerError{
|
||||
Msg: fmt.Sprintf("program too large for this chip (flash overflowed by %d bytes)\n\toptimization guide: https://tinygo.org/docs/guides/optimizing-binaries/", flashOverflow),
|
||||
})
|
||||
}
|
||||
if ramOverflow > 0 {
|
||||
linkErrors = append(linkErrors, LinkerError{
|
||||
Msg: fmt.Sprintf("program uses too much static RAM on this chip (RAM overflowed by %d bytes)", ramOverflow),
|
||||
})
|
||||
}
|
||||
|
||||
return newMultiError(linkErrors, "")
|
||||
}
|
||||
|
||||
// LLD linker error that could not be parsed or doesn't refer to a source
|
||||
// location.
|
||||
type LinkerError struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e LinkerError) Error() string {
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ package builder
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(outfile, output, 0644)
|
||||
return os.WriteFile(outfile, output, 0644)
|
||||
}
|
||||
|
||||
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var libWasmBuiltins = Library{
|
||||
name: "wasmbuiltins",
|
||||
makeHeaders: func(target, includeDir string) error {
|
||||
if err := os.Mkdir(includeDir+"/bits", 0o777); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(includeDir + "/bits/alltypes.h")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.Write([]byte(wasmAllTypes)); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
},
|
||||
cflags: func(target, headerPath string) []string {
|
||||
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
|
||||
return []string{
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-std=gnu11",
|
||||
"-nostdlibinc",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/arch/wasm32",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/arch/generic",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/src/internal",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/src/include",
|
||||
"-isystem", libcDir + "/libc-top-half/musl/include",
|
||||
"-isystem", libcDir + "/libc-bottom-half/headers/public",
|
||||
"-I" + headerPath,
|
||||
}
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
return []string{
|
||||
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
|
||||
// llvm.memset.* LLVM intrinsics.
|
||||
"libc-top-half/musl/src/string/memcpy.c",
|
||||
"libc-top-half/musl/src/string/memmove.c",
|
||||
"libc-top-half/musl/src/string/memset.c",
|
||||
|
||||
// exp, exp2, and log are needed for LLVM math builtin functions
|
||||
// like llvm.exp.*.
|
||||
"libc-top-half/musl/src/math/__math_divzero.c",
|
||||
"libc-top-half/musl/src/math/__math_invalid.c",
|
||||
"libc-top-half/musl/src/math/__math_oflow.c",
|
||||
"libc-top-half/musl/src/math/__math_uflow.c",
|
||||
"libc-top-half/musl/src/math/__math_xflow.c",
|
||||
"libc-top-half/musl/src/math/exp.c",
|
||||
"libc-top-half/musl/src/math/exp_data.c",
|
||||
"libc-top-half/musl/src/math/exp2.c",
|
||||
"libc-top-half/musl/src/math/log.c",
|
||||
"libc-top-half/musl/src/math/log_data.c",
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// alltypes.h for wasm-libc, using the types as defined inside Clang.
|
||||
const wasmAllTypes = `
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef __INT8_TYPE__ int8_t;
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
typedef __INT32_TYPE__ int32_t;
|
||||
typedef __INT64_TYPE__ int64_t;
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
typedef __UINT16_TYPE__ uint16_t;
|
||||
typedef __UINT32_TYPE__ uint32_t;
|
||||
typedef __UINT64_TYPE__ uint64_t;
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
|
||||
// This type is used internally in wasi-libc.
|
||||
typedef double double_t;
|
||||
`
|
||||
+454
-551
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
//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
|
||||
}
|
||||
}
|
||||
+104
-27
@@ -5,13 +5,14 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -22,9 +23,15 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
||||
|
||||
// normalizeResult normalizes Go source code that comes out of tests across
|
||||
// platforms and Go versions.
|
||||
func normalizeResult(result string) string {
|
||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
||||
return actual
|
||||
func normalizeResult(t *testing.T, result string) string {
|
||||
result = strings.ReplaceAll(result, "\r\n", "\n")
|
||||
|
||||
// This changed to 'undefined:', in Go 1.20.
|
||||
result = strings.ReplaceAll(result, ": undeclared name:", ": undefined:")
|
||||
// Go 1.20 added a bit more detail
|
||||
result = regexp.MustCompile(`(unknown field z in struct literal).*`).ReplaceAllString(result, "$1")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func TestCGo(t *testing.T) {
|
||||
@@ -40,20 +47,6 @@ func TestCGo(t *testing.T) {
|
||||
} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Skip tests that require specific Go version.
|
||||
if name == "errors" {
|
||||
ok := false
|
||||
for _, version := range build.Default.ReleaseTags {
|
||||
if version == "go1.16" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
t.Skip("Results for errors test are only valid for Go 1.16+")
|
||||
}
|
||||
}
|
||||
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
fset := token.NewFileSet()
|
||||
@@ -63,7 +56,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
@@ -74,7 +67,7 @@ func TestCGo(t *testing.T) {
|
||||
Importer: simpleImporter{},
|
||||
Sizes: types.SizesFor("gccgo", "arm"),
|
||||
}
|
||||
_, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil)
|
||||
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
||||
if err != nil && len(typecheckErrors) == 0 {
|
||||
// Only report errors when no type errors are found (an
|
||||
// unexpected condition).
|
||||
@@ -99,15 +92,15 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
err = format.Node(buf, fset, cgoAST)
|
||||
err = format.Node(buf, fset, cgoFiles[0])
|
||||
if err != nil {
|
||||
t.Errorf("could not write out CGo AST: %v", err)
|
||||
}
|
||||
actual := normalizeResult(buf.String())
|
||||
actual := normalizeResult(t, buf.String())
|
||||
|
||||
// Read the file with the expected output, to compare against.
|
||||
outfile := filepath.Join("testdata", name+".out.go")
|
||||
expectedBytes, err := ioutil.ReadFile(outfile)
|
||||
expectedBytes, err := os.ReadFile(outfile)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read expected output: %v", err)
|
||||
}
|
||||
@@ -118,7 +111,7 @@ func TestCGo(t *testing.T) {
|
||||
// It is not. Test failed.
|
||||
if *flagUpdate {
|
||||
// Update the file with the expected data.
|
||||
err := ioutil.WriteFile(outfile, []byte(actual), 0666)
|
||||
err := os.WriteFile(outfile, []byte(actual), 0666)
|
||||
if err != nil {
|
||||
t.Error("could not write updated output file:", err)
|
||||
}
|
||||
@@ -130,6 +123,84 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cgoPackage_isEquivalentAST(t *testing.T) {
|
||||
fieldA := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "a"}}
|
||||
fieldB := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "b"}}
|
||||
listOfFieldA := &ast.FieldList{List: []*ast.Field{fieldA}}
|
||||
listOfFieldB := &ast.FieldList{List: []*ast.Field{fieldB}}
|
||||
funcDeclA := &ast.FuncDecl{Name: &ast.Ident{Name: "a"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldA}}
|
||||
funcDeclB := &ast.FuncDecl{Name: &ast.Ident{Name: "b"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldB}}
|
||||
funcDeclNoResults := &ast.FuncDecl{Name: &ast.Ident{Name: "C"}, Type: &ast.FuncType{Params: &ast.FieldList{}}}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
a, b ast.Node
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "both nil",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "not same type",
|
||||
a: fieldA,
|
||||
b: &ast.FuncDecl{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Field same",
|
||||
a: fieldA,
|
||||
b: fieldA,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Field different",
|
||||
a: fieldA,
|
||||
b: fieldB,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results nil",
|
||||
a: funcDeclNoResults,
|
||||
b: funcDeclNoResults,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results same",
|
||||
a: funcDeclA,
|
||||
b: funcDeclA,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results different",
|
||||
a: funcDeclA,
|
||||
b: funcDeclB,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results a nil",
|
||||
a: funcDeclNoResults,
|
||||
b: funcDeclB,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "FuncDecl Type Results b nil",
|
||||
a: funcDeclA,
|
||||
b: funcDeclNoResults,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := &cgoPackage{}
|
||||
if got := p.isEquivalentAST(tc.a, tc.b); tc.expected != got {
|
||||
t.Errorf("expected %v, got %v", tc.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// simpleImporter implements the types.Importer interface, but only allows
|
||||
// importing the unsafe package.
|
||||
type simpleImporter struct {
|
||||
@@ -146,10 +217,16 @@ func (i simpleImporter) Import(path string) (*types.Package, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// formatDiagnostics formats the error message to be an indented comment. It
|
||||
// formatDiagnostic formats the error message to be an indented comment. It
|
||||
// also fixes Windows path name issues (backward slashes).
|
||||
func formatDiagnostic(err error) string {
|
||||
msg := err.Error()
|
||||
var msg string
|
||||
switch err := err.(type) {
|
||||
case scanner.Error:
|
||||
msg = err.Pos.String() + ": " + err.Msg
|
||||
default:
|
||||
msg = err.Error()
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// Fix Windows path slashes.
|
||||
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
|
||||
|
||||
+176
-7
@@ -14,6 +14,11 @@ import (
|
||||
var (
|
||||
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
|
||||
precedences = map[token.Token]int{
|
||||
token.OR: precedenceOr,
|
||||
token.XOR: precedenceXor,
|
||||
token.AND: precedenceAnd,
|
||||
token.SHL: precedenceShift,
|
||||
token.SHR: precedenceShift,
|
||||
token.ADD: precedenceAdd,
|
||||
token.SUB: precedenceAdd,
|
||||
token.MUL: precedenceMul,
|
||||
@@ -22,8 +27,13 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// See: https://en.cppreference.com/w/c/language/operator_precedence
|
||||
const (
|
||||
precedenceLowest = iota + 1
|
||||
precedenceOr
|
||||
precedenceXor
|
||||
precedenceAnd
|
||||
precedenceShift
|
||||
precedenceAdd
|
||||
precedenceMul
|
||||
precedencePrefix
|
||||
@@ -44,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 {
|
||||
@@ -76,7 +150,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
|
||||
|
||||
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
|
||||
switch t.peekToken {
|
||||
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
||||
case token.OR, token.XOR, token.AND, token.SHL, token.SHR, token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
||||
t.Next()
|
||||
leftExpr, err = parseBinaryExpr(t, leftExpr)
|
||||
}
|
||||
@@ -86,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,
|
||||
@@ -154,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()
|
||||
@@ -185,7 +325,9 @@ func (t *tokenizer) Next() {
|
||||
t.curValue = t.peekValue
|
||||
|
||||
// Parse the next peek token.
|
||||
t.peekPos += token.Pos(len(t.curValue))
|
||||
if t.peekPos != token.NoPos {
|
||||
t.peekPos += token.Pos(len(t.curValue))
|
||||
}
|
||||
for {
|
||||
if len(t.buf) == 0 {
|
||||
t.peekToken = token.EOF
|
||||
@@ -197,9 +339,28 @@ func (t *tokenizer) Next() {
|
||||
// Skip whitespace.
|
||||
// Based on this source, not sure whether it represents C whitespace:
|
||||
// https://en.cppreference.com/w/cpp/string/byte/isspace
|
||||
t.peekPos++
|
||||
if t.peekPos != token.NoPos {
|
||||
t.peekPos++
|
||||
}
|
||||
t.buf = t.buf[1:]
|
||||
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
|
||||
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&" || string(t.buf[:2]) == "<<" || string(t.buf[:2]) == ">>"):
|
||||
// Two-character tokens.
|
||||
switch c {
|
||||
case '&':
|
||||
t.peekToken = token.LAND
|
||||
case '|':
|
||||
t.peekToken = token.LOR
|
||||
case '<':
|
||||
t.peekToken = token.SHL
|
||||
case '>':
|
||||
t.peekToken = token.SHR
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
t.peekValue = t.buf[:2]
|
||||
t.buf = t.buf[2:]
|
||||
return
|
||||
case c == '(' || c == ')' || c == ',' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
|
||||
// Single-character tokens.
|
||||
// TODO: ++ (increment) and -- (decrement) operators.
|
||||
switch c {
|
||||
@@ -207,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 '-':
|
||||
@@ -217,6 +380,12 @@ func (t *tokenizer) Next() {
|
||||
t.peekToken = token.QUO
|
||||
case '%':
|
||||
t.peekToken = token.REM
|
||||
case '&':
|
||||
t.peekToken = token.AND
|
||||
case '|':
|
||||
t.peekToken = token.OR
|
||||
case '^':
|
||||
t.peekToken = token.XOR
|
||||
}
|
||||
t.peekValue = t.buf[:1]
|
||||
t.buf = t.buf[1:]
|
||||
|
||||
+9
-1
@@ -37,6 +37,14 @@ func TestParseConst(t *testing.T) {
|
||||
{`5*5`, `5 * 5`},
|
||||
{`5/5`, `5 / 5`},
|
||||
{`5%5`, `5 % 5`},
|
||||
{`5&5`, `5 & 5`},
|
||||
{`5|5`, `5 | 5`},
|
||||
{`5^5`, `5 ^ 5`},
|
||||
{`5<<5`, `5 << 5`},
|
||||
{`5>>5`, `5 >> 5`},
|
||||
{`5>>5 + 3`, `5 >> (5 + 3)`},
|
||||
{`5>>5 ^ 3`, `5>>5 ^ 3`},
|
||||
{`5||5`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet
|
||||
{`(5/5)`, `(5 / 5)`},
|
||||
{`1 - 2`, `1 - 2`},
|
||||
{`1 - 2 + 3`, `1 - 2 + 3`},
|
||||
@@ -51,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: ") {
|
||||
|
||||
+506
-233
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
//go:build !byollvm && llvm11
|
||||
// +build !byollvm,llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,16 +0,0 @@
|
||||
//go:build !byollvm && llvm12
|
||||
// +build !byollvm,llvm12
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
|
||||
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
|
||||
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
|
||||
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
|
||||
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,16 +0,0 @@
|
||||
//go:build !byollvm && !llvm11 && !llvm12
|
||||
// +build !byollvm,!llvm11,!llvm12
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-13/include
|
||||
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@13/include
|
||||
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@13/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm13/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-13/lib -lclang
|
||||
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@13/lib -lclang -lffi
|
||||
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@13/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm13/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm15
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-15/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@15/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,21 @@
|
||||
//go:build !byollvm && llvm16
|
||||
|
||||
package cgo
|
||||
|
||||
// As of 2023-05-05, there is a packaging issue with LLVM 16 on Debian:
|
||||
// https://github.com/llvm/llvm-project/issues/62199
|
||||
// A workaround is to fix this locally, using something like this:
|
||||
//
|
||||
// ln -sf ../../x86_64-linux-gnu/libclang-16.so.1 /usr/lib/llvm-16/lib/libclang.so
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-16/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@16/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm17
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-17 -I/usr/include/llvm-c-17 -I/usr/lib/llvm-17/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@17/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@17/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm17/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-17/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@17/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@17/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm17/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-18 -I/usr/include/llvm-c-18 -I/usr/lib/llvm-18/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@18/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@18/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm18/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-18/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@18/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@18/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm18/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
+22
-2
@@ -3,7 +3,7 @@
|
||||
// are slightly different from the ones defined in libclang.go, but they
|
||||
// should be ABI compatible.
|
||||
|
||||
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||
|
||||
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
|
||||
return clang_getTranslationUnitCursor(tu);
|
||||
@@ -17,6 +17,14 @@ CXString tinygo_clang_getCursorSpelling(CXCursor c) {
|
||||
return clang_getCursorSpelling(c);
|
||||
}
|
||||
|
||||
CXString tinygo_clang_getCursorPrettyPrinted(CXCursor c, CXPrintingPolicy policy) {
|
||||
return clang_getCursorPrettyPrinted(c, policy);
|
||||
}
|
||||
|
||||
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(CXCursor c) {
|
||||
return clang_getCursorPrintingPolicy(c);
|
||||
}
|
||||
|
||||
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
|
||||
return clang_getCursorKind(c);
|
||||
}
|
||||
@@ -45,6 +53,10 @@ CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
|
||||
return clang_Cursor_getArgument(c, i);
|
||||
}
|
||||
|
||||
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(CXCursor c) {
|
||||
return clang_Cursor_getStorageClass(c);
|
||||
}
|
||||
|
||||
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
||||
return clang_getCursorLocation(c);
|
||||
}
|
||||
@@ -65,6 +77,14 @@ CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
|
||||
return clang_getEnumDeclIntegerType(c);
|
||||
}
|
||||
|
||||
unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
|
||||
return clang_Cursor_isAnonymous(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);
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ var validLinkerFlags = []*regexp.Regexp{
|
||||
re(`-L([^@\-].*)`),
|
||||
re(`-O`),
|
||||
re(`-O([^@\-].*)`),
|
||||
re(`--export=([^@\-].*)`),
|
||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||
re(`-f(no-)?openmp(-simd)?`),
|
||||
re(`-fsanitize=([^@\-].*)`),
|
||||
|
||||
@@ -108,6 +108,7 @@ var goodLinkerFlags = [][]string{
|
||||
{"-Fbar"},
|
||||
{"-lbar"},
|
||||
{"-Lbar"},
|
||||
{"--export=my_symbol"},
|
||||
{"-fpic"},
|
||||
{"-fno-pic"},
|
||||
{"-fPIC"},
|
||||
|
||||
Vendored
+20
-20
@@ -24,23 +24,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
//go:linkname C.__CBytes runtime.cgo_CBytes
|
||||
func C.__CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func C.CBytes(b []byte) unsafe.Pointer {
|
||||
return C.__CBytes(b)
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
Vendored
+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
+27
-22
@@ -24,26 +24,31 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
const C.bar = C.foo
|
||||
const C.foo = 3
|
||||
//go:linkname C.__CBytes runtime.cgo_CBytes
|
||||
func C.__CBytes([]byte) unsafe.Pointer
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
func C.CBytes(b []byte) unsafe.Pointer {
|
||||
return C.__CBytes(b)
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.foo = 3
|
||||
const C.bar = C.foo
|
||||
const C.unreferenced = 4
|
||||
const C.referenced = C.unreferenced
|
||||
const C.fnlike_val = 5
|
||||
const C.square_val = (20 * 20)
|
||||
const C.add_val = (3 + 5)
|
||||
|
||||
Vendored
+22
-1
@@ -13,21 +13,31 @@ typedef someType noType; // undefined type
|
||||
#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
|
||||
#define SOME_CONST_b 3 ) // const with lots of weird whitespace (to test error locations)
|
||||
# define SOME_CONST_startspace 3)
|
||||
*/
|
||||
//
|
||||
//
|
||||
// #define SOME_CONST_4 8) // after some empty lines
|
||||
// #cgo CFLAGS: -DSOME_PARAM_CONST_invalid=3/+3
|
||||
// #cgo CFLAGS: -DSOME_PARAM_CONST_valid=3+4
|
||||
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.
|
||||
//
|
||||
//line errors.go:100
|
||||
var (
|
||||
// constant too large
|
||||
_ C.uint8_t = 2 << 10
|
||||
_ C.char = 2 << 10
|
||||
|
||||
// z member does not exist
|
||||
_ C.point_t = C.point_t{z: 3}
|
||||
@@ -38,4 +48,15 @@ var (
|
||||
_ byte = C.SOME_CONST_3
|
||||
|
||||
_ = C.SOME_CONST_4
|
||||
|
||||
_ = C.SOME_CONST_b
|
||||
|
||||
_ = C.SOME_CONST_startspace
|
||||
|
||||
// constants passed by a command line parameter
|
||||
_ = C.SOME_PARAM_CONST_invalid
|
||||
_ = C.SOME_PARAM_CONST_valid
|
||||
|
||||
_ = C.add_toomuch
|
||||
_ = C.add_toolittle
|
||||
)
|
||||
|
||||
Vendored
+39
-27
@@ -1,16 +1,26 @@
|
||||
// CGo errors:
|
||||
// testdata/errors.go:4:2: warning: some warning
|
||||
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
||||
// testdata/errors.go:22:5: warning: another warning
|
||||
// testdata/errors.go:26:5: warning: another warning
|
||||
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:19:26: 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
|
||||
// -: unexpected token INT, expected end of expression
|
||||
// testdata/errors.go:30:35: unexpected number of parameters: expected 2, got 3
|
||||
// testdata/errors.go:31:31: unexpected number of parameters: expected 2, got 1
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||
// testdata/errors.go:105: unknown field z in struct literal
|
||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||
// testdata/errors.go:108: undefined: C.SOME_CONST_1
|
||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
// testdata/errors.go:112: undeclared name: C.SOME_CONST_4
|
||||
// testdata/errors.go:112: undefined: C.SOME_CONST_4
|
||||
// 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:122: undefined: C.add_toomuch
|
||||
// testdata/errors.go:123: undefined: C.add_toolittle
|
||||
|
||||
package main
|
||||
|
||||
@@ -38,29 +48,31 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
//go:linkname C.__CBytes runtime.cgo_CBytes
|
||||
func C.__CBytes([]byte) unsafe.Pointer
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.point_t = struct {
|
||||
func C.CBytes(b []byte) unsafe.Pointer {
|
||||
return C.__CBytes(b)
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C.struct_point_t struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point_t = C.struct_point_t
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
const C.SOME_PARAM_CONST_valid = 3 + 4
|
||||
|
||||
Vendored
+21
-21
@@ -29,26 +29,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__CBytes runtime.cgo_CBytes
|
||||
func C.__CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func C.CBytes(b []byte) unsafe.Pointer {
|
||||
return C.__CBytes(b)
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.BAR = 3
|
||||
const C.FOO_H = 1
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+2
@@ -5,6 +5,7 @@ package main
|
||||
int foo(int a, int b);
|
||||
void variadic0();
|
||||
void variadic2(int x, int y, ...);
|
||||
static void staticfunc(int x);
|
||||
|
||||
// Global variable signatures.
|
||||
extern int someValue;
|
||||
@@ -16,6 +17,7 @@ func accessFunctions() {
|
||||
C.foo(3, 4)
|
||||
C.variadic0()
|
||||
C.variadic2(3, 5)
|
||||
C.staticfunc(3)
|
||||
}
|
||||
|
||||
func accessGlobals() {
|
||||
|
||||
Vendored
+30
-23
@@ -24,41 +24,48 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
//go:linkname C.__CBytes runtime.cgo_CBytes
|
||||
func C.__CBytes([]byte) unsafe.Pointer
|
||||
|
||||
func C.CBytes(b []byte) unsafe.Pointer {
|
||||
return C.__CBytes(b)
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
//export foo
|
||||
func C.foo(a C.int, b C.int) C.int
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic0
|
||||
//go:variadic
|
||||
func C.variadic0()
|
||||
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic2
|
||||
//go:variadic
|
||||
func C.variadic2(x C.int, y C.int)
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
|
||||
//export _Cgo_static_173c95a79b6df1980521_staticfunc
|
||||
func C.staticfunc!symbols.go(x C.int)
|
||||
|
||||
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
|
||||
|
||||
//go:extern someValue
|
||||
var C.someValue C.int
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+4
@@ -112,6 +112,10 @@ import "C"
|
||||
import "C"
|
||||
|
||||
var (
|
||||
// aliases
|
||||
_ C.float
|
||||
_ C.double
|
||||
|
||||
// Simple typedefs.
|
||||
_ C.myint
|
||||
|
||||
|
||||
Vendored
+102
-103
@@ -24,132 +24,131 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
const C.option2A = 20
|
||||
const C.optionA = 0
|
||||
const C.optionB = 1
|
||||
const C.optionC = -5
|
||||
const C.optionD = -4
|
||||
const C.optionE = 10
|
||||
const C.optionF = 11
|
||||
const C.optionG = 12
|
||||
const C.unused1 = 5
|
||||
//go:linkname C.__CBytes runtime.cgo_CBytes
|
||||
func C.__CBytes([]byte) unsafe.Pointer
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.bitfield_t = C.struct_4
|
||||
type C.myIntArray = [10]C.int
|
||||
func C.CBytes(b []byte) unsafe.Pointer {
|
||||
return C.__CBytes(b)
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C.myint = C.int
|
||||
type C.option2_t = C.uint
|
||||
type C.option_t = C.enum_option
|
||||
type C.point2d_t = struct {
|
||||
type C.struct_point2d_t struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_nested_t = struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C.union_2
|
||||
}
|
||||
type C.types_t = struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.union1_t = struct{ i C.int }
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.union3_t = C.union_1
|
||||
type C.union_nested_t = C.union_3
|
||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||
|
||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.struct_4 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
}
|
||||
type C.point2d_t = C.struct_point2d_t
|
||||
type C.struct_point3d struct {
|
||||
x C.int
|
||||
y C.int
|
||||
z C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_type1 struct {
|
||||
_type C.int
|
||||
__type C.int
|
||||
___type C.int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
type C.union_union1_t struct{ i C.int }
|
||||
type C.union1_t = C.union_union1_t
|
||||
type C.union_union3_t struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||
|
||||
type C.union_1 struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *C.union_union3_t) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union3_t) unionfield_d() *float64 {
|
||||
return (*float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
func (union *C.union_union3_t) unionfield_s() *C.short {
|
||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_2 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_3 struct{ $union [2]uint64 }
|
||||
type C.union3_t = C.union_union3_t
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
type C.enum_option C.int
|
||||
type C.enum_unused C.uint
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.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 }
|
||||
|
||||
func (union *C._Ctype_union___0) unionfield_area() *C.point2d_t {
|
||||
return (*C.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))
|
||||
}
|
||||
|
||||
type C.struct_struct_nested_t struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C._Ctype_union___0
|
||||
}
|
||||
type C.struct_nested_t = C.struct_struct_nested_t
|
||||
type C.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 *C.union_union_nested_t) unionfield_array() *C.unionarray_t {
|
||||
return (*C.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))
|
||||
}
|
||||
|
||||
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 {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.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
|
||||
|
||||
d C.uchar
|
||||
e C.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) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C.struct_bitfield_t) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_bitfield_t) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C.struct_bitfield_t) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_bitfield_t) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.bitfield_t = C.struct_bitfield_t
|
||||
|
||||
+207
-52
@@ -10,6 +10,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,6 @@ type Config struct {
|
||||
Options *Options
|
||||
Target *TargetSpec
|
||||
GoMinorVersion int
|
||||
ClangHeaders string // Clang built-in header include path
|
||||
TestConfig TestConfig
|
||||
}
|
||||
|
||||
@@ -33,6 +33,17 @@ 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.
|
||||
@@ -46,6 +57,12 @@ func (c *Config) Features() string {
|
||||
return c.Target.Features + "," + c.Options.LLVMFeatures
|
||||
}
|
||||
|
||||
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
||||
// string is returned if the target doesn't specify an ABI.
|
||||
func (c *Config) ABI() string {
|
||||
return c.Target.ABI
|
||||
}
|
||||
|
||||
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
||||
// for example, bare-metal targets will usually pretend to be linux to get the
|
||||
// standard library to compile.
|
||||
@@ -54,7 +71,7 @@ func (c *Config) GOOS() string {
|
||||
}
|
||||
|
||||
// GOARCH returns the GOARCH of the target. This might not always be the actual
|
||||
// archtecture: for example, the AVR target is not supported by the Go standard
|
||||
// architecture: for example, the AVR target is not supported by the Go standard
|
||||
// library so such targets will usually pretend to be linux/arm.
|
||||
func (c *Config) GOARCH() string {
|
||||
return c.Target.GOARCH
|
||||
@@ -66,26 +83,31 @@ func (c *Config) GOARM() string {
|
||||
return c.Options.GOARM
|
||||
}
|
||||
|
||||
// GOMIPS will return the GOMIPS environment variable given to the compiler when
|
||||
// building a program.
|
||||
func (c *Config) GOMIPS() string {
|
||||
return c.Options.GOMIPS
|
||||
}
|
||||
|
||||
// BuildTags returns the complete list of build tags used during this build.
|
||||
func (c *Config) BuildTags() []string {
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||
tags := append([]string(nil), c.Target.BuildTags...) // copy slice (avoid a race)
|
||||
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
|
||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 {
|
||||
tags = append(tags, extraTags...)
|
||||
}
|
||||
tags = append(tags, c.Options.Tags...)
|
||||
return tags
|
||||
}
|
||||
|
||||
// CgoEnabled returns true if (and only if) CGo is enabled. It is true by
|
||||
// default and false if CGO_ENABLED is set to "0".
|
||||
func (c *Config) CgoEnabled() bool {
|
||||
return goenv.Get("CGO_ENABLED") == "1"
|
||||
}
|
||||
|
||||
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||
// values are "none", "leaking", and "conservative".
|
||||
// values are "none", "leaking", "conservative" and "precise".
|
||||
func (c *Config) GC() string {
|
||||
if c.Options.GC != "" {
|
||||
return c.Options.GC
|
||||
@@ -100,7 +122,7 @@ func (c *Config) GC() string {
|
||||
// that can be traced by the garbage collector.
|
||||
func (c *Config) NeedsStackObjects() bool {
|
||||
switch c.GC() {
|
||||
case "conservative":
|
||||
case "conservative", "custom", "precise":
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "tinygo.wasm" {
|
||||
return true
|
||||
@@ -140,18 +162,18 @@ func (c *Config) Serial() string {
|
||||
|
||||
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
|
||||
// threshold as used in the LLVM optimization pipeline.
|
||||
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
||||
func (c *Config) OptLevel() (level string, speedLevel, sizeLevel int) {
|
||||
switch c.Options.Opt {
|
||||
case "none", "0":
|
||||
return 0, 0, 0 // -O0
|
||||
return "O0", 0, 0
|
||||
case "1":
|
||||
return 1, 0, 0 // -O1
|
||||
return "O1", 1, 0
|
||||
case "2":
|
||||
return 2, 0, 225 // -O2
|
||||
return "O2", 2, 0
|
||||
case "s":
|
||||
return 2, 1, 225 // -Os
|
||||
return "Os", 2, 1
|
||||
case "z":
|
||||
return 2, 2, 5 // -Oz, default
|
||||
return "Oz", 2, 2 // default
|
||||
default:
|
||||
// This is not shown to the user: valid choices are already checked as
|
||||
// part of Options.Verify(). It is here as a sanity check.
|
||||
@@ -176,6 +198,24 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// StackSize returns the default stack size to be used for goroutines, if the
|
||||
// stack size could not be determined automatically at compile time.
|
||||
func (c *Config) StackSize() uint64 {
|
||||
if c.Options.StackSize != 0 {
|
||||
return c.Options.StackSize
|
||||
}
|
||||
return c.Target.DefaultStackSize
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return 1024
|
||||
}
|
||||
|
||||
return 256
|
||||
}
|
||||
|
||||
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||
func (c *Config) RP2040BootPatch() bool {
|
||||
@@ -185,14 +225,26 @@ func (c *Config) RP2040BootPatch() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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]
|
||||
if arch == "arm64" {
|
||||
return "aarch64"
|
||||
}
|
||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||
return "arm"
|
||||
}
|
||||
if arch == "mipsel" {
|
||||
return "mips"
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
// MuslArchitecture returns the architecture name as used in musl libc. It is
|
||||
// usually the same as the first part of the LLVM triple, but not always.
|
||||
func MuslArchitecture(triple string) string {
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||
arch = "arm"
|
||||
}
|
||||
return arch
|
||||
return CanonicalArchName(triple)
|
||||
}
|
||||
|
||||
// LibcPath returns the path to the libc directory. The libc path will be either
|
||||
@@ -203,6 +255,12 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
if c.CPU() != "" {
|
||||
archname += "-" + c.CPU()
|
||||
}
|
||||
if c.ABI() != "" {
|
||||
archname += "-" + c.ABI()
|
||||
}
|
||||
if c.Target.SoftFloat {
|
||||
archname += "-softfloat"
|
||||
}
|
||||
|
||||
// Try to load a precompiled library.
|
||||
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
|
||||
@@ -217,20 +275,60 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
|
||||
}
|
||||
|
||||
// DefaultBinaryExtension returns the default extension for binaries, such as
|
||||
// .exe, .wasm, or no extension (depending on the target).
|
||||
func (c *Config) DefaultBinaryExtension() string {
|
||||
parts := strings.Split(c.Triple(), "-")
|
||||
if parts[0] == "wasm32" {
|
||||
// WebAssembly files always have the .wasm file extension.
|
||||
return ".wasm"
|
||||
}
|
||||
if len(parts) >= 3 && parts[2] == "windows" {
|
||||
// Windows uses .exe.
|
||||
return ".exe"
|
||||
}
|
||||
if len(parts) >= 3 && parts[2] == "unknown" {
|
||||
// There appears to be a convention to use the .elf file extension for
|
||||
// ELF files intended for microcontrollers. I'm not aware of the origin
|
||||
// of this, it's just something that is used by many projects.
|
||||
// I think it's a good tradition, so let's keep it.
|
||||
return ".elf"
|
||||
}
|
||||
// Linux, MacOS, etc, don't use a file extension. Use it as a fallback.
|
||||
return ""
|
||||
}
|
||||
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
// preprocessing.
|
||||
func (c *Config) CFlags() []string {
|
||||
func (c *Config) CFlags(libclang bool) []string {
|
||||
var cflags []string
|
||||
for _, flag := range c.Target.CFlags {
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
resourceDir := goenv.ClangResourceDir(libclang)
|
||||
if resourceDir != "" {
|
||||
// The resource directory contains the built-in clang headers like
|
||||
// stdbool.h, stdint.h, float.h, etc.
|
||||
// It is left empty if we're using an external compiler (that already
|
||||
// knows these headers).
|
||||
cflags = append(cflags,
|
||||
"-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,
|
||||
"--sysroot="+path,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
)
|
||||
@@ -246,12 +344,17 @@ func (c *Config) CFlags() []string {
|
||||
)
|
||||
case "wasi-libc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
|
||||
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,
|
||||
"--sysroot="+path,
|
||||
"-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",
|
||||
@@ -264,7 +367,7 @@ func (c *Config) CFlags() []string {
|
||||
panic("unknown libc: " + c.Target.Libc)
|
||||
}
|
||||
// Always emit debug information. It is optionally stripped at link time.
|
||||
cflags = append(cflags, "-g")
|
||||
cflags = append(cflags, "-gdwarf-4")
|
||||
// Use the same optimization level as TinyGo.
|
||||
cflags = append(cflags, "-O"+c.Options.Opt)
|
||||
// Set the LLVM target triple.
|
||||
@@ -282,6 +385,10 @@ func (c *Config) CFlags() []string {
|
||||
cflags = append(cflags, "-mcpu="+c.Target.CPU)
|
||||
}
|
||||
}
|
||||
// Set the -mabi flag, if needed.
|
||||
if c.ABI() != "" {
|
||||
cflags = append(cflags, "-mabi="+c.ABI())
|
||||
}
|
||||
return cflags
|
||||
}
|
||||
|
||||
@@ -299,6 +406,8 @@ func (c *Config) LDFlags() []string {
|
||||
if c.Target.LinkerScript != "" {
|
||||
ldflags = append(ldflags, "-T", c.Target.LinkerScript)
|
||||
}
|
||||
ldflags = append(ldflags, c.Options.ExtLDFlags...)
|
||||
|
||||
return ldflags
|
||||
}
|
||||
|
||||
@@ -321,8 +430,8 @@ func (c *Config) VerifyIR() bool {
|
||||
}
|
||||
|
||||
// Debug returns whether debug (DWARF) information should be retained by the
|
||||
// linker. By default, debug information is retained but it can be removed with
|
||||
// the -no-debug flag.
|
||||
// linker. By default, debug information is retained, but it can be removed
|
||||
// with the -no-debug flag.
|
||||
func (c *Config) Debug() bool {
|
||||
return c.Options.Debug
|
||||
}
|
||||
@@ -337,6 +446,13 @@ func (c *Config) BinaryFormat(ext string) string {
|
||||
return c.Target.BinaryFormat
|
||||
}
|
||||
return "bin"
|
||||
case ".img":
|
||||
// Image file. Only defined for the ESP32 at the moment, where it is a
|
||||
// full (runnable) image that can be used in the Espressif QEMU fork.
|
||||
if c.Target.BinaryFormat != "" {
|
||||
return c.Target.BinaryFormat + "-img"
|
||||
}
|
||||
return "bin"
|
||||
case ".hex":
|
||||
// Similar to bin, but includes the start address and is thus usually a
|
||||
// better format.
|
||||
@@ -359,7 +475,7 @@ func (c *Config) BinaryFormat(ext string) string {
|
||||
|
||||
// Programmer returns the flash method and OpenOCD interface name given a
|
||||
// particular configuration. It may either be all configured in the target JSON
|
||||
// file or be modified using the -programmmer command-line option.
|
||||
// file or be modified using the -programmer command-line option.
|
||||
func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
switch c.Options.Programmer {
|
||||
case "":
|
||||
@@ -386,26 +502,33 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
||||
if openocdInterface == "" {
|
||||
return nil, errors.New("OpenOCD programmer not set")
|
||||
}
|
||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(openocdInterface) {
|
||||
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(openocdInterface) {
|
||||
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface)
|
||||
}
|
||||
if c.Target.OpenOCDTarget == "" {
|
||||
return nil, errors.New("OpenOCD chip not set")
|
||||
}
|
||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(c.Target.OpenOCDTarget) {
|
||||
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(c.Target.OpenOCDTarget) {
|
||||
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget)
|
||||
}
|
||||
if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" {
|
||||
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport)
|
||||
}
|
||||
args = []string{"-f", "interface/" + openocdInterface + ".cfg"}
|
||||
if c.Target.OpenOCDTransport != "" {
|
||||
transport := c.Target.OpenOCDTransport
|
||||
if transport == "swd" {
|
||||
switch openocdInterface {
|
||||
case "stlink-dap":
|
||||
transport = "dapdirect_swd"
|
||||
}
|
||||
}
|
||||
args = append(args, "-c", "transport select "+transport)
|
||||
}
|
||||
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
|
||||
for _, cmd := range c.Target.OpenOCDCommands {
|
||||
args = append(args, "-c", cmd)
|
||||
}
|
||||
if c.Target.OpenOCDTransport != "" {
|
||||
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
|
||||
}
|
||||
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
|
||||
return args, nil
|
||||
}
|
||||
|
||||
@@ -428,25 +551,57 @@ func (c *Config) RelocationModel() string {
|
||||
return "static"
|
||||
}
|
||||
|
||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file, and
|
||||
// the value is overridden by `-wasm-abi` flag if it is provided
|
||||
func (c *Config) WasmAbi() string {
|
||||
if c.Options.WasmAbi != "" {
|
||||
return c.Options.WasmAbi
|
||||
// EmulatorName is a shorthand to get the command for this emulator, something
|
||||
// like qemu-system-arm or simavr.
|
||||
func (c *Config) EmulatorName() string {
|
||||
parts := strings.SplitN(c.Target.Emulator, " ", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[0]
|
||||
}
|
||||
return c.Target.WasmAbi
|
||||
return ""
|
||||
}
|
||||
|
||||
// Emulator returns the emulator target config
|
||||
func (c *Config) Emulator() []string {
|
||||
var emulator []string
|
||||
for _, s := range c.Target.Emulator {
|
||||
emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
|
||||
// EmulatorFormat returns the binary format for the emulator and the associated
|
||||
// file extension. An empty string means to pass directly whatever the linker
|
||||
// produces directly without conversion (usually ELF format).
|
||||
func (c *Config) EmulatorFormat() (format, fileExt string) {
|
||||
switch {
|
||||
case strings.Contains(c.Target.Emulator, "{img}"):
|
||||
return "img", ".img"
|
||||
default:
|
||||
return "", ""
|
||||
}
|
||||
return emulator
|
||||
}
|
||||
|
||||
// Emulator returns a ready-to-run command to run the given binary in an
|
||||
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
|
||||
// the compiled binary.
|
||||
func (c *Config) Emulator(format, binary string) ([]string, error) {
|
||||
parts, err := shlex.Split(c.Target.Emulator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse emulator command: %w", err)
|
||||
}
|
||||
var emulator []string
|
||||
for _, s := range parts {
|
||||
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
|
||||
// Allow replacement of what's usually /tmp except notably Windows.
|
||||
s = strings.ReplaceAll(s, "{tmpDir}", os.TempDir())
|
||||
s = strings.ReplaceAll(s, "{"+format+"}", binary)
|
||||
emulator = append(emulator, s)
|
||||
}
|
||||
return emulator, nil
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
CompileOnly bool
|
||||
Verbose bool
|
||||
Short bool
|
||||
RunRegexp string
|
||||
SkipRegexp string
|
||||
Count *int
|
||||
BenchRegexp string
|
||||
BenchTime string
|
||||
BenchMem bool
|
||||
Shuffle string
|
||||
}
|
||||
|
||||
+29
-7
@@ -4,12 +4,14 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "conservative"}
|
||||
validBuildModeOptions = []string{"default", "c-shared"}
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||
validSerialOptions = []string{"none", "uart", "usb"}
|
||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||
@@ -22,33 +24,53 @@ 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
|
||||
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
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands func(cmd string, args ...string)
|
||||
Semaphore chan struct{} // -p flag controls cap
|
||||
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
|
||||
WasmAbi string
|
||||
Tags []string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Directory string
|
||||
PrintJSON bool
|
||||
Monitor bool
|
||||
BaudRate int
|
||||
Timeout time.Duration
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
ExtLDFlags []string
|
||||
}
|
||||
|
||||
// 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 := isInArray(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)
|
||||
if !valid {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestVerifyOptions(t *testing.T) {
|
||||
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative`)
|
||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
|
||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||
@@ -48,6 +48,12 @@ func TestVerifyOptions(t *testing.T) {
|
||||
GC: "conservative",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GCOptionCustom",
|
||||
opts: compileopts.Options{
|
||||
GC: "custom",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidSchedulerOption",
|
||||
opts: compileopts.Options{
|
||||
|
||||
+351
-155
@@ -12,11 +12,9 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Target specification for a given target. Used for bare metal targets.
|
||||
@@ -25,48 +23,53 @@ import (
|
||||
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
|
||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||
type TargetSpec struct {
|
||||
Inherits []string `json:"inherits"`
|
||||
Triple string `json:"llvm-target"`
|
||||
CPU string `json:"cpu"`
|
||||
Features string `json:"features"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc"`
|
||||
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time.
|
||||
DefaultStackSize uint64 `json:"default-stack-size"` // Default stack size if the size couldn't be determined at compile time.
|
||||
CFlags []string `json:"cflags"`
|
||||
LDFlags []string `json:"ldflags"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
||||
FlashCommand string `json:"flash-command"`
|
||||
GDB []string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid"
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume string `json:"msd-volume-name"`
|
||||
FlashFilename string `json:"msd-firmware-name"`
|
||||
UF2FamilyID string `json:"uf2-family-id"`
|
||||
BinaryFormat string `json:"binary-format"`
|
||||
OpenOCDInterface string `json:"openocd-interface"`
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
OpenOCDCommands []string `json:"openocd-commands"`
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
RelocationModel string `json:"relocation-model"`
|
||||
WasmAbi string `json:"wasm-abi"`
|
||||
Inherits []string `json:"inherits,omitempty"`
|
||||
Triple string `json:"llvm-target,omitempty"`
|
||||
CPU string `json:"cpu,omitempty"`
|
||||
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
|
||||
Features string `json:"features,omitempty"`
|
||||
GOOS string `json:"goos,omitempty"`
|
||||
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)
|
||||
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.
|
||||
CFlags []string `json:"cflags,omitempty"`
|
||||
LDFlags []string `json:"ldflags,omitempty"`
|
||||
LinkerScript string `json:"linkerscript,omitempty"`
|
||||
ExtraFiles []string `json:"extra-files,omitempty"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator string `json:"emulator,omitempty"`
|
||||
FlashCommand string `json:"flash-command,omitempty"`
|
||||
GDB []string `json:"gdb,omitempty"`
|
||||
PortReset string `json:"flash-1200-bps-reset,omitempty"`
|
||||
SerialPort []string `json:"serial-port,omitempty"` // serial port IDs in the form "vid:pid"
|
||||
FlashMethod string `json:"flash-method,omitempty"`
|
||||
FlashVolume []string `json:"msd-volume-name,omitempty"`
|
||||
FlashFilename string `json:"msd-firmware-name,omitempty"`
|
||||
UF2FamilyID string `json:"uf2-family-id,omitempty"`
|
||||
BinaryFormat string `json:"binary-format,omitempty"`
|
||||
OpenOCDInterface string `json:"openocd-interface,omitempty"`
|
||||
OpenOCDTarget string `json:"openocd-target,omitempty"`
|
||||
OpenOCDTransport string `json:"openocd-transport,omitempty"`
|
||||
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||
CodeModel string `json:"code-model,omitempty"`
|
||||
RelocationModel string `json:"relocation-model,omitempty"`
|
||||
WITPackage string `json:"wit-package,omitempty"`
|
||||
WITWorld string `json:"wit-world,omitempty"`
|
||||
}
|
||||
|
||||
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
||||
func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
||||
func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
|
||||
specType := reflect.TypeOf(spec).Elem()
|
||||
specValue := reflect.ValueOf(spec).Elem()
|
||||
childValue := reflect.ValueOf(child).Elem()
|
||||
@@ -85,27 +88,30 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
||||
if src.Uint() != 0 {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Bool:
|
||||
if src.Bool() {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Ptr: // for pointers, copy if not nil
|
||||
if !src.IsNil() {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Slice: // for slices...
|
||||
if src.Len() > 0 { // ... if not empty ...
|
||||
switch tag := field.Tag.Get("override"); tag {
|
||||
case "copy":
|
||||
// copy the field of child to spec
|
||||
dst.Set(src)
|
||||
case "append", "":
|
||||
// or append the field of child to spec
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
default:
|
||||
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
||||
case reflect.Slice: // for slices, append the field and check for duplicates
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
for i := 0; i < dst.Len(); i++ {
|
||||
v := dst.Index(i).String()
|
||||
for j := i + 1; j < dst.Len(); j++ {
|
||||
w := dst.Index(j).String()
|
||||
if v == w {
|
||||
return fmt.Errorf("duplicate value '%s' in field %s", v, field.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("unknown field type : " + kind.String())
|
||||
return fmt.Errorf("unknown field type: %s", kind)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// load reads a target specification from the JSON in the given io.Reader. It
|
||||
@@ -120,10 +126,10 @@ func (spec *TargetSpec) load(r io.Reader) error {
|
||||
}
|
||||
|
||||
// loadFromGivenStr loads the TargetSpec from the given string that could be:
|
||||
// - targets/ directory inside the compiler sources
|
||||
// - a relative or absolute path to custom (project specific) target specification .json file;
|
||||
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
|
||||
// as well as path to custom files (ex. myAwesomeProject.json)
|
||||
// - targets/ directory inside the compiler sources
|
||||
// - a relative or absolute path to custom (project specific) target specification .json file;
|
||||
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
|
||||
// as well as path to custom files (ex. myAwesomeProject.json)
|
||||
func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||
path := ""
|
||||
if strings.HasSuffix(str, ".json") {
|
||||
@@ -153,11 +159,17 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSpec.overrideProperties(subtarget)
|
||||
err = newSpec.overrideProperties(subtarget)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// When all properties are loaded, make sure they are properly inherited.
|
||||
newSpec.overrideProperties(spec)
|
||||
err := newSpec.overrideProperties(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*spec = *newSpec
|
||||
|
||||
return nil
|
||||
@@ -166,52 +178,7 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
// Load a target specification.
|
||||
func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
if options.Target == "" {
|
||||
// Configure based on GOOS/GOARCH environment variables (falling back to
|
||||
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
||||
var llvmarch string
|
||||
switch options.GOARCH {
|
||||
case "386":
|
||||
llvmarch = "i386"
|
||||
case "amd64":
|
||||
llvmarch = "x86_64"
|
||||
case "arm64":
|
||||
llvmarch = "aarch64"
|
||||
case "arm":
|
||||
switch options.GOARM {
|
||||
case "5":
|
||||
llvmarch = "armv5"
|
||||
case "6":
|
||||
llvmarch = "armv6"
|
||||
case "7":
|
||||
llvmarch = "armv7"
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||
}
|
||||
default:
|
||||
llvmarch = options.GOARCH
|
||||
}
|
||||
llvmos := options.GOOS
|
||||
if llvmos == "darwin" {
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||
// to iOS!
|
||||
llvmos = "macosx10.12.0"
|
||||
if llvmarch == "aarch64" {
|
||||
// Looks like Apple prefers to call this architecture ARM64
|
||||
// instead of AArch64.
|
||||
llvmarch = "arm64"
|
||||
}
|
||||
}
|
||||
// Target triples (which actually have four components, but are called
|
||||
// triples for historical reasons) have the form:
|
||||
// arch-vendor-os-environment
|
||||
target := llvmarch + "-unknown-" + llvmos
|
||||
if options.GOARCH == "arm" {
|
||||
target += "-gnueabihf"
|
||||
}
|
||||
if options.GOOS == "windows" {
|
||||
target += "-gnu"
|
||||
}
|
||||
return defaultTarget(options.GOOS, options.GOARCH, target)
|
||||
return defaultTarget(options)
|
||||
}
|
||||
|
||||
// See whether there is a target specification for this target (e.g.
|
||||
@@ -225,7 +192,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
// it includes all parents as specified in the "inherits" key.
|
||||
err = spec.resolveInherits()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s : %w", options.Target, err)
|
||||
}
|
||||
|
||||
if spec.Scheduler == "asyncify" {
|
||||
@@ -235,51 +202,220 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// No target spec available. Use the default one, useful on most systems
|
||||
// with a regular OS.
|
||||
// GetTargetSpecs retrieves target specifications from the TINYGOROOT targets
|
||||
// directory. Only valid target JSON files are considered, and the function
|
||||
// returns a map of target names to their respective TargetSpec.
|
||||
func GetTargetSpecs() (map[string]*TargetSpec, error) {
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list targets: %w", err)
|
||||
}
|
||||
|
||||
maps := map[string]*TargetSpec{}
|
||||
for _, entry := range entries {
|
||||
entryInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get entry info: %w", err)
|
||||
}
|
||||
if !entryInfo.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
|
||||
// Only inspect JSON files.
|
||||
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.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).
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
name = name[:len(name)-5]
|
||||
maps[name] = spec
|
||||
}
|
||||
return maps, nil
|
||||
}
|
||||
|
||||
// Load a target from environment variables (which default to
|
||||
// runtime.GOOS/runtime.GOARCH).
|
||||
func defaultTarget(options *Options) (*TargetSpec, error) {
|
||||
spec := TargetSpec{
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
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"},
|
||||
PortReset: "false",
|
||||
}
|
||||
switch goarch {
|
||||
|
||||
// Configure target based on GOARCH.
|
||||
var llvmarch string
|
||||
switch options.GOARCH {
|
||||
case "386":
|
||||
llvmarch = "i386"
|
||||
spec.CPU = "pentium4"
|
||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "amd64":
|
||||
llvmarch = "x86_64"
|
||||
spec.CPU = "x86-64"
|
||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "arm":
|
||||
spec.CPU = "generic"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables")
|
||||
switch strings.Split(triple, "-")[0] {
|
||||
case "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"
|
||||
case "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"
|
||||
case "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"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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 of form <num>,[hardfloat|softfloat] where num is 5, 6, or 7", options.GOARM)
|
||||
}
|
||||
case "arm64":
|
||||
spec.CPU = "generic"
|
||||
spec.Features = "+neon"
|
||||
llvmarch = "aarch64"
|
||||
if options.GOOS == "darwin" {
|
||||
spec.Features = "+fp-armv8,+neon"
|
||||
// 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"
|
||||
} else { // linux
|
||||
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
|
||||
}
|
||||
case "mips", "mipsle":
|
||||
spec.CPU = "mips32"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-pic")
|
||||
if options.GOARCH == "mips" {
|
||||
llvmarch = "mips" // big endian
|
||||
} else {
|
||||
llvmarch = "mipsel" // little endian
|
||||
}
|
||||
switch options.GOMIPS {
|
||||
case "hardfloat":
|
||||
spec.Features = "+fpxx,+mips32,+nooddspreg,-noabicalls"
|
||||
case "softfloat":
|
||||
spec.SoftFloat = true
|
||||
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",
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GOARCH=%s", options.GOARCH)
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
} else if goos == "linux" {
|
||||
|
||||
// Configure target based on GOOS.
|
||||
llvmos := options.GOOS
|
||||
llvmvendor := "unknown"
|
||||
switch options.GOOS {
|
||||
case "darwin":
|
||||
platformVersion := "10.12.0"
|
||||
if options.GOARCH == "arm64" {
|
||||
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "darwin-libSystem"
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
||||
// iOS!
|
||||
llvmos = "macosx" + platformVersion
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-flavor", "darwin",
|
||||
"-dead_strip",
|
||||
"-arch", llvmarch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
)
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/os_darwin.c",
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
case "linux":
|
||||
spec.Linker = "ld.lld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "musl"
|
||||
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
||||
} else if goos == "windows" {
|
||||
if options.GOARCH == "arm64" {
|
||||
// Disable outline atomics. For details, see:
|
||||
// https://cpufun.substack.com/p/atomics-in-aarch64
|
||||
// A better way would be to fully support outline atomics, which
|
||||
// makes atomics slightly more efficient on systems with many cores.
|
||||
// But the instructions are only supported on newer aarch64 CPUs, so
|
||||
// this feature is normally put in a system library which does
|
||||
// feature detection for you.
|
||||
// We take the lazy way out and simply disable this feature, instead
|
||||
// of enabling it in compiler-rt (which is a bit more complicated).
|
||||
// We don't really need this feature anyway as we don't even support
|
||||
// proper threading.
|
||||
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/runtime_unix.c",
|
||||
"src/runtime/signal.c")
|
||||
case "windows":
|
||||
spec.Linker = "ld.lld"
|
||||
spec.Libc = "mingw-w64"
|
||||
// Note: using a medium code model, low image base and no ASLR
|
||||
@@ -288,47 +424,107 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// 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 "amd64":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "i386pep",
|
||||
"--image-base", "0x400000",
|
||||
)
|
||||
case "arm64":
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "arm64pe",
|
||||
)
|
||||
}
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-m", "i386pep",
|
||||
"-Bdynamic",
|
||||
"--image-base", "0x400000",
|
||||
"--gc-sections",
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if llvmMajor >= 12 {
|
||||
// This flag was added in LLVM 12. At the same time, LLVM 12
|
||||
// switched the default from --dynamicbase to --no-dynamicbase.
|
||||
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
|
||||
}
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
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 run --dir={tmpDir}::/tmp {}"
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/asm_tinygowasm.S",
|
||||
"src/internal/task/task_asyncify_wasm.S",
|
||||
)
|
||||
llvmos = "wasi"
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GOOS=%s", options.GOOS)
|
||||
}
|
||||
if goarch != "wasm" {
|
||||
|
||||
// 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.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).
|
||||
if options.GOARCH != "wasm" {
|
||||
suffix := ""
|
||||
if goos == "windows" {
|
||||
// Windows uses a different calling convention from other operating
|
||||
// systems so we need separate assembly files.
|
||||
if options.GOOS == "windows" && options.GOARCH == "amd64" {
|
||||
// Windows uses a different calling convention on amd64 from other
|
||||
// operating systems so we need separate assembly files.
|
||||
suffix = "_windows"
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||
asmGoarch := options.GOARCH
|
||||
if options.GOARCH == "mips" || options.GOARCH == "mipsle" {
|
||||
asmGoarch = "mipsx"
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
|
||||
// Configure the emulator.
|
||||
if options.GOARCH != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
spec.GDB = []string{"gdb-multiarch"}
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
spec.Emulator = []string{"qemu-arm"}
|
||||
}
|
||||
if goarch == "arm64" && goos == "linux" {
|
||||
spec.Emulator = []string{"qemu-aarch64"}
|
||||
if options.GOOS == "linux" {
|
||||
switch options.GOARCH {
|
||||
case "386":
|
||||
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
|
||||
if runtime.GOARCH != "amd64" {
|
||||
spec.Emulator = "qemu-i386 {}"
|
||||
}
|
||||
case "amd64":
|
||||
spec.Emulator = "qemu-x86_64 {}"
|
||||
case "arm":
|
||||
spec.Emulator = "qemu-arm {}"
|
||||
case "arm64":
|
||||
spec.Emulator = "qemu-aarch64 {}"
|
||||
case "mips":
|
||||
spec.Emulator = "qemu-mips {}"
|
||||
case "mipsle":
|
||||
spec.Emulator = "qemu-mipsel {}"
|
||||
}
|
||||
}
|
||||
}
|
||||
if goos != runtime.GOOS {
|
||||
if goos == "windows" {
|
||||
spec.Emulator = []string{"wine"}
|
||||
if options.GOOS != runtime.GOOS {
|
||||
if options.GOOS == "windows" {
|
||||
spec.Emulator = "wine {}"
|
||||
}
|
||||
}
|
||||
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package compileopts
|
||||
|
||||
import (
|
||||
"os"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -17,7 +18,7 @@ func TestLoadTarget(t *testing.T) {
|
||||
t.Error("LoadTarget should have failed with non existing target")
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
t.Error("LoadTarget failed for wrong reason:", err)
|
||||
}
|
||||
}
|
||||
@@ -29,7 +30,6 @@ func TestOverrideProperties(t *testing.T) {
|
||||
CPU: "baseCpu",
|
||||
CFlags: []string{"-base-foo", "-base-bar"},
|
||||
BuildTags: []string{"bt1", "bt2"},
|
||||
Emulator: []string{"be1", "be2"},
|
||||
DefaultStackSize: 42,
|
||||
AutoStackSize: &baseAutoStackSize,
|
||||
}
|
||||
@@ -38,7 +38,6 @@ func TestOverrideProperties(t *testing.T) {
|
||||
GOOS: "",
|
||||
CPU: "chlidCpu",
|
||||
CFlags: []string{"-child-foo", "-child-bar"},
|
||||
Emulator: []string{"ce1", "ce2"},
|
||||
AutoStackSize: &childAutoStackSize,
|
||||
DefaultStackSize: 64,
|
||||
}
|
||||
@@ -57,9 +56,6 @@ func TestOverrideProperties(t *testing.T) {
|
||||
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.BuildTags)
|
||||
}
|
||||
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.Emulator)
|
||||
}
|
||||
if *base.AutoStackSize != false {
|
||||
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
||||
}
|
||||
|
||||
+8
-45
@@ -16,60 +16,23 @@ import "tinygo.org/x/go-llvm"
|
||||
|
||||
var stdlibAliases = map[string]string{
|
||||
// crypto packages
|
||||
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
|
||||
"crypto/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",
|
||||
|
||||
// AES
|
||||
"crypto/aes.decryptBlockAsm": "crypto/aes.decryptBlock",
|
||||
"crypto/aes.encryptBlockAsm": "crypto/aes.encryptBlock",
|
||||
|
||||
// math package
|
||||
"math.Asin": "math.asin",
|
||||
"math.Asinh": "math.asinh",
|
||||
"math.Acos": "math.acos",
|
||||
"math.Acosh": "math.acosh",
|
||||
"math.Atan": "math.atan",
|
||||
"math.Atanh": "math.atanh",
|
||||
"math.Atan2": "math.atan2",
|
||||
"math.Cbrt": "math.cbrt",
|
||||
"math.Ceil": "math.ceil",
|
||||
"math.archCeil": "math.ceil",
|
||||
"math.Cos": "math.cos",
|
||||
"math.Cosh": "math.cosh",
|
||||
"math.Erf": "math.erf",
|
||||
"math.Erfc": "math.erfc",
|
||||
"math.Exp": "math.exp",
|
||||
"math.archExp": "math.exp",
|
||||
"math.Expm1": "math.expm1",
|
||||
"math.Exp2": "math.exp2",
|
||||
"math.archExp2": "math.exp2",
|
||||
"math.Floor": "math.floor",
|
||||
"math.archFloor": "math.floor",
|
||||
"math.Frexp": "math.frexp",
|
||||
"math.Hypot": "math.hypot",
|
||||
"math.archHypot": "math.hypot",
|
||||
"math.Ldexp": "math.ldexp",
|
||||
"math.Log": "math.log",
|
||||
"math.archLog": "math.log",
|
||||
"math.Log1p": "math.log1p",
|
||||
"math.Log10": "math.log10",
|
||||
"math.Log2": "math.log2",
|
||||
"math.Max": "math.max",
|
||||
"math.archMax": "math.max",
|
||||
"math.Min": "math.min",
|
||||
"math.archMin": "math.min",
|
||||
"math.Mod": "math.mod",
|
||||
"math.Modf": "math.modf",
|
||||
"math.archModf": "math.modf",
|
||||
"math.Pow": "math.pow",
|
||||
"math.Remainder": "math.remainder",
|
||||
"math.Sin": "math.sin",
|
||||
"math.Sinh": "math.sinh",
|
||||
"math.Sqrt": "math.sqrt",
|
||||
"math.archSqrt": "math.sqrt",
|
||||
"math.Tan": "math.tan",
|
||||
"math.Tanh": "math.tanh",
|
||||
"math.Trunc": "math.trunc",
|
||||
"math.archTrunc": "math.trunc",
|
||||
}
|
||||
|
||||
// createAlias implements the function (in the builder) as a call to the alias
|
||||
@@ -86,14 +49,14 @@ func (b *builder) createAlias(alias llvm.Value) {
|
||||
pos := b.program.Fset.Position(b.fn.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||
entryBlock := b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
if b.llvmFn.Type() != alias.Type() {
|
||||
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||
b.CreateUnreachable()
|
||||
return
|
||||
}
|
||||
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
||||
result := b.CreateCall(alias.GlobalValueType(), alias, b.llvmFn.Params(), "")
|
||||
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateRetVoid()
|
||||
} else {
|
||||
|
||||
+14
-11
@@ -89,23 +89,24 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
|
||||
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||
}
|
||||
|
||||
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
||||
// function must panic if the ptr/len parameters are invalid.
|
||||
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
||||
// From the documentation of unsafe.Slice:
|
||||
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice
|
||||
// and unsafe.String. This function must panic if the ptr/len parameters are
|
||||
// invalid.
|
||||
func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, elementType llvm.Type, lenType *types.Basic) {
|
||||
// From the documentation of unsafe.Slice and unsafe.String:
|
||||
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||
// > zero, a run-time panic occurs.
|
||||
// However, in practice, it is also necessary to check that the length is
|
||||
// not too big that a GEP wouldn't be possible without wrapping the pointer.
|
||||
// These two checks (non-negative and not too big) can be merged into one
|
||||
// using an unsiged greater than.
|
||||
// using an unsigned greater than.
|
||||
|
||||
// Make sure the len value is at least as big as a uintptr.
|
||||
len = b.extendInteger(len, lenType, b.uintptrType)
|
||||
|
||||
// Determine the maximum slice size, and therefore the maximum value of the
|
||||
// len parameter.
|
||||
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
||||
maxSize := b.maxSliceSize(elementType)
|
||||
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||
|
||||
// Do the check. By using unsigned greater than for the length check, signed
|
||||
@@ -117,7 +118,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
|
||||
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
||||
b.createRuntimeAssert(assert, name, "unsafeSlicePanic")
|
||||
}
|
||||
|
||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||
@@ -134,7 +135,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
||||
|
||||
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in an
|
||||
// uintptr if uintptrs were signed.
|
||||
maxBufSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
|
||||
maxBufSize := b.CreateLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||
if elementSize > maxBufSize.ZExtValue() {
|
||||
b.addError(pos, fmt.Sprintf("channel element type is too big (%v bytes)", elementSize))
|
||||
return
|
||||
@@ -145,11 +146,11 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
||||
}
|
||||
// Make the maxBufSize actually the maximum allowed value (in number of
|
||||
// elements in the channel buffer).
|
||||
maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false))
|
||||
maxBufSize = b.CreateUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false), "")
|
||||
|
||||
// Make sure maxBufSize has the same type as bufSize.
|
||||
if maxBufSize.Type() != bufSize.Type() {
|
||||
maxBufSize = llvm.ConstZExt(maxBufSize, bufSize.Type())
|
||||
maxBufSize = b.CreateZExt(maxBufSize, bufSize.Type(), "")
|
||||
}
|
||||
|
||||
// Do the check for a too large (or negative) buffer size.
|
||||
@@ -240,8 +241,10 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
||||
}
|
||||
}
|
||||
|
||||
// Put the fault block at the end of the function and the next block at the
|
||||
// current insert position.
|
||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
|
||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
|
||||
+32
-90
@@ -1,115 +1,57 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation and true if the call could
|
||||
// be lowered inline, and false otherwise.
|
||||
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
name := call.Value.(*ssa.Function).Name()
|
||||
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic
|
||||
// operation. It returns the result of the operation, or a zero llvm.Value if
|
||||
// the result is void.
|
||||
func (b *builder) createAtomicOp(name string) llvm.Value {
|
||||
switch name {
|
||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
// AtomicRMW does not work on AVR as intended:
|
||||
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
|
||||
// - The result is the new value instead of the old value
|
||||
vType := val.Type()
|
||||
name := fmt.Sprintf("__sync_fetch_and_add_%d", vType.IntTypeWidth()/8)
|
||||
fn := b.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false))
|
||||
}
|
||||
oldVal := b.createCall(fn, []llvm.Value{ptr, val}, "")
|
||||
// Return the new value, not the original value returned.
|
||||
return b.CreateAdd(oldVal, val, ""), true
|
||||
}
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
// Return the new value, not the original value returned by atomicrmw.
|
||||
return b.CreateAdd(oldVal, val, ""), true
|
||||
return b.CreateAdd(oldVal, val, "")
|
||||
case "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(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
|
||||
if isPointer {
|
||||
// atomicrmw only supports integers, so cast to an integer.
|
||||
val = b.CreatePtrToInt(val, b.uintptrType, "")
|
||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
|
||||
}
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
if isPointer {
|
||||
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
||||
}
|
||||
return oldVal, true
|
||||
return oldVal
|
||||
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
old := b.getValue(call.Args[1])
|
||||
newVal := b.getValue(call.Args[2])
|
||||
if strings.HasSuffix(name, "64") {
|
||||
if strings.HasPrefix(b.Triple, "thumb") {
|
||||
// Work around a bug in LLVM, at least LLVM 11:
|
||||
// https://reviews.llvm.org/D95891
|
||||
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
||||
if compareAndSwap.IsNil() {
|
||||
// Declare the function if it isn't already declared.
|
||||
i64Type := b.ctx.Int64Type()
|
||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
||||
}
|
||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
||||
// The __sync_val_compare_and_swap_8 function returns the old
|
||||
// value. However, we shouldn't return the old value, we should
|
||||
// return whether the compare/exchange was successful. This is
|
||||
// easily done by comparing the returned (actual) old value with
|
||||
// the expected old value passed to
|
||||
// __sync_val_compare_and_swap_8.
|
||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
||||
return swapped, true
|
||||
}
|
||||
}
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
old := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
newVal := b.getValue(b.fn.Params[2], getPos(b.fn))
|
||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||
return swapped, true
|
||||
return swapped
|
||||
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.CreateLoad(ptr, "")
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.CreateLoad(b.getLLVMType(b.fn.Signature.Results().At(0).Type()), ptr, "")
|
||||
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return val, true
|
||||
return val
|
||||
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
||||
ptr := b.getValue(call.Args[0])
|
||||
val := b.getValue(call.Args[1])
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
|
||||
vType := val.Type()
|
||||
isPointer := vType.TypeKind() == llvm.PointerTypeKind
|
||||
if isPointer {
|
||||
// libcalls only supports integers, so cast to an integer.
|
||||
vType = b.uintptrType
|
||||
val = b.CreatePtrToInt(val, vType, "")
|
||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(vType, 0), "")
|
||||
}
|
||||
name := fmt.Sprintf("__atomic_store_%d", vType.IntTypeWidth()/8)
|
||||
fn := b.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
||||
}
|
||||
return b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, ""), true
|
||||
}
|
||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||
store := b.CreateStore(val, ptr)
|
||||
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||
return store, true
|
||||
return llvm.Value{}
|
||||
default:
|
||||
return llvm.Value{}, false
|
||||
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name())
|
||||
return llvm.Value{}
|
||||
}
|
||||
}
|
||||
|
||||
+65
-43
@@ -20,7 +20,7 @@ const maxFieldsPerParam = 3
|
||||
type paramInfo struct {
|
||||
llvmType llvm.Type
|
||||
name string // name, possibly with suffixes for e.g. struct fields
|
||||
flags paramFlags
|
||||
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||
}
|
||||
|
||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||
@@ -33,26 +33,58 @@ const (
|
||||
paramIsDeferenceableOrNull = 1 << iota
|
||||
)
|
||||
|
||||
// createCall creates a new call to runtime.<fnName> with the given arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||
llvmFn := b.getFunction(fn)
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
// createRuntimeInvoke instead.
|
||||
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||
member := b.program.ImportedPackage("runtime").Members[fnName]
|
||||
if member == nil {
|
||||
panic("unknown runtime call: " + fnName)
|
||||
}
|
||||
fn := member.(*ssa.Function)
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||
}
|
||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||
return b.createCall(llvmFn, args, name)
|
||||
args = append(args, llvm.Undef(b.dataPtrType)) // unused context parameter
|
||||
if isInvoke {
|
||||
return b.createInvoke(fnType, llvmFn, args, name)
|
||||
}
|
||||
return b.createCall(fnType, llvmFn, args, name)
|
||||
}
|
||||
|
||||
// createRuntimeCall creates a new call to runtime.<fnName> with the given
|
||||
// arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
return b.createRuntimeCallCommon(fnName, args, name, false)
|
||||
}
|
||||
|
||||
// createRuntimeInvoke creates a new call to runtime.<fnName> with the given
|
||||
// arguments. If the runtime call panics, control flow is diverted to the
|
||||
// landing pad block.
|
||||
// Note that "invoke" here is meant in the LLVM sense (a call that can
|
||||
// panic/throw), not in the Go sense (an interface method call).
|
||||
func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
return b.createRuntimeCallCommon(fnName, args, name, true)
|
||||
}
|
||||
|
||||
// createCall creates a call to the given function with the arguments possibly
|
||||
// expanded.
|
||||
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
expanded := make([]llvm.Value, 0, len(args))
|
||||
for _, arg := range args {
|
||||
fragments := b.expandFormalParam(arg)
|
||||
expanded = append(expanded, fragments...)
|
||||
}
|
||||
return b.CreateCall(fn, expanded, name)
|
||||
return b.CreateCall(fnType, fn, expanded, name)
|
||||
}
|
||||
|
||||
// createInvoke is like createCall but continues execution at the landing pad if
|
||||
// the call resulted in a panic.
|
||||
func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||
if b.hasDeferFrame() {
|
||||
b.createInvokeCheckpoint()
|
||||
}
|
||||
return b.createCall(fnType, fn, args, name)
|
||||
}
|
||||
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
@@ -68,13 +100,7 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
|
||||
// failed to expand this parameter: too many fields
|
||||
}
|
||||
// TODO: split small arrays
|
||||
return []paramInfo{
|
||||
{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: getTypeFlags(goType),
|
||||
},
|
||||
}
|
||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||
}
|
||||
|
||||
// expandFormalParamOffsets returns a list of offsets from the start of an
|
||||
@@ -124,7 +150,6 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||
// with the passed in type if this is not possible.
|
||||
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||
typeFlags := getTypeFlags(goType)
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
var paramInfos []paramInfo
|
||||
@@ -155,40 +180,37 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
}
|
||||
}
|
||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||
for i := range subInfos {
|
||||
subInfos[i].flags |= typeFlags
|
||||
}
|
||||
paramInfos = append(paramInfos, subInfos...)
|
||||
}
|
||||
return paramInfos
|
||||
default:
|
||||
return []paramInfo{
|
||||
{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
flags: typeFlags,
|
||||
},
|
||||
}
|
||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||
}
|
||||
}
|
||||
|
||||
// getTypeFlags returns the type flags for a given type. It will not recurse
|
||||
// into sub-types (such as in structs).
|
||||
func getTypeFlags(t types.Type) paramFlags {
|
||||
if t == nil {
|
||||
return 0
|
||||
// getParamInfo collects information about a parameter. For example, if this
|
||||
// parameter is pointer-like, it will also store the element type for the
|
||||
// dereferenceable_or_null attribute.
|
||||
func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Type) paramInfo {
|
||||
info := paramInfo{
|
||||
llvmType: t,
|
||||
name: name,
|
||||
}
|
||||
switch t.Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
// Pointers in Go must either point to an object or be nil.
|
||||
return paramIsDeferenceableOrNull
|
||||
case *types.Chan, *types.Map:
|
||||
// Channels and maps are implemented as pointers pointing to some
|
||||
// object, and follow the same rules as *types.Pointer.
|
||||
return paramIsDeferenceableOrNull
|
||||
default:
|
||||
return 0
|
||||
if goType != nil {
|
||||
switch underlying := goType.Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
// Pointers in Go must either point to an object or be nil.
|
||||
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMType(underlying.Elem()))
|
||||
case *types.Chan:
|
||||
// Channels are implemented simply as a *runtime.channel.
|
||||
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("channel"))
|
||||
case *types.Map:
|
||||
// Maps are similar to channels: they are implemented as a
|
||||
// *runtime.hashmap.
|
||||
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("hashmap"))
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// extractSubfield extracts a field from a struct, or returns null if this is
|
||||
@@ -211,7 +233,7 @@ func extractSubfield(t types.Type, field int) types.Type {
|
||||
}
|
||||
}
|
||||
|
||||
// flattenAggregateTypeOffset returns the offsets from the start of an object of
|
||||
// flattenAggregateTypeOffsets returns the offsets from the start of an object of
|
||||
// type t if this object were flattened like in flattenAggregate. Used together
|
||||
// with flattenAggregate to know the start indices of each value in the
|
||||
// non-flattened object.
|
||||
|
||||
+38
-42
@@ -14,7 +14,7 @@ import (
|
||||
func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
|
||||
elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().Underlying().(*types.Chan).Elem()))
|
||||
elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false)
|
||||
bufSize := b.getValue(expr.Size)
|
||||
bufSize := b.getValue(expr.Size, getPos(expr))
|
||||
b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos())
|
||||
if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||
bufSize = b.CreateZExt(bufSize, b.uintptrType, "")
|
||||
@@ -27,34 +27,33 @@ func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
|
||||
// createChanSend emits a pseudo chan send operation. It is lowered to the
|
||||
// actual channel send operation during goroutine lowering.
|
||||
func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
ch := b.getValue(instr.Chan)
|
||||
chanValue := b.getValue(instr.X)
|
||||
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, valueAllocaCast, valueAllocaSize llvm.Value
|
||||
var valueAlloca, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
b.CreateStore(chanValue, valueAlloca)
|
||||
}
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
|
||||
// Do the send.
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
||||
|
||||
// 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(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
||||
if !isZeroSize {
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,32 +61,31 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||
// actual channel receive operation during goroutine lowering.
|
||||
func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Chan).Elem())
|
||||
ch := b.getValue(unop.X)
|
||||
ch := b.getValue(unop.X, getPos(unop))
|
||||
|
||||
// Allocate memory to receive into.
|
||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||
var valueAlloca, valueAllocaSize llvm.Value
|
||||
if isZeroSize {
|
||||
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||
} else {
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||
}
|
||||
|
||||
// Allocate blockedlist buffer.
|
||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||
|
||||
// Do the receive.
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
||||
var received llvm.Value
|
||||
if isZeroSize {
|
||||
received = llvm.ConstNull(valueType)
|
||||
} else {
|
||||
received = b.CreateLoad(valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||
}
|
||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
||||
|
||||
if unop.CommaOk {
|
||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||
@@ -140,7 +138,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
var selectStates []llvm.Value
|
||||
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
|
||||
for _, state := range expr.States {
|
||||
ch := b.getValue(state.Chan)
|
||||
ch := b.getValue(state.Chan, state.Pos)
|
||||
selectState := llvm.ConstNull(chanSelectStateType)
|
||||
selectState = b.CreateInsertValue(selectState, ch, 0, "")
|
||||
switch state.Dir {
|
||||
@@ -156,11 +154,10 @@ 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)
|
||||
sendValue := b.getValue(state.Send, state.Pos)
|
||||
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
|
||||
b.CreateStore(sendValue, alloca)
|
||||
ptr := b.CreateBitCast(alloca, b.i8ptrType, "")
|
||||
selectState = b.CreateInsertValue(selectState, ptr, 1, "")
|
||||
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
@@ -168,12 +165,12 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
}
|
||||
|
||||
// Create a receive buffer, where the received value will be stored.
|
||||
recvbuf := llvm.Undef(b.i8ptrType)
|
||||
recvbuf := llvm.Undef(b.dataPtrType)
|
||||
if recvbufSize != 0 {
|
||||
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
||||
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||
recvbufAlloca, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||
recvbuf = b.CreateGEP(recvbufAlloca, []llvm.Value{
|
||||
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.recvbuf")
|
||||
@@ -181,16 +178,16 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
|
||||
// Create the states slice (allocated on the stack).
|
||||
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
|
||||
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
||||
statesAlloca, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
||||
for i, state := range selectStates {
|
||||
// Set each slice element to the appropriate channel.
|
||||
gep := b.CreateGEP(statesAlloca, []llvm.Value{
|
||||
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}, "")
|
||||
b.CreateStore(state, gep)
|
||||
}
|
||||
statesPtr := b.CreateGEP(statesAlloca, []llvm.Value{
|
||||
statesPtr := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.states")
|
||||
@@ -202,9 +199,9 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
// 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, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
||||
chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
||||
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||
chBlockPtr := b.CreateGEP(chBlockAlloca, []llvm.Value{
|
||||
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
}, "select.block")
|
||||
@@ -216,7 +213,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
}, "select.result")
|
||||
|
||||
// Terminate the lifetime of the operation structures.
|
||||
b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize)
|
||||
b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
|
||||
} else {
|
||||
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
|
||||
recvbuf,
|
||||
@@ -225,7 +222,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
}
|
||||
|
||||
// Terminate the lifetime of the states alloca.
|
||||
b.emitLifetimeEnd(statesI8, statesSize)
|
||||
b.emitLifetimeEnd(statesAlloca, statesSize)
|
||||
|
||||
// The result value does not include all the possible received values,
|
||||
// because we can't load them in advance. Instead, the *ssa.Extract
|
||||
@@ -247,7 +244,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
||||
func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
||||
if expr.Index == 0 {
|
||||
// index
|
||||
value := b.getValue(expr.Tuple)
|
||||
value := b.getValue(expr.Tuple, getPos(expr))
|
||||
index := b.CreateExtractValue(value, expr.Index, "")
|
||||
if index.Type().IntTypeWidth() < b.intType.IntTypeWidth() {
|
||||
index = b.CreateSExt(index, b.intType, "")
|
||||
@@ -255,7 +252,7 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
||||
return index
|
||||
} else if expr.Index == 1 {
|
||||
// comma-ok
|
||||
value := b.getValue(expr.Tuple)
|
||||
value := b.getValue(expr.Tuple, getPos(expr))
|
||||
return b.CreateExtractValue(value, expr.Index, "")
|
||||
} else {
|
||||
// Select statements are (index, ok, ...) where ... is a number of
|
||||
@@ -264,8 +261,7 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
||||
// receive can proceed at a time) so we'll get that alloca, bitcast
|
||||
// it to the correct type, and dereference it.
|
||||
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
||||
typ := llvm.PointerType(b.getLLVMType(expr.Type()), 0)
|
||||
ptr := b.CreateBitCast(recvbuf, typ, "")
|
||||
return b.CreateLoad(ptr, "")
|
||||
typ := b.getLLVMType(expr.Type())
|
||||
return b.CreateLoad(typ, recvbuf, "")
|
||||
}
|
||||
}
|
||||
|
||||
+785
-329
File diff suppressed because it is too large
Load Diff
+102
-78
@@ -3,7 +3,7 @@ package compiler
|
||||
import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -28,21 +28,8 @@ type testCase struct {
|
||||
func TestCompiler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Check LLVM version.
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if err != nil {
|
||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||
}
|
||||
if llvmMajor < 11 {
|
||||
// It is likely this version needs to be bumped in the future.
|
||||
// The goal is to at least test the LLVM version that's used by default
|
||||
// in TinyGo and (if possible without too many workarounds) also some
|
||||
// previous versions.
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
_, goMinor, err := goenv.GetGorootVersion()
|
||||
if err != nil {
|
||||
t.Fatal("could not read Go version:", err)
|
||||
}
|
||||
@@ -56,20 +43,19 @@ func TestCompiler(t *testing.T) {
|
||||
{"float.go", "", ""},
|
||||
{"interface.go", "", ""},
|
||||
{"func.go", "", ""},
|
||||
{"defer.go", "cortex-m-qemu", ""},
|
||||
{"pragma.go", "", ""},
|
||||
{"goroutine.go", "wasm", "asyncify"},
|
||||
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||
{"channel.go", "", ""},
|
||||
{"intrinsics.go", "cortex-m-qemu", ""},
|
||||
{"intrinsics.go", "wasm", ""},
|
||||
{"gc.go", "", ""},
|
||||
{"zeromap.go", "", ""},
|
||||
}
|
||||
if llvmMajor >= 12 {
|
||||
tests = append(tests, testCase{"intrinsics.go", "cortex-m-qemu", ""})
|
||||
tests = append(tests, testCase{"intrinsics.go", "wasm", ""})
|
||||
if goMinor >= 20 {
|
||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||
}
|
||||
if goMinor >= 17 {
|
||||
tests = append(tests, testCase{"go1.17.go", "", ""})
|
||||
if goMinor >= 21 {
|
||||
tests = append(tests, testCase{"go1.21.go", "", ""})
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -87,49 +73,11 @@ func TestCompiler(t *testing.T) {
|
||||
options := &compileopts.Options{
|
||||
Target: targetString,
|
||||
}
|
||||
target, err := compileopts.LoadTarget(options)
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
if tc.scheduler != "" {
|
||||
options.Scheduler = tc.scheduler
|
||||
}
|
||||
config := &compileopts.Config{
|
||||
Options: options,
|
||||
Target: target,
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
Scheduler: config.Scheduler(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
}
|
||||
machine, err := NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse test case %s: %s", tc.file, err)
|
||||
}
|
||||
|
||||
// Compile AST to IR.
|
||||
program := lprogram.LoadSSA()
|
||||
pkg := lprogram.MainPkg()
|
||||
mod, errs := CompilePackage(tc.file, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
mod, errs := testCompilePackage(t, options, tc.file)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
@@ -137,20 +85,18 @@ func TestCompiler(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
err = llvm.VerifyModule(mod, llvm.PrintMessageAction)
|
||||
err := llvm.VerifyModule(mod, llvm.PrintMessageAction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Optimize IR a little.
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
funcPasses.AddInstructionCombiningPass()
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
funcPasses.RunFunc(fn)
|
||||
passOptions := llvm.NewPassBuilderOptions()
|
||||
defer passOptions.Dispose()
|
||||
err = mod.RunPasses("instcombine", llvm.TargetMachine{}, passOptions)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
|
||||
outFilePrefix := tc.file[:len(tc.file)-3]
|
||||
if tc.target != "" {
|
||||
@@ -163,14 +109,14 @@ func TestCompiler(t *testing.T) {
|
||||
|
||||
// Update test if needed. Do not check the result.
|
||||
if *flagUpdate {
|
||||
err := ioutil.WriteFile(outPath, []byte(mod.String()), 0666)
|
||||
err := os.WriteFile(outPath, []byte(mod.String()), 0666)
|
||||
if err != nil {
|
||||
t.Error("failed to write updated output file:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(outPath)
|
||||
expected, err := os.ReadFile(outPath)
|
||||
if err != nil {
|
||||
t.Fatal("failed to read golden file:", err)
|
||||
}
|
||||
@@ -221,17 +167,95 @@ func filterIrrelevantIRLines(lines []string) []string {
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
|
||||
// Ignore attribute groups. These may change between LLVM versions.
|
||||
// Right now test outputs are for LLVM 12 and higher.
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 13 && strings.HasPrefix(line, "target datalayout = ") {
|
||||
if llvmVersion < 15 && strings.HasPrefix(line, "target datalayout = ") {
|
||||
// The datalayout string may vary betewen LLVM versions.
|
||||
// Right now test outputs are for LLVM 13 and higher.
|
||||
// Right now test outputs are for LLVM 15 and higher.
|
||||
continue
|
||||
}
|
||||
out = append(out, line)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestCompilerErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Read expected errors from the test file.
|
||||
var expectedErrors []string
|
||||
errorsFile, err := os.ReadFile("testdata/errors.go")
|
||||
if err != nil {
|
||||
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: "))
|
||||
}
|
||||
}
|
||||
|
||||
// Compile the Go file with errors.
|
||||
options := &compileopts.Options{
|
||||
Target: "wasm",
|
||||
}
|
||||
_, errs := testCompilePackage(t, options, "errors.go")
|
||||
|
||||
// Check whether the actual errors match the expected errors.
|
||||
expectedErrorsIdx := 0
|
||||
for _, err := range errs {
|
||||
err := err.(types.Error)
|
||||
position := err.Fset.Position(err.Pos)
|
||||
position.Filename = "errors.go" // don't use a full path
|
||||
if expectedErrorsIdx >= len(expectedErrors) || expectedErrors[expectedErrorsIdx] != err.Msg {
|
||||
t.Errorf("unexpected compiler error: %s: %s", position.String(), err.Msg)
|
||||
continue
|
||||
}
|
||||
expectedErrorsIdx++
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
config := &compileopts.Config{
|
||||
Options: options,
|
||||
Target: target,
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
Features: config.Features(),
|
||||
ABI: config.ABI(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
Scheduler: config.Scheduler(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.StackSize(),
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
}
|
||||
machine, err := NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, "./testdata/"+file, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse test case %s: %s", file, err)
|
||||
}
|
||||
|
||||
// Compile AST to IR.
|
||||
program := lprogram.LoadSSA()
|
||||
pkg := lprogram.MainPkg()
|
||||
return CompilePackage(file, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
}
|
||||
|
||||
+259
-68
@@ -15,12 +15,40 @@ package compiler
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// supportsRecover returns whether the compiler supports the recover() builtin
|
||||
// for the current architecture.
|
||||
func (b *builder) supportsRecover() bool {
|
||||
switch b.archFamily() {
|
||||
case "wasm32":
|
||||
// Probably needs to be implemented using the exception handling
|
||||
// proposal of WebAssembly:
|
||||
// https://github.com/WebAssembly/exception-handling
|
||||
return false
|
||||
case "riscv64", "xtensa":
|
||||
// TODO: add support for these architectures
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// hasDeferFrame returns whether the current function needs to catch panics and
|
||||
// run defers.
|
||||
func (b *builder) hasDeferFrame() bool {
|
||||
if b.fn.Recover == nil {
|
||||
return false
|
||||
}
|
||||
return b.supportsRecover()
|
||||
}
|
||||
|
||||
// deferInitFunc sets up this function for future deferred calls. It must be
|
||||
// called from within the entry block when this function contains deferred
|
||||
// calls.
|
||||
@@ -33,9 +61,171 @@ func (b *builder) deferInitFunc() {
|
||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||
|
||||
// Create defer list pointer.
|
||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
|
||||
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
|
||||
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
|
||||
// function prologue/epilogue (an invariant maintained by TinyGo but
|
||||
// possibly broken by the C alloca function).
|
||||
// The frame pointer is _not_ saved, because it is marked as clobbered
|
||||
// in the setjmp-like inline assembly.
|
||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||
stackPointer := b.readStackPointer()
|
||||
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
||||
|
||||
// Create the landing pad block, which is where control transfers after
|
||||
// a panic.
|
||||
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
||||
}
|
||||
}
|
||||
|
||||
// createLandingPad fills in the landing pad block. This block runs the deferred
|
||||
// functions and returns (by jumping to the recover block). If the function is
|
||||
// still panicking after the defers are run, the panic will be re-raised in
|
||||
// destroyDeferFrame.
|
||||
func (b *builder) createLandingPad() {
|
||||
b.SetInsertPointAtEnd(b.landingpad)
|
||||
|
||||
// Add debug info, if needed.
|
||||
// The location used is the closing bracket of the function.
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(b.fn.Syntax().End())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
b.createRunDefers()
|
||||
|
||||
// Continue at the 'recover' block, which returns to the parent in an
|
||||
// appropriate way.
|
||||
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||
}
|
||||
|
||||
// createInvokeCheckpoint saves the function state at the given point, to
|
||||
// continue at the landing pad if a panic happened. This is implemented using a
|
||||
// setjmp-like construct.
|
||||
func (b *builder) createInvokeCheckpoint() {
|
||||
// Construct inline assembly equivalents of setjmp.
|
||||
// The assembly works as follows:
|
||||
// * All registers (both callee-saved and caller saved) are clobbered
|
||||
// after the inline assembly returns.
|
||||
// * The assembly stores the address just past the end of the assembly
|
||||
// into the jump buffer.
|
||||
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||
// assembly but set to an unspecified non-zero value when jumping using
|
||||
// a longjmp.
|
||||
var asmString, constraints string
|
||||
resultType := b.uintptrType
|
||||
switch b.archFamily() {
|
||||
case "i386":
|
||||
asmString = `
|
||||
xorl %eax, %eax
|
||||
movl $$1f, 4(%ebx)
|
||||
1:`
|
||||
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
// This doesn't include the floating point stack because TinyGo uses
|
||||
// newer floating point instructions.
|
||||
case "x86_64":
|
||||
asmString = `
|
||||
leaq 1f(%rip), %rax
|
||||
movq %rax, 8(%rbx)
|
||||
xorq %rax, %rax
|
||||
1:`
|
||||
constraints = "={rax},{rbx},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||
// This list doesn't include AVX/AVX512 registers because TinyGo
|
||||
// doesn't currently enable support for AVX instructions.
|
||||
case "arm":
|
||||
// Note: the following assembly takes into account that the PC is
|
||||
// always 4 bytes ahead on ARM. The PC that is stored always points
|
||||
// to the instruction just after the assembly fragment so that
|
||||
// tinygo_longjmp lands at the correct instruction.
|
||||
if b.isThumb() {
|
||||
// Instructions are 2 bytes in size.
|
||||
asmString = `
|
||||
movs r0, #0
|
||||
mov r2, pc
|
||||
str r2, [r1, #4]`
|
||||
} else {
|
||||
// Instructions are 4 bytes in size.
|
||||
asmString = `
|
||||
str pc, [r1, #4]
|
||||
movs r0, #0`
|
||||
}
|
||||
constraints = "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"
|
||||
case "aarch64":
|
||||
asmString = `
|
||||
adr x2, 1f
|
||||
str x2, [x1, #8]
|
||||
mov x0, #0
|
||||
1:
|
||||
`
|
||||
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
||||
if b.GOOS != "darwin" && b.GOOS != "windows" {
|
||||
// These registers cause the following warning when compiling for
|
||||
// MacOS and Windows:
|
||||
// warning: inline asm clobber list contains reserved registers:
|
||||
// X18, FP
|
||||
// Reserved registers on the clobber list may not be preserved
|
||||
// across the asm statement, and clobbering them may lead to
|
||||
// undefined behaviour.
|
||||
constraints += ",~{x18},~{fp}"
|
||||
}
|
||||
// TODO: SVE registers, which we don't use in TinyGo at the moment.
|
||||
case "avr":
|
||||
// Note: the Y register (R28:R29) is a fixed register and therefore
|
||||
// needs to be saved manually. TODO: do this only once per function with
|
||||
// a defer frame, not for every call.
|
||||
resultType = b.ctx.Int8Type()
|
||||
asmString = `
|
||||
ldi r24, pm_lo8(1f)
|
||||
ldi r25, pm_hi8(1f)
|
||||
std z+2, r24
|
||||
std z+3, r25
|
||||
std z+4, r28
|
||||
std z+5, r29
|
||||
ldi r24, 0
|
||||
1:`
|
||||
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
||||
case "mips":
|
||||
// $4 flag (zero or non-zero)
|
||||
// $5 defer frame
|
||||
asmString = `
|
||||
.set noat
|
||||
move $$4, $$zero
|
||||
jal 1f
|
||||
1:
|
||||
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},~{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":
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
sw a2, 4(a1)
|
||||
li a0, 0
|
||||
1:`
|
||||
constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
|
||||
default:
|
||||
// This case should have been handled by b.supportsRecover().
|
||||
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
||||
}
|
||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
b.blockExits[b.currentBlock] = continueBB
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
@@ -77,7 +267,7 @@ func isInLoop(start *ssa.BasicBlock) bool {
|
||||
func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// The pointer to the previous defer struct, which we will replace to
|
||||
// make a linked list.
|
||||
next := b.CreateLoad(b.deferPtr, "defer.next")
|
||||
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
|
||||
|
||||
var values []llvm.Value
|
||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
||||
@@ -94,13 +284,13 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
// 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) // interface
|
||||
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.uintptrType, b.i8ptrType)
|
||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||
for _, arg := range instr.Call.Args {
|
||||
val := b.getValue(arg)
|
||||
val := b.getValue(arg, getPos(instr))
|
||||
values = append(values, val)
|
||||
valueTypes = append(valueTypes, val.Type())
|
||||
}
|
||||
@@ -117,7 +307,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// runtime._defer fields).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param)
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
@@ -129,7 +319,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// pointer.
|
||||
// TODO: ignore this closure entirely and put pointers to the free
|
||||
// variables directly in the defer struct, avoiding a memory allocation.
|
||||
closure := b.getValue(instr.Call.Value)
|
||||
closure := b.getValue(instr.Call.Value, getPos(instr))
|
||||
context := b.CreateExtractValue(closure, 0, "")
|
||||
|
||||
// Get the callback number.
|
||||
@@ -145,7 +335,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// context pointer).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param)
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
@@ -157,7 +347,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
var argValues []llvm.Value
|
||||
for _, arg := range instr.Call.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
argValues = append(argValues, b.getValue(arg))
|
||||
argValues = append(argValues, b.getValue(arg, getPos(instr)))
|
||||
}
|
||||
|
||||
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
|
||||
@@ -180,7 +370,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
}
|
||||
|
||||
} else {
|
||||
funcValue := b.getValue(instr.Call.Value)
|
||||
funcValue := b.getValue(instr.Call.Value, getPos(instr))
|
||||
|
||||
if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok {
|
||||
b.deferExprFuncs[instr.Call.Value] = len(b.allDeferFuncs)
|
||||
@@ -195,44 +385,45 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
values = []llvm.Value{callback, next, funcValue}
|
||||
valueTypes = append(valueTypes, funcValue.Type())
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param)
|
||||
llvmParam := b.getValue(param, getPos(instr))
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// Make a struct out of the collected values to put in the defer frame.
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFrame := llvm.ConstNull(deferFrameType)
|
||||
// Make a struct out of the collected values to put in the deferred call
|
||||
// struct.
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
deferredCall := llvm.ConstNull(deferredCallType)
|
||||
for i, value := range values {
|
||||
deferFrame = b.CreateInsertValue(deferFrame, value, i, "")
|
||||
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca")
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||
} else {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := b.targetData.TypeAllocSize(deferFrameType)
|
||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
nilPtr := llvm.ConstNull(b.i8ptrType)
|
||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
nilPtr := llvm.ConstNull(b.dataPtrType)
|
||||
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||
}
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
}
|
||||
b.CreateStore(deferFrame, alloca)
|
||||
b.CreateStore(deferredCall, alloca)
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
b.CreateStore(allocaCast, b.deferPtr)
|
||||
b.CreateStore(alloca, b.deferPtr)
|
||||
}
|
||||
|
||||
// createRunDefers emits code to run all deferred functions.
|
||||
func (b *builder) createRunDefers() {
|
||||
deferType := b.getLLVMRuntimeType("_defer")
|
||||
|
||||
// Add a loop like the following:
|
||||
// for stack != nil {
|
||||
// _stack := stack
|
||||
@@ -248,17 +439,17 @@ func (b *builder) createRunDefers() {
|
||||
// }
|
||||
// }
|
||||
|
||||
// Create loop.
|
||||
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
|
||||
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
|
||||
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
|
||||
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
|
||||
// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end.
|
||||
end := b.insertBasicBlock("rundefers.end")
|
||||
unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default")
|
||||
loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop")
|
||||
loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead")
|
||||
b.CreateBr(loophead)
|
||||
|
||||
// Create loop head:
|
||||
// for stack != nil {
|
||||
b.SetInsertPointAtEnd(loophead)
|
||||
deferData := b.CreateLoad(b.deferPtr, "")
|
||||
deferData := b.CreateLoad(b.dataPtrType, b.deferPtr, "")
|
||||
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
|
||||
b.CreateCondBr(stackIsNil, end, loop)
|
||||
|
||||
@@ -267,24 +458,24 @@ func (b *builder) createRunDefers() {
|
||||
// stack = stack.next
|
||||
// switch stack.callback {
|
||||
b.SetInsertPointAtEnd(loop)
|
||||
nextStackGEP := b.CreateInBoundsGEP(deferData, []llvm.Value{
|
||||
nextStackGEP := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
|
||||
}, "stack.next.gep")
|
||||
nextStack := b.CreateLoad(nextStackGEP, "stack.next")
|
||||
nextStack := b.CreateLoad(b.dataPtrType, nextStackGEP, "stack.next")
|
||||
b.CreateStore(nextStack, b.deferPtr)
|
||||
gep := b.CreateInBoundsGEP(deferData, []llvm.Value{
|
||||
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field
|
||||
}, "callback.gep")
|
||||
callback := b.CreateLoad(gep, "callback")
|
||||
callback := b.CreateLoad(b.uintptrType, gep, "callback")
|
||||
sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
|
||||
|
||||
for i, callback := range b.allDeferFuncs {
|
||||
// Create switch case, for example:
|
||||
// case 0:
|
||||
// // run first deferred call
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
|
||||
block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i))
|
||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
|
||||
b.SetInsertPointAtEnd(block)
|
||||
switch callback := callback.(type) {
|
||||
@@ -292,33 +483,32 @@ func (b *builder) createRunDefers() {
|
||||
// Call on an value or interface value.
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
|
||||
if !callback.IsInvoke() {
|
||||
//Expect funcValue to be passed through the defer frame.
|
||||
//Expect funcValue to be passed through the deferred call.
|
||||
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||
} else {
|
||||
//Expect typecode
|
||||
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||
}
|
||||
|
||||
for _, arg := range callback.Args {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||
}
|
||||
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
|
||||
// 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(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
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)
|
||||
}
|
||||
|
||||
var fnPtr llvm.Value
|
||||
var fnType llvm.Type
|
||||
|
||||
if !callback.IsInvoke() {
|
||||
// Isolate the func value.
|
||||
@@ -326,8 +516,9 @@ func (b *builder) createRunDefers() {
|
||||
forwardParams = forwardParams[1:]
|
||||
|
||||
//Get function pointer and context
|
||||
fp, context := b.decodeFuncValue(funcValue, callback.Signature())
|
||||
fnPtr = fp
|
||||
var context llvm.Value
|
||||
fnPtr, context = b.decodeFuncValue(funcValue)
|
||||
fnType = b.getLLVMFunctionType(callback.Signature())
|
||||
|
||||
//Pass context
|
||||
forwardParams = append(forwardParams, context)
|
||||
@@ -336,74 +527,75 @@ func (b *builder) createRunDefers() {
|
||||
// parameters.
|
||||
forwardParams = append(forwardParams[1:], forwardParams[0])
|
||||
fnPtr = b.getInvokeFunction(callback)
|
||||
fnType = fnPtr.GlobalValueType()
|
||||
|
||||
// Add the context parameter. An interface call cannot also be a
|
||||
// closure but we have to supply the parameter anyway for platforms
|
||||
// with a strict calling convention.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||
}
|
||||
|
||||
b.createCall(fnPtr, forwardParams, "")
|
||||
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||
|
||||
case *ssa.Function:
|
||||
// Direct call.
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
for _, param := range getParams(callback.Signature) {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||
}
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := range getParams(callback.Signature) {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, 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)
|
||||
}
|
||||
|
||||
// Plain TinyGo functions add some extra parameters to implement async functionality and function recievers.
|
||||
// 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 {
|
||||
// Add the context parameter. We know it is ignored by the receiving
|
||||
// function, but we have to pass one anyway.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||
}
|
||||
|
||||
// Call real function.
|
||||
b.createCall(b.getFunction(callback), forwardParams, "")
|
||||
fnType, fn := b.getFunction(callback)
|
||||
b.createInvoke(fnType, fn, forwardParams, "")
|
||||
|
||||
case *ssa.MakeClosure:
|
||||
// Get the real defer struct type and cast to it.
|
||||
fn := callback.Fn.(*ssa.Function)
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
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 = append(valueTypes, b.i8ptrType) // closure
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
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(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
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)
|
||||
}
|
||||
|
||||
// Call deferred function.
|
||||
b.createCall(b.getFunction(fn), forwardParams, "")
|
||||
fnType, llvmFn := b.getFunction(fn)
|
||||
b.createCall(fnType, llvmFn, forwardParams, "")
|
||||
case *ssa.Builtin:
|
||||
db := b.deferBuiltinFuncs[callback]
|
||||
|
||||
//Get parameter types
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||
|
||||
//Get signature from call results
|
||||
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||
@@ -411,15 +603,14 @@ func (b *builder) createRunDefers() {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||
}
|
||||
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Extract the params from the struct.
|
||||
var argValues []llvm.Value
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
gep := b.CreateInBoundsGEP(deferredCallType, 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)
|
||||
}
|
||||
|
||||
|
||||
+15
-40
@@ -13,34 +13,14 @@ import (
|
||||
// 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 {
|
||||
return b.compilerContext.createFuncValue(b.Builder, funcPtr, context, sig)
|
||||
}
|
||||
|
||||
// createFuncValue creates a function value from a raw function pointer with no
|
||||
// context.
|
||||
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||
// Closure is: {context, function pointer}
|
||||
funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
|
||||
funcValueType := c.getFuncType(sig)
|
||||
funcValueType := b.getFuncType(sig)
|
||||
funcValue := llvm.Undef(funcValueType)
|
||||
funcValue = builder.CreateInsertValue(funcValue, context, 0, "")
|
||||
funcValue = builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
|
||||
funcValue = b.CreateInsertValue(funcValue, context, 0, "")
|
||||
funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "")
|
||||
return funcValue
|
||||
}
|
||||
|
||||
// getFuncSignatureID returns a new external global for a given signature. This
|
||||
// global reference is not real, it is only used during func lowering to assign
|
||||
// signature types to functions and will then be removed.
|
||||
func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value {
|
||||
sigGlobalName := "reflect/types.funcid:" + getTypeCodeName(sig)
|
||||
sigGlobal := c.mod.NamedGlobal(sigGlobalName)
|
||||
if sigGlobal.IsNil() {
|
||||
sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName)
|
||||
sigGlobal.SetGlobalConstant(true)
|
||||
}
|
||||
return sigGlobal
|
||||
}
|
||||
|
||||
// extractFuncScalar returns some scalar that can be used in comparisons. It is
|
||||
// a cheap operation.
|
||||
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
||||
@@ -54,26 +34,20 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||
}
|
||||
|
||||
// decodeFuncValue extracts the context and the function pointer from this func
|
||||
// value. This may be an expensive operation.
|
||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||
// value.
|
||||
func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) {
|
||||
context = b.CreateExtractValue(funcValue, 0, "")
|
||||
bitcast := b.CreateExtractValue(funcValue, 1, "")
|
||||
if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
|
||||
funcPtr = bitcast.Operand(0)
|
||||
return
|
||||
}
|
||||
llvmSig := b.getRawFuncType(sig)
|
||||
funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
|
||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||
return
|
||||
}
|
||||
|
||||
// getFuncType returns the type of a func value given a signature.
|
||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
||||
return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false)
|
||||
}
|
||||
|
||||
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
||||
func (c *compilerContext) getRawFuncType(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() {
|
||||
@@ -101,7 +75,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
if recv.StructName() == "runtime._interface" {
|
||||
// This is a call on an interface, not a concrete type.
|
||||
// The receiver is not an interface, but a i8* type.
|
||||
recv = c.i8ptrType
|
||||
recv = c.dataPtrType
|
||||
}
|
||||
for _, info := range c.expandFormalParamType(recv, "", nil) {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
@@ -114,10 +88,10 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||
}
|
||||
}
|
||||
// All functions take these parameters at the end.
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||
paramTypes = append(paramTypes, c.dataPtrType) // context
|
||||
|
||||
// Make a func type out of the signature.
|
||||
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
|
||||
return llvm.FunctionType(returnType, paramTypes, false)
|
||||
}
|
||||
|
||||
// parseMakeClosure makes a function value (with context) from the given
|
||||
@@ -132,7 +106,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
boundVars := make([]llvm.Value, len(expr.Bindings))
|
||||
for i, binding := range expr.Bindings {
|
||||
// The context stores the bound variables.
|
||||
llvmBoundVar := b.getValue(binding)
|
||||
llvmBoundVar := b.getValue(binding, getPos(expr))
|
||||
boundVars[i] = llvmBoundVar
|
||||
}
|
||||
|
||||
@@ -141,5 +115,6 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
context := b.emitPointerPack(boundVars)
|
||||
|
||||
// Create the closure.
|
||||
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
|
||||
_, fn := b.getFunction(f)
|
||||
return b.createFuncValue(fn, context, f.Signature), nil
|
||||
}
|
||||
|
||||
+2
-5
@@ -78,13 +78,10 @@ func (b *builder) trackValue(value llvm.Value) {
|
||||
}
|
||||
}
|
||||
|
||||
// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
|
||||
// trackPointer creates a call to runtime.trackPointer, bitcasting the pointer
|
||||
// first if needed. The input value must be of LLVM pointer type.
|
||||
func (b *builder) trackPointer(value llvm.Value) {
|
||||
if value.Type() != b.i8ptrType {
|
||||
value = b.CreateBitCast(value, b.i8ptrType, "")
|
||||
}
|
||||
b.createRuntimeCall("trackPointer", []llvm.Value{value}, "")
|
||||
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
|
||||
}
|
||||
|
||||
// typeHasPointers returns whether this type is a pointer or contains pointers.
|
||||
|
||||
+321
-80
@@ -14,36 +14,7 @@ import (
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
var prefix string
|
||||
var funcPtr llvm.Value
|
||||
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)
|
||||
context = b.extractFuncContext(funcValue)
|
||||
default:
|
||||
panic("StaticCallee returned an unexpected value")
|
||||
}
|
||||
if !context.IsNil() {
|
||||
params = append(params, context) // context parameter
|
||||
hasContext = true
|
||||
}
|
||||
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.
|
||||
@@ -70,16 +41,49 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
var argValues []llvm.Value
|
||||
for _, arg := range instr.Call.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
argValues = append(argValues, b.getValue(arg))
|
||||
argValues = append(argValues, b.getValue(arg, getPos(instr)))
|
||||
}
|
||||
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
|
||||
return
|
||||
}
|
||||
|
||||
// Get all function parameters to pass to the goroutine.
|
||||
var params []llvm.Value
|
||||
for _, param := range instr.Call.Args {
|
||||
params = append(params, b.expandFormalParam(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 instr.Call.IsInvoke() {
|
||||
// This is a method call on an interface value.
|
||||
itf := b.getValue(instr.Call.Value)
|
||||
itf := b.getValue(instr.Call.Value, getPos(instr))
|
||||
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
||||
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
|
||||
} else {
|
||||
@@ -89,7 +93,8 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
// * The function context, for closures.
|
||||
// * The function pointer (for tasks).
|
||||
var context llvm.Value
|
||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||
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)
|
||||
@@ -97,14 +102,14 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
var stackSize llvm.Value
|
||||
callee := b.createGoroutineStartWrapper(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
|
||||
// section that contains the stack size (and is modified after
|
||||
// linking).
|
||||
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.dataPtrType)}, "stacksize")
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// constant, it can be optimized.
|
||||
@@ -113,23 +118,164 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
}
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
}
|
||||
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "")
|
||||
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
||||
}
|
||||
|
||||
// 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.fn.RelString(nil)+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:
|
||||
//
|
||||
// func add(x, y int) int { ... }
|
||||
// func add(x, y int) int { ... }
|
||||
//
|
||||
// It creates a wrapper like this:
|
||||
//
|
||||
// func add$gowrapper(ptr *unsafe.Pointer) {
|
||||
// args := (*struct{
|
||||
// x, y int
|
||||
// })(ptr)
|
||||
// add(args.x, args.y)
|
||||
// }
|
||||
// func add$gowrapper(ptr *unsafe.Pointer) {
|
||||
// args := (*struct{
|
||||
// x, y int
|
||||
// })(ptr)
|
||||
// add(args.x, args.y)
|
||||
// }
|
||||
//
|
||||
// This is useful because the task-based goroutine start implementation only
|
||||
// allows a single (pointer) argument to the newly started goroutine. Also, it
|
||||
@@ -140,34 +286,42 @@ func (b *builder) createGo(instr *ssa.Go) {
|
||||
// to last parameter of the function) is used for this wrapper. If hasContext is
|
||||
// false, the parameter bundle is assumed to have no context parameter and undef
|
||||
// is passed instead.
|
||||
func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
|
||||
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext, isWasmExport bool, pos token.Pos) llvm.Value {
|
||||
var wrapper llvm.Value
|
||||
|
||||
builder := c.ctx.NewBuilder()
|
||||
defer builder.Dispose()
|
||||
b := &builder{
|
||||
compilerContext: c,
|
||||
Builder: c.ctx.NewBuilder(),
|
||||
}
|
||||
defer b.Dispose()
|
||||
|
||||
var deadlock llvm.Value
|
||||
var deadlockType llvm.Type
|
||||
if c.Scheduler == "asyncify" {
|
||||
deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
||||
deadlockType, deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
||||
}
|
||||
|
||||
if !fn.IsAFunction().IsNil() {
|
||||
// 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.i8ptrType}, false)
|
||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
|
||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapperType)
|
||||
c.addStandardAttributes(wrapper)
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(pos)
|
||||
@@ -188,26 +342,113 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
Optimized: true,
|
||||
})
|
||||
wrapper.SetSubprogram(difunc)
|
||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Create the list of params for the call.
|
||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
||||
if !hasContext {
|
||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||
}
|
||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
||||
if !hasContext {
|
||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
||||
}
|
||||
if !isWasmExport {
|
||||
// Regular 'go' instruction.
|
||||
|
||||
// Create the call.
|
||||
builder.CreateCall(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" {
|
||||
builder.CreateCall(deadlock, []llvm.Value{
|
||||
llvm.Undef(c.i8ptrType),
|
||||
}, "")
|
||||
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 := 0; i < numParams; i++ {
|
||||
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 {
|
||||
@@ -229,14 +470,14 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
// merged into one.
|
||||
|
||||
// Create the wrapper.
|
||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
|
||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||
c.addStandardAttributes(wrapper)
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
b.SetInsertPointAtEnd(entry)
|
||||
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(pos)
|
||||
@@ -257,37 +498,37 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
Optimized: true,
|
||||
})
|
||||
wrapper.SetSubprogram(difunc)
|
||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Get the list of parameters, with the extra parameters at the end.
|
||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
||||
paramTypes := fnType.ParamTypes()
|
||||
paramTypes = append(paramTypes, fn.Type()) // the last element is the function pointer
|
||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
||||
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||
|
||||
// Get the function pointer.
|
||||
fnPtr := params[len(params)-1]
|
||||
params = params[:len(params)-1]
|
||||
|
||||
// Create the call.
|
||||
builder.CreateCall(fnPtr, params, "")
|
||||
b.CreateCall(fnType, fnPtr, params, "")
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
builder.CreateCall(deadlock, []llvm.Value{
|
||||
llvm.Undef(c.i8ptrType),
|
||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||
llvm.Undef(c.dataPtrType),
|
||||
}, "")
|
||||
}
|
||||
}
|
||||
|
||||
if c.Scheduler == "asyncify" {
|
||||
// The goroutine was terminated via deadlock.
|
||||
builder.CreateUnreachable()
|
||||
b.CreateUnreachable()
|
||||
} else {
|
||||
// Finish the function. Every basic block must end in a terminator, and
|
||||
// because goroutines never return a value we can simply return void.
|
||||
builder.CreateRetVoid()
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// Return a ptrtoint of the wrapper, not the function itself.
|
||||
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
|
||||
return llvm.ConstPtrToInt(wrapper, c.uintptrType)
|
||||
}
|
||||
|
||||
+40
-36
@@ -5,6 +5,7 @@ package compiler
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
// operands or return values. It is useful for trivial instructions, like wfi in
|
||||
// ARM or sleep in AVR.
|
||||
//
|
||||
// func Asm(asm string)
|
||||
// func Asm(asm string)
|
||||
//
|
||||
// The provided assembly must be a constant.
|
||||
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
@@ -25,23 +26,23 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
|
||||
asm := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0, false)
|
||||
return b.CreateCall(target, nil, ""), nil
|
||||
return b.CreateCall(fnType, target, nil, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin, which allows assembly to be called in a flexible
|
||||
// way.
|
||||
//
|
||||
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
//
|
||||
// The asm parameter must be a constant string. The regs parameter must be
|
||||
// provided immediately. For example:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
registers := map[string]llvm.Value{}
|
||||
@@ -55,7 +56,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
|
||||
}
|
||||
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
||||
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
|
||||
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X, getPos(instr))
|
||||
case *ssa.Call:
|
||||
if r.Common() == instr {
|
||||
break
|
||||
@@ -98,7 +99,10 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
case llvm.IntegerTypeKind:
|
||||
constraints = append(constraints, "r")
|
||||
case llvm.PointerTypeKind:
|
||||
constraints = append(constraints, "*m")
|
||||
// Memory references require a type starting with LLVM 14,
|
||||
// probably as a preparation for opaque pointers.
|
||||
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
|
||||
return s
|
||||
default:
|
||||
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
||||
return s
|
||||
@@ -117,7 +121,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
}
|
||||
fnType := llvm.FunctionType(outputType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0, false)
|
||||
result := b.CreateCall(target, args, "")
|
||||
result := b.CreateCall(fnType, target, args, "")
|
||||
if hasOutput {
|
||||
return result, nil
|
||||
} else {
|
||||
@@ -129,15 +133,15 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||
// be one of:
|
||||
//
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
//
|
||||
// The num parameter must be a constant. All other parameters may be any scalar
|
||||
// value supported by LLVM inline assembly.
|
||||
func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
||||
func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error) {
|
||||
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
@@ -150,7 +154,7 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
||||
} else {
|
||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
llvmValue := b.getValue(arg)
|
||||
llvmValue := b.getValue(arg, pos)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
@@ -160,22 +164,22 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
||||
constraints += ",~{r1},~{r2},~{r3}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
return b.CreateCall(target, llvmArgs, ""), nil
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||
// be one of:
|
||||
//
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
//
|
||||
// The num parameter must be a constant. All other parameters may be any scalar
|
||||
// value supported by LLVM inline assembly.
|
||||
// Same as emitSVCall but for AArch64
|
||||
func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
|
||||
func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, error) {
|
||||
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
@@ -188,7 +192,7 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
|
||||
} else {
|
||||
constraints += ",{x" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
llvmValue := b.getValue(arg)
|
||||
llvmValue := b.getValue(arg, pos)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
@@ -198,15 +202,15 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
|
||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
||||
return b.CreateCall(target, llvmArgs, ""), nil
|
||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
||||
//
|
||||
// func (csr CSR) Get() uintptr
|
||||
// func (csr CSR) Set(uintptr)
|
||||
// func (csr CSR) SetBits(uintptr) uintptr
|
||||
// func (csr CSR) ClearBits(uintptr) uintptr
|
||||
// func (csr CSR) Get() uintptr
|
||||
// func (csr CSR) Set(uintptr)
|
||||
// func (csr CSR) SetBits(uintptr) uintptr
|
||||
// func (csr CSR) ClearBits(uintptr) uintptr
|
||||
//
|
||||
// The csr parameter (method receiver) must be a constant. Other parameter can
|
||||
// be any value.
|
||||
@@ -223,24 +227,24 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(b.uintptrType, nil, false)
|
||||
asm := fmt.Sprintf("csrr $0, %d", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0, false)
|
||||
return b.CreateCall(target, nil, ""), nil
|
||||
return b.CreateCall(fnType, target, nil, ""), nil
|
||||
case "Set":
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrw %d, $0", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0, false)
|
||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1], getPos(call))}, ""), nil
|
||||
case "SetBits":
|
||||
// Note: it may be possible to optimize this to csrrsi in many cases.
|
||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1], getPos(call))}, ""), nil
|
||||
case "ClearBits":
|
||||
// Note: it may be possible to optimize this to csrrci in many cases.
|
||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1], getPos(call))}, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||
}
|
||||
|
||||
+648
-270
File diff suppressed because it is too large
Load Diff
+11
-6
@@ -24,7 +24,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
// Note that bound functions are allowed if the function has a pointer
|
||||
// receiver and is a global. This is rather strict but still allows for
|
||||
// idiomatic Go code.
|
||||
funcValue := b.getValue(instr.Args[1])
|
||||
funcValue := b.getValue(instr.Args[1], getPos(instr))
|
||||
if funcValue.IsAConstant().IsNil() {
|
||||
// Try to determine the cause of the non-constantness for a nice error
|
||||
// message.
|
||||
@@ -36,11 +36,13 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
// Fall back to a generic error.
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
||||
}
|
||||
funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
|
||||
funcRawPtr, funcContext := b.decodeFuncValue(funcValue)
|
||||
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
|
||||
|
||||
// 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,10 +50,13 @@ 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 = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
|
||||
initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
|
||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
|
||||
initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
|
||||
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
|
||||
initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{
|
||||
llvm.ConstInt(b.intType, uint64(id.Int64()), true),
|
||||
}), 2, "")
|
||||
global.SetInitializer(initializer)
|
||||
|
||||
// Add debug info to the interrupt global.
|
||||
@@ -85,7 +90,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
|
||||
}
|
||||
b.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
||||
b.CreateCall(useFn.GlobalValueType(), useFn, []llvm.Value{interrupt}, "")
|
||||
}
|
||||
|
||||
return interrupt, nil
|
||||
|
||||
+235
-59
@@ -3,102 +3,278 @@ package compiler
|
||||
// This file contains helper functions to create calls to LLVM intrinsics.
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove
|
||||
// Define unimplemented intrinsic functions.
|
||||
//
|
||||
// Some functions are either normally implemented in Go assembly (like
|
||||
// sync/atomic functions) or intentionally left undefined to be implemented
|
||||
// directly in the compiler (like runtime/volatile functions). Either way, look
|
||||
// for these and implement them if this is the case.
|
||||
func (b *builder) defineIntrinsicFunction() {
|
||||
name := b.fn.RelString(nil)
|
||||
switch {
|
||||
case name == "runtime.memcpy" || name == "runtime.memmove":
|
||||
b.createMemoryCopyImpl()
|
||||
case name == "runtime.memzero":
|
||||
b.createMemoryZeroImpl()
|
||||
case name == "runtime.stacksave":
|
||||
b.createStackSaveImpl()
|
||||
case name == "runtime.KeepAlive":
|
||||
b.createKeepAliveImpl()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
b.createVolatileLoad()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
b.createVolatileStore()
|
||||
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
|
||||
b.createFunctionStart(true)
|
||||
returnValue := b.createAtomicOp(b.fn.Name())
|
||||
if !returnValue.IsNil() {
|
||||
b.CreateRet(returnValue)
|
||||
} else {
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
|
||||
// function, declaring this function if needed. These calls are treated
|
||||
// specially by optimization passes possibly resulting in better generated code,
|
||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||
func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) {
|
||||
fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
func (b *builder) createMemoryCopyImpl() {
|
||||
b.createFunctionStart(true)
|
||||
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
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 args {
|
||||
params = append(params, b.getValue(param))
|
||||
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, params, "")
|
||||
return llvm.Value{}, nil
|
||||
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of
|
||||
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||
// memory, declaring the function if needed. These calls will be lowered to
|
||||
// regular libc memset calls if they aren't optimized out in a different way.
|
||||
func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
||||
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||
}
|
||||
func (b *builder) createMemoryZeroImpl() {
|
||||
b.createFunctionStart(true)
|
||||
llvmFn := b.getMemsetFunc()
|
||||
params := []llvm.Value{
|
||||
b.getValue(args[0]),
|
||||
b.getValue(b.fn.Params[0], getPos(b.fn)),
|
||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||
b.getValue(args[1]),
|
||||
b.getValue(b.fn.Params[1], getPos(b.fn)),
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||
}
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
}
|
||||
|
||||
// createStackSaveImpl creates a call to llvm.stacksave.p0 to read the current
|
||||
// stack pointer.
|
||||
func (b *builder) createStackSaveImpl() {
|
||||
b.createFunctionStart(true)
|
||||
sp := b.readStackPointer()
|
||||
b.CreateRet(sp)
|
||||
}
|
||||
|
||||
// Return the llvm.memset.p0.i8 function declaration.
|
||||
func (c *compilerContext) getMemsetFunc() llvm.Value {
|
||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
|
||||
}
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
||||
// using inline assembly.
|
||||
func (b *builder) createKeepAliveImpl() {
|
||||
b.createFunctionStart(true)
|
||||
|
||||
// Get the underlying value of the interface value.
|
||||
interfaceValue := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||
pointerValue := b.CreateExtractValue(interfaceValue, 1, "")
|
||||
|
||||
// Create an equivalent of the following C code, which is basically just a
|
||||
// nop but ensures the pointerValue is kept alive:
|
||||
//
|
||||
// __asm__ __volatile__("" : : "r"(pointerValue))
|
||||
//
|
||||
// It should be portable to basically everything as the "r" register type
|
||||
// exists basically everywhere.
|
||||
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.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.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
"math.Exp2": "llvm.exp2.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Log": "llvm.log.f64",
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
||||
// possible. It returns the call result if possible, and a boolean whether it
|
||||
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
||||
// intrinsic.
|
||||
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
// Check whether this intrinsic is supported on the given GOARCH.
|
||||
// If it is unsupported, this can have two reasons:
|
||||
//
|
||||
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
||||
// the result doesn't pass the tests of the math package.
|
||||
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
||||
// libm function call, but the libm function call also fails the math
|
||||
// package tests.
|
||||
//
|
||||
// Whatever the implementation, it must pass the tests in the math package
|
||||
// so unfortunately only the below intrinsic+architecture combinations are
|
||||
// supported.
|
||||
name := call.StaticCallee().RelString(nil)
|
||||
switch name {
|
||||
case "math.Ceil", "math.Floor", "math.Trunc":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
case "math.Sqrt":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, false // only the above functions are supported.
|
||||
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
|
||||
// instead of the regular Go implementation. This allows LLVM to reason about
|
||||
// the math operation and (depending on the architecture) allows it to lower the
|
||||
// operation to very fast floating point instructions. If this is not possible,
|
||||
// LLVM will emit a call to a libm function that implements the same operation.
|
||||
//
|
||||
// One example of an optimization that LLVM can do is to convert
|
||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||
// more expensive than 32-bit ones.
|
||||
func (b *builder) defineMathOp() {
|
||||
b.createFunctionStart(true)
|
||||
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||
if llvmName == "" {
|
||||
panic("unreachable: unknown math operation") // sanity check
|
||||
}
|
||||
|
||||
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
||||
llvmFn := b.mod.NamedFunction(llvmName)
|
||||
if llvmFn.IsNil() {
|
||||
// The intrinsic doesn't exist yet, so declare it.
|
||||
// At the moment, all supported intrinsics have the form "double
|
||||
// foo(double %x)" so we can hardcode the signature here.
|
||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
||||
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
args := make([]llvm.Value, len(call.Args))
|
||||
for i, arg := range call.Args {
|
||||
args[i] = b.getValue(arg)
|
||||
args := make([]llvm.Value, len(b.fn.Params))
|
||||
for i, param := range b.fn.Params {
|
||||
args[i] = b.getValue(param, getPos(b.fn))
|
||||
}
|
||||
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||
b.CreateRet(result)
|
||||
}
|
||||
|
||||
// Implement most math/bits functions.
|
||||
//
|
||||
// This implements all the functions that operate on bits. It does not yet
|
||||
// implement the arithmetic functions (like bits.Add), which also have LLVM
|
||||
// intrinsics.
|
||||
func (b *builder) defineMathBitsIntrinsic() bool {
|
||||
if b.fn.Pkg.Pkg.Path() != "math/bits" {
|
||||
return false
|
||||
}
|
||||
name := b.fn.Name()
|
||||
switch name {
|
||||
case "LeadingZeros", "LeadingZeros8", "LeadingZeros16", "LeadingZeros32", "LeadingZeros64",
|
||||
"TrailingZeros", "TrailingZeros8", "TrailingZeros16", "TrailingZeros32", "TrailingZeros64":
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
valueType := param.Type()
|
||||
var intrinsicName string
|
||||
if strings.HasPrefix(name, "Leading") { // LeadingZeros
|
||||
intrinsicName = "llvm.ctlz.i" + strconv.Itoa(valueType.IntTypeWidth())
|
||||
} else { // TrailingZeros
|
||||
intrinsicName = "llvm.cttz.i" + strconv.Itoa(valueType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(intrinsicName)
|
||||
llvmFnType := llvm.FunctionType(valueType, []llvm.Type{valueType, b.ctx.Int1Type()}, false)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFn = llvm.AddFunction(b.mod, intrinsicName, llvmFnType)
|
||||
}
|
||||
result := b.createCall(llvmFnType, llvmFn, []llvm.Value{
|
||||
param,
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
result = b.createZExtOrTrunc(result, b.intType)
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
case "Len", "Len8", "Len16", "Len32", "Len64":
|
||||
// bits.Len can be implemented as:
|
||||
// (unsafe.Sizeof(v) * 8) - bits.LeadingZeros(n)
|
||||
// Not sure why this isn't already done in the standard library, as it
|
||||
// is much simpler than a lookup table.
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
valueType := param.Type()
|
||||
valueBits := valueType.IntTypeWidth()
|
||||
intrinsicName := "llvm.ctlz.i" + strconv.Itoa(valueBits)
|
||||
llvmFn := b.mod.NamedFunction(intrinsicName)
|
||||
llvmFnType := llvm.FunctionType(valueType, []llvm.Type{valueType, b.ctx.Int1Type()}, false)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFn = llvm.AddFunction(b.mod, intrinsicName, llvmFnType)
|
||||
}
|
||||
result := b.createCall(llvmFnType, llvmFn, []llvm.Value{
|
||||
param,
|
||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
result = b.createZExtOrTrunc(result, b.intType)
|
||||
maxLen := llvm.ConstInt(b.intType, uint64(valueBits), false) // number of bits in the value
|
||||
result = b.CreateSub(maxLen, result, "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
case "OnesCount", "OnesCount8", "OnesCount16", "OnesCount32", "OnesCount64":
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
valueType := param.Type()
|
||||
intrinsicName := "llvm.ctpop.i" + strconv.Itoa(valueType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(intrinsicName)
|
||||
llvmFnType := llvm.FunctionType(valueType, []llvm.Type{valueType}, false)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFn = llvm.AddFunction(b.mod, intrinsicName, llvmFnType)
|
||||
}
|
||||
result := b.createCall(llvmFnType, llvmFn, []llvm.Value{param}, "")
|
||||
result = b.createZExtOrTrunc(result, b.intType)
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
case "Reverse", "Reverse8", "Reverse16", "Reverse32", "Reverse64",
|
||||
"ReverseBytes", "ReverseBytes16", "ReverseBytes32", "ReverseBytes64":
|
||||
b.createFunctionStart(true)
|
||||
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
valueType := param.Type()
|
||||
var intrinsicName string
|
||||
if strings.HasPrefix(name, "ReverseBytes") {
|
||||
intrinsicName = "llvm.bswap.i" + strconv.Itoa(valueType.IntTypeWidth())
|
||||
} else { // Reverse
|
||||
intrinsicName = "llvm.bitreverse.i" + strconv.Itoa(valueType.IntTypeWidth())
|
||||
}
|
||||
llvmFn := b.mod.NamedFunction(intrinsicName)
|
||||
llvmFnType := llvm.FunctionType(valueType, []llvm.Type{valueType}, false)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFn = llvm.AddFunction(b.mod, intrinsicName, llvmFnType)
|
||||
}
|
||||
result := b.createCall(llvmFnType, llvmFn, []llvm.Value{param}, "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
case "RotateLeft", "RotateLeft8", "RotateLeft16", "RotateLeft32", "RotateLeft64":
|
||||
// Warning: the documentation says these functions must be constant time.
|
||||
// I do not think LLVM guarantees this, but there's a good chance LLVM
|
||||
// already recognized the rotate instruction so it probably won't get
|
||||
// any _worse_ by implementing these rotate functions.
|
||||
b.createFunctionStart(true)
|
||||
x := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||
k := b.getValue(b.fn.Params[1], b.fn.Pos())
|
||||
valueType := x.Type()
|
||||
intrinsicName := "llvm.fshl.i" + strconv.Itoa(valueType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(intrinsicName)
|
||||
llvmFnType := llvm.FunctionType(valueType, []llvm.Type{valueType, valueType, valueType}, false)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFn = llvm.AddFunction(b.mod, intrinsicName, llvmFnType)
|
||||
}
|
||||
k = b.createZExtOrTrunc(k, valueType)
|
||||
result := b.createCall(llvmFnType, llvmFn, []llvm.Value{x, x, k}, "")
|
||||
b.CreateRet(result)
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return b.CreateCall(llvmFn, args, ""), true
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
||||
return fmt.Errorf("type %q uses global context", t.String())
|
||||
default:
|
||||
// we used some other context by accident
|
||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
|
||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx)
|
||||
}
|
||||
|
||||
// if this is a composite type, check the components of the type
|
||||
@@ -70,10 +70,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
||||
return fmt.Errorf("failed to verify element type of array type %s: %s", t.String(), err.Error())
|
||||
}
|
||||
case llvm.PointerTypeKind:
|
||||
// check underlying type
|
||||
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
||||
return fmt.Errorf("failed to verify underlying type of pointer type %s: %s", t.String(), err.Error())
|
||||
}
|
||||
// Pointers can't be checked in an opaque pointer world.
|
||||
case llvm.VectorTypeKind:
|
||||
// check element type
|
||||
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
||||
|
||||
+244
-17
@@ -5,7 +5,9 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -19,10 +21,27 @@ import (
|
||||
//
|
||||
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
||||
// end the lifetime using emitLifetimeEnd after you're done with it.
|
||||
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, size llvm.Value) {
|
||||
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
||||
}
|
||||
|
||||
// insertBasicBlock inserts a new basic block after the current basic block.
|
||||
// This is useful when inserting new basic blocks while converting a
|
||||
// *ssa.BasicBlock to a llvm.BasicBlock and the LLVM basic block needs some
|
||||
// extra blocks.
|
||||
// It does not update b.blockExits, this must be done by the caller.
|
||||
func (b *builder) insertBasicBlock(name string) llvm.BasicBlock {
|
||||
currentBB := b.Builder.GetInsertBlock()
|
||||
nextBB := llvm.NextBasicBlock(currentBB)
|
||||
if nextBB.IsNil() {
|
||||
// Last basic block in the function, so add one to the end.
|
||||
return b.ctx.AddBasicBlock(b.llvmFn, name)
|
||||
}
|
||||
// Insert a basic block before the next basic block - that is, at the
|
||||
// current insert location.
|
||||
return b.ctx.InsertBasicBlock(nextBB, name)
|
||||
}
|
||||
|
||||
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
@@ -33,29 +52,172 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||
// emitPointerPack packs the list of values into a single pointer value using
|
||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||
// pointer value directly. It returns the pointer with the packed data.
|
||||
// If the values are all constants, they are be stored in a constant global and
|
||||
// deduplicated.
|
||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.pkg.Path(), b.NeedsStackObjects, values)
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
valueTypes[i] = value.Type()
|
||||
}
|
||||
packedType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Allocate memory for the packed data.
|
||||
size := b.targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
return llvm.ConstPointerNull(b.dataPtrType)
|
||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||
return values[0]
|
||||
} else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
|
||||
// Packed data fits in a pointer, so store it directly inside the
|
||||
// pointer.
|
||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||
// Try to keep this cast in SSA form.
|
||||
return b.CreateIntToPtr(values[0], b.dataPtrType, "pack.int")
|
||||
}
|
||||
|
||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||
// it in a *i8 alloca first and load the *i8 value from there. This is
|
||||
// effectively a bitcast.
|
||||
packedAlloc, _ := b.createTemporaryAlloca(b.dataPtrType, "")
|
||||
|
||||
if size < b.targetData.TypeAllocSize(b.dataPtrType) {
|
||||
// The alloca is bigger than the value that will be stored in it.
|
||||
// To avoid having some bits undefined, zero the alloca first.
|
||||
// Hopefully this will get optimized away.
|
||||
b.CreateStore(llvm.ConstNull(b.dataPtrType), packedAlloc)
|
||||
}
|
||||
|
||||
// Store all values in the alloca.
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||
b.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Load value (the *i8) from the alloca.
|
||||
result := b.CreateLoad(b.dataPtrType, packedAlloc, "")
|
||||
|
||||
// End the lifetime of the alloca, to help the optimizer.
|
||||
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||
b.emitLifetimeEnd(packedAlloc, packedSize)
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Check if the values are all constants.
|
||||
constant := true
|
||||
for _, v := range values {
|
||||
if !v.IsConstant() {
|
||||
constant = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if constant {
|
||||
// The data is known at compile time, so store it in a constant global.
|
||||
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
|
||||
global := llvm.AddGlobal(b.mod, packedType, b.pkg.Path()+"$pack")
|
||||
global.SetInitializer(b.ctx.ConstStruct(values, false))
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
return global
|
||||
}
|
||||
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||
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)))
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(packedAlloc)
|
||||
}
|
||||
|
||||
// Store all values in the heap pointer.
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||
b.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Return the original heap allocation pointer, which already is an *i8.
|
||||
return packedAlloc
|
||||
}
|
||||
}
|
||||
|
||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||
return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
|
||||
packedType := b.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Get a correctly-typed pointer to the packed data.
|
||||
var packedAlloc llvm.Value
|
||||
needsLifetimeEnd := false
|
||||
size := b.targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
// No data to unpack.
|
||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||
// A single pointer is always stored directly.
|
||||
return []llvm.Value{ptr}
|
||||
} else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
|
||||
// Packed data stored directly in pointer.
|
||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||
// Keep this cast in SSA form.
|
||||
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||
}
|
||||
// Fallback: load it using an alloca.
|
||||
packedAlloc, _ = b.createTemporaryAlloca(b.dataPtrType, "unpack.raw.alloc")
|
||||
b.CreateStore(ptr, packedAlloc)
|
||||
needsLifetimeEnd = true
|
||||
} else {
|
||||
// Packed data stored on the heap.
|
||||
packedAlloc = ptr
|
||||
}
|
||||
// Load each value from the packed data.
|
||||
values := make([]llvm.Value, len(valueTypes))
|
||||
for i, valueType := range valueTypes {
|
||||
if b.targetData.TypeAllocSize(valueType) == 0 {
|
||||
// This value has length zero, so there's nothing to load.
|
||||
values[i] = llvm.ConstNull(valueType)
|
||||
continue
|
||||
}
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||
values[i] = b.CreateLoad(valueType, gep, "")
|
||||
}
|
||||
if needsLifetimeEnd {
|
||||
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
|
||||
b.emitLifetimeEnd(packedAlloc, allocSize)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||
// contents, and returns the global.
|
||||
// contents, and returns the global and initializer type.
|
||||
// Note that it is left with the default linkage etc., you should set
|
||||
// linkage/constant/etc properties yourself.
|
||||
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value {
|
||||
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) (llvm.Type, llvm.Value) {
|
||||
globalType := llvm.ArrayType(elementType, len(buf))
|
||||
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||
value := llvm.Undef(globalType)
|
||||
for i := 0; i < len(buf); i++ {
|
||||
ch := uint64(buf[i])
|
||||
value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
|
||||
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||
}
|
||||
global.SetInitializer(value)
|
||||
return global
|
||||
return globalType, global
|
||||
}
|
||||
|
||||
// createObjectLayout returns a LLVM value (of type i8*) that describes where
|
||||
@@ -66,6 +228,8 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
||||
// which words contain a pointer (indicated by setting the given bit to 1). For
|
||||
// arrays, only the element is stored. This works because the GC knows the
|
||||
// object size and can therefore know how this value is repeated in the object.
|
||||
//
|
||||
// For details on what's in this value, see src/runtime/gc_precise.go.
|
||||
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||
// Use the element type for arrays. This works even for nested arrays.
|
||||
for {
|
||||
@@ -87,12 +251,12 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
// Do a few checks to see whether we need to generate any object layout
|
||||
// information at all.
|
||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||
pointerSize := c.targetData.TypeAllocSize(c.i8ptrType)
|
||||
pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||
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.i8ptrType)
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
bitmap := c.getPointerBitmap(t, pos)
|
||||
if bitmap.BitLen() == 0 {
|
||||
@@ -100,13 +264,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
// 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.i8ptrType)
|
||||
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.i8ptrType)
|
||||
return llvm.ConstNull(c.dataPtrType)
|
||||
}
|
||||
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
||||
|
||||
@@ -131,7 +295,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
// 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.i8ptrType)
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||
}
|
||||
|
||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||
@@ -142,12 +306,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if !global.IsNil() {
|
||||
return llvm.ConstBitCast(global, c.i8ptrType)
|
||||
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))
|
||||
@@ -192,13 +357,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
||||
global.AddMetadata(0, diglobal)
|
||||
}
|
||||
|
||||
return llvm.ConstBitCast(global, c.i8ptrType)
|
||||
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.i8ptrType)
|
||||
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||
switch typ.TypeKind() {
|
||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
return big.NewInt(0)
|
||||
@@ -211,7 +376,7 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
|
||||
// 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.i8ptrType, c.i8ptrType}, false)
|
||||
typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
|
||||
}
|
||||
for i, subtyp := range typ.StructElementTypes() {
|
||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||
@@ -253,3 +418,65 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
|
||||
panic("unknown LLVM type")
|
||||
}
|
||||
}
|
||||
|
||||
// archFamily returns the architecture from the LLVM triple but with some
|
||||
// architecture names ("armv6", "thumbv7m", etc) merged into a single
|
||||
// architecture name ("arm").
|
||||
func (c *compilerContext) archFamily() string {
|
||||
return compileopts.CanonicalArchName(c.Triple)
|
||||
}
|
||||
|
||||
// isThumb returns whether we're in ARM or in Thumb mode. It panics if the
|
||||
// features string is not one for an ARM architecture.
|
||||
func (c *compilerContext) isThumb() bool {
|
||||
var isThumb, isNotThumb bool
|
||||
for _, feature := range strings.Split(c.Features, ",") {
|
||||
if feature == "+thumb-mode" {
|
||||
isThumb = true
|
||||
}
|
||||
if feature == "-thumb-mode" {
|
||||
isNotThumb = true
|
||||
}
|
||||
}
|
||||
if isThumb == isNotThumb {
|
||||
panic("unexpected feature flags")
|
||||
}
|
||||
return isThumb
|
||||
}
|
||||
|
||||
// readStackPointer emits a LLVM intrinsic call that returns the current stack
|
||||
// pointer as an *i8.
|
||||
func (b *builder) readStackPointer() llvm.Value {
|
||||
name := "llvm.stacksave.p0"
|
||||
if llvmutil.Version() < 18 {
|
||||
name = "llvm.stacksave" // backwards compatibility with LLVM 17 and below
|
||||
}
|
||||
stacksave := b.mod.NamedFunction(name)
|
||||
if stacksave.IsNil() {
|
||||
fnType := llvm.FunctionType(b.dataPtrType, nil, false)
|
||||
stacksave = llvm.AddFunction(b.mod, name, fnType)
|
||||
}
|
||||
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||
}
|
||||
|
||||
// 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 {
|
||||
valueBits := value.Type().IntTypeWidth()
|
||||
resultBits := t.IntTypeWidth()
|
||||
if valueBits > resultBits {
|
||||
value = b.CreateTrunc(value, t, "")
|
||||
} else if valueBits < resultBits {
|
||||
value = b.CreateZExt(value, t, "")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Reverse a slice of bytes. From the wiki:
|
||||
// https://github.com/golang/go/wiki/SliceTricks#reversing
|
||||
func reverseBytes(buf []byte) {
|
||||
for i := len(buf)/2 - 1; i >= 0; i-- {
|
||||
opp := len(buf) - 1 - i
|
||||
buf[i], buf[opp] = buf[opp], buf[i]
|
||||
}
|
||||
}
|
||||
|
||||
+85
-24
@@ -1,5 +1,5 @@
|
||||
// Package llvmutil contains utility functions used across multiple compiler
|
||||
// packages. For example, they may be used by both the compiler pacakge and
|
||||
// packages. For example, they may be used by both the compiler package and
|
||||
// transformation packages.
|
||||
//
|
||||
// Normally, utility packages are avoided. However, in this case, the utility
|
||||
@@ -7,7 +7,13 @@
|
||||
// places would be a big risk if only one of them is updated.
|
||||
package llvmutil
|
||||
|
||||
import "tinygo.org/x/go-llvm"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
|
||||
// the IR builder is located elsewhere. It assumes that the insert point is
|
||||
@@ -26,19 +32,19 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
||||
}
|
||||
|
||||
// CreateTemporaryAlloca creates a new alloca in the entry block and adds
|
||||
// lifetime start infromation in the IR signalling that the alloca won't be used
|
||||
// lifetime start information in the IR signalling that the alloca won't be used
|
||||
// before this point.
|
||||
//
|
||||
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
||||
// end the lifetime using emitLifetimeEnd after you're done with it.
|
||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) {
|
||||
ctx := t.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
defer targetData.Dispose()
|
||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
||||
fnType, fn := getLifetimeStartFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,19 +52,20 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
||||
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
defer targetData.Dispose()
|
||||
|
||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||
builder.SetInsertPointBefore(inst)
|
||||
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
||||
fnType, fn := getLifetimeStartFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||
builder.SetInsertPointBefore(next)
|
||||
} else {
|
||||
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||
}
|
||||
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, bitcast}, "")
|
||||
fnType, fn = getLifetimeEndFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||
return alloca
|
||||
}
|
||||
|
||||
@@ -66,33 +73,36 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
|
||||
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, ptr}, "")
|
||||
fnType, fn := getLifetimeEndFunc(mod)
|
||||
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
|
||||
}
|
||||
|
||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||
// first if it doesn't exist yet.
|
||||
func getLifetimeStartFunc(mod llvm.Module) llvm.Value {
|
||||
fn := mod.NamedFunction("llvm.lifetime.start.p0i8")
|
||||
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||
fnName := "llvm.lifetime.start.p0"
|
||||
fn := mod.NamedFunction(fnName)
|
||||
ctx := mod.Context()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||
if fn.IsNil() {
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||
fn = llvm.AddFunction(mod, "llvm.lifetime.start.p0i8", fnType)
|
||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||
}
|
||||
return fn
|
||||
return fnType, fn
|
||||
}
|
||||
|
||||
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||
// first if it doesn't exist yet.
|
||||
func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
||||
fn := mod.NamedFunction("llvm.lifetime.end.p0i8")
|
||||
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||
fnName := "llvm.lifetime.end.p0"
|
||||
fn := mod.NamedFunction(fnName)
|
||||
ctx := mod.Context()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||
if fn.IsNil() {
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||
fn = llvm.AddFunction(mod, "llvm.lifetime.end.p0i8", fnType)
|
||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||
}
|
||||
return fn
|
||||
return fnType, fn
|
||||
}
|
||||
|
||||
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||
@@ -166,3 +176,54 @@ func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llv
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
// AppendToGlobal appends the given values to a global array like llvm.used. The global might
|
||||
// not exist yet. The values can be any pointer type, they will be cast to i8*.
|
||||
func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
|
||||
// Read the existing values in the llvm.used array (if it exists).
|
||||
var usedValues []llvm.Value
|
||||
if used := mod.NamedGlobal(globalName); !used.IsNil() {
|
||||
builder := mod.Context().NewBuilder()
|
||||
defer builder.Dispose()
|
||||
usedInitializer := used.Initializer()
|
||||
num := usedInitializer.Type().ArrayLength()
|
||||
for i := 0; i < num; i++ {
|
||||
usedValues = append(usedValues, builder.CreateExtractValue(usedInitializer, i, ""))
|
||||
}
|
||||
used.EraseFromParentAsGlobal()
|
||||
}
|
||||
|
||||
// Add the new values.
|
||||
ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
for _, value := range values {
|
||||
// Note: the bitcast is necessary to cast AVR function pointers to
|
||||
// address space 0 pointer types.
|
||||
usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType))
|
||||
}
|
||||
|
||||
// Create a new array (with the old and new values).
|
||||
usedInitializer := llvm.ConstArray(ptrType, usedValues)
|
||||
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
|
||||
used.SetInitializer(usedInitializer)
|
||||
used.SetLinkage(llvm.AppendingLinkage)
|
||||
}
|
||||
|
||||
// Version returns the LLVM major version.
|
||||
func Version() int {
|
||||
majorStr := strings.Split(llvm.Version, ".")[0]
|
||||
major, err := strconv.Atoi(majorStr)
|
||||
if err != nil {
|
||||
panic("unexpected error while parsing LLVM version: " + err.Error()) // should not happen
|
||||
}
|
||||
return major
|
||||
}
|
||||
|
||||
// Return the byte order for the given target triple. Most targets are little
|
||||
// endian, but for example MIPS can be big-endian.
|
||||
func ByteOrder(target string) binary.ByteOrder {
|
||||
if strings.HasPrefix(target, "mips-") {
|
||||
return binary.BigEndian
|
||||
} else {
|
||||
return binary.LittleEndian
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user