mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-31 17:59:03 +00:00
Compare commits
488 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae9eb7e64e | |||
| 6b53c58f92 | |||
| a7626e87e9 | |||
| 421d98b248 | |||
| b420a8be17 | |||
| f6e502e121 | |||
| f924a9e3ea | |||
| b9577ac8aa | |||
| a213d9ae46 | |||
| ac98a3df85 | |||
| 86d58db1af | |||
| 31fff2c9a3 | |||
| 8d6240a5e6 | |||
| 8f8ce06594 | |||
| 840af7a842 | |||
| 242f34d71a | |||
| f71b63053c | |||
| 1579b235e8 | |||
| af429a597b | |||
| 93bc65479f | |||
| 1958d6ff9d | |||
| 1991b00395 | |||
| ada22bc691 | |||
| e3bf9306b3 | |||
| 6cd0c00060 | |||
| dc97300c2e | |||
| 6136fb7b95 | |||
| f21e027b5c | |||
| ac45c35924 | |||
| 338af91ae6 | |||
| d74f70bdd0 | |||
| 80506a9cea | |||
| c33682cf00 | |||
| 570a3deac2 | |||
| c4219439eb | |||
| 7a9c649268 | |||
| 213d10838f | |||
| b573ef3813 | |||
| 854f91ecf2 | |||
| c2346570fb | |||
| 169daed2a1 | |||
| cd5e2580c1 | |||
| 8b3200cec8 | |||
| b0298c6a69 | |||
| 0fe4c619eb | |||
| 16fc1ea2bb | |||
| 9105cce340 | |||
| 7756941f39 | |||
| 259842dc2e | |||
| 528476f2b3 | |||
| 0c68e1215e | |||
| 9c2eafce6f | |||
| 3797e89600 | |||
| 40ed956d6c | |||
| 2a49216152 | |||
| 1e3baff966 | |||
| 4fcf03414c | |||
| 888d0c0f97 | |||
| fc17c3565e | |||
| 6aa966af0a | |||
| aee0581a32 | |||
| 2dea20b769 | |||
| 0c17b918fb | |||
| 8727b696a5 | |||
| 94736ac0e5 | |||
| 9e9be09e9a | |||
| 9e7d89d4d5 | |||
| 3b7c9e24f5 | |||
| bc35d087c2 | |||
| deaf532d61 | |||
| 5ec2632461 | |||
| 39a105dab9 | |||
| aa914bea5c | |||
| 3071e339cd | |||
| b536dd6f79 | |||
| 3ad913acb8 | |||
| 9e9f1d0b96 | |||
| cd4e479dd5 | |||
| cf5bed8227 | |||
| babdfc9e10 | |||
| 2602d4c25d | |||
| 0a9b3f91bc | |||
| 3b137c3053 | |||
| 5f02d6b659 | |||
| b8adb803c4 | |||
| 36d3b83e9c | |||
| a2a638c198 | |||
| 0ffa3eb748 | |||
| 922f583dd4 | |||
| 91a56b28a2 | |||
| 9c864f0d79 | |||
| 6203b624c5 | |||
| ea003da13f | |||
| 7994d2e912 | |||
| f1c39e8356 | |||
| 801bd484ab | |||
| be7b6b316e | |||
| ec184e90c1 | |||
| 07c9cdcbc4 | |||
| bd1d11d166 | |||
| 919c998355 | |||
| a3375fb2c4 | |||
| 09bda77b78 | |||
| 047ed57005 | |||
| c7923d1c64 | |||
| 73db9776b1 | |||
| 6d49f0177b | |||
| 35a61ac8c5 | |||
| c1a4ed1489 | |||
| 140c82e012 | |||
| dc23ce73d6 | |||
| e039ce131c | |||
| d59c706e60 | |||
| 26d3645317 | |||
| 432ebe13ed | |||
| 16eefc59eb | |||
| fb1bf2e6bf | |||
| 0922e3e956 | |||
| bd61913973 | |||
| 2dbfdbae10 | |||
| 05029c4fb5 | |||
| a7b30639d8 | |||
| 4adc661197 | |||
| 311c071ae6 | |||
| f1fdfe2313 | |||
| 7c3dc05117 | |||
| e569dcfe38 | |||
| 20038817ae | |||
| 9ce73cfafa | |||
| 0033c23848 | |||
| 5c15a68d18 | |||
| 02384c9414 | |||
| e79edb58b2 | |||
| bf80e9b446 | |||
| 30355cec9f | |||
| 9111927f85 | |||
| 10224a4bf8 | |||
| 33ddee76db | |||
| 7fa50a55cf | |||
| 1c0c26b8b7 | |||
| 3abe58448b | |||
| 364ac2dcb2 | |||
| e111b489ec | |||
| f31b39775f | |||
| 6c30583062 | |||
| d29e8e5f22 | |||
| a93640662e | |||
| 3fb0a90854 | |||
| df09dbdf0f | |||
| 134de98ba5 | |||
| 221ea6a54c | |||
| 7ffbcbc666 | |||
| 5f66260c3a | |||
| ba95eb3e1b | |||
| f16697624d | |||
| cb311ed25e | |||
| 6f686d12af | |||
| 6a0acfc735 | |||
| 27f8ec17fc | |||
| a683881eb7 | |||
| 335b085fd2 | |||
| 11c2b76e29 | |||
| 6605b6e43f | |||
| ec18e89365 | |||
| 39cde9f9a3 | |||
| 2747027ef2 | |||
| 4465360f3d | |||
| c33113ab5f | |||
| eab43851ac | |||
| c3f1832d9a | |||
| 2328d1e799 | |||
| 5fabc203db | |||
| d9d19e812e | |||
| 45dd431f19 | |||
| 088a660c00 | |||
| 1ff348fb1f | |||
| cd364a9315 | |||
| ca584de84a | |||
| 594be6db63 | |||
| aef70a5839 | |||
| e6e7250a47 | |||
| 7b3dc19445 | |||
| 8e36d2758b | |||
| 7cbbfd6fc5 | |||
| e8bb38f7c9 | |||
| 1b9bb143bf | |||
| 41f666c670 | |||
| 469e2434fd | |||
| 278aa09819 | |||
| dded832238 | |||
| 8ffabbea64 | |||
| 29b4c6723f | |||
| 039f48f3d2 | |||
| eed4afda63 | |||
| ce888e1042 | |||
| f5d0ec93ef | |||
| 3a0d72457b | |||
| c980e48ad4 | |||
| f5bd750a4d | |||
| 0a5875ab2e | |||
| 54ecf74261 | |||
| 9b83acffe6 | |||
| 67aa15e92e | |||
| 0d6efc6429 | |||
| 1bd3ade825 | |||
| fb8ed8fde8 | |||
| c698eeacba | |||
| 906a20d3bf | |||
| 1be377c47d | |||
| 4dbb5dd4da | |||
| 997974376d | |||
| 7d51044892 | |||
| 37c080083b | |||
| 70a0dee8c8 | |||
| de83d67364 | |||
| 7a5a50d949 | |||
| d348d8b4cf | |||
| 443de9fe1c | |||
| c72f1d5e62 | |||
| ca8fcc9479 | |||
| d01a932201 | |||
| 85d223c5e2 | |||
| 7b486e398a | |||
| da69f18e91 | |||
| 6971b8a69e | |||
| 6682e41ab4 | |||
| 18033ebc36 | |||
| 89d9e33bca | |||
| e80e7e5c10 | |||
| 6233915ecf | |||
| fd8e0bd70b | |||
| 45e2863e9f | |||
| 4204f3d065 | |||
| ddbd65beec | |||
| 1a1506ef79 | |||
| 8793dc37fa | |||
| e9d78a78ea | |||
| 7ec9cfc61c | |||
| 294cf28e43 | |||
| 2f7a66e6d3 | |||
| a4f9c9d1b6 | |||
| aa10b682e4 | |||
| eafbe4ee69 | |||
| cf5912412d | |||
| 1f114b2acf | |||
| 94ebddbdfe | |||
| aa1ca27789 | |||
| 9999320cd0 | |||
| 74429b4a14 | |||
| bcb191c01e | |||
| ddb848816f | |||
| 889911d0a6 | |||
| a4dd38de81 | |||
| 486ee37a40 | |||
| 944176beef | |||
| c396418187 | |||
| 1b4ab772d2 | |||
| aca0fe0412 | |||
| af511c1118 | |||
| 5ba8766cbc | |||
| b792c10680 | |||
| 12d41ba0c1 | |||
| 020cca8c3c | |||
| 4cbd34d32f | |||
| 6426bf79b4 | |||
| aed8116b22 | |||
| f6c759ccbf | |||
| 4b389b90e8 | |||
| 4c3680635e | |||
| 0755145824 | |||
| eabd1ddce0 | |||
| 0e84235f9a | |||
| 894839484c | |||
| 1fadd0e772 | |||
| eba6e7a428 | |||
| f4a6e0373b | |||
| d274545e63 | |||
| f74d25c882 | |||
| 618c689246 | |||
| 9d29971755 | |||
| 451c57af92 | |||
| 59feac28b9 | |||
| c1f48fc79a | |||
| 3c07a36e95 | |||
| 98b3c27c76 | |||
| ddacdfacd5 | |||
| 2d477e069d | |||
| ed1a56785a | |||
| f496571747 | |||
| 17fed5980b | |||
| 2ff04f1a87 | |||
| f763865c5c | |||
| 52edf52245 | |||
| 8161b766d3 | |||
| 709349e24b | |||
| 5fd4878574 | |||
| a24dc8c543 | |||
| c3d514a751 | |||
| 5b7a2c73db | |||
| aaf6a36c55 | |||
| dc6e0e23b9 | |||
| 44b0c79eaf | |||
| 27e5baeb57 | |||
| a6a4f85e61 | |||
| 3386316b9f | |||
| 410bb9c103 | |||
| f176b3a480 | |||
| 9b1a3a2236 | |||
| b29edadf48 | |||
| d04332d688 | |||
| 547288529c | |||
| bd8de8719f | |||
| 2fd44ac341 | |||
| dc30aef687 | |||
| 29cadaa23b | |||
| a7c149f8dc | |||
| c2a04e1a45 | |||
| f748dc6fd8 | |||
| bfc21ad906 | |||
| 41726fab9d | |||
| 82f4fcf2a3 | |||
| 3a00f00f62 | |||
| a71f776203 | |||
| d2f60c02bb | |||
| 7ca53138bc | |||
| a9c0501826 | |||
| 60fb4b95fa | |||
| 323b62b405 | |||
| 40fb3466c1 | |||
| 2333ef2454 | |||
| 0ac7bf8c8b | |||
| 38a3ccee52 | |||
| 47115f0380 | |||
| 1f00425eb3 | |||
| 71245845c8 | |||
| d9f6f3b81f | |||
| b81a7c2bca | |||
| 0ec2f0818a | |||
| 4a6a82ae79 | |||
| b14ee51ff6 | |||
| 394116e998 | |||
| f902437848 | |||
| 86cc3b6c12 | |||
| 6bb6ea2432 | |||
| 3c590e36ad | |||
| 69a4dda8c8 | |||
| 8fbaffe56e | |||
| bfafef8df1 | |||
| 26ff622148 | |||
| 88a68eaf7e | |||
| 46a2a177c1 | |||
| b0ea05a853 | |||
| f3b79c412f | |||
| f4f3ad5051 | |||
| 0757ccc657 | |||
| 47282be619 | |||
| 9bdbe943ce | |||
| 180220a2e3 | |||
| 8ef4af45d7 | |||
| 9215aa7ece | |||
| b497c5da71 | |||
| bf4d2fef4a | |||
| c053b8fc8a | |||
| d364ac6024 | |||
| d78be5112b | |||
| b5233eea56 | |||
| 09a6612742 | |||
| 1778ad620f | |||
| 458b50bc07 | |||
| fca8784dc3 | |||
| a846ee2321 | |||
| df087b592f | |||
| eade931650 | |||
| a7649f5b42 | |||
| f0b953dec9 | |||
| bb8196653f | |||
| a1b44ddd58 | |||
| 48b0d7f434 | |||
| d8b6b257f4 | |||
| c98972a3e0 | |||
| b93759936c | |||
| d62dda8140 | |||
| ca36fba7e4 | |||
| 743bf45e00 | |||
| 9bc5d21185 | |||
| c8b56bd161 | |||
| fe20079300 | |||
| 068d7c1b44 | |||
| 42fed60e61 | |||
| 53c1ccaa42 | |||
| 72f667cac9 | |||
| 4fff472808 | |||
| cfd74c2954 | |||
| 9bcca974ba | |||
| a40586d092 | |||
| bc9708f51a | |||
| 8ef36ed939 | |||
| c7f62bae9b | |||
| 1b741bf2b7 | |||
| 266d70816f | |||
| 27cedf09e8 | |||
| db9f1182f5 | |||
| f7fd5cf2d8 | |||
| 8d9c9a16e3 | |||
| 82deccac40 | |||
| 0e540dc3da | |||
| 897f2bc5fd | |||
| 750b0e5c18 | |||
| a65ac105cc | |||
| 6970c80d34 | |||
| dfbc42f833 | |||
| e356daa229 | |||
| 1c27ecb66f | |||
| d01d0bb358 | |||
| 8fcf3658b1 | |||
| b37535bcfb | |||
| ee0a10ec51 | |||
| 39e0333663 | |||
| 3afd2c3597 | |||
| bd19f3baa3 | |||
| 908fa84d49 | |||
| 571f34e0e6 | |||
| 01723025d7 | |||
| 505b5ee565 | |||
| 26ac03a3f6 | |||
| c9aa88b8ef | |||
| 887ac286ff | |||
| b1083d78c7 | |||
| 7863bdb484 | |||
| 5c3363bf25 | |||
| 9404bb8712 | |||
| 20e22d4507 | |||
| 9118e91b72 | |||
| fbbaa5e580 | |||
| 97a2cb4a37 | |||
| 6dee98c3a1 | |||
| 484ccf830a | |||
| 5690204224 | |||
| b203314c2f | |||
| 5ae8fd1f6f | |||
| c80da7eb0d | |||
| 326ca4202e | |||
| 852dde671e | |||
| bf613175e7 | |||
| 4d62cca267 | |||
| a63aa143bb | |||
| ed0fb774b9 | |||
| 9be956f4bc | |||
| 48d741f68a | |||
| 35e6b8a67d | |||
| 8a31ae14a0 | |||
| 26ee3fb93e | |||
| 8872d5ebfc | |||
| faf455283d | |||
| 562b4c5020 | |||
| 7460734522 | |||
| 3be7100e89 | |||
| aedaf7d925 | |||
| 256e84f912 | |||
| 0a87846bd8 | |||
| 073862ee92 | |||
| c820d83ae2 | |||
| 109e0767e7 | |||
| 821f2aeae0 | |||
| 93a11e266f | |||
| e29e583919 | |||
| 310df7acb5 | |||
| b9387febe0 | |||
| 3dce224183 | |||
| 5d8afa25b8 | |||
| 0dca016749 | |||
| 28a911bcce | |||
| eb36120c6e | |||
| dfbb133ea6 | |||
| 79ab77facd | |||
| 2c99692019 | |||
| 0ca0aad7ce | |||
| 3ddba4e620 | |||
| eab3dceb48 | |||
| 663a94fcab | |||
| 373ca88f14 | |||
| be33de1086 | |||
| e5bdfb0962 | |||
| e80c6c48a1 | |||
| 381644a0c0 | |||
| 13bb59c334 | |||
| 632e5f9872 | |||
| 37531930b8 |
@@ -1,119 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
commands:
|
|
||||||
submodules:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Pull submodules"
|
|
||||||
command: git submodule update --init
|
|
||||||
llvm-source-linux:
|
|
||||||
steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- llvm-source-19-v1
|
|
||||||
- run:
|
|
||||||
name: "Fetch LLVM source"
|
|
||||||
command: make llvm-source
|
|
||||||
- save_cache:
|
|
||||||
key: llvm-source-19-v1
|
|
||||||
paths:
|
|
||||||
- llvm-project/clang/lib/Headers
|
|
||||||
- llvm-project/clang/include
|
|
||||||
- llvm-project/compiler-rt
|
|
||||||
- llvm-project/lld/include
|
|
||||||
- llvm-project/llvm/include
|
|
||||||
hack-ninja-jobs:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Hack Ninja to use less jobs"
|
|
||||||
command: |
|
|
||||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
|
||||||
chmod +x /go/bin/ninja
|
|
||||||
build-binaryen-linux:
|
|
||||||
steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- binaryen-linux-v3
|
|
||||||
- run:
|
|
||||||
name: "Build Binaryen"
|
|
||||||
command: |
|
|
||||||
make binaryen
|
|
||||||
- save_cache:
|
|
||||||
key: binaryen-linux-v3
|
|
||||||
paths:
|
|
||||||
- build/wasm-opt
|
|
||||||
test-linux:
|
|
||||||
parameters:
|
|
||||||
llvm:
|
|
||||||
type: string
|
|
||||||
fmt-check:
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- submodules
|
|
||||||
- run:
|
|
||||||
name: "Install apt dependencies"
|
|
||||||
command: |
|
|
||||||
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
|
||||||
apt-get update
|
|
||||||
apt-get install --no-install-recommends -y \
|
|
||||||
llvm-<<parameters.llvm>>-dev \
|
|
||||||
clang-<<parameters.llvm>> \
|
|
||||||
libclang-<<parameters.llvm>>-dev \
|
|
||||||
lld-<<parameters.llvm>> \
|
|
||||||
cmake \
|
|
||||||
ninja-build
|
|
||||||
- hack-ninja-jobs
|
|
||||||
- build-binaryen-linux
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
|
||||||
- go-cache-v4-{{ checksum "go.mod" }}
|
|
||||||
- llvm-source-linux
|
|
||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
|
||||||
- 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-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
|
||||||
paths:
|
|
||||||
- ~/.cache/go-build
|
|
||||||
- /go/pkg/mod
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test-oldest:
|
|
||||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
|
||||||
# least the smoke tests still pass.
|
|
||||||
docker:
|
|
||||||
- image: golang:1.22-bullseye
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "15"
|
|
||||||
resource_class: large
|
|
||||||
test-newest:
|
|
||||||
# This tests the latest supported LLVM version when linking against system
|
|
||||||
# libraries.
|
|
||||||
docker:
|
|
||||||
- image: golang:1.25-bullseye
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "20"
|
|
||||||
resource_class: large
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
test-all:
|
|
||||||
jobs:
|
|
||||||
- test-oldest
|
|
||||||
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub.
|
|
||||||
# - test-newest
|
|
||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
llvm-*/
|
llvm-*/
|
||||||
|
!llvm-version.txt
|
||||||
.github
|
.github
|
||||||
.circleci
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,36 +16,37 @@ jobs:
|
|||||||
name: build-macos
|
name: build-macos
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# macos-13: amd64 (oldest supported version as of 18-10-2024)
|
# macos-14: arm64 (oldest supported version as of 18-11-2025)
|
||||||
# macos-14: arm64 (oldest arm64 version)
|
# macos-15-intel: amd64 (last intel version to be supported by github runners)
|
||||||
os: [macos-13, macos-14]
|
# See https://github.com/actions/runner-images/issues/13046
|
||||||
|
os: [macos-14, macos-15-intel]
|
||||||
include:
|
include:
|
||||||
- os: macos-13
|
|
||||||
goarch: amd64
|
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
|
- os: macos-15-intel
|
||||||
|
goarch: amd64
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
id: version
|
id: version
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-${{ matrix.os }}-v1
|
key: llvm-source-22-${{ matrix.os }}-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -56,7 +57,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -67,10 +68,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-19-${{ matrix.os }}-v1
|
key: llvm-build-22-${{ matrix.os }}-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -79,12 +80,12 @@ jobs:
|
|||||||
rm -rf llvm-project
|
rm -rf llvm-project
|
||||||
make llvm-source
|
make llvm-source
|
||||||
# install dependencies
|
# install dependencies
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install ninja
|
||||||
# build!
|
# build!
|
||||||
make llvm-build
|
make llvm-build
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
@@ -100,18 +101,13 @@ jobs:
|
|||||||
- name: Make release artifact
|
- name: Make release artifact
|
||||||
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
# Note: this release artifact is double-zipped, see:
|
uses: actions/upload-artifact@v7
|
||||||
# https://github.com/actions/upload-artifact/issues/39
|
|
||||||
# We can essentially pick one of these:
|
|
||||||
# - have a double-zipped artifact when downloaded from the UI
|
|
||||||
# - have a very slow artifact upload
|
|
||||||
# We're doing the former here, to keep artifact uploads fast.
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
||||||
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
|
||||||
|
archive: false
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
run: make smoketest-quick TINYGO=$(PWD)/build/tinygo
|
||||||
test-macos-homebrew:
|
test-macos-homebrew:
|
||||||
name: homebrew-install
|
name: homebrew-install
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
@@ -120,7 +116,7 @@ jobs:
|
|||||||
version: [16, 17, 18, 19, 20]
|
version: [16, 17, 18, 19, 20]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Homebrew
|
- name: Set up Homebrew
|
||||||
uses: Homebrew/actions/setup-homebrew@master
|
uses: Homebrew/actions/setup-homebrew@main
|
||||||
- name: Fix Python symlinks
|
- name: Fix Python symlinks
|
||||||
run: |
|
run: |
|
||||||
# Github runners have broken symlinks, so relink
|
# Github runners have broken symlinks, so relink
|
||||||
@@ -129,12 +125,13 @@ jobs:
|
|||||||
- name: Install LLVM
|
- name: Install LLVM
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@${{ matrix.version }}
|
brew install llvm@${{ matrix.version }}
|
||||||
|
brew link llvm@${{ matrix.version }}
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||||
run: go install -tags=llvm${{ matrix.version }}
|
run: go install -tags=llvm${{ matrix.version }}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# This CI job checks whether at least the smoke tests pass for the oldest
|
||||||
|
# Go/LLVM version we claim to support.
|
||||||
|
|
||||||
|
name: Version compatibility test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
- release
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-compat:
|
||||||
|
runs-on: ubuntu-22.04 # this must be a specific version for the apt install below
|
||||||
|
env:
|
||||||
|
# Oldest versions currently supported by TinyGo
|
||||||
|
LLVM: "15"
|
||||||
|
Go: "1.25" # when updating this, also update minorMin in builder/config.go
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.Go }}
|
||||||
|
cache: true
|
||||||
|
- name: Install LLVM
|
||||||
|
run: |
|
||||||
|
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ env.LLVM }} main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||||
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --no-install-recommends -y \
|
||||||
|
llvm-${{ env.LLVM }}-dev \
|
||||||
|
clang-${{ env.LLVM }} \
|
||||||
|
libclang-${{ env.LLVM }}-dev \
|
||||||
|
lld-${{ env.LLVM }} \
|
||||||
|
binaryen
|
||||||
|
- name: Restore LLVM source cache
|
||||||
|
uses: actions/cache/restore@v5
|
||||||
|
id: cache-llvm-source
|
||||||
|
with:
|
||||||
|
key: llvm-source-22-linux-compat-${{ hashFiles('llvm-version.txt') }}
|
||||||
|
path: llvm-project/compiler-rt
|
||||||
|
- name: Download LLVM source
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
run: make llvm-source
|
||||||
|
- name: Save LLVM source cache
|
||||||
|
uses: actions/cache/save@v5
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: llvm-project/compiler-rt
|
||||||
|
- name: Go cache
|
||||||
|
uses: actions/cache@v5
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: go-build-${{ env.Go }}-llvm${{ env.LLVM }}-${{ hashFiles('go.sum') }}
|
||||||
|
- name: Build TinyGo
|
||||||
|
run: go install -tags=llvm${{ env.LLVM }}
|
||||||
|
- run: tinygo version
|
||||||
|
- run: make gen-device -j4
|
||||||
|
- run: go test -tags=llvm${{ env.LLVM }} -short -skip=TestErrors
|
||||||
|
- run: make smoketest-quick XTENSA=0
|
||||||
@@ -31,14 +31,14 @@ jobs:
|
|||||||
sudo rm -rf /usr/local/share/boost
|
sudo rm -rf /usr/local/share/boost
|
||||||
df -h
|
df -h
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/tinygo-dev
|
tinygo/tinygo-dev
|
||||||
@@ -47,64 +47,22 @@ jobs:
|
|||||||
type=sha,format=long
|
type=sha,format=long
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
- name: Log in to Github Container Registry
|
- name: Log in to Github Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/tinygo-dev:buildcache
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/tinygo-dev:buildcache,mode=max
|
||||||
- name: Trigger Drivers repo build on Github Actions
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
|
|
||||||
-d '{"ref": "dev"}'
|
|
||||||
- name: Trigger Bluetooth repo build on Github Actions
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
|
|
||||||
-d '{"ref": "dev"}'
|
|
||||||
- name: Trigger TinyFS repo build on Github Actions
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
|
|
||||||
-d '{"ref": "dev"}'
|
|
||||||
- name: Trigger TinyFont repo build on Github Actions
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
|
|
||||||
-d '{"ref": "dev"}'
|
|
||||||
- name: Trigger TinyDraw repo build on Github Actions
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \
|
|
||||||
-d '{"ref": "dev"}'
|
|
||||||
- name: Trigger TinyTerm repo build on Github Actions
|
|
||||||
run: |
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
|
|
||||||
-d '{"ref": "dev"}'
|
|
||||||
|
|||||||
+138
-60
@@ -12,43 +12,61 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
go-mod-tidy:
|
||||||
|
# Check that go.sum is up to date.
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: '1.27.0'
|
||||||
|
cache: true
|
||||||
|
- name: Run go mod tidy
|
||||||
|
run: go mod tidy
|
||||||
|
- name: Check go.mod and go.sum are up to date
|
||||||
|
run: git diff --exit-code
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
# Build Linux binaries, ready for release.
|
# Build Linux binaries, ready for release.
|
||||||
# This runs inside an Alpine Linux container so we can more easily create a
|
# This runs inside an Alpine Linux container so we can more easily create a
|
||||||
# statically linked binary.
|
# statically linked binary.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.25-alpine
|
image: golang:1.27-alpine
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install apk dependencies
|
- name: Install apk dependencies
|
||||||
# tar: needed for actions/cache@v4
|
# tar: needed for actions/cache@v5
|
||||||
# git+openssh: needed for checkout (I think?)
|
# git+openssh: needed for checkout (I think?)
|
||||||
# ruby: needed to install fpm
|
# ruby: needed to install fpm
|
||||||
run: apk add tar git openssh make g++ ruby-dev
|
run: apk add tar git openssh make g++ ruby-dev mold
|
||||||
- name: Work around CVE-2022-24765
|
- name: Work around CVE-2022-24765
|
||||||
# We're not on a multi-user machine, so this is safe.
|
# We're not on a multi-user machine, so this is safe.
|
||||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
id: version
|
id: version
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-alpine-v2-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-linux-alpine-v1
|
key: llvm-source-22-linux-alpine-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -59,7 +77,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -70,10 +88,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-19-linux-alpine-v1
|
key: llvm-build-22-linux-alpine-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -88,16 +106,16 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-alpine-v1
|
key: binaryen-linux-alpine-v3
|
||||||
path: build/wasm-opt
|
path: build/wasm-opt
|
||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
@@ -118,26 +136,31 @@ jobs:
|
|||||||
make release deb -j3 STATIC=1
|
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.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
|
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
- name: Publish release artifact
|
- name: "Publish release artifact: tarball"
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
|
name: tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||||
path: |
|
path: /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
||||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
|
archive: false
|
||||||
/tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
- name: "Publish release artifact: Debian package"
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
|
path: /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
|
archive: false
|
||||||
test-linux-build:
|
test-linux-build:
|
||||||
# Test the binaries built in the build-linux job by running the smoke tests.
|
# Test the binaries built in the build-linux job by running the smoke tests.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||||
@@ -146,9 +169,9 @@ jobs:
|
|||||||
- name: Install wasm-tools
|
- name: Install wasm-tools
|
||||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||||
- name: Download release artifact
|
- name: Download release artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||||
- name: Extract release tarball
|
- name: Extract release tarball
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/lib
|
mkdir -p ~/lib
|
||||||
@@ -157,14 +180,50 @@ jobs:
|
|||||||
- run: make tinygo-test-wasip1-fast
|
- run: make tinygo-test-wasip1-fast
|
||||||
- run: make tinygo-test-wasip2-fast
|
- run: make tinygo-test-wasip2-fast
|
||||||
- run: make tinygo-test-wasm
|
- run: make tinygo-test-wasm
|
||||||
- run: make smoketest
|
smoketest-linux:
|
||||||
|
# Build a binary for every supported board, using the binaries built in the
|
||||||
|
# build-linux job. This is the only job that runs the full smoke test. The
|
||||||
|
# result does not depend on the host OS, so the other jobs build a
|
||||||
|
# representative board for each architecture with smoketest-quick.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-linux
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# Balanced with the measured build time of each group, so that the
|
||||||
|
# shards finish at about the same time.
|
||||||
|
group:
|
||||||
|
- smoketest-rp2xxx smoketest-selftest smoketest-wasm-sim smoketest-py32
|
||||||
|
- smoketest-esp smoketest-riscv smoketest-flags
|
||||||
|
- smoketest-examples smoketest-nxp smoketest-pwm-usb smoketest-avr
|
||||||
|
- smoketest-samd smoketest-stm32 smoketest-nrf smoketest-wasm
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: '1.27.0'
|
||||||
|
cache: true
|
||||||
|
- name: Download release artifact
|
||||||
|
uses: actions/download-artifact@v8
|
||||||
|
with:
|
||||||
|
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||||
|
- name: Extract release tarball
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/lib
|
||||||
|
tar -C ~/lib -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||||
|
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||||
|
- run: make testchdir ${{ matrix.group }}
|
||||||
assert-test-linux:
|
assert-test-linux:
|
||||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||||
# potential bugs.
|
# potential bugs.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
@@ -173,31 +232,43 @@ jobs:
|
|||||||
cat /proc/cpuinfo
|
cat /proc/cpuinfo
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends \
|
sudo apt-get install --no-install-recommends \
|
||||||
|
libgcrypt20 \
|
||||||
|
libpixman-1-0 \
|
||||||
|
libsdl2-2.0-0 \
|
||||||
|
libslirp0 \
|
||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-system-riscv32 \
|
qemu-system-riscv32 \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
simavr \
|
simavr \
|
||||||
ninja-build
|
ninja-build
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '22'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||||
with:
|
with:
|
||||||
version: "29.0.1"
|
version: "29.0.1"
|
||||||
- name: Setup `wasm-tools`
|
- name: Setup `wasm-tools`
|
||||||
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
uses: bytecodealliance/actions/wasm-tools/setup@v1
|
||||||
|
- name: Install Espressif QEMU
|
||||||
|
run: |
|
||||||
|
release=esp-develop-9.2.2-20260417
|
||||||
|
archive=qemu-xtensa-softmmu-${release//-/_}-x86_64-linux-gnu.tar.xz
|
||||||
|
curl -fL --retry 3 -o "$RUNNER_TEMP/$archive" \
|
||||||
|
"https://github.com/espressif/qemu/releases/download/$release/$archive"
|
||||||
|
tar -xJf "$RUNNER_TEMP/$archive" -C "$RUNNER_TEMP"
|
||||||
|
echo "$RUNNER_TEMP/qemu/bin" >> "$GITHUB_PATH"
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-linux-asserts-v1
|
key: llvm-source-22-linux-asserts-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -208,7 +279,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -219,10 +290,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-19-linux-asserts-v1
|
key: llvm-build-22-linux-asserts-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -235,13 +306,13 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-asserts-v1
|
key: binaryen-linux-asserts-v1
|
||||||
@@ -258,9 +329,11 @@ jobs:
|
|||||||
echo "$(pwd)/build" >> $GITHUB_PATH
|
echo "$(pwd)/build" >> $GITHUB_PATH
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
run: make tinygo-test
|
run: make tinygo-test
|
||||||
- run: make smoketest
|
- run: make smoketest-quick
|
||||||
- run: make wasmtest
|
- run: make wasmtest
|
||||||
- run: make tinygo-test-baremetal
|
- run: make tinygo-test-baremetal
|
||||||
|
- name: Check Go code formatting
|
||||||
|
run: make fmt-check lint
|
||||||
build-linux-cross:
|
build-linux-cross:
|
||||||
# Build ARM Linux binaries, ready for release.
|
# Build ARM Linux binaries, ready for release.
|
||||||
# This intentionally uses an older Linux image, so that we compile against
|
# This intentionally uses an older Linux image, so that we compile against
|
||||||
@@ -284,7 +357,7 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Get TinyGo version
|
- name: Get TinyGo version
|
||||||
id: version
|
id: version
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
@@ -296,15 +369,15 @@ jobs:
|
|||||||
g++-${{ matrix.toolchain }} \
|
g++-${{ matrix.toolchain }} \
|
||||||
libc6-dev-${{ matrix.libc }}-cross
|
libc6-dev-${{ matrix.libc }}-cross
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-linux-v1
|
key: llvm-source-22-linux-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -315,7 +388,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -326,10 +399,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-19-linux-${{ matrix.goarch }}-v3
|
key: llvm-build-22-linux-${{ matrix.goarch }}-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -344,16 +417,16 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-${{ matrix.goarch }}-v4
|
key: binaryen-linux-${{ matrix.goarch }}-v5
|
||||||
path: build/wasm-opt
|
path: build/wasm-opt
|
||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
@@ -370,9 +443,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
make CROSS=${{ matrix.toolchain }}
|
make CROSS=${{ matrix.toolchain }}
|
||||||
- name: Download amd64 release
|
- name: Download amd64 release
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
|
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
|
||||||
- name: Extract amd64 release
|
- name: Extract amd64 release
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build/release
|
mkdir -p build/release
|
||||||
@@ -384,12 +457,17 @@ jobs:
|
|||||||
- name: Create ${{ matrix.goarch }} release
|
- name: Create ${{ matrix.goarch }} release
|
||||||
run: |
|
run: |
|
||||||
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
|
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.tar.gz /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
cp -p build/release.deb /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||||
- name: Publish release artifact
|
- name: "Publish release artifact: tarball"
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: linux-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
|
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||||
path: |
|
path: /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
||||||
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
|
archive: false
|
||||||
/tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
|
- name: "Publish release artifact: Debian package"
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
|
||||||
|
path: /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
|
||||||
|
archive: false
|
||||||
|
|||||||
@@ -25,18 +25,18 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/llvm-19
|
tinygo/llvm-22
|
||||||
ghcr.io/${{ github.repository_owner }}/llvm-19
|
ghcr.io/${{ github.repository_owner }}/llvm-22
|
||||||
tags: |
|
tags: |
|
||||||
type=sha,format=long
|
type=sha,format=long
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
@@ -46,18 +46,18 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
- name: Log in to Github Container Registry
|
- name: Log in to Github Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
target: tinygo-llvm-build
|
target: tinygo-llvm-build
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/llvm-22:buildcache
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/llvm-22:buildcache,mode=max
|
||||||
|
|||||||
@@ -21,28 +21,28 @@ jobs:
|
|||||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||||
run: sudo apt-get remove llvm-18
|
run: sudo apt-get remove llvm-18
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Pull musl, bdwgc
|
- name: Pull musl, bdwgc
|
||||||
run: |
|
run: |
|
||||||
git submodule update --init lib/musl lib/bdwgc
|
git submodule update --init lib/musl lib/bdwgc
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-linux-nix-v1
|
key: llvm-source-22-linux-nix-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@v31
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
||||||
|
|||||||
@@ -19,25 +19,29 @@ jobs:
|
|||||||
- name: Add GOBIN to $PATH
|
- name: Add GOBIN to $PATH
|
||||||
run: |
|
run: |
|
||||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: '~1.25'
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: ./.github/workflows/sizediff-install-pkgs.sh
|
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-sizediff-v1
|
key: llvm-source-22-sizediff-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -17,21 +17,14 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
|
||||||
uses: al-cheb/configure-pagefile-action@v1.4
|
|
||||||
with:
|
|
||||||
minimum-size: 8GB
|
|
||||||
maximum-size: 24GB
|
|
||||||
disk-root: "C:"
|
|
||||||
- uses: MinoruSekine/setup-scoop@v4
|
- uses: MinoruSekine/setup-scoop@v4
|
||||||
with:
|
|
||||||
scoop_update: 'false'
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
scoop config use_external_7zip true
|
||||||
scoop install ninja binaryen
|
scoop install ninja binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
@@ -39,15 +32,15 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-19-windows-v1
|
key: llvm-source-22-windows-v3-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -58,7 +51,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save cached LLVM source
|
- name: Save cached LLVM source
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -69,10 +62,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore cached LLVM build
|
- name: Restore cached LLVM build
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-19-windows-v1
|
key: llvm-build-22-windows-v1-${{ hashFiles('llvm-version.txt') }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -86,20 +79,21 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save cached LLVM build
|
- name: Save cached LLVM build
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@v5
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Go cache
|
- name: Cache Go cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-windows-v3-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
C:/Users/runneradmin/AppData/Local/go-build
|
C:/Users/runneradmin/AppData/Local/go-build
|
||||||
C:/Users/runneradmin/go/pkg/mod
|
C:/Users/runneradmin/go/pkg/mod
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
|
scoop config use_external_7zip true
|
||||||
scoop install wasmtime@29.0.1
|
scoop install wasmtime@29.0.1
|
||||||
- name: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
@@ -114,80 +108,56 @@ jobs:
|
|||||||
working-directory: build/release
|
working-directory: build/release
|
||||||
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo
|
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
# Note: this release artifact is double-zipped, see:
|
uses: actions/upload-artifact@v7
|
||||||
# https://github.com/actions/upload-artifact/issues/39
|
|
||||||
# We can essentially pick one of these:
|
|
||||||
# - have a dobule-zipped artifact when downloaded from the UI
|
|
||||||
# - have a very slow artifact upload
|
|
||||||
# We're doing the former here, to keep artifact uploads fast.
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped-${{ steps.version.outputs.version }}
|
name: tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||||
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
|
||||||
|
archive: false
|
||||||
|
|
||||||
smoke-test-windows:
|
smoke-test-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
needs: build-windows
|
needs: build-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
|
||||||
uses: al-cheb/configure-pagefile-action@v1.4
|
|
||||||
with:
|
|
||||||
minimum-size: 8GB
|
|
||||||
maximum-size: 24GB
|
|
||||||
disk-root: "C:"
|
|
||||||
- uses: MinoruSekine/setup-scoop@v4
|
- uses: MinoruSekine/setup-scoop@v4
|
||||||
with:
|
|
||||||
scoop_update: 'false'
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
scoop config use_external_7zip true
|
||||||
scoop install binaryen
|
scoop install binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||||
path: build/
|
path: build/
|
||||||
- name: Unzip TinyGo build
|
# This build is already unzipped.
|
||||||
shell: bash
|
|
||||||
working-directory: build
|
|
||||||
run: 7z x tinygo*.windows-amd64.zip -r
|
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
run: make smoketest-quick TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||||
|
|
||||||
stdlib-test-windows:
|
stdlib-test-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
needs: build-windows
|
needs: build-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
|
||||||
uses: al-cheb/configure-pagefile-action@v1.4
|
|
||||||
with:
|
|
||||||
minimum-size: 8GB
|
|
||||||
maximum-size: 24GB
|
|
||||||
disk-root: "C:"
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||||
path: build/
|
path: build/
|
||||||
- name: Unzip TinyGo build
|
# This build is already unzipped.
|
||||||
shell: bash
|
|
||||||
working-directory: build
|
|
||||||
run: 7z x tinygo*.windows-amd64.zip -r
|
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||||
|
|
||||||
@@ -195,34 +165,24 @@ jobs:
|
|||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
needs: build-windows
|
needs: build-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
|
||||||
uses: al-cheb/configure-pagefile-action@v1.4
|
|
||||||
with:
|
|
||||||
minimum-size: 8GB
|
|
||||||
maximum-size: 24GB
|
|
||||||
disk-root: "C:"
|
|
||||||
- uses: MinoruSekine/setup-scoop@v4
|
- uses: MinoruSekine/setup-scoop@v4
|
||||||
with:
|
|
||||||
scoop_update: 'false'
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
scoop config use_external_7zip true
|
||||||
|
scoop install binaryen wasmtime@29.0.1
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: '1.25.0'
|
go-version: '1.27.0'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
|
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip
|
||||||
path: build/
|
path: build/
|
||||||
- name: Unzip TinyGo build
|
# This build is already unzipped.
|
||||||
shell: bash
|
|
||||||
working-directory: build
|
|
||||||
run: 7z x tinygo*.windows-amd64.zip -r
|
|
||||||
- name: Test stdlib packages on wasip1
|
- name: Test stdlib packages on wasip1
|
||||||
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ src/device/renesas/*.go
|
|||||||
src/device/renesas/*.s
|
src/device/renesas/*.s
|
||||||
src/device/rp/*.go
|
src/device/rp/*.go
|
||||||
src/device/rp/*.s
|
src/device/rp/*.s
|
||||||
|
src/device/py32/*.go
|
||||||
|
src/device/py32/*.s
|
||||||
./vendor
|
./vendor
|
||||||
llvm-build
|
llvm-build
|
||||||
llvm-project
|
llvm-project
|
||||||
|
|||||||
+5
-2
@@ -16,13 +16,13 @@
|
|||||||
url = https://github.com/WebAssembly/wasi-libc
|
url = https://github.com/WebAssembly/wasi-libc
|
||||||
[submodule "lib/picolibc"]
|
[submodule "lib/picolibc"]
|
||||||
path = lib/picolibc
|
path = lib/picolibc
|
||||||
url = https://github.com/keith-packard/picolibc.git
|
url = https://github.com/picolibc/picolibc.git
|
||||||
[submodule "lib/stm32-svd"]
|
[submodule "lib/stm32-svd"]
|
||||||
path = lib/stm32-svd
|
path = lib/stm32-svd
|
||||||
url = https://github.com/tinygo-org/stm32-svd
|
url = https://github.com/tinygo-org/stm32-svd
|
||||||
[submodule "lib/musl"]
|
[submodule "lib/musl"]
|
||||||
path = lib/musl
|
path = lib/musl
|
||||||
url = git://git.musl-libc.org/musl
|
url = https://github.com/tinygo-org/musl-libc.git
|
||||||
[submodule "lib/binaryen"]
|
[submodule "lib/binaryen"]
|
||||||
path = lib/binaryen
|
path = lib/binaryen
|
||||||
url = https://github.com/WebAssembly/binaryen.git
|
url = https://github.com/WebAssembly/binaryen.git
|
||||||
@@ -41,3 +41,6 @@
|
|||||||
[submodule "lib/bdwgc"]
|
[submodule "lib/bdwgc"]
|
||||||
path = lib/bdwgc
|
path = lib/bdwgc
|
||||||
url = https://github.com/ivmai/bdwgc.git
|
url = https://github.com/ivmai/bdwgc.git
|
||||||
|
[submodule "lib/py32-svd"]
|
||||||
|
path = lib/py32-svd
|
||||||
|
url = https://github.com/tinygo-org/py32-svd.git
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
## Communication style
|
||||||
|
Use ASD-STE-100 Simplified Technical English when you speak to the operator.
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
Use ASD-STE100 Simplified Technical English for content outside of the code like comments, PR description, PR title, github comments, and issues.
|
||||||
|
|
||||||
|
Generally omit extra redundant comments. If a comment is absolutely needed, always keep it brief, no more than 2 lines. Avoid extra use of colons, semicolons, and dashes. When comments are required due to some specific requirements, include a reference such as to a datasheet or other definitive source to explain why this is the case, including the chapter/section number, page number, and/or URL.
|
||||||
|
|
||||||
|
## No coding tool attributions
|
||||||
|
Never add "created by XXX" or any other attributions from coding tools to any PRs, issues, comments, or anywhere else.
|
||||||
+9
-2
@@ -30,7 +30,10 @@ on a different system like Mac.
|
|||||||
|
|
||||||
## Using GNU Make
|
## Using GNU Make
|
||||||
|
|
||||||
The static build of TinyGo is driven by GNUmakefile, which provides a help target for quick reference:
|
The static build of TinyGo is driven by GNUmakefile, which includes the topic
|
||||||
|
files in the `make/` directory (`config.mk`, `llvm.mk`, `gen-device.mk`,
|
||||||
|
`build.mk`, `test.mk`, `smoketest.mk`, `release.mk`, and `tools.mk`).
|
||||||
|
It provides a help target for quick reference:
|
||||||
|
|
||||||
% make help
|
% make help
|
||||||
clean Remove build directory
|
clean Remove build directory
|
||||||
@@ -50,6 +53,10 @@ the git repository). Then, inside the directory, download the LLVM source:
|
|||||||
|
|
||||||
make llvm-source
|
make llvm-source
|
||||||
|
|
||||||
|
The LLVM commit to use is pinned in `llvm-version.txt`. A change to that file
|
||||||
|
makes CI build LLVM again, because the file is part of the LLVM cache key. All
|
||||||
|
other changes reuse the cached LLVM build.
|
||||||
|
|
||||||
You can also store LLVM outside of the TinyGo root directory by setting the
|
You can also store LLVM outside of the TinyGo root directory by setting the
|
||||||
`LLVM_BUILDDIR`, `CLANG_SRC` and `LLD_SRC` make variables, but that is not
|
`LLVM_BUILDDIR`, `CLANG_SRC` and `LLD_SRC` make variables, but that is not
|
||||||
covered by this guide.
|
covered by this guide.
|
||||||
@@ -64,7 +71,7 @@ while producing binaries that are about as fast.
|
|||||||
export CC=clang
|
export CC=clang
|
||||||
export CXX=clang++
|
export CXX=clang++
|
||||||
|
|
||||||
The Makefile includes a default configuration that is good for most users. It
|
`make/config.mk` holds a default configuration that is good for most users. It
|
||||||
builds a release version of LLVM (optimized, no asserts) and includes all
|
builds a release version of LLVM (optimized, no asserts) and includes all
|
||||||
targets supported by TinyGo:
|
targets supported by TinyGo:
|
||||||
|
|
||||||
|
|||||||
+258
@@ -1,3 +1,261 @@
|
|||||||
|
0.41.1
|
||||||
|
---
|
||||||
|
* **machine**
|
||||||
|
- esp32c3: correct pin interrupt setup call that was overlooked from #5320
|
||||||
|
* **runtime**
|
||||||
|
- esp32s3: wait for TIMG0 update register to clear before reading timer registers
|
||||||
|
* **net**
|
||||||
|
- update net module to a version that is backwards compatible with Go 1.25.x to fix #5332
|
||||||
|
|
||||||
|
0.41.0
|
||||||
|
---
|
||||||
|
* **general**
|
||||||
|
- go.mod, compiler: bump minimum Go version to 1.23
|
||||||
|
- builder: support Go 1.26 now
|
||||||
|
- feat: add inheritable-only field to filter processor-level targets (#5270)
|
||||||
|
- feature: add esp32flash flash method for esp32s3/esp32c3/esp32/esp8266
|
||||||
|
- flashing: introduce flash method 'esp32jtag' for esp32c3/esp32s3 targets
|
||||||
|
- fashing: add "adb" flash method using Android Debug Bridge
|
||||||
|
- main: auto-use best available flashing options on esp32
|
||||||
|
- espflasher: update to espflasher 0.6.0
|
||||||
|
* **compiler**
|
||||||
|
- implement method-set based AssignableTo and Implements (#5304)
|
||||||
|
- simplify createObjectLayout
|
||||||
|
- implement copy directly
|
||||||
|
- fix min/max on floats by using intrinsics
|
||||||
|
- add element layout to sliceAppend
|
||||||
|
- add intrinsic for crypto/internal/constanttime.boolToUint8
|
||||||
|
- fix SyscallN handling for Windows on Go 1.26+
|
||||||
|
* **core**
|
||||||
|
- reflect: add TypeAssert
|
||||||
|
- reflect: fix TypeAssert for structs
|
||||||
|
- sync: add WaitGroup.Go
|
||||||
|
- os: add UserCacheDir and UserConfigDir
|
||||||
|
- os: implement Lchown function for changing file ownership (#5161)
|
||||||
|
- testing: add b.ReportMetric()
|
||||||
|
- errors: add errors package to passing list
|
||||||
|
- internal/gclayout: use correct lengths
|
||||||
|
- internal/abi: add EscapeNonString, EscapeToResultNonString
|
||||||
|
- internal, runtime: add internal fips bool
|
||||||
|
- internal/itoa: resurrect from Go stdlib
|
||||||
|
- internal/syscall/unix: implement GetRandom for WASI targets
|
||||||
|
- interp: make ptrtoint size mismatch a recoverable error
|
||||||
|
- loader: support "all:" prefix in //go:embed patterns
|
||||||
|
- loader, crypto/internal/entropy: fix 32 MiB RAM overflow on microcontrollers
|
||||||
|
- loader, unicode/utf8: fix hiBits overflow on 16-bit AVR targets
|
||||||
|
- builder: order embedded files deterministically
|
||||||
|
- builder: fix panic in findPackagePath when using -size short
|
||||||
|
- builder: fix SIGSEGV when stripping duplicate function definitions
|
||||||
|
- builder, runtime: fix duplicate symbol error with Go 1.26+ on Windows
|
||||||
|
- builder: use target-specific -march for RISC-V library compilation
|
||||||
|
- transform: modify output format from the -print-allocs flag to match the go coverage tool format
|
||||||
|
- cgo: allow for changes to 'short-enums' flag
|
||||||
|
- wasm: add more stubbed file operations
|
||||||
|
* **machine**
|
||||||
|
- esp32c3: implement BlockDevice for esp32c3 flash
|
||||||
|
- esp32c3: clear GPIO STATUS before dispatching pin callbacks
|
||||||
|
- esp32c3: implement USBDevice using interrupts
|
||||||
|
- esp32c3: add Enable stub for QEMU test target
|
||||||
|
- esp32c3: fix interrupt disable handling
|
||||||
|
- esp32c3: clear MCAUSE after handling interrupt
|
||||||
|
- esp32c3: map missing IRAM sections in linker script
|
||||||
|
- esp32c3: fix to use PROVIDE() for WiFi/BLE ROM function addresses to allow blob overrides
|
||||||
|
- esp32c3: add WiFi/BLE ROM linker support
|
||||||
|
- esp32c3: use TIMG0 alarm interrupt for sleepTicks instead of busy-waiting
|
||||||
|
- esp32s3: implement Pin.SetInterrupt for GPIO pin change interrupts
|
||||||
|
- esp32s3: use edge-triggered CPU interrupt for GPIO pin interrupts
|
||||||
|
- esp32s3: add interrupt support (#5244)
|
||||||
|
- esp32s3: switch USB implementation to use interrupts instead of polling
|
||||||
|
- esp32s3: replace inline ISR with full interrupt vector handler
|
||||||
|
- esp32s3: use TIMG0 alarm interrupt for sleepTicks
|
||||||
|
- esp32s3: improve exception handlers, add procPin/procUnpin, and linker wrap flags
|
||||||
|
- esp32s3: fix Xtensa register window spill using recursive call4 in task switching
|
||||||
|
- esp32s3: update linker script and boot assembly for multi-page flash XIP mapping
|
||||||
|
- esp32s3: add flash XIP boot assembly with cache/MMU init
|
||||||
|
- esp32s3: implement SPI (#5169)
|
||||||
|
- esp32s3: implement I2C interface (#5210)
|
||||||
|
- esp32s3: implement RNG based on onboard hardware random number generator
|
||||||
|
- esp32s3,esp32c3: add USB serial support
|
||||||
|
- esp32s3,esp32c3: add txStalled flag to skip USB serial spin when no host
|
||||||
|
- esp32s3,esp32c3: make USB Serial/JTAG writes non-blocking when FIFO is full
|
||||||
|
- esp32c3/esp32s3: refactor ADC implementation to reduce code duplication
|
||||||
|
- esp32s3,esp32c3: add ADC support (#5231)
|
||||||
|
- esp32s3,esp32c3: add PWM support (#5215)
|
||||||
|
- esp32c3/esp32s3: refactoring and corrections for SPI implementation
|
||||||
|
- esp32xx: add WASM simulator support for ESP32-C3/ESP32-S3 targets
|
||||||
|
- esp32: default SPI CS pin to NoPin when unset
|
||||||
|
- esp: fix tinygo_scanCurrentStack to spill register windows
|
||||||
|
- stm32: fix UART interrupt storm caused by uncleared overrun error
|
||||||
|
- stm32: fix PWM problem due to register shifting
|
||||||
|
- stm32: add STM32U585 target definition and runtime
|
||||||
|
- stm32: add STM32U5 GPIO, pin interrupts, and UART support
|
||||||
|
- stm32: add STM32U5 I2C support
|
||||||
|
- stm32: add STM32U5 SPI support
|
||||||
|
- stm32u585: implement ADC
|
||||||
|
- stm32g0b1: add support (#5150)
|
||||||
|
- stm32g0b1: add Watchdog + ADC support (#5158)
|
||||||
|
- stm32l0: add flash support
|
||||||
|
- stm32l0x1,l0x2: TIM: adapt to 32-bit register access
|
||||||
|
- stm32g0: sync with updated stm32 device files
|
||||||
|
- attiny85: add USI-based SPI support (#5181)
|
||||||
|
- attiny85: add PWM support (#5171)
|
||||||
|
- rp: add Close function to UART to allow for removing all system resources/power usage
|
||||||
|
- rp: use blockReset() and unresetBlockWait() helper functions for peripheral reset/unreset
|
||||||
|
- rp2: prevent PWM Period method overflow
|
||||||
|
- rp2: add per-byte timeout budget for I2C (#5189)
|
||||||
|
- feather-m0: export UART0 and pins
|
||||||
|
- usb/cdc: better ring buffer implementation (#5209)
|
||||||
|
- fix: replace ! with ~ for register mask
|
||||||
|
- atmega328p_simulator: add correct build tag for arduino_uno after renaming
|
||||||
|
* **net**
|
||||||
|
- update to Go 1.26.2 net package with UDP and JS improvements
|
||||||
|
* **runtime**
|
||||||
|
- implement fminimum/fmaximum
|
||||||
|
- make timeoffset atomic
|
||||||
|
- add MemStats.HeapObjects
|
||||||
|
- handle GODEBUG on wasip2 (#5312)
|
||||||
|
- fix Darwin syscall return value truncation and lost arguments
|
||||||
|
- add syscall.runtimeClearenv for Go 1.26
|
||||||
|
- split syscall functions for darwin changes for 1.26
|
||||||
|
- fix: init heap before random number seed on wasm platforms
|
||||||
|
* **targets**
|
||||||
|
- add esp32s3-supermini board target
|
||||||
|
- add support for Seeedstudio Xiao-RP2350 board
|
||||||
|
- add Shrike Lite board (#5170)
|
||||||
|
- rename arduino target to arduino-uno
|
||||||
|
- add pins/setup for Arduino UNO Q board QWIIC connector
|
||||||
|
- correct pin mapping for Arduino UNO Q STM32 MCU
|
||||||
|
- add stm32u585 openocd commands for flashing on Arduino UNO Q
|
||||||
|
- correct name/tag use for esp32s3-wroom1 board
|
||||||
|
- modify esp32, esp32s3, esp32c3, & esp8266 targets to use built-in esp32flash
|
||||||
|
- switch rp2040/rp2350 to default to tasks scheduler
|
||||||
|
- change default stack size for esp32c3 and esp32s3 to 8196
|
||||||
|
- swan: rename group build tag stm32l4x5 => stm32l4y5 to avoid clash with stm32 device name
|
||||||
|
- nucleo-f722ze: add build-tag stm32f722
|
||||||
|
- fix: set stm32u5x clock rate to default
|
||||||
|
- create generic targets for esp32 boards
|
||||||
|
* **libs**
|
||||||
|
- stm32-svd: update submodule
|
||||||
|
- update tools/gen-device-svd for recent changes to stm32-svd file structure (#5212)
|
||||||
|
* **build/test**
|
||||||
|
- build: use Golang 1.26 for all builds
|
||||||
|
- build: actually use the version of llvm we are supposed to be testing by using brew link command
|
||||||
|
- build: let scoop run updates to resolve binaryen install failures on Windows
|
||||||
|
- ci: don't double zip release artifacts
|
||||||
|
- test: increase default timeout from 1 to 2 minutes to prevent spurious CI fails
|
||||||
|
- testdata: fix flaky timers test by draining ticker channel after Stop
|
||||||
|
- testdata: more corpus entries (#5182)
|
||||||
|
- add soypat/lneto to corpus
|
||||||
|
- GNUmakefile: add context and expvar to stdlib tests
|
||||||
|
- GNUmakefile: add encoding/xml to stdlib tests on Linux
|
||||||
|
- main (test): skip mipsle test if the emulator is missing
|
||||||
|
- make: remove machine without board from smoketest
|
||||||
|
- stm32: add Arduino UNO Q to smoketests
|
||||||
|
- flake: bump to nixpkgs 25.11
|
||||||
|
- chore: update all github actions to latest versions
|
||||||
|
- fix: use external 7zip for scoop installs on Windows
|
||||||
|
|
||||||
|
0.40.1
|
||||||
|
---
|
||||||
|
* **machine**
|
||||||
|
- nrf: fix flash writes when SoftDevice is enabled
|
||||||
|
* **runtime**
|
||||||
|
- runtime: avoid fixed math/rand sequence on RP2040/RP2350 (#5124)
|
||||||
|
- runtime: add calls to initRand() during run() for all schedulers
|
||||||
|
- runtime: call initRand() before initHeap() during initialization
|
||||||
|
- runtime: use rand_hwrng hardwareRand for RP2040/RP2350 (#5135)
|
||||||
|
* **libs**
|
||||||
|
- picolibc: use updated location for git repo
|
||||||
|
|
||||||
|
0.40.0
|
||||||
|
---
|
||||||
|
* **general**
|
||||||
|
- all: add full LLVM 20 support
|
||||||
|
- core: feat: enable //go:linkname pragma for globals
|
||||||
|
- core: feature: Add flag to ignore go compatibility matrix (#5078)
|
||||||
|
- chore: update version for 0.40 development cycle
|
||||||
|
* **compiler**
|
||||||
|
- emit an error when the actual arch doesn't match GOARCH
|
||||||
|
- mark string parameters as readonly
|
||||||
|
- use Tarjan's SCC algorithm to detect loops for defer
|
||||||
|
- lower "large stack" limit to 16kb
|
||||||
|
* **core**
|
||||||
|
- shrink bdwgc library
|
||||||
|
- Fix linker errors for runtime.vgetrandom and crypto/internal/sysrand.fatal
|
||||||
|
- fix: add TryLock to sync.RWMutex
|
||||||
|
- fix: correct linter issues exposed by the fix in #4679
|
||||||
|
- fix: don't hardcode success return state
|
||||||
|
- fix: expand RTT debugger compatibility
|
||||||
|
- internal/task (threads): save stack bounds instead of scanning under a lock
|
||||||
|
- internal/task: create detached threads and fix error handling
|
||||||
|
- interp: better errors when debugging interp
|
||||||
|
- transform (gc): create stack slots in callers of external functions
|
||||||
|
- internal/task: prevent semaphore resource leak for threads scheduler
|
||||||
|
* **machine**
|
||||||
|
- cortexm: optimize code size for the HardFault_Handler
|
||||||
|
- fe310: add I2C pins for the HiFive1b
|
||||||
|
- clarify WriteAt semantics of BlockDevice
|
||||||
|
- fix deprecated AsmFull comment (#5005)
|
||||||
|
- make sure DMA buffers do not escape unnecessarily
|
||||||
|
- only enable USB-CDC when needed
|
||||||
|
- use larger SPI MAXCNT on nrf52833 and nrf52840
|
||||||
|
- fix: update m.queuedBytes when clamping output to avoid corrupting sentBytes
|
||||||
|
- fix: use int64 in ReadTemperature to avoid overflow
|
||||||
|
- fix(rp2): disable DBGPAUSE on startup
|
||||||
|
- fix(rp2): possible integer overflow while computing factors for SPI baudrate
|
||||||
|
- fix(rp2): reset spinlocks at startup
|
||||||
|
- fix(rp2): switch spinlock busy loop to wfe
|
||||||
|
- fix(rp2): use side-effect-free spinlocks
|
||||||
|
- nrf: add ADC_VDDH which is an ADC pin for VDDH
|
||||||
|
- nrf: don't block SPI transfer
|
||||||
|
- nrf: don't set PSELN, it's ignored in single ended mode anyway
|
||||||
|
- nrf: fix typo in ADC configuration
|
||||||
|
- nrf: refactor SoftDevice enabled check
|
||||||
|
- nrf: rename pwmPin to adcPin
|
||||||
|
- nrf: support flash operations while the SoftDevice is enabled
|
||||||
|
- rp2040: allow writing to the UART inside interrupts
|
||||||
|
- machine,nrf528: stop the bus only once on I2C bus error and ensures the first error is returned
|
||||||
|
* **net**
|
||||||
|
- update submodule to latest commits
|
||||||
|
* **runtime**
|
||||||
|
- (avr): fix infinite longjmp loop if stack is aligned to 256 bytes
|
||||||
|
- (gc_blocks.go): clear full size of allocation
|
||||||
|
- (gc_blocks.go): make sweep branchless
|
||||||
|
- (gc_blocks.go): simplify scanning logic
|
||||||
|
- (gc_blocks.go): use a linked stack to scan marked objects
|
||||||
|
- (gc_blocks.go): use best-fit allocation
|
||||||
|
- (gc_boehm.go): fix world already stopped check
|
||||||
|
- (wasm): scan the system stack
|
||||||
|
- fix sleep duration for long sleeps
|
||||||
|
- remove copied code for nrf52840
|
||||||
|
- src/syscall: update src buffer after write
|
||||||
|
- wasm: fix C realloc and optimize it a bit
|
||||||
|
* **targets**
|
||||||
|
- add xiao-esp32s3 board target
|
||||||
|
- Add ESP32-S3 support (#5091)
|
||||||
|
- Added Gopher ARCADE board
|
||||||
|
- Create "pico2-ice" target board (#5062)
|
||||||
|
* **build/test**
|
||||||
|
- Add testing.T.Context() and testing.B.Context()
|
||||||
|
- create separate go.mod file for testing dependencies for wasm tests that use Chromium headless browser to avoid use of older incompatible version.
|
||||||
|
- go back to normal scheduler instead of tasks scheduler for macos CI
|
||||||
|
- update CI to use Go 1.25.5
|
||||||
|
- update macOS GH actions builds to handle sunset of macOS 13
|
||||||
|
- use task scheduler on macOS builds to avoid test race condition lockups
|
||||||
|
- update all CI builds to use latest stable Go release. Also update some of the actions to their latest releases.
|
||||||
|
- update GH actions builds to use Go 1.25.4
|
||||||
|
- uninstall cmake before install
|
||||||
|
- fix: point the submodule for musl-lib to a mirror in the TinyGo GitHub org
|
||||||
|
- fix: remove macOS 15 from CI build matrix (conflicts with macOS 14 build)
|
||||||
|
- fix: separate host expected bytes from device intended bytes
|
||||||
|
- fix/typo: makeESPFirmwareImage
|
||||||
|
- make: GNUmakefile: shrink TinyGo binaries on Linux
|
||||||
|
- move the directory list into a variable
|
||||||
|
- several improvements to the macOS GH actions build
|
||||||
|
- Fix for #4678: top-level 'make lint' wasn't working
|
||||||
|
- fix: increase the timeout for chromedp to connect to the headless browser used for running the wasm tests.
|
||||||
|
- testdata: some more packages for the test corpus
|
||||||
|
|
||||||
0.39.0
|
0.39.0
|
||||||
---
|
---
|
||||||
* **general**
|
* **general**
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,5 @@
|
|||||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||||
FROM golang:1.25 AS tinygo-llvm
|
FROM golang:1.27 AS tinygo-llvm
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
|
||||||
@@ -10,6 +10,8 @@ RUN apt-get update && \
|
|||||||
/tmp/*
|
/tmp/*
|
||||||
|
|
||||||
COPY ./GNUmakefile /tinygo/GNUmakefile
|
COPY ./GNUmakefile /tinygo/GNUmakefile
|
||||||
|
COPY ./make /tinygo/make
|
||||||
|
COPY ./llvm-version.txt /tinygo/llvm-version.txt
|
||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
make llvm-source
|
make llvm-source
|
||||||
@@ -33,7 +35,7 @@ RUN cd /tinygo/ && \
|
|||||||
|
|
||||||
# tinygo-compiler copies the compiler build over to a base Go container (without
|
# tinygo-compiler copies the compiler build over to a base Go container (without
|
||||||
# all the build tools etc).
|
# all the build tools etc).
|
||||||
FROM golang:1.25 AS tinygo-compiler
|
FROM golang:1.27 AS tinygo-compiler
|
||||||
|
|
||||||
# Copy tinygo build.
|
# Copy tinygo build.
|
||||||
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
|
||||||
|
|||||||
+10
-1123
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
|||||||
Copyright (c) 2018-2025 The TinyGo Authors. All rights reserved.
|
Copyright (c) 2018-2026 The TinyGo Authors. All rights reserved.
|
||||||
|
|
||||||
TinyGo includes portions of the Go standard library.
|
TinyGo includes portions of the Go standard library.
|
||||||
Copyright (c) 2009-2024 The Go Authors. All rights reserved.
|
Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
See https://github.com/golang/go/blob/master/LICENSE for license information.
|
||||||
|
|
||||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
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.
|
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
# TinyGo - Go compiler for small places
|
# TinyGo - Go compiler for small places
|
||||||
|
|
||||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml)
|
||||||
|
|
||||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
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.
|
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> You can help TinyGo with a financial contribution using OpenCollective. Please see https://opencollective.com/tinygo for more information. Thank you!
|
||||||
|
|
||||||
## Embedded
|
## Embedded
|
||||||
|
|
||||||
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
||||||
@@ -31,10 +34,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above program can be compiled and run without modification on an Arduino Uno, an Adafruit ItsyBitsy M0, or any of the supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
The above program can be compiled and run without modification on an [Arduino Uno](https://tinygo.org/docs/reference/microcontrollers/boards/arduino-uno), an [Adafruit Circuit Playground Express](https://tinygo.org/docs/reference/microcontrollers/featured/circuitplay-express), a [Seeed Studio XIAO-ESP32S3](https://tinygo.org/docs/reference/microcontrollers/featured/xiao-esp32s3) or any of the many supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
tinygo flash -target arduino examples/blinky1
|
tinygo flash -target arduino-uno examples/blinky1
|
||||||
```
|
```
|
||||||
|
|
||||||
## WebAssembly
|
## WebAssembly
|
||||||
@@ -63,7 +66,7 @@ tinygo build -buildmode=c-shared -o add.wasm -target=wasip1 add.go
|
|||||||
You can also use the same syntax as Go 1.24+:
|
You can also use the same syntax as Go 1.24+:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
GOARCH=wasip1 GOOS=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
GOOS=wasip1 GOARCH=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -74,7 +77,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
|||||||
|
|
||||||
### Embedded
|
### Embedded
|
||||||
|
|
||||||
You can compile TinyGo programs for over 94 different microcontroller boards.
|
You can compile TinyGo programs for over 150 different microcontroller boards.
|
||||||
|
|
||||||
For more information, please see https://tinygo.org/docs/reference/microcontrollers/
|
For more information, please see https://tinygo.org/docs/reference/microcontrollers/
|
||||||
|
|
||||||
@@ -139,7 +142,7 @@ Non-goals:
|
|||||||
|
|
||||||
## Why this project exists
|
## Why this project exists
|
||||||
|
|
||||||
> We never expected Go to be an embedded language and so its got serious problems...
|
> We never expected Go to be an embedded language, and so it’s got serious problems...
|
||||||
|
|
||||||
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -30,7 +30,10 @@ var BoehmGC = Library{
|
|||||||
// Use a minimal environment.
|
// Use a minimal environment.
|
||||||
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
||||||
"-DDONT_USE_ATEXIT",
|
"-DDONT_USE_ATEXIT",
|
||||||
"-DNO_GETENV",
|
"-DNO_GETENV", // smaller binary, more predictable configuration
|
||||||
|
"-DNO_CLOCK", // don't use system clock
|
||||||
|
"-DNO_DEBUGGING", // reduce code size
|
||||||
|
"-DGC_NO_FINALIZATION", // finalization is not used at the moment
|
||||||
|
|
||||||
// Special flag to work around the lack of __data_start in ld.lld.
|
// Special flag to work around the lack of __data_start in ld.lld.
|
||||||
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
||||||
@@ -39,6 +42,8 @@ var BoehmGC = Library{
|
|||||||
|
|
||||||
// Do not scan the stack. We have our own mechanism to do this.
|
// Do not scan the stack. We have our own mechanism to do this.
|
||||||
"-DSTACK_NOT_SCANNED",
|
"-DSTACK_NOT_SCANNED",
|
||||||
|
"-DNO_PROC_STAT", // we scan the stack manually (don't read /proc/self/stat on Linux)
|
||||||
|
"-DSTACKBOTTOM=0", // dummy value, we scan the stack manually
|
||||||
|
|
||||||
// Assertions can be enabled while debugging GC issues.
|
// Assertions can be enabled while debugging GC issues.
|
||||||
//"-DGC_ASSERTIONS",
|
//"-DGC_ASSERTIONS",
|
||||||
@@ -63,7 +68,6 @@ var BoehmGC = Library{
|
|||||||
"blacklst.c",
|
"blacklst.c",
|
||||||
"dbg_mlc.c",
|
"dbg_mlc.c",
|
||||||
"dyn_load.c",
|
"dyn_load.c",
|
||||||
"finalize.c",
|
|
||||||
"headers.c",
|
"headers.c",
|
||||||
"mach_dep.c",
|
"mach_dep.c",
|
||||||
"malloc.c",
|
"malloc.c",
|
||||||
@@ -71,7 +75,6 @@ var BoehmGC = Library{
|
|||||||
"mark_rts.c",
|
"mark_rts.c",
|
||||||
"misc.c",
|
"misc.c",
|
||||||
"new_hblk.c",
|
"new_hblk.c",
|
||||||
"obj_map.c",
|
|
||||||
"os_dep.c",
|
"os_dep.c",
|
||||||
"reclaim.c",
|
"reclaim.c",
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-20
@@ -14,14 +14,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
|
"maps"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/gofrs/flock"
|
"github.com/gofrs/flock"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -136,9 +139,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if _, ok := globalValues[pkgPath]; !ok {
|
if _, ok := globalValues[pkgPath]; !ok {
|
||||||
globalValues[pkgPath] = map[string]string{}
|
globalValues[pkgPath] = map[string]string{}
|
||||||
}
|
}
|
||||||
for k, v := range vals {
|
maps.Copy(globalValues[pkgPath], vals)
|
||||||
globalValues[pkgPath][k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a libc dependency.
|
// Check for a libc dependency.
|
||||||
@@ -254,9 +255,22 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip default initializers for -X globals from the type info before
|
||||||
|
// building SSA. This prevents go/ssa from emitting init stores for them,
|
||||||
|
// so that makeGlobalsModule can supply the correct values at final link
|
||||||
|
// time without any runtime init overwriting them. The -X values themselves
|
||||||
|
// are kept out of the per-package build cache; only the variable names
|
||||||
|
// appear in the cache key.
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
for name := range globalValues[pkg.Pkg.Path()] {
|
||||||
|
pkg.StripVarInitializer(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// program so it's pretty fast and doesn't need to be parallelized.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
|
buildProgram := sync.OnceFunc(program.Build)
|
||||||
|
|
||||||
// Add jobs to compile each package.
|
// Add jobs to compile each package.
|
||||||
// Packages that have a cache hit will not be compiled again.
|
// Packages that have a cache hit will not be compiled again.
|
||||||
@@ -265,7 +279,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
var embedFileObjects []*compileJob
|
var embedFileObjects []*compileJob
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg // necessary to avoid a race condition
|
|
||||||
|
|
||||||
var undefinedGlobals []string
|
var undefinedGlobals []string
|
||||||
for name := range globalValues[pkg.Pkg.Path()] {
|
for name := range globalValues[pkg.Pkg.Path()] {
|
||||||
@@ -281,9 +294,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
allFiles[file.Name] = append(allFiles[file.Name], file)
|
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, files := range allFiles {
|
// Sort embedded files by name to maintain output determinism.
|
||||||
name := name
|
embedNames := make([]string, 0, len(allFiles))
|
||||||
files := files
|
for _, files := range allFiles {
|
||||||
|
embedNames = append(embedNames, files[0].Name)
|
||||||
|
}
|
||||||
|
slices.Sort(embedNames)
|
||||||
|
for _, name := range embedNames {
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "make object file for " + name,
|
description: "make object file for " + name,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
@@ -298,7 +315,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
sum := sha256.Sum256(data)
|
sum := sha256.Sum256(data)
|
||||||
hexSum := hex.EncodeToString(sum[:16])
|
hexSum := hex.EncodeToString(sum[:16])
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range allFiles[name] {
|
||||||
file.Size = uint64(len(data))
|
file.Size = uint64(len(data))
|
||||||
file.Hash = hexSum
|
file.Hash = hexSum
|
||||||
if file.NeedsData {
|
if file.NeedsData {
|
||||||
@@ -383,8 +400,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile AST to IR. The compiler.CompilePackage function will
|
// SSA package builds may run concurrently, but the resulting
|
||||||
// build the SSA as needed.
|
// functions cannot be inspected until all builds have finished:
|
||||||
|
// generic instances and wrappers can be shared across packages.
|
||||||
|
// Build the whole program once before compiling any package.
|
||||||
|
buildProgram()
|
||||||
|
|
||||||
|
// Compile AST to IR.
|
||||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
defer mod.Context().Dispose()
|
defer mod.Context().Dispose()
|
||||||
defer mod.Dispose()
|
defer mod.Dispose()
|
||||||
@@ -472,7 +494,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if pkgInit.IsNil() {
|
if pkgInit.IsNil() {
|
||||||
panic("init not found for " + pkg.Pkg.Path())
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
}
|
}
|
||||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -541,6 +563,23 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||||
}
|
}
|
||||||
|
// Resolve duplicate function definitions before linking.
|
||||||
|
// This can happen when a newer Go version adds a function
|
||||||
|
// body in a standard library package that was previously
|
||||||
|
// just a declaration provided by //go:linkname from the
|
||||||
|
// runtime. In that case, keep the existing (runtime)
|
||||||
|
// definition by weakening the new one's linkage so the
|
||||||
|
// LLVM linker discards it in favor of the existing one.
|
||||||
|
for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
|
if fn.IsDeclaration() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existing := mod.NamedFunction(fn.Name())
|
||||||
|
if existing.IsNil() || existing.IsDeclaration() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
}
|
||||||
err = llvm.LinkModules(mod, pkgMod)
|
err = llvm.LinkModules(mod, pkgMod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to link module: %w", err)
|
return fmt.Errorf("failed to link module: %w", err)
|
||||||
@@ -741,7 +780,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
// TODO: do this as part of building the package to be able to link the
|
// TODO: do this as part of building the package to be able to link the
|
||||||
// bitcode files together.
|
// bitcode files together.
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg
|
|
||||||
for _, filename := range pkg.CFiles {
|
for _, filename := range pkg.CFiles {
|
||||||
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -808,22 +846,25 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
|
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
|
||||||
if config.GOOS() == "windows" {
|
switch config.LinkerFlavor() {
|
||||||
// Options for the MinGW wrapper for the lld COFF linker.
|
case "coff":
|
||||||
|
// Options for driving ld.lld in PE/COFF mode.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
} else if config.GOOS() == "darwin" {
|
case "darwin":
|
||||||
// Options for the ld64-compatible lld linker.
|
// Options for the ld64-compatible lld linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"--lto-O"+strconv.Itoa(speedLevel),
|
"--lto-O"+strconv.Itoa(speedLevel),
|
||||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
} else {
|
case "gnu":
|
||||||
// Options for the ELF linker.
|
// Options for the ELF linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"--lto-O"+strconv.Itoa(speedLevel),
|
"--lto-O"+strconv.Itoa(speedLevel),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
)
|
)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown linker flavor: %s", config.LinkerFlavor())
|
||||||
}
|
}
|
||||||
if config.CodeModel() != "default" {
|
if config.CodeModel() != "default" {
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
@@ -880,7 +921,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run wasm-opt for wasm binaries
|
// Run wasm-opt for wasm binaries
|
||||||
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
if arch, _, _ := strings.Cut(config.Triple(), "-"); arch == "wasm32" {
|
||||||
optLevel, _, _ := config.OptLevel()
|
optLevel, _, _ := config.OptLevel()
|
||||||
opt := "-" + optLevel
|
opt := "-" + optLevel
|
||||||
|
|
||||||
@@ -1042,11 +1083,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp32c6", "esp8266":
|
||||||
// Special format for the ESP family of chips (parsed by the ROM
|
// Special format for the ESP family of chips (parsed by the ROM
|
||||||
// bootloader).
|
// bootloader).
|
||||||
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||||
err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
|
err := makeESPFirmwareImage(result.Executable, result.Binary, outputBinaryFormat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
@@ -1174,7 +1215,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
|||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxLoopIterations, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1318,6 +1359,14 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
|||||||
}
|
}
|
||||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||||
|
|
||||||
|
// Account for the bytes that tinygo_swapTask pushes onto the goroutine stack
|
||||||
|
// on every context switch. The static analysis correctly traces Go calls,
|
||||||
|
// but it cannot see into the assembly-level register push.
|
||||||
|
var contextSwitchOverhead uint64
|
||||||
|
if swapFuncs, ok := functions["tinygo_swapTask"]; ok && len(swapFuncs) == 1 {
|
||||||
|
contextSwitchOverhead = swapFuncs[0].FrameSize
|
||||||
|
}
|
||||||
|
|
||||||
sizes := make(map[string]functionStackSize)
|
sizes := make(map[string]functionStackSize)
|
||||||
|
|
||||||
// Add the reset handler function, for convenience. The reset handler runs
|
// Add the reset handler function, for convenience. The reset handler runs
|
||||||
@@ -1366,6 +1415,12 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
|||||||
// overflow will occur even before the goroutine is started.
|
// overflow will occur even before the goroutine is started.
|
||||||
stackSize = baseStackSize
|
stackSize = baseStackSize
|
||||||
}
|
}
|
||||||
|
if stackSizeType == stacksize.Bounded {
|
||||||
|
// Add the overhead of context switching. This is needed because the
|
||||||
|
// context switch (tinygo_swapTask) pushes callee-saved registers
|
||||||
|
// onto the current stack, which is not seen by the static analysis.
|
||||||
|
stackSize += contextSwitchOverhead
|
||||||
|
}
|
||||||
sizes[name] = functionStackSize{
|
sizes[name] = functionStackSize{
|
||||||
stackSize: stackSize,
|
stackSize: stackSize,
|
||||||
stackSizeType: stackSizeType,
|
stackSizeType: stackSizeType,
|
||||||
|
|||||||
+63
-4
@@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -28,12 +29,15 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
"cortex-m4",
|
"cortex-m4",
|
||||||
"cortex-m7",
|
"cortex-m7",
|
||||||
"esp32c3",
|
"esp32c3",
|
||||||
|
"esp32c6",
|
||||||
|
"esp32s3",
|
||||||
"fe310",
|
"fe310",
|
||||||
"gameboy-advance",
|
"gameboy-advance",
|
||||||
"k210",
|
"k210",
|
||||||
"nintendoswitch",
|
"nintendoswitch",
|
||||||
"riscv-qemu",
|
"riscv-qemu",
|
||||||
"tkey",
|
"tkey",
|
||||||
|
"uefi-amd64",
|
||||||
"wasip1",
|
"wasip1",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasm",
|
"wasm",
|
||||||
@@ -45,7 +49,6 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
targetNames = append(targetNames, "esp32", "esp8266")
|
targetNames = append(targetNames, "esp32", "esp8266")
|
||||||
}
|
}
|
||||||
for _, targetName := range targetNames {
|
for _, targetName := range targetNames {
|
||||||
targetName := targetName
|
|
||||||
t.Run(targetName, func(t *testing.T) {
|
t.Run(targetName, func(t *testing.T) {
|
||||||
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
||||||
})
|
})
|
||||||
@@ -127,8 +130,10 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|||||||
defer mod.Dispose()
|
defer mod.Dispose()
|
||||||
|
|
||||||
// Check whether the LLVM target matches.
|
// Check whether the LLVM target matches.
|
||||||
if mod.Target() != config.Triple() {
|
// Use ClangTriple since LLVM 22 normalizes wasm32-unknown-wasi to wasip1.
|
||||||
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
expectedTriple := compileopts.ClangTriple(config.Triple())
|
||||||
|
if mod.Target() != expectedTriple {
|
||||||
|
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", expectedTriple, mod.Target())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the "target-cpu" and "target-features" string attribute of the add
|
// Check the "target-cpu" and "target-features" string attribute of the add
|
||||||
@@ -152,11 +157,65 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|||||||
// The reason is that Debian has patched Clang in a way that
|
// The reason is that Debian has patched Clang in a way that
|
||||||
// modifies the LLVM features string, changing lots of FPU/float
|
// modifies the LLVM features string, changing lots of FPU/float
|
||||||
// related flags. We want to test vanilla Clang, not Debian Clang.
|
// related flags. We want to test vanilla Clang, not Debian Clang.
|
||||||
t.Errorf("target has LLVM features\n\t%#v\nbut Clang makes it\n\t%#v", config.Features(), features)
|
//
|
||||||
|
// Rather than requiring an exact match (which breaks across LLVM
|
||||||
|
// versions as new features are added), check that every feature
|
||||||
|
// TinyGo specifies is consistent with what Clang produces:
|
||||||
|
// - A "+feature" in TinyGo must appear in Clang's output.
|
||||||
|
// - A "-feature" in TinyGo must not be "+feature" in Clang's output.
|
||||||
|
checkFeatureFlags(t, config.Features(), features)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkFeatureFlags verifies that all features specified by TinyGo's target
|
||||||
|
// configuration are consistent with Clang's output. This allows Clang to add
|
||||||
|
// new features across LLVM versions without breaking the test.
|
||||||
|
func checkFeatureFlags(t *testing.T, targetFeatures, clangFeatures string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// Build a set of Clang's features for fast lookup.
|
||||||
|
clangSet := make(map[string]bool) // feature name -> enabled
|
||||||
|
for _, f := range strings.Split(clangFeatures, ",") {
|
||||||
|
f = strings.TrimSpace(f)
|
||||||
|
if len(f) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
enabled := f[0] == '+'
|
||||||
|
name := f[1:]
|
||||||
|
clangSet[name] = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each feature that TinyGo specifies.
|
||||||
|
var missing, conflicts []string
|
||||||
|
for _, f := range strings.Split(targetFeatures, ",") {
|
||||||
|
f = strings.TrimSpace(f)
|
||||||
|
if len(f) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wantEnabled := f[0] == '+'
|
||||||
|
name := f[1:]
|
||||||
|
|
||||||
|
clangEnabled, inClang := clangSet[name]
|
||||||
|
if wantEnabled && (!inClang || !clangEnabled) {
|
||||||
|
// TinyGo requires +feature but Clang doesn't enable it.
|
||||||
|
missing = append(missing, f)
|
||||||
|
} else if !wantEnabled && inClang && clangEnabled {
|
||||||
|
// TinyGo requires -feature but Clang enables it.
|
||||||
|
conflicts = append(conflicts, fmt.Sprintf("target has %q but Clang has %q", f, "+"+name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missing) > 0 {
|
||||||
|
t.Errorf("target specifies features not present in Clang output: %s\n\ttarget features: %s\n\tclang features: %s",
|
||||||
|
strings.Join(missing, ", "), targetFeatures, clangFeatures)
|
||||||
|
}
|
||||||
|
if len(conflicts) > 0 {
|
||||||
|
t.Errorf("target disables features that Clang enables: %s",
|
||||||
|
strings.Join(conflicts, "; "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||||
// to recursive tests.
|
// to recursive tests.
|
||||||
|
|||||||
+29
-3
@@ -204,7 +204,18 @@ var avrBuiltins = []string{
|
|||||||
|
|
||||||
// Builtins needed specifically for windows/386.
|
// Builtins needed specifically for windows/386.
|
||||||
var windowsI386Builtins = []string{
|
var windowsI386Builtins = []string{
|
||||||
"i386/chkstk.S", // also _alloca
|
"i386/chkstk.S", // __chkstk_ms
|
||||||
|
"i386/chkstk2.S", // _alloca (__alloca)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builtins needed specifically for windows/amd64.
|
||||||
|
var windowsAMD64Builtins = []string{
|
||||||
|
"x86_64/chkstk.S",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builtins needed specifically for windows/arm64.
|
||||||
|
var windowsARM64Builtins = []string{
|
||||||
|
"aarch64/chkstk.S",
|
||||||
}
|
}
|
||||||
|
|
||||||
// libCompilerRT is a library with symbols required by programs compiled with
|
// libCompilerRT is a library with symbols required by programs compiled with
|
||||||
@@ -233,13 +244,28 @@ var libCompilerRT = Library{
|
|||||||
builtins = append(builtins, aeabiBuiltins...)
|
builtins = append(builtins, aeabiBuiltins...)
|
||||||
case "avr":
|
case "avr":
|
||||||
builtins = append(builtins, avrBuiltins...)
|
builtins = append(builtins, avrBuiltins...)
|
||||||
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
case "x86_64":
|
||||||
|
builtins = append(builtins, genericBuiltins128...)
|
||||||
|
if isWindowsTriple(target) {
|
||||||
|
builtins = append(builtins, windowsAMD64Builtins...)
|
||||||
|
}
|
||||||
|
case "aarch64":
|
||||||
|
builtins = append(builtins, genericBuiltins128...)
|
||||||
|
if isWindowsTriple(target) {
|
||||||
|
builtins = append(builtins, windowsARM64Builtins...)
|
||||||
|
}
|
||||||
|
case "riscv64":
|
||||||
builtins = append(builtins, genericBuiltins128...)
|
builtins = append(builtins, genericBuiltins128...)
|
||||||
case "i386":
|
case "i386":
|
||||||
if strings.Split(target, "-")[2] == "windows" {
|
if isWindowsTriple(target) {
|
||||||
builtins = append(builtins, windowsI386Builtins...)
|
builtins = append(builtins, windowsI386Builtins...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builtins, nil
|
return builtins, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isWindowsTriple(target string) bool {
|
||||||
|
parts := strings.Split(target, "-")
|
||||||
|
return len(parts) > 2 && parts[2] == "windows"
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -281,7 +281,7 @@ func parseDepFile(s string) ([]string, error) {
|
|||||||
s = strings.ReplaceAll(s, "\\\n", " ")
|
s = strings.ReplaceAll(s, "\\\n", " ")
|
||||||
|
|
||||||
// Only use the first line, which is expected to begin with "deps:".
|
// Only use the first line, which is expected to begin with "deps:".
|
||||||
line := strings.SplitN(s, "\n", 2)[0]
|
line, _, _ := strings.Cut(s, "\n")
|
||||||
if !strings.HasPrefix(line, "deps:") {
|
if !strings.HasPrefix(line, "deps:") {
|
||||||
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-25
@@ -23,7 +23,7 @@
|
|||||||
#include "clang/Basic/Diagnostic.h"
|
#include "clang/Basic/Diagnostic.h"
|
||||||
#include "clang/Basic/DiagnosticOptions.h"
|
#include "clang/Basic/DiagnosticOptions.h"
|
||||||
#include "clang/Driver/DriverDiagnostic.h"
|
#include "clang/Driver/DriverDiagnostic.h"
|
||||||
#include "clang/Driver/Options.h"
|
#include "clang/Options/Options.h"
|
||||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||||
#include "clang/Frontend/Utils.h"
|
#include "clang/Frontend/Utils.h"
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "llvm/MC/MCAsmInfo.h"
|
#include "llvm/MC/MCAsmInfo.h"
|
||||||
#include "llvm/MC/MCCodeEmitter.h"
|
#include "llvm/MC/MCCodeEmitter.h"
|
||||||
#include "llvm/MC/MCContext.h"
|
#include "llvm/MC/MCContext.h"
|
||||||
|
#include "llvm/MC/MCInstPrinter.h"
|
||||||
#include "llvm/MC/MCInstrInfo.h"
|
#include "llvm/MC/MCInstrInfo.h"
|
||||||
#include "llvm/MC/MCObjectFileInfo.h"
|
#include "llvm/MC/MCObjectFileInfo.h"
|
||||||
#include "llvm/MC/MCObjectWriter.h"
|
#include "llvm/MC/MCObjectWriter.h"
|
||||||
@@ -67,8 +68,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace clang::driver;
|
using namespace clang::options;
|
||||||
using namespace clang::driver::options;
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::opt;
|
using namespace llvm::opt;
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
// Construct the invocation.
|
// Construct the invocation.
|
||||||
|
|
||||||
// Target Options
|
// Target Options
|
||||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
Opts.Triple = llvm::Triple(llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)));
|
||||||
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
||||||
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
||||||
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
|
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
|
||||||
@@ -125,8 +125,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||||
|
|
||||||
// Use the default target triple if unspecified.
|
// Use the default target triple if unspecified.
|
||||||
if (Opts.Triple.empty())
|
if (Opts.Triple.getTriple().empty())
|
||||||
Opts.Triple = llvm::sys::getDefaultTargetTriple();
|
Opts.Triple = llvm::Triple(llvm::sys::getDefaultTargetTriple());
|
||||||
|
|
||||||
// Language Options
|
// Language Options
|
||||||
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
||||||
@@ -144,8 +144,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
.Default(llvm::DebugCompressionType::None);
|
.Default(llvm::DebugCompressionType::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
|
||||||
Opts.SSE2AVX = Args.hasArg(OPT_msse2avx);
|
|
||||||
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
|
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
|
||||||
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
|
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
|
||||||
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||||
@@ -236,6 +234,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
Opts.EmitCompactUnwindNonCanonical =
|
Opts.EmitCompactUnwindNonCanonical =
|
||||||
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
||||||
Opts.Crel = Args.hasArg(OPT_crel);
|
Opts.Crel = Args.hasArg(OPT_crel);
|
||||||
|
Opts.ImplicitMapsyms = Args.hasArg(OPT_mmapsyms_implicit);
|
||||||
|
Opts.X86RelaxRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
||||||
|
Opts.X86Sse2Avx = Args.hasArg(OPT_msse2avx);
|
||||||
|
|
||||||
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||||
|
|
||||||
@@ -266,7 +267,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
std::string Error;
|
std::string Error;
|
||||||
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||||
if (!TheTarget)
|
if (!TheTarget)
|
||||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
|
||||||
|
|
||||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||||
@@ -294,8 +295,9 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
||||||
MCOptions.MCSaveTempLabels = Opts.SaveTemporaryLabels;
|
MCOptions.MCSaveTempLabels = Opts.SaveTemporaryLabels;
|
||||||
MCOptions.Crel = Opts.Crel;
|
MCOptions.Crel = Opts.Crel;
|
||||||
MCOptions.X86RelaxRelocations = Opts.RelaxELFRelocations;
|
MCOptions.ImplicitMapSyms = Opts.ImplicitMapsyms;
|
||||||
MCOptions.X86Sse2Avx = Opts.SSE2AVX;
|
MCOptions.X86RelaxRelocations = Opts.X86RelaxRelocations;
|
||||||
|
MCOptions.X86Sse2Avx = Opts.X86Sse2Avx;
|
||||||
MCOptions.CompressDebugSections = Opts.CompressDebugSections;
|
MCOptions.CompressDebugSections = Opts.CompressDebugSections;
|
||||||
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||||
|
|
||||||
@@ -325,7 +327,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||||
assert(STI && "Unable to create subtarget info!");
|
assert(STI && "Unable to create subtarget info!");
|
||||||
|
|
||||||
MCContext Ctx(Triple(Opts.Triple), MAI.get(), MRI.get(), STI.get(), &SrcMgr,
|
MCContext Ctx(Opts.Triple, MAI.get(), MRI.get(), STI.get(), &SrcMgr,
|
||||||
&MCOptions);
|
&MCOptions);
|
||||||
|
|
||||||
bool PIC = false;
|
bool PIC = false;
|
||||||
@@ -343,10 +345,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
||||||
std::unique_ptr<MCObjectFileInfo> MOFI(
|
std::unique_ptr<MCObjectFileInfo> MOFI(
|
||||||
TheTarget->createMCObjectFileInfo(Ctx, PIC));
|
TheTarget->createMCObjectFileInfo(Ctx, PIC));
|
||||||
if (Opts.DarwinTargetVariantTriple)
|
|
||||||
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
|
|
||||||
if (!Opts.DarwinTargetVariantSDKVersion.empty())
|
|
||||||
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
|
|
||||||
Ctx.setObjectFileInfo(MOFI.get());
|
Ctx.setObjectFileInfo(MOFI.get());
|
||||||
|
|
||||||
if (Opts.GenDwarfForAssembly)
|
if (Opts.GenDwarfForAssembly)
|
||||||
@@ -392,8 +390,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||||
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
||||||
MCInstPrinter *IP = TheTarget->createMCInstPrinter(
|
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
|
||||||
llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
|
Opts.Triple, Opts.OutputAsmVariant, *MAI, *MCII, *MRI));
|
||||||
|
|
||||||
std::unique_ptr<MCCodeEmitter> CE;
|
std::unique_ptr<MCCodeEmitter> CE;
|
||||||
if (Opts.ShowEncoding)
|
if (Opts.ShowEncoding)
|
||||||
@@ -402,7 +400,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||||
|
|
||||||
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
|
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
|
||||||
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), IP,
|
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), std::move(IP),
|
||||||
std::move(CE), std::move(MAB)));
|
std::move(CE), std::move(MAB)));
|
||||||
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||||
Str.reset(createNullStreamer(Ctx));
|
Str.reset(createNullStreamer(Ctx));
|
||||||
@@ -424,10 +422,17 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||||
: MAB->createObjectWriter(*Out);
|
: MAB->createObjectWriter(*Out);
|
||||||
|
|
||||||
Triple T(Opts.Triple);
|
Triple T = Opts.Triple;
|
||||||
Str.reset(TheTarget->createMCObjectStreamer(
|
Str.reset(TheTarget->createMCObjectStreamer(
|
||||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI));
|
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI));
|
||||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||||
|
if (T.isOSBinFormatMachO() && T.isOSDarwin()) {
|
||||||
|
Triple *TVT = Opts.DarwinTargetVariantTriple
|
||||||
|
? &*Opts.DarwinTargetVariantTriple
|
||||||
|
: nullptr;
|
||||||
|
Str->emitVersionForTarget(T, VersionTuple(), TVT,
|
||||||
|
Opts.DarwinTargetVariantSDKVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||||
@@ -448,7 +453,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
std::unique_ptr<MCTargetAsmParser> TAP(
|
std::unique_ptr<MCTargetAsmParser> TAP(
|
||||||
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
||||||
if (!TAP)
|
if (!TAP)
|
||||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
|
||||||
|
|
||||||
// Set values for symbols, if any.
|
// Set values for symbols, if any.
|
||||||
for (auto &S : Opts.SymbolDefs) {
|
for (auto &S : Opts.SymbolDefs) {
|
||||||
@@ -501,12 +506,12 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
|||||||
InitializeAllAsmParsers();
|
InitializeAllAsmParsers();
|
||||||
|
|
||||||
// Construct our diagnostic client.
|
// Construct our diagnostic client.
|
||||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
DiagnosticOptions DiagOpts;
|
||||||
TextDiagnosticPrinter *DiagClient
|
TextDiagnosticPrinter *DiagClient
|
||||||
= new TextDiagnosticPrinter(errs(), &*DiagOpts);
|
= new TextDiagnosticPrinter(errs(), DiagOpts);
|
||||||
DiagClient->setPrefix("clang -cc1as");
|
DiagClient->setPrefix("clang -cc1as");
|
||||||
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||||
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
|
DiagnosticsEngine Diags(DiagID, DiagOpts, DiagClient);
|
||||||
|
|
||||||
// Set an error handler, so that any LLVM backend diagnostics go through our
|
// Set an error handler, so that any LLVM backend diagnostics go through our
|
||||||
// error handler.
|
// error handler.
|
||||||
@@ -523,7 +528,7 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
|||||||
llvm::outs(), "clang -cc1as [options] file...",
|
llvm::outs(), "clang -cc1as [options] file...",
|
||||||
"Clang Integrated Assembler", /*ShowHidden=*/false,
|
"Clang Integrated Assembler", /*ShowHidden=*/false,
|
||||||
/*ShowAllAliases=*/false,
|
/*ShowAllAliases=*/false,
|
||||||
llvm::opt::Visibility(driver::options::CC1AsOption));
|
llvm::opt::Visibility(clang::options::CC1AsOption));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-8
@@ -20,7 +20,7 @@ struct AssemblerInvocation {
|
|||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// The name of the target triple to assemble for.
|
/// The name of the target triple to assemble for.
|
||||||
std::string Triple;
|
llvm::Triple Triple;
|
||||||
|
|
||||||
/// If given, the name of the target CPU to determine which instructions
|
/// If given, the name of the target CPU to determine which instructions
|
||||||
/// are legal.
|
/// are legal.
|
||||||
@@ -45,10 +45,6 @@ struct AssemblerInvocation {
|
|||||||
LLVM_PREFERRED_TYPE(bool)
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned GenDwarfForAssembly : 1;
|
unsigned GenDwarfForAssembly : 1;
|
||||||
LLVM_PREFERRED_TYPE(bool)
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned RelaxELFRelocations : 1;
|
|
||||||
LLVM_PREFERRED_TYPE(bool)
|
|
||||||
unsigned SSE2AVX : 1;
|
|
||||||
LLVM_PREFERRED_TYPE(bool)
|
|
||||||
unsigned Dwarf64 : 1;
|
unsigned Dwarf64 : 1;
|
||||||
unsigned DwarfVersion;
|
unsigned DwarfVersion;
|
||||||
std::string DwarfDebugFlags;
|
std::string DwarfDebugFlags;
|
||||||
@@ -111,12 +107,19 @@ struct AssemblerInvocation {
|
|||||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||||
|
|
||||||
// Whether to emit compact-unwind for non-canonical entries.
|
// Whether to emit compact-unwind for non-canonical entries.
|
||||||
// Note: maybe overriden by other constraints.
|
// Note: maybe overridden by other constraints.
|
||||||
LLVM_PREFERRED_TYPE(bool)
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||||
|
|
||||||
LLVM_PREFERRED_TYPE(bool)
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned Crel : 1;
|
unsigned Crel : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned ImplicitMapsyms : 1;
|
||||||
|
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned X86RelaxRelocations : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned X86Sse2Avx : 1;
|
||||||
|
|
||||||
/// The name of the relocation model to use.
|
/// The name of the relocation model to use.
|
||||||
std::string RelocationModel;
|
std::string RelocationModel;
|
||||||
@@ -139,7 +142,7 @@ struct AssemblerInvocation {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
AssemblerInvocation() {
|
AssemblerInvocation() {
|
||||||
Triple = "";
|
Triple = llvm::Triple();
|
||||||
NoInitialTextSection = 0;
|
NoInitialTextSection = 0;
|
||||||
InputFile = "-";
|
InputFile = "-";
|
||||||
OutputPath = "-";
|
OutputPath = "-";
|
||||||
@@ -148,7 +151,6 @@ public:
|
|||||||
ShowInst = 0;
|
ShowInst = 0;
|
||||||
ShowEncoding = 0;
|
ShowEncoding = 0;
|
||||||
RelaxAll = 0;
|
RelaxAll = 0;
|
||||||
SSE2AVX = 0;
|
|
||||||
NoExecStack = 0;
|
NoExecStack = 0;
|
||||||
FatalWarnings = 0;
|
FatalWarnings = 0;
|
||||||
NoWarn = 0;
|
NoWarn = 0;
|
||||||
@@ -160,6 +162,9 @@ public:
|
|||||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||||
EmitCompactUnwindNonCanonical = false;
|
EmitCompactUnwindNonCanonical = false;
|
||||||
Crel = false;
|
Crel = false;
|
||||||
|
ImplicitMapsyms = 0;
|
||||||
|
X86RelaxRelocations = 0;
|
||||||
|
X86Sse2Avx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||||
|
|||||||
+3
-3
@@ -27,9 +27,9 @@ bool tinygo_clang_driver(int argc, char **argv) {
|
|||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
|
|
||||||
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
||||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
|
clang::DiagnosticOptions DiagOpts;
|
||||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
|
||||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false);
|
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), DiagOpts, &DiagnosticPrinter, false);
|
||||||
|
|
||||||
// Create the clang driver
|
// Create the clang driver
|
||||||
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import (
|
|||||||
var commands = map[string][]string{}
|
var commands = map[string][]string{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
llvmMajor, _, _ := strings.Cut(llvm.Version, ".")
|
||||||
commands["clang"] = []string{"clang-" + llvmMajor}
|
commands["clang"] = []string{"clang-" + llvmMajor}
|
||||||
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
||||||
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
||||||
|
|||||||
+9
-6
@@ -25,18 +25,21 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Version range supported by TinyGo.
|
// Version range supported by TinyGo.
|
||||||
const minorMin = 19
|
const minorMin = 25 // when updating the min version, also update .github/workflows/compat.yml
|
||||||
const minorMax = 25
|
const minorMax = 27
|
||||||
|
|
||||||
// Check that we support this Go toolchain version.
|
// Check that we support this Go toolchain version.
|
||||||
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
|
||||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
if options.GoCompatibility {
|
||||||
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
// Note: when this gets updated, also update the Go compatibility matrix:
|
||||||
|
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||||
|
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the Go toolchain version isn't too new, if we haven't been
|
// Check that the Go toolchain version isn't too new, if we haven't been
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
|||||||
return &compileJob{
|
return &compileJob{
|
||||||
description: "compile Darwin libSystem.dylib",
|
description: "compile Darwin libSystem.dylib",
|
||||||
run: func(job *compileJob) (err error) {
|
run: func(job *compileJob) (err error) {
|
||||||
arch := strings.Split(config.Triple(), "-")[0]
|
arch, _, _ := strings.Cut(config.Triple(), "-")
|
||||||
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
||||||
objpath := filepath.Join(tmpdir, "libSystem.o")
|
objpath := filepath.Join(tmpdir, "libSystem.o")
|
||||||
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
||||||
|
|||||||
+153
-14
@@ -23,7 +23,7 @@ type espImageSegment struct {
|
|||||||
data []byte
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeESPFirmareImage converts an input ELF file to an image file for an ESP32 or
|
// makeESPFirmwareImage converts an input ELF file to an image file for an ESP32 or
|
||||||
// ESP8266 chip. This is a special purpose image format just for the ESP chip
|
// 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.
|
// family, and is parsed by the on-chip mask ROM bootloader.
|
||||||
//
|
//
|
||||||
@@ -31,7 +31,7 @@ type espImageSegment struct {
|
|||||||
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
||||||
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
|
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
|
||||||
// https://github.com/espressif/esptool/blob/master/esptool.py
|
// https://github.com/espressif/esptool/blob/master/esptool.py
|
||||||
func makeESPFirmareImage(infile, outfile, format string) error {
|
func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||||
inf, err := elf.Open(infile)
|
inf, err := elf.Open(infile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -65,15 +65,6 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
// Sort the segments by address. This is what esptool does too.
|
// Sort the segments by address. This is what esptool does too.
|
||||||
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
|
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
|
||||||
|
|
||||||
// Calculate checksum over the segment data. This is used in the image
|
|
||||||
// footer.
|
|
||||||
checksum := uint8(0xef)
|
|
||||||
for _, segment := range segments {
|
|
||||||
for _, b := range segment.data {
|
|
||||||
checksum ^= b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write first to an in-memory buffer, primarily so that we can easily
|
// Write first to an in-memory buffer, primarily so that we can easily
|
||||||
// calculate a hash over the entire image.
|
// calculate a hash over the entire image.
|
||||||
// An added benefit is that we don't need to check for errors all the time.
|
// An added benefit is that we don't need to check for errors all the time.
|
||||||
@@ -88,6 +79,83 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
chip = format[:len(format)-len("-img")]
|
chip = format[:len(format)-len("-img")]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For ESP32 (original): separate RAM segments (loadable by ROM bootloader)
|
||||||
|
// from flash-mapped segments (DROM/IROM, require MMU setup by startup code).
|
||||||
|
// The ROM bootloader on ESP32 does NOT handle flash-mapped segments —
|
||||||
|
// it tries to memcpy to the virtual address, which crashes.
|
||||||
|
var flashSegments []*espImageSegment
|
||||||
|
if chip == "esp32" {
|
||||||
|
var ramSegments []*espImageSegment
|
||||||
|
for _, seg := range segments {
|
||||||
|
if (seg.addr >= 0x3F400000 && seg.addr < 0x3F800000) || // DROM
|
||||||
|
(seg.addr >= 0x400D0000 && seg.addr < 0x40400000) { // IROM
|
||||||
|
flashSegments = append(flashSegments, seg)
|
||||||
|
} else {
|
||||||
|
ramSegments = append(ramSegments, seg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segments = ramSegments
|
||||||
|
}
|
||||||
|
|
||||||
|
// ESP32 flash XIP: compute where the DROM segment will be placed in flash
|
||||||
|
// (page-aligned, right after the RAM segments) and patch the
|
||||||
|
// _drom_flash_addr variable so the startup code can program the cache MMU.
|
||||||
|
// This must happen before the checksum/hash are computed so the patched
|
||||||
|
// value is covered by both.
|
||||||
|
const esp32FlashBase = 0x1000 // esptool flashes the image at 0x1000
|
||||||
|
// The ESP32 flash cache MMU supports configurable page sizes down to 256 B. 64 KiB is the reset/default size.
|
||||||
|
// If the startup code ever changes the MMU page size, this constant must change too.
|
||||||
|
const esp32PageSize = 0x10000 // 64KB MMU pages
|
||||||
|
var esp32DromFlashAddr uint32
|
||||||
|
if chip == "esp32" && len(flashSegments) > 0 {
|
||||||
|
// Compute the size of the RAM portion of the image (everything the ROM
|
||||||
|
// bootloader loads, up to and including the appended SHA256 hash).
|
||||||
|
ramImageSize := 0
|
||||||
|
ramImageSize += 24 // image header (8) + trailer fields (16)
|
||||||
|
for _, seg := range segments {
|
||||||
|
ramImageSize += 8 + len(seg.data) // segment header + data (4-aligned)
|
||||||
|
}
|
||||||
|
ramImageSize += 16 - ramImageSize%16 // footer padding + checksum byte
|
||||||
|
ramImageSize += 32 // appended SHA256 hash
|
||||||
|
|
||||||
|
// DROM flash address must be 64KB page-aligned.
|
||||||
|
esp32DromFlashAddr = uint32(esp32FlashBase+ramImageSize+esp32PageSize-1) &^ (esp32PageSize - 1)
|
||||||
|
|
||||||
|
// Patch _drom_flash_addr in whichever RAM segment contains it.
|
||||||
|
syms, _ := inf.Symbols()
|
||||||
|
var dromSymAddr uint64
|
||||||
|
for _, s := range syms {
|
||||||
|
if s.Name == "_drom_flash_addr" {
|
||||||
|
dromSymAddr = s.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dromSymAddr == 0 {
|
||||||
|
return fmt.Errorf("ESP32: _drom_flash_addr symbol not found")
|
||||||
|
}
|
||||||
|
patched := false
|
||||||
|
for _, seg := range segments {
|
||||||
|
if dromSymAddr >= uint64(seg.addr) && dromSymAddr+4 <= uint64(seg.addr)+uint64(len(seg.data)) {
|
||||||
|
off := int(dromSymAddr - uint64(seg.addr))
|
||||||
|
binary.LittleEndian.PutUint32(seg.data[off:], esp32DromFlashAddr)
|
||||||
|
patched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !patched {
|
||||||
|
return fmt.Errorf("ESP32: _drom_flash_addr (0x%x) not in any RAM segment", dromSymAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate checksum over the segment data. This is used in the image
|
||||||
|
// footer.
|
||||||
|
checksum := uint8(0xef)
|
||||||
|
for _, segment := range segments {
|
||||||
|
for _, b := range segment.data {
|
||||||
|
checksum ^= b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if makeImage {
|
if makeImage {
|
||||||
// The bootloader starts at 0x1000, or 4096.
|
// The bootloader starts at 0x1000, or 4096.
|
||||||
// TinyGo doesn't use a separate bootloader and runs the entire
|
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||||
@@ -100,11 +168,24 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
chip_id := map[string]uint16{
|
chip_id := map[string]uint16{
|
||||||
"esp32": 0x0000,
|
"esp32": 0x0000,
|
||||||
"esp32c3": 0x0005,
|
"esp32c3": 0x0005,
|
||||||
|
"esp32c6": 0x000d,
|
||||||
|
"esp32s3": 0x0009,
|
||||||
|
}[chip]
|
||||||
|
|
||||||
|
// SPI flash speed/size byte (byte 3 of header):
|
||||||
|
// Upper nibble = flash size, lower nibble = flash frequency.
|
||||||
|
// The espflasher auto-detects and patches the flash size (upper nibble),
|
||||||
|
// but the frequency (lower nibble) must be correct per chip.
|
||||||
|
spiSpeedSize := map[string]uint8{
|
||||||
|
"esp32": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||||
|
"esp32c3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||||
|
"esp32c6": 0x10, // 80MHz=0x00, 2MB=0x10 (C6 uses different freq encoding)
|
||||||
|
"esp32s3": 0x1f, // 80MHz=0x0F, 2MB=0x10
|
||||||
}[chip]
|
}[chip]
|
||||||
|
|
||||||
// Image header.
|
// Image header.
|
||||||
switch chip {
|
switch chip {
|
||||||
case "esp32", "esp32c3":
|
case "esp32", "esp32c3", "esp32s3", "esp32c6":
|
||||||
// Header format:
|
// Header format:
|
||||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||||
// Note: not adding a SHA256 hash as the binary is modified by
|
// Note: not adding a SHA256 hash as the binary is modified by
|
||||||
@@ -125,8 +206,8 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
}{
|
}{
|
||||||
magic: 0xE9,
|
magic: 0xE9,
|
||||||
segment_count: byte(len(segments)),
|
segment_count: byte(len(segments)),
|
||||||
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||||
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
spi_speed_size: spiSpeedSize,
|
||||||
entry_addr: uint32(inf.Entry),
|
entry_addr: uint32(inf.Entry),
|
||||||
wp_pin: 0xEE, // disable WP pin
|
wp_pin: 0xEE, // disable WP pin
|
||||||
chip_id: chip_id,
|
chip_id: chip_id,
|
||||||
@@ -178,6 +259,64 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
outf.Write(hash[:])
|
outf.Write(hash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For ESP32: append flash-mapped segments (DROM/IROM) at page-aligned flash
|
||||||
|
// offsets after the RAM portion. The startup code maps them via the flash
|
||||||
|
// cache MMU (DROM at esp32DromFlashAddr, patched into _drom_flash_addr).
|
||||||
|
if len(flashSegments) > 0 {
|
||||||
|
const flashBase = esp32FlashBase
|
||||||
|
const pageSize = esp32PageSize
|
||||||
|
dromFlashAddr := esp32DromFlashAddr
|
||||||
|
|
||||||
|
// Separate DROM and IROM segments.
|
||||||
|
var dromSegs, iromSegs []*espImageSegment
|
||||||
|
for _, seg := range flashSegments {
|
||||||
|
if seg.addr >= 0x3F400000 && seg.addr < 0x3F800000 {
|
||||||
|
dromSegs = append(dromSegs, seg)
|
||||||
|
} else {
|
||||||
|
iromSegs = append(iromSegs, seg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write DROM segments at the computed page-aligned flash offset.
|
||||||
|
dromSize := 0
|
||||||
|
if len(dromSegs) > 0 {
|
||||||
|
targetImageOffset := int(dromFlashAddr - flashBase)
|
||||||
|
if makeImage {
|
||||||
|
targetImageOffset = int(dromFlashAddr)
|
||||||
|
}
|
||||||
|
if outf.Len() > targetImageOffset {
|
||||||
|
return fmt.Errorf("ESP32: RAM segments too large (%d bytes), overlap DROM at flash 0x%x", outf.Len(), dromFlashAddr)
|
||||||
|
}
|
||||||
|
outf.Write(make([]byte, targetImageOffset-outf.Len()))
|
||||||
|
for _, seg := range dromSegs {
|
||||||
|
outf.Write(seg.data)
|
||||||
|
dromSize += len(seg.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write IROM segments immediately after DROM, at the next page boundary.
|
||||||
|
// IROM flash addr = dromFlashAddr + ceil(dromSize/pageSize)*pageSize
|
||||||
|
// (must match the computation in the startup assembly).
|
||||||
|
if len(iromSegs) > 0 {
|
||||||
|
dromPages := (dromSize + pageSize - 1) / pageSize
|
||||||
|
if dromPages == 0 {
|
||||||
|
dromPages = 1
|
||||||
|
}
|
||||||
|
iromFlashAddr := dromFlashAddr + uint32(dromPages)*pageSize
|
||||||
|
targetImageOffset := int(iromFlashAddr - flashBase)
|
||||||
|
if makeImage {
|
||||||
|
targetImageOffset = int(iromFlashAddr)
|
||||||
|
}
|
||||||
|
if outf.Len() > targetImageOffset {
|
||||||
|
return fmt.Errorf("ESP32: DROM too large, overlaps IROM at flash 0x%x", iromFlashAddr)
|
||||||
|
}
|
||||||
|
outf.Write(make([]byte, targetImageOffset-outf.Len()))
|
||||||
|
for _, seg := range iromSegs {
|
||||||
|
outf.Write(seg.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||||
// image to be a certain size.
|
// image to be a certain size.
|
||||||
if makeImage {
|
if makeImage {
|
||||||
|
|||||||
+2
-2
@@ -195,11 +195,11 @@ type intHeap struct {
|
|||||||
sort.IntSlice
|
sort.IntSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *intHeap) Push(x interface{}) {
|
func (h *intHeap) Push(x any) {
|
||||||
h.IntSlice = append(h.IntSlice, x.(int))
|
h.IntSlice = append(h.IntSlice, x.(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *intHeap) Pop() interface{} {
|
func (h *intHeap) Pop() any {
|
||||||
x := h.IntSlice[len(h.IntSlice)-1]
|
x := h.IntSlice[len(h.IntSlice)-1]
|
||||||
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
||||||
return x
|
return x
|
||||||
|
|||||||
+18
-5
@@ -133,7 +133,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||||
// reproducible. Otherwise the temporary directory is stored in the archive
|
// reproducible. Otherwise the temporary directory is stored in the archive
|
||||||
// itself, which varies each run.
|
// itself, which varies each run.
|
||||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+compileopts.ClangTriple(target), "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||||
resourceDir := goenv.ClangResourceDir(false)
|
resourceDir := goenv.ClangResourceDir(false)
|
||||||
if resourceDir != "" {
|
if resourceDir != "" {
|
||||||
args = append(args, "-resource-dir="+resourceDir)
|
args = append(args, "-resource-dir="+resourceDir)
|
||||||
@@ -167,9 +167,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
// double.
|
// double.
|
||||||
args = append(args, "-mdouble=64")
|
args = append(args, "-mdouble=64")
|
||||||
case "riscv32":
|
case "riscv32":
|
||||||
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
args = append(args, "-march="+riscvMarch(config, "rv32imac"), "-fforce-enable-int128")
|
||||||
case "riscv64":
|
case "riscv64":
|
||||||
args = append(args, "-march=rv64gc")
|
args = append(args, "-march="+riscvMarch(config, "rv64gc"))
|
||||||
case "mips":
|
case "mips":
|
||||||
args = append(args, "-fno-pic")
|
args = append(args, "-fno-pic")
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Store this archive in the cache.
|
// Store this archive in the cache.
|
||||||
return os.Rename(f.Name(), archiveFilePath)
|
return robustRename(f.Name(), archiveFilePath)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +232,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
}
|
}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
// Strip leading "../" parts off the path.
|
// Strip leading "../" parts off the path.
|
||||||
path := path
|
|
||||||
cleanpath := path
|
cleanpath := path
|
||||||
for strings.HasPrefix(cleanpath, "../") {
|
for strings.HasPrefix(cleanpath, "../") {
|
||||||
cleanpath = cleanpath[3:]
|
cleanpath = cleanpath[3:]
|
||||||
@@ -298,3 +297,17 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
once.Do(unlock)
|
once.Do(unlock)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// riscvMarch returns the -march value for RISC-V library compilation.
|
||||||
|
// It extracts the value from the target's cflags if present, otherwise
|
||||||
|
// falls back to the provided default. This ensures libraries are compiled
|
||||||
|
// with the correct ISA extensions for each target (e.g. rv32imc for
|
||||||
|
// ESP32-C3 which lacks the atomic extension).
|
||||||
|
func riscvMarch(config *compileopts.Config, defaultMarch string) string {
|
||||||
|
for _, flag := range config.Target.CFlags {
|
||||||
|
if strings.HasPrefix(flag, "-march=") {
|
||||||
|
return flag[len("-march="):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultMarch
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -28,8 +28,8 @@ func buildMuslAllTypes(arch, muslDir, outputBitsDir string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lines := strings.Split(string(data), "\n")
|
lines := strings.SplitSeq(string(data), "\n")
|
||||||
for _, line := range lines {
|
for line := range lines {
|
||||||
if strings.HasPrefix(line, "TYPEDEF ") {
|
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||||
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||||
value := matches[1]
|
value := matches[1]
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func robustRename(oldpath, newpath string) error {
|
||||||
|
return os.Rename(oldpath, newpath)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const robustRenameTimeout = 2 * time.Second
|
||||||
|
|
||||||
|
func robustRename(oldpath, newpath string) error {
|
||||||
|
var bestErr error
|
||||||
|
start := time.Now()
|
||||||
|
nextSleep := time.Millisecond
|
||||||
|
for {
|
||||||
|
err := os.Rename(oldpath, newpath)
|
||||||
|
if err == nil || !isEphemeralRenameError(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bestErr == nil {
|
||||||
|
bestErr = err
|
||||||
|
}
|
||||||
|
if d := time.Since(start) + nextSleep; d >= robustRenameTimeout {
|
||||||
|
return bestErr
|
||||||
|
}
|
||||||
|
time.Sleep(nextSleep)
|
||||||
|
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEphemeralRenameError(err error) bool {
|
||||||
|
var errno syscall.Errno
|
||||||
|
if errors.As(err, &errno) {
|
||||||
|
switch errno {
|
||||||
|
case syscall.Errno(2), // ERROR_FILE_NOT_FOUND
|
||||||
|
syscall.Errno(5), // ERROR_ACCESS_DENIED
|
||||||
|
syscall.Errno(32): // ERROR_SHARING_VIOLATION
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
+13
-3
@@ -501,8 +501,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
Align: section.Addralign,
|
Align: section.Addralign,
|
||||||
Type: memoryStack,
|
Type: memoryStack,
|
||||||
})
|
})
|
||||||
} else {
|
} else if section.Flags&elf.SHF_WRITE != 0 {
|
||||||
// Regular .bss section.
|
// Regular .bss section. Zero-initialized RAM is always
|
||||||
|
// writable, so require SHF_WRITE here.
|
||||||
sections = append(sections, memorySection{
|
sections = append(sections, memorySection{
|
||||||
Address: section.Addr,
|
Address: section.Addr,
|
||||||
Size: section.Size,
|
Size: section.Size,
|
||||||
@@ -510,6 +511,11 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
Type: memoryBSS,
|
Type: memoryBSS,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// Other (non-writable) SHT_NOBITS sections are address-space
|
||||||
|
// placeholders that occupy no RAM, such as the ESP linker
|
||||||
|
// script's .irom_dummy / .rodata_dummy sections which reserve
|
||||||
|
// the flash-mapped XIP virtual address ranges. They must not be
|
||||||
|
// counted as bss/RAM usage.
|
||||||
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_EXECINSTR != 0 {
|
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_EXECINSTR != 0 {
|
||||||
// .text
|
// .text
|
||||||
sections = append(sections, memorySection{
|
sections = append(sections, memorySection{
|
||||||
@@ -954,7 +960,11 @@ func findPackagePath(path string, packagePathMap map[string]string) (packagePath
|
|||||||
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator))
|
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator))
|
||||||
parts := strings.SplitN(libPath, string(os.PathSeparator), 2)
|
parts := strings.SplitN(libPath, string(os.PathSeparator), 2)
|
||||||
packagePath = "C " + parts[0]
|
packagePath = "C " + parts[0]
|
||||||
filename = parts[1]
|
if len(parts) > 1 {
|
||||||
|
filename = parts[1]
|
||||||
|
} else {
|
||||||
|
filename = parts[0]
|
||||||
|
}
|
||||||
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
|
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
|
||||||
packagePath = "C compiler-rt"
|
packagePath = "C compiler-rt"
|
||||||
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
|
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
|
||||||
|
|||||||
+122
-36
@@ -1,8 +1,13 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,21 +16,19 @@ import (
|
|||||||
|
|
||||||
var sema = make(chan struct{}, runtime.NumCPU())
|
var sema = make(chan struct{}, runtime.NumCPU())
|
||||||
|
|
||||||
|
var flagUpdate = flag.Bool("update", false, "update builder package tests")
|
||||||
|
|
||||||
type sizeTest struct {
|
type sizeTest struct {
|
||||||
target string
|
target string
|
||||||
path string
|
path string
|
||||||
codeSize uint64
|
|
||||||
rodataSize uint64
|
|
||||||
dataSize uint64
|
|
||||||
bssSize uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether code and data size is as expected for the given targets.
|
// 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
|
// This tests both the logic of loadProgramSize and checks that code size
|
||||||
// doesn't change unintentionally.
|
// doesn't change unintentionally.
|
||||||
//
|
//
|
||||||
// If you find that code or data size is reduced, then great! You can reduce the
|
// If you find that code or data size is reduced, then great! You can update the
|
||||||
// number in this test.
|
// golden file by passing -update to the test.
|
||||||
// If you find that the code or data size is increased, take a look as to why
|
// 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
|
// 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
|
// is fine, but it could also mean that a recent change introduced this size
|
||||||
@@ -42,35 +45,110 @@ func TestBinarySize(t *testing.T) {
|
|||||||
// This is a small number of very diverse targets that we want to test.
|
// This is a small number of very diverse targets that we want to test.
|
||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 4580, 280, 0, 2264},
|
{"hifive1b", "examples/echo"},
|
||||||
{"microbit", "examples/serial", 2928, 388, 8, 2272},
|
{"microbit", "examples/serial"},
|
||||||
{"wioterminal", "examples/pininterrupt", 7387, 1489, 116, 6912},
|
{"wioterminal", "examples/pininterrupt"},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
// wasm binaries are run through wasm-opt and therefore the
|
// wasm binaries are run through wasm-opt and therefore the
|
||||||
// output varies by binaryen version.
|
// output varies by binaryen version.
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
sizes := measureBinarySizes(t, tests)
|
||||||
tc := tc
|
output := formatSizeTable(tests, sizes)
|
||||||
t.Run(tc.target+"/"+tc.path, func(t *testing.T) {
|
checkGolden(t, "testdata/binary-size.txt", output)
|
||||||
t.Parallel()
|
}
|
||||||
|
|
||||||
// Build the binary.
|
func checkGolden(t *testing.T, path, actual string) {
|
||||||
result := buildBinary(t, tc.target, tc.path)
|
t.Helper()
|
||||||
|
if *flagUpdate {
|
||||||
// Check whether the size of the binary matches the expected size.
|
if err := os.WriteFile(path, []byte(actual), 0o666); err != nil {
|
||||||
sizes, err := loadProgramSize(result.Executable, nil)
|
t.Fatal("failed to write updated golden file:", err)
|
||||||
if err != nil {
|
}
|
||||||
t.Fatal("could not read program size:", err)
|
return
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
expected, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("failed to read golden file:", err)
|
||||||
|
}
|
||||||
|
if actual != string(expected) {
|
||||||
|
t.Errorf("%s does not match expected output (re-run with -update to regenerate):\nexpected:\n%sactual:\n%s", path, expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func measureBinarySizes(t *testing.T, tests []sizeTest) []*programSize {
|
||||||
|
t.Helper()
|
||||||
|
type result struct {
|
||||||
|
index int
|
||||||
|
size *programSize
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make(chan result, len(tests))
|
||||||
|
for i, tc := range tests {
|
||||||
|
tmpdir := t.TempDir()
|
||||||
|
go func() {
|
||||||
|
size, err := measureBinarySize(tc, tmpdir)
|
||||||
|
results <- result{i, size, err}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
sizes := make([]*programSize, len(tests))
|
||||||
|
failed := false
|
||||||
|
for range tests {
|
||||||
|
result := <-results
|
||||||
|
if result.err != nil {
|
||||||
|
tc := tests[result.index]
|
||||||
|
t.Errorf("%s/%s: %v", tc.target, tc.path, result.err)
|
||||||
|
failed = true
|
||||||
|
}
|
||||||
|
sizes[result.index] = result.size
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
return sizes
|
||||||
|
}
|
||||||
|
|
||||||
|
func measureBinarySize(tc sizeTest, tmpdir string) (*programSize, error) {
|
||||||
|
result, err := buildBinaryInDir(tc.target, tc.path, tmpdir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
size, err := loadProgramSize(result.Executable, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not read program size: %w", err)
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSizeTable(tests []sizeTest, sizes []*programSize) string {
|
||||||
|
targetWidth := len("target")
|
||||||
|
packageWidth := len("package")
|
||||||
|
codeWidth := len("code")
|
||||||
|
rodataWidth := len("rodata")
|
||||||
|
dataWidth := len("data")
|
||||||
|
bssWidth := len("bss")
|
||||||
|
for i, tc := range tests {
|
||||||
|
targetWidth = max(targetWidth, len(tc.target))
|
||||||
|
packageWidth = max(packageWidth, len(tc.path))
|
||||||
|
codeWidth = max(codeWidth, len(strconv.FormatUint(sizes[i].Code, 10)))
|
||||||
|
rodataWidth = max(rodataWidth, len(strconv.FormatUint(sizes[i].ROData, 10)))
|
||||||
|
dataWidth = max(dataWidth, len(strconv.FormatUint(sizes[i].Data, 10)))
|
||||||
|
bssWidth = max(bssWidth, len(strconv.FormatUint(sizes[i].BSS, 10)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var output strings.Builder
|
||||||
|
fmt.Fprintf(&output, "%-*s %-*s %*s %*s %*s %*s\n",
|
||||||
|
targetWidth, "target", packageWidth, "package",
|
||||||
|
codeWidth, "code", rodataWidth, "rodata", dataWidth, "data", bssWidth, "bss")
|
||||||
|
for i, tc := range tests {
|
||||||
|
size := sizes[i]
|
||||||
|
fmt.Fprintf(&output, "%-*s %-*s %*d %*d %*d %*d\n",
|
||||||
|
targetWidth, tc.target, packageWidth, tc.path,
|
||||||
|
codeWidth, size.Code, rodataWidth, size.ROData,
|
||||||
|
dataWidth, size.Data, bssWidth, size.BSS)
|
||||||
|
}
|
||||||
|
return output.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the -size=full flag attributes binary size to the correct package
|
// Check that the -size=full flag attributes binary size to the correct package
|
||||||
@@ -85,7 +163,6 @@ func TestSizeFull(t *testing.T) {
|
|||||||
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
||||||
|
|
||||||
for _, target := range tests {
|
for _, target := range tests {
|
||||||
target := target
|
|
||||||
t.Run(target, func(t *testing.T) {
|
t.Run(target, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -99,7 +176,7 @@ func TestSizeFull(t *testing.T) {
|
|||||||
t.Fatal("could not read program size:", err)
|
t.Fatal("could not read program size:", err)
|
||||||
}
|
}
|
||||||
for _, pkg := range sizes.sortedPackageNames() {
|
for _, pkg := range sizes.sortedPackageNames() {
|
||||||
if pkg == "(padding)" || pkg == "(unknown)" {
|
if pkg == "(padding)" || pkg == "(unknown)" || pkg == "Go types" {
|
||||||
// TODO: correctly attribute all unknown binary size.
|
// TODO: correctly attribute all unknown binary size.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -116,6 +193,15 @@ func TestSizeFull(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildBinary(t *testing.T, targetString, pkgName string) BuildResult {
|
func buildBinary(t *testing.T, targetString, pkgName string) BuildResult {
|
||||||
|
t.Helper()
|
||||||
|
result, err := buildBinaryInDir(targetString, pkgName, t.TempDir())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBinaryInDir(targetString, pkgName, tmpdir string) (BuildResult, error) {
|
||||||
options := compileopts.Options{
|
options := compileopts.Options{
|
||||||
Target: targetString,
|
Target: targetString,
|
||||||
Opt: "z",
|
Opt: "z",
|
||||||
@@ -126,15 +212,15 @@ func buildBinary(t *testing.T, targetString, pkgName string) BuildResult {
|
|||||||
}
|
}
|
||||||
target, err := compileopts.LoadTarget(&options)
|
target, err := compileopts.LoadTarget(&options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not load target:", err)
|
return BuildResult{}, fmt.Errorf("could not load target: %w", err)
|
||||||
}
|
}
|
||||||
config := &compileopts.Config{
|
config := &compileopts.Config{
|
||||||
Options: &options,
|
Options: &options,
|
||||||
Target: target,
|
Target: target,
|
||||||
}
|
}
|
||||||
result, err := Build(pkgName, "", t.TempDir(), config)
|
result, err := Build(pkgName, "", tmpdir, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not build:", err)
|
return BuildResult{}, fmt.Errorf("could not build: %w", err)
|
||||||
}
|
}
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
target package code rodata data bss
|
||||||
|
hifive1b examples/echo 4321 323 0 2268
|
||||||
|
microbit examples/serial 2842 382 8 2264
|
||||||
|
wioterminal examples/pininterrupt 8039 1665 132 7496
|
||||||
@@ -28,7 +28,7 @@ func RunTool(tool string, args ...string) error {
|
|||||||
var cflag *C.char
|
var cflag *C.char
|
||||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||||
defer C.free(buf)
|
defer C.free(buf)
|
||||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)]
|
cflags := unsafe.Slice((**C.char)(buf), len(args))
|
||||||
for i, flag := range args {
|
for i, flag := range args {
|
||||||
cflag := C.CString(flag)
|
cflag := C.CString(flag)
|
||||||
cflags[i] = cflag
|
cflags[i] = cflag
|
||||||
|
|||||||
+9
-3
@@ -116,18 +116,24 @@ func parseLLDErrors(text string) error {
|
|||||||
// This can happen in some cases like with CGo and //go:linkname tricker.
|
// This can happen in some cases like with CGo and //go:linkname tricker.
|
||||||
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
||||||
symbolName := matches[2]
|
symbolName := matches[2]
|
||||||
for _, line := range strings.Split(message, "\n") {
|
for line := range strings.SplitSeq(message, "\n") {
|
||||||
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
||||||
if matches != nil {
|
if matches != nil {
|
||||||
parsedError = true
|
parsedError = true
|
||||||
line, _ := strconv.Atoi(matches[3])
|
line, _ := strconv.Atoi(matches[3])
|
||||||
// TODO: detect common mistakes like -gc=none?
|
msg := "linker could not find symbol " + symbolName
|
||||||
|
switch symbolName {
|
||||||
|
case "runtime.alloc":
|
||||||
|
msg = "object allocated on the heap with -gc=none"
|
||||||
|
case "runtime.alloc_noheap":
|
||||||
|
msg = "object allocated on the heap in //go:noheap function"
|
||||||
|
}
|
||||||
linkErrors = append(linkErrors, scanner.Error{
|
linkErrors = append(linkErrors, scanner.Error{
|
||||||
Pos: token.Position{
|
Pos: token.Position{
|
||||||
Filename: matches[2],
|
Filename: matches[2],
|
||||||
Line: line,
|
Line: line,
|
||||||
},
|
},
|
||||||
Msg: "linker could not find symbol " + symbolName,
|
Msg: msg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byt
|
|||||||
}
|
}
|
||||||
bl.SetNumBlocks(len(blocks))
|
bl.SetNumBlocks(len(blocks))
|
||||||
|
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i := range blocks {
|
||||||
bl.SetBlockNo(i)
|
bl.SetBlockNo(i)
|
||||||
bl.SetData(blocks[i])
|
bl.SetData(blocks[i])
|
||||||
|
|
||||||
|
|||||||
+10
-30
@@ -26,10 +26,6 @@ import (
|
|||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Function that's only defined in Go 1.22.
|
|
||||||
var setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// cgoPackage holds all CGo-related information of a package.
|
// cgoPackage holds all CGo-related information of a package.
|
||||||
type cgoPackage struct {
|
type cgoPackage struct {
|
||||||
generated *ast.File
|
generated *ast.File
|
||||||
@@ -44,7 +40,7 @@ type cgoPackage struct {
|
|||||||
tokenFiles map[string]*token.File
|
tokenFiles map[string]*token.File
|
||||||
definedGlobally map[string]ast.Node
|
definedGlobally map[string]ast.Node
|
||||||
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
||||||
anonDecls map[interface{}]string
|
anonDecls map[any]string
|
||||||
cflags []string // CFlags from #cgo lines
|
cflags []string // CFlags from #cgo lines
|
||||||
ldflags []string // LDFlags from #cgo lines
|
ldflags []string // LDFlags from #cgo lines
|
||||||
visitedFiles map[string][]byte
|
visitedFiles map[string][]byte
|
||||||
@@ -263,7 +259,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
tokenFiles: map[string]*token.File{},
|
tokenFiles: map[string]*token.File{},
|
||||||
definedGlobally: map[string]ast.Node{},
|
definedGlobally: map[string]ast.Node{},
|
||||||
noescapingFuncs: map[string]*noescapingFunc{},
|
noescapingFuncs: map[string]*noescapingFunc{},
|
||||||
anonDecls: map[interface{}]string{},
|
anonDecls: map[any]string{},
|
||||||
visitedFiles: map[string][]byte{},
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +302,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Find `import "C"` C fragments in the file.
|
// Find `import "C"` C fragments in the file.
|
||||||
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
|
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
var cgoHeader string
|
var cgoHeader strings.Builder
|
||||||
for i := 0; i < len(f.Decls); i++ {
|
for i := 0; i < len(f.Decls); i++ {
|
||||||
decl := f.Decls[i]
|
decl := f.Decls[i]
|
||||||
genDecl, ok := decl.(*ast.GenDecl)
|
genDecl, ok := decl.(*ast.GenDecl)
|
||||||
@@ -341,7 +337,8 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Iterate through all parts of the CGo header. Note that every //
|
// Iterate through all parts of the CGo header. Note that every //
|
||||||
// line is a new comment.
|
// line is a new comment.
|
||||||
position := fset.Position(genDecl.Doc.Pos())
|
position := fset.Position(genDecl.Doc.Pos())
|
||||||
fragment := fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)
|
var fragment strings.Builder
|
||||||
|
fragment.WriteString(fmt.Sprintf("# %d %#v\n", position.Line, position.Filename))
|
||||||
for _, comment := range genDecl.Doc.List {
|
for _, comment := range genDecl.Doc.List {
|
||||||
// Find all #cgo lines, extract and use their contents, and
|
// Find all #cgo lines, extract and use their contents, and
|
||||||
// replace the lines with spaces (to preserve locations).
|
// replace the lines with spaces (to preserve locations).
|
||||||
@@ -358,12 +355,13 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
} else { // comment
|
} else { // comment
|
||||||
c = " " + c[2:len(c)-2]
|
c = " " + c[2:len(c)-2]
|
||||||
}
|
}
|
||||||
fragment += c + "\n"
|
fragment.WriteString(c)
|
||||||
|
fragment.WriteByte('\n')
|
||||||
}
|
}
|
||||||
cgoHeader += fragment
|
cgoHeader.WriteString(fragment.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
p.cgoHeaders[i] = cgoHeader
|
p.cgoHeaders[i] = cgoHeader.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define CFlags that will be used while parsing the package.
|
// Define CFlags that will be used while parsing the package.
|
||||||
@@ -654,7 +652,6 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "union",
|
Name: "union",
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
Sel: &ast.Ident{
|
Sel: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
@@ -708,7 +705,6 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -764,7 +760,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
Sel: &ast.Ident{
|
Sel: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
@@ -811,11 +806,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
|||||||
{
|
{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: "s",
|
|
||||||
Decl: nil,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: &ast.StarExpr{
|
Type: &ast.StarExpr{
|
||||||
@@ -823,7 +813,6 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -881,7 +870,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
Sel: &ast.Ident{
|
Sel: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
@@ -964,11 +952,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
{
|
{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: "s",
|
|
||||||
Decl: nil,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: &ast.StarExpr{
|
Type: &ast.StarExpr{
|
||||||
@@ -976,7 +959,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -997,7 +979,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
{
|
{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "value",
|
Name: "value",
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: bitfield.field.Type,
|
Type: bitfield.field.Type,
|
||||||
@@ -1015,7 +996,6 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: nil,
|
|
||||||
},
|
},
|
||||||
Sel: &ast.Ident{
|
Sel: &ast.Ident{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
@@ -1239,7 +1219,7 @@ func getPos(node ast.Node) token.Pos {
|
|||||||
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
||||||
// declaration. This is used for structs, unions, and enums that are often
|
// declaration. This is used for structs, unions, and enums that are often
|
||||||
// defined without a name and used in a typedef.
|
// defined without a name and used in a typedef.
|
||||||
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf any) string {
|
||||||
if name, ok := p.anonDecls[itf]; ok {
|
if name, ok := p.anonDecls[itf]; ok {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
//go:build go1.22
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
// Code specifically for Go 1.22.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
setASTFileFields = func(f *ast.File, start, end token.Pos) {
|
|
||||||
f.FileStart = start
|
|
||||||
f.FileEnd = end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,7 +45,6 @@ func TestCGo(t *testing.T) {
|
|||||||
"flags",
|
"flags",
|
||||||
"const",
|
"const",
|
||||||
} {
|
} {
|
||||||
name := name // avoid a race condition
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
// Read the AST in memory.
|
// Read the AST in memory.
|
||||||
path := filepath.Join("testdata", name+".go")
|
path := filepath.Join("testdata", name+".go")
|
||||||
|
|||||||
+18
-70
@@ -130,7 +130,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
// convert Go slice of strings to C array of strings.
|
// convert Go slice of strings to C array of strings.
|
||||||
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
||||||
defer C.free(cmdargsC)
|
defer C.free(cmdargsC)
|
||||||
cmdargs := (*[1 << 16]*C.char)(cmdargsC)
|
cmdargs := unsafe.Slice((**C.char)(cmdargsC), len(cflags))
|
||||||
for i, cflag := range cflags {
|
for i, cflag := range cflags {
|
||||||
s := C.CString(cflag)
|
s := C.CString(cflag)
|
||||||
cmdargs[i] = s
|
cmdargs[i] = s
|
||||||
@@ -160,7 +160,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
pos := f.getClangLocationPosition(location, unit)
|
pos := f.getClangLocationPosition(location, unit)
|
||||||
f.addError(pos, severity+": "+spelling)
|
f.addError(pos, severity+": "+spelling)
|
||||||
}
|
}
|
||||||
for i := 0; i < numDiagnostics; i++ {
|
for i := range numDiagnostics {
|
||||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||||
addDiagnostic(diagnostic)
|
addDiagnostic(diagnostic)
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
// Sanity check. This should (hopefully) never trigger.
|
// Sanity check. This should (hopefully) never trigger.
|
||||||
panic("libclang: file contents was not loaded")
|
panic("libclang: file contents was not loaded")
|
||||||
}
|
}
|
||||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
data := unsafe.Slice((*byte)(unsafe.Pointer(rawData)), size)
|
||||||
|
|
||||||
// Hash the contents if it isn't hashed yet.
|
// Hash the contents if it isn't hashed yet.
|
||||||
if _, ok := f.visitedFiles[path]; !ok {
|
if _, ok := f.visitedFiles[path]; !ok {
|
||||||
@@ -217,10 +217,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Fun,
|
|
||||||
Name: "_Cgo_" + name,
|
|
||||||
}
|
|
||||||
exportName := name
|
exportName := name
|
||||||
localName := name
|
localName := name
|
||||||
var stringSignature string
|
var stringSignature string
|
||||||
@@ -258,7 +254,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "_Cgo_" + localName,
|
Name: "_Cgo_" + localName,
|
||||||
Obj: obj,
|
|
||||||
},
|
},
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
Func: pos,
|
Func: pos,
|
||||||
@@ -283,7 +278,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
Text: strings.Join(doc, "\n"),
|
Text: strings.Join(doc, "\n"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := range numArgs {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||||
@@ -295,11 +290,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
{
|
{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: argName,
|
Name: argName,
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: argName,
|
|
||||||
Decl: decl,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: f.makeDecayingASTType(argType, pos),
|
Type: f.makeDecayingASTType(argType, pos),
|
||||||
@@ -315,7 +305,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obj.Decl = decl
|
|
||||||
return decl, stringSignature
|
return decl, stringSignature
|
||||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||||
typ := f.makeASTRecordType(c, pos)
|
typ := f.makeASTRecordType(c, pos)
|
||||||
@@ -325,39 +314,25 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
// Convert to a single-field struct type.
|
// Convert to a single-field struct type.
|
||||||
typeExpr = f.makeUnionField(typ)
|
typeExpr = f.makeUnionField(typ)
|
||||||
}
|
}
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: typ.pos,
|
NamePos: typ.pos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: obj,
|
|
||||||
},
|
},
|
||||||
Type: typeExpr,
|
Type: typeExpr,
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
|
||||||
return typeSpec, typ
|
return typeSpec, typ
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typeName := "_Cgo_" + name
|
typeName := "_Cgo_" + name
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: obj,
|
|
||||||
},
|
},
|
||||||
Type: f.makeASTType(underlyingType, pos),
|
Assign: pos,
|
||||||
|
Type: f.makeASTType(underlyingType, pos),
|
||||||
}
|
}
|
||||||
if underlyingType.kind != C.CXType_Enum {
|
|
||||||
typeSpec.Assign = pos
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
return typeSpec, nil
|
return typeSpec, nil
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
@@ -376,19 +351,13 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: "_Cgo_" + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{{
|
Names: []*ast.Ident{{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "_Cgo_" + name,
|
Name: "_Cgo_" + name,
|
||||||
Obj: obj,
|
|
||||||
}},
|
}},
|
||||||
Type: typeExpr,
|
Type: typeExpr,
|
||||||
}
|
}
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
return gen, nil
|
return gen, nil
|
||||||
case C.CXCursor_MacroDefinition:
|
case C.CXCursor_MacroDefinition:
|
||||||
@@ -405,26 +374,16 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
Lparen: token.NoPos,
|
Lparen: token.NoPos,
|
||||||
Rparen: token.NoPos,
|
Rparen: token.NoPos,
|
||||||
}
|
}
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Con,
|
|
||||||
Name: "_Cgo_" + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{{
|
Names: []*ast.Ident{{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "_Cgo_" + name,
|
Name: "_Cgo_" + name,
|
||||||
Obj: obj,
|
|
||||||
}},
|
}},
|
||||||
Values: []ast.Expr{expr},
|
Values: []ast.Expr{expr},
|
||||||
}
|
}
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
return gen, nil
|
return gen, nil
|
||||||
case C.CXCursor_EnumDecl:
|
case C.CXCursor_EnumDecl:
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: "_Cgo_" + name,
|
|
||||||
}
|
|
||||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||||
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||||
// instead of types such as C.int, which are used here.
|
// instead of types such as C.int, which are used here.
|
||||||
@@ -432,12 +391,10 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "_Cgo_" + name,
|
Name: "_Cgo_" + name,
|
||||||
Obj: obj,
|
|
||||||
},
|
},
|
||||||
Assign: pos,
|
Assign: pos,
|
||||||
Type: f.makeASTType(underlying, pos),
|
Type: f.makeASTType(underlying, pos),
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
|
||||||
return typeSpec, nil
|
return typeSpec, nil
|
||||||
case C.CXCursor_EnumConstantDecl:
|
case C.CXCursor_EnumConstantDecl:
|
||||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||||
@@ -452,19 +409,13 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
Lparen: token.NoPos,
|
Lparen: token.NoPos,
|
||||||
Rparen: token.NoPos,
|
Rparen: token.NoPos,
|
||||||
}
|
}
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Con,
|
|
||||||
Name: "_Cgo_" + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{{
|
Names: []*ast.Ident{{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "_Cgo_" + name,
|
Name: "_Cgo_" + name,
|
||||||
Obj: obj,
|
|
||||||
}},
|
}},
|
||||||
Values: []ast.Expr{expr},
|
Values: []ast.Expr{expr},
|
||||||
}
|
}
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
return gen, nil
|
return gen, nil
|
||||||
default:
|
default:
|
||||||
@@ -581,7 +532,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
|
|
||||||
// Get the precise location in the source code. Used for uniquely identifying
|
// Get the precise location in the source code. Used for uniquely identifying
|
||||||
// source locations.
|
// source locations.
|
||||||
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} {
|
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) any {
|
||||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
var file C.CXFile
|
var file C.CXFile
|
||||||
var line C.unsigned
|
var line C.unsigned
|
||||||
@@ -624,7 +575,7 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
|||||||
// now by reading the file from libclang.
|
// now by reading the file from libclang.
|
||||||
var size C.size_t
|
var size C.size_t
|
||||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
|
source := unsafe.Slice((*byte)(unsafe.Pointer(sourcePtr)), size)
|
||||||
lines := []int{0}
|
lines := []int{0}
|
||||||
for i := 0; i < len(source)-1; i++ {
|
for i := 0; i < len(source)-1; i++ {
|
||||||
if source[i] == '\n' {
|
if source[i] == '\n' {
|
||||||
@@ -639,7 +590,8 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
|||||||
Package: f.Pos(0),
|
Package: f.Pos(0),
|
||||||
Name: ast.NewIdent(p.packageName),
|
Name: ast.NewIdent(p.packageName),
|
||||||
}
|
}
|
||||||
setASTFileFields(astFile, f.Pos(0), f.Pos(int(size)))
|
astFile.FileStart = f.Pos(0)
|
||||||
|
astFile.FileEnd = f.Pos(int(size))
|
||||||
p.cgoFiles = append(p.cgoFiles, astFile)
|
p.cgoFiles = append(p.cgoFiles, astFile)
|
||||||
}
|
}
|
||||||
positionFile := p.tokenFiles[filename]
|
positionFile := p.tokenFiles[filename]
|
||||||
@@ -852,6 +804,14 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||||
typeName = "<unknown>"
|
typeName = "<unknown>"
|
||||||
}
|
}
|
||||||
|
case C.CXType_Unexposed:
|
||||||
|
// LLVM 22+ may report certain builtin type aliases (e.g. __size_t)
|
||||||
|
// as Unexposed. Resolve via the canonical type.
|
||||||
|
canonical := C.clang_getCanonicalType(typ)
|
||||||
|
if canonical.kind != C.CXType_Unexposed && canonical.kind != C.CXType_Invalid {
|
||||||
|
return f.makeASTType(canonical, pos)
|
||||||
|
}
|
||||||
|
// If still unexposed, fall through to the error below.
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
@@ -956,22 +916,16 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct an *ast.TypeSpec for this type.
|
// Construct an *ast.TypeSpec for this type.
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
spec := &ast.TypeSpec{
|
spec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: obj,
|
|
||||||
},
|
},
|
||||||
Type: &ast.Ident{
|
Type: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: goName,
|
Name: goName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
obj.Decl = spec
|
|
||||||
return spec
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,7 +1058,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
pos: prevField.Names[0].NamePos,
|
pos: prevField.Names[0].NamePos,
|
||||||
})
|
})
|
||||||
prevField.Names[0].Name = bitfieldName
|
prevField.Names[0].Name = bitfieldName
|
||||||
prevField.Names[0].Obj.Name = bitfieldName
|
|
||||||
}
|
}
|
||||||
prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1]
|
prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1]
|
||||||
prevBitfield.endBit = bitfieldOffset
|
prevBitfield.endBit = bitfieldOffset
|
||||||
@@ -1121,11 +1074,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
{
|
{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: name,
|
|
||||||
Decl: field,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fieldList.List = append(fieldList.List, field)
|
fieldList.List = append(fieldList.List, field)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include
|
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include -I/usr/lib64/llvm19/include
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm21 && !llvm22
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include
|
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include -I/usr/lib64/llvm20/include
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !byollvm && llvm21
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/include/llvm-21 -I/usr/include/llvm-c-21 -I/usr/lib/llvm-21/include -I/usr/lib64/llvm21/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@21/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@21/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm21/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-21/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@21/lib -lclang
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@21/lib -lclang
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm21/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !byollvm && llvm22
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/include/llvm-22 -I/usr/include/llvm-c-22 -I/usr/lib/llvm-22/include -I/usr/lib64/llvm22/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@22/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@22/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm22/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-22/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@22/lib -lclang
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@22/lib -lclang
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm22/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
@@ -78,6 +78,7 @@ var validCompilerFlags = []*regexp.Regexp{
|
|||||||
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
re(`-f(no-)?(pic|PIC|pie|PIE)`),
|
||||||
re(`-f(no-)?plt`),
|
re(`-f(no-)?plt`),
|
||||||
re(`-f(no-)?rtti`),
|
re(`-f(no-)?rtti`),
|
||||||
|
re(`-f(no-)?short-enums`),
|
||||||
re(`-f(no-)?split-stack`),
|
re(`-f(no-)?split-stack`),
|
||||||
re(`-f(no-)?stack-(.+)`),
|
re(`-f(no-)?stack-(.+)`),
|
||||||
re(`-f(no-)?strict-aliasing`),
|
re(`-f(no-)?strict-aliasing`),
|
||||||
|
|||||||
+4
-4
@@ -12,17 +12,17 @@ import "C"
|
|||||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||||
// pointer but only a C pointer.
|
// pointer but only a C pointer.
|
||||||
type refMap struct {
|
type refMap struct {
|
||||||
refs map[unsafe.Pointer]interface{}
|
refs map[unsafe.Pointer]any
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||||
// be removed using Remove to avoid memory leaks.
|
// be removed using Remove to avoid memory leaks.
|
||||||
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
func (m *refMap) Put(v any) unsafe.Pointer {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
if m.refs == nil {
|
if m.refs == nil {
|
||||||
m.refs = make(map[unsafe.Pointer]interface{}, 1)
|
m.refs = make(map[unsafe.Pointer]any, 1)
|
||||||
}
|
}
|
||||||
ref := C.malloc(1)
|
ref := C.malloc(1)
|
||||||
m.refs[ref] = v
|
m.refs[ref] = v
|
||||||
@@ -31,7 +31,7 @@ func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
|||||||
|
|
||||||
// Get returns a stored value previously inserted with Put. Use the same
|
// Get returns a stored value previously inserted with Put. Use the same
|
||||||
// reference as you got from Put.
|
// reference as you got from Put.
|
||||||
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
func (m *refMap) Get(ref unsafe.Pointer) any {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
return m.refs[ref]
|
return m.refs[ref]
|
||||||
|
|||||||
+30
-17
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -60,13 +61,15 @@ func (c *Config) BuildMode() string {
|
|||||||
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
|
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
|
||||||
// will be returned.
|
// will be returned.
|
||||||
func (c *Config) Features() string {
|
func (c *Config) Features() string {
|
||||||
|
var features string
|
||||||
if c.Target.Features == "" {
|
if c.Target.Features == "" {
|
||||||
return c.Options.LLVMFeatures
|
features = c.Options.LLVMFeatures
|
||||||
|
} else if c.Options.LLVMFeatures == "" {
|
||||||
|
features = c.Target.Features
|
||||||
|
} else {
|
||||||
|
features = c.Target.Features + "," + c.Options.LLVMFeatures
|
||||||
}
|
}
|
||||||
if c.Options.LLVMFeatures == "" {
|
return patchFeatures(features)
|
||||||
return c.Target.Features
|
|
||||||
}
|
|
||||||
return c.Target.Features + "," + c.Options.LLVMFeatures
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
||||||
@@ -140,13 +143,7 @@ func (c *Config) GC() string {
|
|||||||
func (c *Config) NeedsStackObjects() bool {
|
func (c *Config) NeedsStackObjects() bool {
|
||||||
switch c.GC() {
|
switch c.GC() {
|
||||||
case "conservative", "custom", "precise", "boehm":
|
case "conservative", "custom", "precise", "boehm":
|
||||||
for _, tag := range c.BuildTags() {
|
return slices.Contains(c.BuildTags(), "tinygo.wasm")
|
||||||
if tag == "tinygo.wasm" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -226,7 +223,7 @@ func (c *Config) StackSize() uint64 {
|
|||||||
|
|
||||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||||
func (c *Config) MaxStackAlloc() uint64 {
|
func (c *Config) MaxStackAlloc() uint64 {
|
||||||
if c.StackSize() > 32*1024 {
|
if c.StackSize() >= 16*1024 {
|
||||||
return 1024
|
return 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +242,7 @@ func (c *Config) RP2040BootPatch() bool {
|
|||||||
// Return a canonicalized architecture name, so we don't have to deal with arm*
|
// Return a canonicalized architecture name, so we don't have to deal with arm*
|
||||||
// vs thumb* vs arm64.
|
// vs thumb* vs arm64.
|
||||||
func CanonicalArchName(triple string) string {
|
func CanonicalArchName(triple string) string {
|
||||||
arch := strings.Split(triple, "-")[0]
|
arch, _, _ := strings.Cut(triple, "-")
|
||||||
if arch == "arm64" {
|
if arch == "arm64" {
|
||||||
return "aarch64"
|
return "aarch64"
|
||||||
}
|
}
|
||||||
@@ -352,7 +349,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
|||||||
// Use the same optimization level as TinyGo.
|
// Use the same optimization level as TinyGo.
|
||||||
cflags = append(cflags, "-O"+c.Options.Opt)
|
cflags = append(cflags, "-O"+c.Options.Opt)
|
||||||
// Set the LLVM target triple.
|
// Set the LLVM target triple.
|
||||||
cflags = append(cflags, "--target="+c.Triple())
|
cflags = append(cflags, "--target="+ClangTriple(c.Triple()))
|
||||||
// Set the -mcpu (or similar) flag.
|
// Set the -mcpu (or similar) flag.
|
||||||
if c.Target.CPU != "" {
|
if c.Target.CPU != "" {
|
||||||
if c.GOARCH() == "amd64" || c.GOARCH() == "386" {
|
if c.GOARCH() == "amd64" || c.GOARCH() == "386" {
|
||||||
@@ -466,6 +463,22 @@ func (c *Config) LDFlags() []string {
|
|||||||
return ldflags
|
return ldflags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LinkerFlavor returns how the configured linker should be driven.
|
||||||
|
// Usually this is derived from GOOS, but targets may override it explicitly.
|
||||||
|
func (c *Config) LinkerFlavor() string {
|
||||||
|
if c.Target.LinkerFlavor != "" {
|
||||||
|
return c.Target.LinkerFlavor
|
||||||
|
}
|
||||||
|
switch c.GOOS() {
|
||||||
|
case "windows":
|
||||||
|
return "coff"
|
||||||
|
case "darwin":
|
||||||
|
return "darwin"
|
||||||
|
default:
|
||||||
|
return "gnu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ExtraFiles returns the list of extra files to be built and linked with the
|
// ExtraFiles returns the list of extra files to be built and linked with the
|
||||||
// executable. This can include extra C and assembly files.
|
// executable. This can include extra C and assembly files.
|
||||||
func (c *Config) ExtraFiles() []string {
|
func (c *Config) ExtraFiles() []string {
|
||||||
@@ -536,10 +549,10 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
|||||||
case "":
|
case "":
|
||||||
// No configuration supplied.
|
// No configuration supplied.
|
||||||
return c.Target.FlashMethod, c.Target.OpenOCDInterface
|
return c.Target.FlashMethod, c.Target.OpenOCDInterface
|
||||||
case "openocd", "msd", "command":
|
case "openocd", "msd", "command", "adb":
|
||||||
// The -programmer flag only specifies the flash method.
|
// The -programmer flag only specifies the flash method.
|
||||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||||
case "bmp":
|
case "bmp", "probe-rs":
|
||||||
// The -programmer flag only specifies the flash method.
|
// The -programmer flag only specifies the flash method.
|
||||||
return c.Options.Programmer, ""
|
return c.Options.Programmer, ""
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !llvm22 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
||||||
|
|
||||||
|
package compileopts
|
||||||
|
|
||||||
|
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||||
|
// For LLVM 20/21, features in the target JSON files are already correct.
|
||||||
|
func patchFeatures(features string) string {
|
||||||
|
return features
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//go:build llvm22
|
||||||
|
|
||||||
|
package compileopts
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||||
|
// LLVM 22 renamed several Xtensa target features.
|
||||||
|
func patchFeatures(features string) string {
|
||||||
|
// Xtensa feature renames in LLVM 22:
|
||||||
|
// atomctl → (removed, no direct replacement)
|
||||||
|
// memctl → (removed, no direct replacement)
|
||||||
|
// esp32s3 → esp32s3ops
|
||||||
|
// timerint → timers3 (for esp32/esp32s3) or timers1 (for esp8266)
|
||||||
|
// Since we can't distinguish which timer variant at this level,
|
||||||
|
// just remove the obsolete features. The CPU definition already
|
||||||
|
// implies the correct features in LLVM 22.
|
||||||
|
replacer := strings.NewReplacer(
|
||||||
|
"+atomctl,", "",
|
||||||
|
"+memctl,", "",
|
||||||
|
"+esp32s3,", "+esp32s3ops,",
|
||||||
|
"+timerint,", "",
|
||||||
|
",+timerint", "",
|
||||||
|
)
|
||||||
|
return replacer.Replace(features)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19
|
||||||
|
|
||||||
|
package compileopts
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// patchFeatures applies LLVM-version-specific feature name mappings.
|
||||||
|
// LLVM 19 and earlier do not have +bulk-memory-opt or
|
||||||
|
// +call-indirect-overlong for WebAssembly (added in LLVM 20).
|
||||||
|
func patchFeatures(features string) string {
|
||||||
|
features = strings.ReplaceAll(features, ",+bulk-memory-opt", "")
|
||||||
|
features = strings.ReplaceAll(features, "+bulk-memory-opt,", "")
|
||||||
|
features = strings.ReplaceAll(features, ",+call-indirect-overlong", "")
|
||||||
|
features = strings.ReplaceAll(features, "+call-indirect-overlong,", "")
|
||||||
|
return features
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package compileopts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/build/constraint"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestFinalizerRunnerSchedulerCoverage verifies that each scheduler selects one runner file.
|
||||||
|
// It uses validSchedulerOptions so new schedulers are included.
|
||||||
|
func TestFinalizerRunnerSchedulerCoverage(t *testing.T) {
|
||||||
|
files := []string{
|
||||||
|
"gc_finalizer_sched.go",
|
||||||
|
"gc_finalizer_sched_none.go",
|
||||||
|
"gc_finalizer_sched_other.go",
|
||||||
|
}
|
||||||
|
exprs := make([]constraint.Expr, len(files))
|
||||||
|
for i, name := range files {
|
||||||
|
exprs[i] = readBuildConstraint(t, filepath.Join("..", "src", "runtime", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sched := range validSchedulerOptions {
|
||||||
|
// The finalizer table exists under block GCs.
|
||||||
|
// gc.conservative satisfies the GC condition in every constraint.
|
||||||
|
tags := map[string]bool{
|
||||||
|
"gc.conservative": true,
|
||||||
|
"scheduler." + sched: true,
|
||||||
|
}
|
||||||
|
var matched []string
|
||||||
|
for i, expr := range exprs {
|
||||||
|
if expr.Eval(func(tag string) bool { return tags[tag] }) {
|
||||||
|
matched = append(matched, files[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(matched) != 1 {
|
||||||
|
t.Errorf("scheduler.%s: spawnFinalizerRunner defined in %d files %v, want exactly 1",
|
||||||
|
sched, len(matched), matched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBuildConstraint(t *testing.T, path string) constraint.Expr {
|
||||||
|
t.Helper()
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if constraint.IsGoBuild(line) {
|
||||||
|
expr, err := constraint.Parse(line)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %v", path, err)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
if line != "" && !strings.HasPrefix(line, "//") {
|
||||||
|
break // reached code before any //go:build line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Fatalf("%s: no //go:build line found", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+49
-54
@@ -3,6 +3,7 @@ package compileopts
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -21,50 +22,53 @@ var (
|
|||||||
// usually passed from the command line, but can also be passed in environment
|
// usually passed from the command line, but can also be passed in environment
|
||||||
// variables for example.
|
// variables for example.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
GOOS string // environment variable
|
GOOS string // environment variable
|
||||||
GOARCH string // environment variable
|
GOARCH string // environment variable
|
||||||
GOARM string // environment variable (only used with GOARCH=arm)
|
GOARM string // environment variable (only used with GOARCH=arm)
|
||||||
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
|
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
|
Directory string // working dir, leave it unset to use the current working dir
|
||||||
Target string
|
Target string
|
||||||
BuildMode string // -buildmode flag
|
BuildMode string // -buildmode flag
|
||||||
Opt string
|
Opt string
|
||||||
GC string
|
GC string
|
||||||
PanicStrategy string
|
PanicStrategy string
|
||||||
Scheduler string
|
Scheduler string
|
||||||
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
StackSize uint64 // goroutine stack size (if none could be automatically determined)
|
||||||
Serial string
|
Serial string
|
||||||
Work bool // -work flag to print temporary build directory
|
Work bool // -work flag to print temporary build directory
|
||||||
InterpTimeout time.Duration
|
InterpTimeout time.Duration
|
||||||
PrintIR bool
|
InterpMaxLoopIterations int
|
||||||
DumpSSA bool
|
PrintIR bool
|
||||||
VerifyIR bool
|
DumpSSA bool
|
||||||
SkipDWARF bool
|
VerifyIR bool
|
||||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
SkipDWARF bool
|
||||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||||
Debug bool
|
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||||
Nobounds bool
|
Debug bool
|
||||||
PrintSizes string
|
Nobounds bool
|
||||||
PrintAllocs *regexp.Regexp // regexp string
|
PrintSizes string
|
||||||
PrintStacks bool
|
PrintAllocs *regexp.Regexp // regexp string
|
||||||
Tags []string
|
PrintAllocsCover bool // emit allocs in go coverage tool format
|
||||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
PrintStacks bool
|
||||||
TestConfig TestConfig
|
Tags []string
|
||||||
Programmer string
|
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||||
OpenOCDCommands []string
|
TestConfig TestConfig
|
||||||
LLVMFeatures string
|
Programmer string
|
||||||
Monitor bool
|
OpenOCDCommands []string
|
||||||
BaudRate int
|
LLVMFeatures string
|
||||||
Timeout time.Duration
|
Monitor bool
|
||||||
WITPackage string // pass through to wasm-tools component embed invocation
|
BaudRate int
|
||||||
WITWorld string // pass through to wasm-tools component embed -w option
|
Timeout time.Duration
|
||||||
ExtLDFlags []string
|
WITPackage string // pass through to wasm-tools component embed invocation
|
||||||
|
WITWorld string // pass through to wasm-tools component embed -w option
|
||||||
|
ExtLDFlags []string
|
||||||
|
GoCompatibility bool // enable to check for Go version compatibility
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||||
func (o *Options) Verify() error {
|
func (o *Options) Verify() error {
|
||||||
if o.BuildMode != "" {
|
if o.BuildMode != "" {
|
||||||
valid := isInArray(validBuildModeOptions, o.BuildMode)
|
valid := slices.Contains(validBuildModeOptions, o.BuildMode)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
|
||||||
o.BuildMode,
|
o.BuildMode,
|
||||||
@@ -72,7 +76,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if o.GC != "" {
|
if o.GC != "" {
|
||||||
valid := isInArray(validGCOptions, o.GC)
|
valid := slices.Contains(validGCOptions, o.GC)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
||||||
o.GC,
|
o.GC,
|
||||||
@@ -81,7 +85,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Scheduler != "" {
|
if o.Scheduler != "" {
|
||||||
valid := isInArray(validSchedulerOptions, o.Scheduler)
|
valid := slices.Contains(validSchedulerOptions, o.Scheduler)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
||||||
o.Scheduler,
|
o.Scheduler,
|
||||||
@@ -90,7 +94,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Serial != "" {
|
if o.Serial != "" {
|
||||||
valid := isInArray(validSerialOptions, o.Serial)
|
valid := slices.Contains(validSerialOptions, o.Serial)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
||||||
o.Serial,
|
o.Serial,
|
||||||
@@ -99,7 +103,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.PrintSizes != "" {
|
if o.PrintSizes != "" {
|
||||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
valid := slices.Contains(validPrintSizeOptions, o.PrintSizes)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
||||||
o.PrintSizes,
|
o.PrintSizes,
|
||||||
@@ -108,7 +112,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.PanicStrategy != "" {
|
if o.PanicStrategy != "" {
|
||||||
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
|
valid := slices.Contains(validPanicStrategyOptions, o.PanicStrategy)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
||||||
o.PanicStrategy,
|
o.PanicStrategy,
|
||||||
@@ -117,19 +121,10 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Opt != "" {
|
if o.Opt != "" {
|
||||||
if !isInArray(validOptOptions, o.Opt) {
|
if !slices.Contains(validOptOptions, o.Opt) {
|
||||||
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInArray(arr []string, item string) bool {
|
|
||||||
for _, i := range arr {
|
|
||||||
if i == item {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
+26
-1
@@ -24,6 +24,7 @@ import (
|
|||||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||||
type TargetSpec struct {
|
type TargetSpec struct {
|
||||||
Inherits []string `json:"inherits,omitempty"`
|
Inherits []string `json:"inherits,omitempty"`
|
||||||
|
InheritableOnly bool `json:"inheritable-only"` // this target is only meant to be inherited from, not used directly
|
||||||
Triple string `json:"llvm-target,omitempty"`
|
Triple string `json:"llvm-target,omitempty"`
|
||||||
CPU string `json:"cpu,omitempty"`
|
CPU string `json:"cpu,omitempty"`
|
||||||
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
|
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
|
||||||
@@ -37,7 +38,8 @@ type TargetSpec struct {
|
|||||||
Scheduler string `json:"scheduler,omitempty"`
|
Scheduler string `json:"scheduler,omitempty"`
|
||||||
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
||||||
Linker string `json:"linker,omitempty"`
|
Linker string `json:"linker,omitempty"`
|
||||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
LinkerFlavor string `json:"linker-flavor,omitempty"` // how to drive the configured linker (for example: gnu, coff, darwin)
|
||||||
|
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||||
Libc string `json:"libc,omitempty"`
|
Libc string `json:"libc,omitempty"`
|
||||||
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
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.
|
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
||||||
@@ -63,6 +65,10 @@ type TargetSpec struct {
|
|||||||
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||||
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||||
JLinkDevice string `json:"jlink-device,omitempty"`
|
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||||
|
ADBPreCommands []string `json:"adb-pre-commands,omitempty"`
|
||||||
|
ADBPushRemote string `json:"adb-push-remote,omitempty"`
|
||||||
|
ADBPostCommands []string `json:"adb-post-commands,omitempty"`
|
||||||
|
ProbeRSChip string `json:"probe-rs-chip,omitempty"`
|
||||||
CodeModel string `json:"code-model,omitempty"`
|
CodeModel string `json:"code-model,omitempty"`
|
||||||
RelocationModel string `json:"relocation-model,omitempty"`
|
RelocationModel string `json:"relocation-model,omitempty"`
|
||||||
WITPackage string `json:"wit-package,omitempty"`
|
WITPackage string `json:"wit-package,omitempty"`
|
||||||
@@ -148,6 +154,11 @@ func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
|||||||
|
|
||||||
// resolveInherits loads inherited targets, recursively.
|
// resolveInherits loads inherited targets, recursively.
|
||||||
func (spec *TargetSpec) resolveInherits() error {
|
func (spec *TargetSpec) resolveInherits() error {
|
||||||
|
// Save InheritableOnly before resolving, since it must not propagate
|
||||||
|
// from parent to child (a board target should not become inheritable-only
|
||||||
|
// just because its parent processor target is).
|
||||||
|
inheritableOnly := spec.InheritableOnly
|
||||||
|
|
||||||
// First create a new spec with all the inherited properties.
|
// First create a new spec with all the inherited properties.
|
||||||
newSpec := &TargetSpec{}
|
newSpec := &TargetSpec{}
|
||||||
for _, name := range spec.Inherits {
|
for _, name := range spec.Inherits {
|
||||||
@@ -173,6 +184,9 @@ func (spec *TargetSpec) resolveInherits() error {
|
|||||||
}
|
}
|
||||||
*spec = *newSpec
|
*spec = *newSpec
|
||||||
|
|
||||||
|
// Restore InheritableOnly from the original spec, not from parents.
|
||||||
|
spec.InheritableOnly = inheritableOnly
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,10 +253,17 @@ func GetTargetSpecs() (map[string]*TargetSpec, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
path := filepath.Join(dir, entry.Name())
|
path := filepath.Join(dir, entry.Name())
|
||||||
|
|
||||||
spec, err := LoadTarget(&Options{Target: path})
|
spec, err := LoadTarget(&Options{Target: path})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not list target: %w", err)
|
return nil, fmt.Errorf("could not list target: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if spec.InheritableOnly {
|
||||||
|
// Skip targets that are only meant to be inherited from, not used directly.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||||
// This doesn't look like a regular target file, but rather like
|
// This doesn't look like a regular target file, but rather like
|
||||||
// a parent target (such as targets/cortex-m.json).
|
// a parent target (such as targets/cortex-m.json).
|
||||||
@@ -454,10 +475,12 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
"-m", "i386pep",
|
"-m", "i386pep",
|
||||||
"--image-base", "0x400000",
|
"--image-base", "0x400000",
|
||||||
)
|
)
|
||||||
|
spec.RTLib = "compiler-rt"
|
||||||
case "arm64":
|
case "arm64":
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-m", "arm64pe",
|
"-m", "arm64pe",
|
||||||
)
|
)
|
||||||
|
spec.RTLib = "compiler-rt"
|
||||||
}
|
}
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-Bdynamic",
|
"-Bdynamic",
|
||||||
@@ -465,6 +488,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
"--no-insert-timestamp",
|
"--no-insert-timestamp",
|
||||||
"--no-dynamicbase",
|
"--no-dynamicbase",
|
||||||
)
|
)
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||||
|
"src/runtime/runtime_windows.c")
|
||||||
case "wasm", "wasip1", "wasip2":
|
case "wasm", "wasip1", "wasip2":
|
||||||
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS)
|
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS)
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package compileopts
|
package compileopts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -23,6 +25,69 @@ func TestLoadTarget(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetTargetSpecs_InheritableOnlyTargetsExcluded(t *testing.T) {
|
||||||
|
specs, err := GetTargetSpecs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("GetTargetSpecs failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inheritable-only processor-level targets should not appear in the listing.
|
||||||
|
inheritableOnlyTargets := []string{"esp32", "esp32c3", "esp32s3", "esp8266", "rp2040", "rp2350", "rp2350b"}
|
||||||
|
for _, name := range inheritableOnlyTargets {
|
||||||
|
if _, ok := specs[name]; ok {
|
||||||
|
t.Errorf("inheritable-only target %q should not appear in GetTargetSpecs", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Board targets that inherit from inheritable-only targets should still appear.
|
||||||
|
boardTargets := []string{"esp32-coreboard-v2", "pico"}
|
||||||
|
for _, name := range boardTargets {
|
||||||
|
if _, ok := specs[name]; !ok {
|
||||||
|
t.Errorf("board target %q should appear in GetTargetSpecs", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadTarget_InheritableOnlyTargetStillLoadable(t *testing.T) {
|
||||||
|
// Inheritable-only targets should still be loadable directly (for building).
|
||||||
|
_, err := LoadTarget(&Options{Target: "esp32"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("LoadTarget should still load inheritable-only target esp32: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadPY32ConcreteTargets(t *testing.T) {
|
||||||
|
file, err := os.Open("../tools/gen-py32-targets/devices.csv")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
records, err := csv.NewReader(file).ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, record := range records[1:] {
|
||||||
|
part, core := record[0], record[2]
|
||||||
|
t.Run(part, func(t *testing.T) {
|
||||||
|
spec, err := LoadTarget(&Options{Target: part})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if spec.LinkerScript == "" {
|
||||||
|
t.Error("concrete target has no linker script")
|
||||||
|
}
|
||||||
|
wantCPU := "cortex-m0plus"
|
||||||
|
if core == "m4" {
|
||||||
|
wantCPU = "cortex-m4"
|
||||||
|
}
|
||||||
|
if spec.CPU != wantCPU {
|
||||||
|
t.Errorf("CPU is %q, want %q", spec.CPU, wantCPU)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestOverrideProperties(t *testing.T) {
|
func TestOverrideProperties(t *testing.T) {
|
||||||
baseAutoStackSize := true
|
baseAutoStackSize := true
|
||||||
base := &TargetSpec{
|
base := &TargetSpec{
|
||||||
@@ -81,3 +146,52 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigLinkerFlavor(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
target *TargetSpec
|
||||||
|
goos string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default gnu",
|
||||||
|
target: &TargetSpec{},
|
||||||
|
goos: "linux",
|
||||||
|
want: "gnu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default coff",
|
||||||
|
target: &TargetSpec{},
|
||||||
|
goos: "windows",
|
||||||
|
want: "coff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default darwin",
|
||||||
|
target: &TargetSpec{},
|
||||||
|
goos: "darwin",
|
||||||
|
want: "darwin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "target override",
|
||||||
|
target: &TargetSpec{
|
||||||
|
LinkerFlavor: "coff",
|
||||||
|
},
|
||||||
|
goos: "linux",
|
||||||
|
want: "coff",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc.target.GOOS = tc.goos
|
||||||
|
config := &Config{
|
||||||
|
Options: &Options{},
|
||||||
|
Target: tc.target,
|
||||||
|
}
|
||||||
|
if got := config.LinkerFlavor(); got != tc.want {
|
||||||
|
t.Fatalf("LinkerFlavor() = %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build llvm22
|
||||||
|
|
||||||
|
package compileopts
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// ClangTriple returns the target triple to pass to Clang's --target flag.
|
||||||
|
// LLVM 22 deprecated the "wasm32-unknown-wasi" triple in favor of
|
||||||
|
// "wasm32-unknown-wasip1", so we substitute it here to avoid warnings.
|
||||||
|
func ClangTriple(triple string) string {
|
||||||
|
return strings.Replace(triple, "wasm32-unknown-wasi", "wasm32-unknown-wasip1", 1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !llvm22
|
||||||
|
|
||||||
|
package compileopts
|
||||||
|
|
||||||
|
// ClangTriple returns the target triple to pass to Clang's --target flag.
|
||||||
|
// For pre-LLVM 22, the triple is used as-is.
|
||||||
|
func ClangTriple(triple string) string {
|
||||||
|
return triple
|
||||||
|
}
|
||||||
+22
-9
@@ -241,24 +241,37 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the fault block at the end of the function and the next block at the
|
faultBlock := b.getRuntimeAssertBlock(blockPrefix, assertFunc)
|
||||||
// current insert position.
|
|
||||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
|
||||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes
|
||||||
|
|
||||||
// Now branch to the out-of-bounds or the regular block.
|
// Now branch to the out-of-bounds or the regular block.
|
||||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||||
|
|
||||||
// Fail: the assert triggered so panic.
|
|
||||||
b.SetInsertPointAtEnd(faultBlock)
|
|
||||||
b.createRuntimeCall(assertFunc, nil, "")
|
|
||||||
b.CreateUnreachable()
|
|
||||||
|
|
||||||
// Ok: assert didn't trigger so continue normally.
|
// Ok: assert didn't trigger so continue normally.
|
||||||
b.SetInsertPointAtEnd(nextBlock)
|
b.SetInsertPointAtEnd(nextBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) getRuntimeAssertBlock(blockPrefix, assertFunc string) llvm.BasicBlock {
|
||||||
|
if b.runtimeAssertBlocks == nil {
|
||||||
|
b.runtimeAssertBlocks = make(map[string]llvm.BasicBlock)
|
||||||
|
}
|
||||||
|
if block := b.runtimeAssertBlocks[assertFunc]; !block.IsNil() {
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
savedBlock := b.GetInsertBlock()
|
||||||
|
block := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||||
|
b.runtimeAssertBlocks[assertFunc] = block
|
||||||
|
b.SetInsertPointAtEnd(block)
|
||||||
|
if b.hasDeferFrame() {
|
||||||
|
b.createFaultCheckpoint()
|
||||||
|
}
|
||||||
|
b.createRuntimeCall(assertFunc, nil, "")
|
||||||
|
b.CreateUnreachable()
|
||||||
|
b.SetInsertPointAtEnd(savedBlock)
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|
||||||
// extendInteger extends the value to at least targetType using a zero or sign
|
// extendInteger extends the value to at least targetType using a zero or sign
|
||||||
// extend. The resulting value is not truncated: it may still be bigger than
|
// extend. The resulting value is not truncated: it may still be bigger than
|
||||||
// targetType.
|
// targetType.
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ const (
|
|||||||
// Whether this is a full or partial Go parameter (int, slice, etc).
|
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||||
// The extra context parameter is not a Go parameter.
|
// The extra context parameter is not a Go parameter.
|
||||||
paramIsGoParam = 1 << iota
|
paramIsGoParam = 1 << iota
|
||||||
|
|
||||||
|
// Whether this is a readonly parameter (for example, a string pointer).
|
||||||
|
paramIsReadonly
|
||||||
|
|
||||||
|
// Whether this parameter is passed through backing storage.
|
||||||
|
paramIsIndirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
@@ -99,6 +105,18 @@ func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Valu
|
|||||||
// Expand an argument type to a list that can be used in a function call
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// parameter list.
|
// parameter list.
|
||||||
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
|
if c.isIndirectAggregate(t) {
|
||||||
|
return []paramInfo{{
|
||||||
|
llvmType: c.dataPtrType,
|
||||||
|
name: name,
|
||||||
|
elemSize: c.targetData.TypeAllocSize(t),
|
||||||
|
flags: paramIsGoParam | paramIsReadonly | paramIsIndirect,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
return c.expandDirectFormalParamType(t, name, goType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldInfos := c.flattenAggregateType(t, name, goType)
|
fieldInfos := c.flattenAggregateType(t, name, goType)
|
||||||
@@ -112,6 +130,38 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
|
|||||||
return []paramInfo{c.getParamInfo(t, name, goType)}
|
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) storedParamType(t llvm.Type, exported bool) llvm.Type {
|
||||||
|
if c.isIndirectParam(t, exported) {
|
||||||
|
return c.dataPtrType
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) isIndirectParam(t llvm.Type, exported bool) bool {
|
||||||
|
return !exported && c.isIndirectAggregate(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) appendStoredValueTypes(valueTypes []llvm.Type, values []ssa.Value, exported bool) []llvm.Type {
|
||||||
|
for _, value := range values {
|
||||||
|
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(value.Type()), exported))
|
||||||
|
}
|
||||||
|
return valueTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) appendStoredParamTypes(valueTypes []llvm.Type, params []*types.Var, exported bool) []llvm.Type {
|
||||||
|
for _, param := range params {
|
||||||
|
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(param.Type()), exported))
|
||||||
|
}
|
||||||
|
return valueTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) prependIndirectResult(sig *types.Signature, exported bool, params []llvm.Value, name string) []llvm.Value {
|
||||||
|
if resultType, indirect := b.hasIndirectResult(sig); !exported && indirect {
|
||||||
|
return append([]llvm.Value{b.createIndirectStorage(resultType, name)}, params...)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
// expandFormalParamOffsets returns a list of offsets from the start of an
|
// expandFormalParamOffsets returns a list of offsets from the start of an
|
||||||
// object of type t after it would have been split up by expandFormalParam. This
|
// object of type t after it would have been split up by expandFormalParam. This
|
||||||
// is useful for debug information, where it is necessary to know the offset
|
// is useful for debug information, where it is necessary to know the offset
|
||||||
@@ -167,6 +217,7 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
suffix := strconv.Itoa(i)
|
suffix := strconv.Itoa(i)
|
||||||
|
isString := false
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
// Try to come up with a good suffix for this struct field,
|
// Try to come up with a good suffix for this struct field,
|
||||||
// depending on which Go type it's based on.
|
// depending on which Go type it's based on.
|
||||||
@@ -183,12 +234,16 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
suffix = []string{"r", "i"}[i]
|
suffix = []string{"r", "i"}[i]
|
||||||
case types.String:
|
case types.String:
|
||||||
suffix = []string{"data", "len"}[i]
|
suffix = []string{"data", "len"}[i]
|
||||||
|
isString = true
|
||||||
}
|
}
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
suffix = []string{"context", "funcptr"}[i]
|
suffix = []string{"context", "funcptr"}[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
|
if isString {
|
||||||
|
subInfos[0].flags |= paramIsReadonly
|
||||||
|
}
|
||||||
paramInfos = append(paramInfos, subInfos...)
|
paramInfos = append(paramInfos, subInfos...)
|
||||||
}
|
}
|
||||||
return paramInfos
|
return paramInfos
|
||||||
|
|||||||
+23
-39
@@ -30,17 +30,15 @@ func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
|
|||||||
// actual channel send operation during goroutine lowering.
|
// actual channel send operation during goroutine lowering.
|
||||||
func (b *builder) createChanSend(instr *ssa.Send) {
|
func (b *builder) createChanSend(instr *ssa.Send) {
|
||||||
ch := b.getValue(instr.Chan, getPos(instr))
|
ch := b.getValue(instr.Chan, getPos(instr))
|
||||||
chanValue := b.getValue(instr.X, getPos(instr))
|
|
||||||
|
|
||||||
// store value-to-send
|
// store value-to-send
|
||||||
valueType := b.getLLVMType(instr.X.Type())
|
valueType := b.getLLVMType(instr.X.Type())
|
||||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
var valueAlloca, valueAllocaSize llvm.Value
|
var storage valueStorage
|
||||||
if isZeroSize {
|
if isZeroSize {
|
||||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
storage.ptr = llvm.ConstNull(b.dataPtrType)
|
||||||
} else {
|
} else {
|
||||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
storage = b.getValueStorage(instr.X, "chan.value")
|
||||||
b.CreateStore(chanValue, valueAlloca)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffer for the channel operation.
|
// Allocate buffer for the channel operation.
|
||||||
@@ -48,15 +46,13 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, storage.ptr, channelOpAlloca}, "")
|
||||||
|
|
||||||
// End the lifetime of the allocas.
|
// End the lifetime of the allocas.
|
||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||||
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||||
if !isZeroSize {
|
b.endValueStorage(storage)
|
||||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||||
@@ -66,42 +62,22 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
ch := b.getValue(unop.X, getPos(unop))
|
ch := b.getValue(unop.X, getPos(unop))
|
||||||
|
|
||||||
// Allocate memory to receive into.
|
// Allocate memory to receive into.
|
||||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan")
|
||||||
var valueAlloca, valueAllocaSize llvm.Value
|
|
||||||
if isZeroSize {
|
|
||||||
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
|
||||||
} else {
|
|
||||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate buffer for the channel operation.
|
// Allocate buffer for the channel operation.
|
||||||
channelOp := b.getLLVMRuntimeType("channelOp")
|
channelOp := b.getLLVMRuntimeType("channelOp")
|
||||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, result.valuePtr, channelOpAlloca}, "")
|
||||||
var received llvm.Value
|
received := result.finish(b, commaOk, "chan.received")
|
||||||
if isZeroSize {
|
|
||||||
received = llvm.ConstNull(valueType)
|
|
||||||
} else {
|
|
||||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
|
||||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
|
||||||
}
|
|
||||||
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||||
|
return received
|
||||||
if unop.CommaOk {
|
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
|
||||||
tuple = b.CreateInsertValue(tuple, received, 0, "")
|
|
||||||
tuple = b.CreateInsertValue(tuple, commaOk, 1, "")
|
|
||||||
return tuple
|
|
||||||
} else {
|
|
||||||
return received
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createChanClose closes the given channel.
|
// createChanClose closes the given channel.
|
||||||
func (b *builder) createChanClose(ch llvm.Value) {
|
func (b *builder) createChanClose(ch llvm.Value) {
|
||||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
b.createRuntimeInvoke("chanClose", []llvm.Value{ch}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSelect emits all IR necessary for a select statements. That's a
|
// createSelect emits all IR necessary for a select statements. That's a
|
||||||
@@ -170,9 +146,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
case types.SendOnly:
|
case types.SendOnly:
|
||||||
// Store this value in an alloca and put a pointer to this alloca
|
// Store this value in an alloca and put a pointer to this alloca
|
||||||
// in the send state.
|
// in the send state.
|
||||||
sendValue := b.getValue(state.Send, state.Pos)
|
alloca := b.getSelectSendStorage(state.Send)
|
||||||
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
|
|
||||||
b.CreateStore(sendValue, alloca)
|
|
||||||
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
|
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
@@ -280,7 +254,17 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
|||||||
// receive can proceed at a time) so we'll get that alloca, bitcast
|
// receive can proceed at a time) so we'll get that alloca, bitcast
|
||||||
// it to the correct type, and dereference it.
|
// it to the correct type, and dereference it.
|
||||||
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
||||||
typ := b.getLLVMType(expr.Type())
|
return b.loadFromStorage(recvbuf, expr.Type(), "select.received")
|
||||||
return b.CreateLoad(typ, recvbuf, "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) getSelectSendStorage(value ssa.Value) llvm.Value {
|
||||||
|
typ := b.getLLVMType(value.Type())
|
||||||
|
if b.isIndirectAggregate(typ) {
|
||||||
|
return b.getValuePointer(value)
|
||||||
|
}
|
||||||
|
llvmValue := b.getValue(value, getPos(value))
|
||||||
|
ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value")
|
||||||
|
b.CreateStore(llvmValue, ptr)
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|||||||
+492
-107
@@ -90,8 +90,10 @@ type compilerContext struct {
|
|||||||
astComments map[string]*ast.CommentGroup
|
astComments map[string]*ast.CommentGroup
|
||||||
embedGlobals map[string][]*loader.EmbedFile
|
embedGlobals map[string][]*loader.EmbedFile
|
||||||
pkg *types.Package
|
pkg *types.Package
|
||||||
packageDir string // directory for this package
|
loaderPkg *loader.Package // current package being compiled (for AST access)
|
||||||
|
packageDir string // directory for this package
|
||||||
runtimePkg *types.Package
|
runtimePkg *types.Package
|
||||||
|
localTypeNames typeutil.Map // *types.Named (synthetic local from generic instantiation) -> string
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCompilerContext returns a new compiler context ready for use, most
|
// newCompilerContext returns a new compiler context ready for use, most
|
||||||
@@ -152,10 +154,14 @@ type builder struct {
|
|||||||
llvmFnType llvm.Type
|
llvmFnType llvm.Type
|
||||||
llvmFn llvm.Value
|
llvmFn llvm.Value
|
||||||
info functionInfo
|
info functionInfo
|
||||||
locals map[ssa.Value]llvm.Value // local variables
|
locals map[ssa.Value]llvm.Value // local variables
|
||||||
blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
|
indirectValues map[ssa.Value]llvm.Value
|
||||||
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
|
indirectReturn llvm.Value
|
||||||
|
blockInfo []blockInfo
|
||||||
currentBlock *ssa.BasicBlock
|
currentBlock *ssa.BasicBlock
|
||||||
|
currentBlockInfo *blockInfo
|
||||||
|
tarjanStack []uint
|
||||||
|
tarjanIndex uint
|
||||||
phis []phiNode
|
phis []phiNode
|
||||||
deferPtr llvm.Value
|
deferPtr llvm.Value
|
||||||
deferFrame llvm.Value
|
deferFrame llvm.Value
|
||||||
@@ -165,7 +171,7 @@ type builder struct {
|
|||||||
dilocals map[*types.Var]llvm.Metadata
|
dilocals map[*types.Var]llvm.Metadata
|
||||||
initInlinedAt llvm.Metadata // fake inlinedAt position
|
initInlinedAt llvm.Metadata // fake inlinedAt position
|
||||||
initPseudoFuncs map[string]llvm.Metadata // fake "inlined" functions for proper init debug locations
|
initPseudoFuncs map[string]llvm.Metadata // fake "inlined" functions for proper init debug locations
|
||||||
allDeferFuncs []interface{}
|
allDeferFuncs []any
|
||||||
deferFuncs map[*ssa.Function]int
|
deferFuncs map[*ssa.Function]int
|
||||||
deferInvokeFuncs map[string]int
|
deferInvokeFuncs map[string]int
|
||||||
deferClosureFuncs map[*ssa.Function]int
|
deferClosureFuncs map[*ssa.Function]int
|
||||||
@@ -174,6 +180,9 @@ type builder struct {
|
|||||||
deferBuiltinFuncs map[ssa.Value]deferBuiltin
|
deferBuiltinFuncs map[ssa.Value]deferBuiltin
|
||||||
runDefersBlock []llvm.BasicBlock
|
runDefersBlock []llvm.BasicBlock
|
||||||
afterDefersBlock []llvm.BasicBlock
|
afterDefersBlock []llvm.BasicBlock
|
||||||
|
|
||||||
|
runtimeAssertBlocks map[string]llvm.BasicBlock
|
||||||
|
interfaceAssertBlock llvm.BasicBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
||||||
@@ -186,12 +195,24 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
|
|||||||
llvmFn: fn,
|
llvmFn: fn,
|
||||||
info: c.getFunctionInfo(f),
|
info: c.getFunctionInfo(f),
|
||||||
locals: make(map[ssa.Value]llvm.Value),
|
locals: make(map[ssa.Value]llvm.Value),
|
||||||
|
indirectValues: make(map[ssa.Value]llvm.Value),
|
||||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||||
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
|
||||||
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type blockInfo struct {
|
||||||
|
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
|
||||||
|
entry llvm.BasicBlock
|
||||||
|
|
||||||
|
// exit is the LLVM basic block corresponding to the end of this *ssa.Block.
|
||||||
|
// It will be different than entry if any of the block's instructions contain internal branches.
|
||||||
|
exit llvm.BasicBlock
|
||||||
|
|
||||||
|
// tarjan holds state for applying Tarjan's strongly connected components algorithm to the CFG.
|
||||||
|
// This is used by defer.go to determine whether to stack- or heap-allocate defer data.
|
||||||
|
tarjan tarjanNode
|
||||||
|
}
|
||||||
|
|
||||||
type deferBuiltin struct {
|
type deferBuiltin struct {
|
||||||
callName string
|
callName string
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
@@ -275,17 +296,23 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompilePackage compiles a single package to a LLVM module.
|
// CompilePackage compiles a single package to a LLVM module.
|
||||||
|
//
|
||||||
|
// The SSA package must already be built. When packages are compiled
|
||||||
|
// concurrently, the entire SSA program must be built before compilation starts.
|
||||||
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||||
defer c.dispose()
|
defer c.dispose()
|
||||||
c.packageDir = pkg.OriginalDir()
|
c.packageDir = pkg.OriginalDir()
|
||||||
c.embedGlobals = pkg.EmbedGlobals
|
c.embedGlobals = pkg.EmbedGlobals
|
||||||
c.pkg = pkg.Pkg
|
c.pkg = pkg.Pkg
|
||||||
|
c.loaderPkg = pkg
|
||||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c.program = ssaPkg.Prog
|
c.program = ssaPkg.Prog
|
||||||
|
|
||||||
// Convert AST to SSA.
|
// Assign names to function-local named types before compiling the
|
||||||
ssaPkg.Build()
|
// package, so that types declared in different functions (or in
|
||||||
|
// different instantiations of a generic function) do not collide.
|
||||||
|
c.scanLocalTypes(ssaPkg)
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
@@ -301,9 +328,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
// Load comments such as //go:extern on globals.
|
// Load comments such as //go:extern on globals.
|
||||||
c.loadASTComments(pkg)
|
c.loadASTComments(pkg)
|
||||||
|
|
||||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
|
||||||
// functionality.
|
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
|
||||||
if c.NeedsStackObjects {
|
if c.NeedsStackObjects {
|
||||||
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
||||||
@@ -401,19 +425,19 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
return c.ctx.Int8Type()
|
return c.ctx.Int8Type()
|
||||||
case types.Int16, types.Uint16:
|
case types.Int16, types.Uint16:
|
||||||
return c.ctx.Int16Type()
|
return c.ctx.Int16Type()
|
||||||
case types.Int32, types.Uint32:
|
case types.Int32, types.Uint32, types.UntypedRune:
|
||||||
return c.ctx.Int32Type()
|
return c.ctx.Int32Type()
|
||||||
case types.Int, types.Uint:
|
case types.Int, types.Uint, types.UntypedInt:
|
||||||
return c.intType
|
return c.intType
|
||||||
case types.Int64, types.Uint64:
|
case types.Int64, types.Uint64:
|
||||||
return c.ctx.Int64Type()
|
return c.ctx.Int64Type()
|
||||||
case types.Float32:
|
case types.Float32:
|
||||||
return c.ctx.FloatType()
|
return c.ctx.FloatType()
|
||||||
case types.Float64:
|
case types.Float64, types.UntypedFloat:
|
||||||
return c.ctx.DoubleType()
|
return c.ctx.DoubleType()
|
||||||
case types.Complex64:
|
case types.Complex64:
|
||||||
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)
|
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)
|
||||||
case types.Complex128:
|
case types.Complex128, types.UntypedComplex:
|
||||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
||||||
case types.String, types.UntypedString:
|
case types.String, types.UntypedString:
|
||||||
return c.getLLVMRuntimeType("_string")
|
return c.getLLVMRuntimeType("_string")
|
||||||
@@ -849,10 +873,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
}
|
}
|
||||||
// Create the function definition.
|
// Create the function definition.
|
||||||
b := newBuilder(c, irbuilder, member)
|
b := newBuilder(c, irbuilder, member)
|
||||||
if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
|
if ok := b.defineMathOp(); ok {
|
||||||
// The body of this function (if there is one) is ignored and
|
// The body of this function (if there is one) is ignored and
|
||||||
// replaced with a LLVM intrinsic call.
|
// replaced with a LLVM intrinsic call.
|
||||||
b.defineMathOp()
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ok := b.defineMathBitsIntrinsic(); ok {
|
if ok := b.defineMathBitsIntrinsic(); ok {
|
||||||
@@ -860,6 +883,10 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
// with a LLVM intrinsic.
|
// with a LLVM intrinsic.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if ok := b.defineCryptoIntrinsic(); ok {
|
||||||
|
// Body of this function was replaced
|
||||||
|
continue
|
||||||
|
}
|
||||||
if member.Blocks == nil {
|
if member.Blocks == nil {
|
||||||
// Try to define this as an intrinsic function.
|
// Try to define this as an intrinsic function.
|
||||||
b.defineIntrinsicFunction()
|
b.defineIntrinsicFunction()
|
||||||
@@ -1220,14 +1247,29 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
|||||||
// intrinsic (like an atomic operation). Create the entry block
|
// intrinsic (like an atomic operation). Create the entry block
|
||||||
// manually.
|
// manually.
|
||||||
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||||
} else {
|
// Intrinsics may create internal branches (e.g. nil checks).
|
||||||
for _, block := range b.fn.DomPreorder() {
|
// They will attempt to access b.currentBlockInfo to update the exit block.
|
||||||
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
// Create some fake block info for them to access.
|
||||||
b.blockEntries[block] = llvmBlock
|
blockInfo := []blockInfo{
|
||||||
b.blockExits[block] = llvmBlock
|
{
|
||||||
|
entry: entryBlock,
|
||||||
|
exit: entryBlock,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
b.blockInfo = blockInfo
|
||||||
|
b.currentBlockInfo = &blockInfo[0]
|
||||||
|
} else {
|
||||||
|
blocks := b.fn.Blocks
|
||||||
|
blockInfo := make([]blockInfo, len(blocks))
|
||||||
|
for _, block := range b.fn.DomPreorder() {
|
||||||
|
info := &blockInfo[block.Index]
|
||||||
|
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
||||||
|
info.entry = llvmBlock
|
||||||
|
info.exit = llvmBlock
|
||||||
|
}
|
||||||
|
b.blockInfo = blockInfo
|
||||||
// Normal functions have an entry block.
|
// Normal functions have an entry block.
|
||||||
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
entryBlock = blockInfo[0].entry
|
||||||
}
|
}
|
||||||
b.SetInsertPointAtEnd(entryBlock)
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
|
|
||||||
@@ -1243,10 +1285,28 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
|||||||
|
|
||||||
// Load function parameters
|
// Load function parameters
|
||||||
llvmParamIndex := 0
|
llvmParamIndex := 0
|
||||||
|
if _, indirectResult := b.hasIndirectResult(b.fn.Signature); indirectResult && !b.info.exported {
|
||||||
|
b.indirectReturn = b.llvmFn.Param(llvmParamIndex)
|
||||||
|
b.indirectReturn.SetName("return")
|
||||||
|
llvmParamIndex++
|
||||||
|
}
|
||||||
for _, param := range b.fn.Params {
|
for _, param := range b.fn.Params {
|
||||||
llvmType := b.getLLVMType(param.Type())
|
llvmType := b.getLLVMType(param.Type())
|
||||||
|
if b.isIndirectParam(llvmType, b.info.exported) {
|
||||||
|
llvmParam := b.llvmFn.Param(llvmParamIndex)
|
||||||
|
llvmParam.SetName(param.Name())
|
||||||
|
b.indirectValues[param] = llvmParam
|
||||||
|
llvmParamIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var paramInfos []paramInfo
|
||||||
|
if b.info.exported {
|
||||||
|
paramInfos = b.expandDirectFormalParamType(llvmType, param.Name(), param.Type())
|
||||||
|
} else {
|
||||||
|
paramInfos = b.expandFormalParamType(llvmType, param.Name(), param.Type())
|
||||||
|
}
|
||||||
fields := make([]llvm.Value, 0, 1)
|
fields := make([]llvm.Value, 0, 1)
|
||||||
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
for _, info := range paramInfos {
|
||||||
param := b.llvmFn.Param(llvmParamIndex)
|
param := b.llvmFn.Param(llvmParamIndex)
|
||||||
param.SetName(info.name)
|
param.SetName(info.name)
|
||||||
fields = append(fields, param)
|
fields = append(fields, param)
|
||||||
@@ -1323,8 +1383,9 @@ func (b *builder) createFunction() {
|
|||||||
if b.DumpSSA {
|
if b.DumpSSA {
|
||||||
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
||||||
}
|
}
|
||||||
b.SetInsertPointAtEnd(b.blockEntries[block])
|
|
||||||
b.currentBlock = block
|
b.currentBlock = block
|
||||||
|
b.currentBlockInfo = &b.blockInfo[block.Index]
|
||||||
|
b.SetInsertPointAtEnd(b.currentBlockInfo.entry)
|
||||||
for _, instr := range block.Instrs {
|
for _, instr := range block.Instrs {
|
||||||
if instr, ok := instr.(*ssa.DebugRef); ok {
|
if instr, ok := instr.(*ssa.DebugRef); ok {
|
||||||
if !b.Debug {
|
if !b.Debug {
|
||||||
@@ -1383,8 +1444,8 @@ func (b *builder) createFunction() {
|
|||||||
for _, phi := range b.phis {
|
for _, phi := range b.phis {
|
||||||
block := phi.ssa.Block()
|
block := phi.ssa.Block()
|
||||||
for i, edge := range phi.ssa.Edges {
|
for i, edge := range phi.ssa.Edges {
|
||||||
llvmVal := b.getValue(edge, getPos(phi.ssa))
|
llvmVal := b.getCallArgument(edge, false)
|
||||||
llvmBlock := b.blockExits[block.Preds[i]]
|
llvmBlock := b.blockInfo[block.Preds[i].Index].exit
|
||||||
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
|
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1392,6 +1453,9 @@ func (b *builder) createFunction() {
|
|||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
// Track phi nodes.
|
// Track phi nodes.
|
||||||
for _, phi := range b.phis {
|
for _, phi := range b.phis {
|
||||||
|
if b.isOversizedAggregate(phi.ssa.Type()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
insertPoint := llvm.NextInstruction(phi.llvm)
|
insertPoint := llvm.NextInstruction(phi.llvm)
|
||||||
for !insertPoint.IsAPHINode().IsNil() {
|
for !insertPoint.IsAPHINode().IsNil() {
|
||||||
insertPoint = llvm.NextInstruction(insertPoint)
|
insertPoint = llvm.NextInstruction(insertPoint)
|
||||||
@@ -1483,10 +1547,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
b.diagnostics = append(b.diagnostics, err)
|
b.diagnostics = append(b.diagnostics, err)
|
||||||
b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type()))
|
b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type()))
|
||||||
} else {
|
} else {
|
||||||
b.locals[instr] = value
|
b.setValue(instr, value)
|
||||||
if len(*instr.Referrers()) != 0 && b.NeedsStackObjects {
|
|
||||||
b.trackExpr(instr, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case *ssa.DebugRef:
|
case *ssa.DebugRef:
|
||||||
// ignore
|
// ignore
|
||||||
@@ -1498,18 +1559,16 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
case *ssa.If:
|
case *ssa.If:
|
||||||
cond := b.getValue(instr.Cond, getPos(instr))
|
cond := b.getValue(instr.Cond, getPos(instr))
|
||||||
block := instr.Block()
|
block := instr.Block()
|
||||||
blockThen := b.blockEntries[block.Succs[0]]
|
blockThen := b.blockInfo[block.Succs[0].Index].entry
|
||||||
blockElse := b.blockEntries[block.Succs[1]]
|
blockElse := b.blockInfo[block.Succs[1].Index].entry
|
||||||
b.CreateCondBr(cond, blockThen, blockElse)
|
b.CreateCondBr(cond, blockThen, blockElse)
|
||||||
case *ssa.Jump:
|
case *ssa.Jump:
|
||||||
blockJump := b.blockEntries[instr.Block().Succs[0]]
|
blockJump := b.blockInfo[instr.Block().Succs[0].Index].entry
|
||||||
b.CreateBr(blockJump)
|
b.CreateBr(blockJump)
|
||||||
case *ssa.MapUpdate:
|
case *ssa.MapUpdate:
|
||||||
m := b.getValue(instr.Map, getPos(instr))
|
m := b.getValue(instr.Map, getPos(instr))
|
||||||
key := b.getValue(instr.Key, getPos(instr))
|
|
||||||
value := b.getValue(instr.Value, getPos(instr))
|
|
||||||
mapType := instr.Map.Type().Underlying().(*types.Map)
|
mapType := instr.Map.Type().Underlying().(*types.Map)
|
||||||
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
b.createMapUpdate(mapType.Key(), m, instr.Key, instr.Value, instr.Pos())
|
||||||
case *ssa.Panic:
|
case *ssa.Panic:
|
||||||
value := b.getValue(instr.X, getPos(instr))
|
value := b.getValue(instr.X, getPos(instr))
|
||||||
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
|
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
|
||||||
@@ -1518,19 +1577,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
if b.hasDeferFrame() {
|
if b.hasDeferFrame() {
|
||||||
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
|
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
|
||||||
}
|
}
|
||||||
if len(instr.Results) == 0 {
|
b.createReturn(instr.Results, getPos(instr))
|
||||||
b.CreateRetVoid()
|
|
||||||
} else if len(instr.Results) == 1 {
|
|
||||||
b.CreateRet(b.getValue(instr.Results[0], getPos(instr)))
|
|
||||||
} else {
|
|
||||||
// Multiple return values. Put them all in a struct.
|
|
||||||
retVal := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
|
|
||||||
for i, result := range instr.Results {
|
|
||||||
val := b.getValue(result, getPos(instr))
|
|
||||||
retVal = b.CreateInsertValue(retVal, val, i, "")
|
|
||||||
}
|
|
||||||
b.CreateRet(retVal)
|
|
||||||
}
|
|
||||||
case *ssa.RunDefers:
|
case *ssa.RunDefers:
|
||||||
// Note where we're going to put the rundefers block
|
// Note where we're going to put the rundefers block
|
||||||
run := b.insertBasicBlock("rundefers.block")
|
run := b.insertBasicBlock("rundefers.block")
|
||||||
@@ -1544,18 +1591,246 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
b.createChanSend(instr)
|
b.createChanSend(instr)
|
||||||
case *ssa.Store:
|
case *ssa.Store:
|
||||||
llvmAddr := b.getValue(instr.Addr, getPos(instr))
|
llvmAddr := b.getValue(instr.Addr, getPos(instr))
|
||||||
llvmVal := b.getValue(instr.Val, getPos(instr))
|
|
||||||
b.createNilCheck(instr.Addr, llvmAddr, "store")
|
b.createNilCheck(instr.Addr, llvmAddr, "store")
|
||||||
if b.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
llvmType := b.getLLVMType(instr.Val.Type())
|
||||||
|
if b.targetData.TypeAllocSize(llvmType) == 0 {
|
||||||
// nothing to store
|
// nothing to store
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.CreateStore(llvmVal, llvmAddr)
|
b.storeValue(llvmAddr, instr.Val)
|
||||||
default:
|
default:
|
||||||
b.addError(instr.Pos(), "unknown instruction: "+instr.String())
|
b.addError(instr.Pos(), "unknown instruction: "+instr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) setValue(value ssa.Value, llvmValue llvm.Value) {
|
||||||
|
if b.isAggregateValue(value.Type()) && !llvmValue.IsNil() && llvmValue.Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
|
b.indirectValues[value] = llvmValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.locals[value] = llvmValue
|
||||||
|
if len(*value.Referrers()) != 0 && b.NeedsStackObjects {
|
||||||
|
b.trackExpr(value, llvmValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) createReturn(results []ssa.Value, pos token.Pos) {
|
||||||
|
if len(results) == 0 {
|
||||||
|
b.CreateRetVoid()
|
||||||
|
} else if !b.indirectReturn.IsNil() {
|
||||||
|
if len(results) == 1 {
|
||||||
|
b.storeValue(b.indirectReturn, results[0])
|
||||||
|
} else {
|
||||||
|
returnType := b.getLLVMResultType(b.fn.Signature)
|
||||||
|
for i, result := range results {
|
||||||
|
fieldPtr := b.CreateStructGEP(returnType, b.indirectReturn, i, "")
|
||||||
|
b.storeValue(fieldPtr, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.CreateRetVoid()
|
||||||
|
} else if len(results) == 1 {
|
||||||
|
b.CreateRet(b.getValue(results[0], pos))
|
||||||
|
} else {
|
||||||
|
result := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
|
||||||
|
for i, value := range results {
|
||||||
|
result = b.CreateInsertValue(result, b.getValue(value, pos), i, "")
|
||||||
|
}
|
||||||
|
b.CreateRet(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) isOversizedAggregate(typ types.Type) bool {
|
||||||
|
if !b.isAggregateValue(typ) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return b.isIndirectAggregate(b.getLLVMType(typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) isAggregateValue(typ types.Type) bool {
|
||||||
|
if tuple, ok := typ.(*types.Tuple); ok {
|
||||||
|
for i := 0; i < tuple.Len(); i++ {
|
||||||
|
if !isLLVMValueType(tuple.At(i).Type()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch typ.Underlying().(type) {
|
||||||
|
case *types.Array, *types.Struct:
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !isLLVMValueType(typ) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) getValuePointer(value ssa.Value) llvm.Value {
|
||||||
|
if ptr, ok := b.indirectValues[value]; ok {
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
llvmType := b.getLLVMType(value.Type())
|
||||||
|
ptr := b.createIndirectStorage(llvmType, value.Name())
|
||||||
|
b.storeValue(ptr, value)
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) getCallArgument(value ssa.Value, exported bool) llvm.Value {
|
||||||
|
paramType := b.getLLVMType(value.Type())
|
||||||
|
if b.isIndirectParam(paramType, exported) {
|
||||||
|
return b.getValuePointer(value)
|
||||||
|
}
|
||||||
|
return b.getValue(value, getPos(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) createIndirectStorage(typ llvm.Type, name string) llvm.Value {
|
||||||
|
// Use runtime.alloc here so storage that escapes remains valid. The
|
||||||
|
// allocation optimizer moves bounded non-escaping storage to the stack.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false)
|
||||||
|
layout := b.createObjectLayout(typ, b.fn.Pos())
|
||||||
|
ptr := b.createAlloc(size, layout, b.targetData.ABITypeAlignment(typ), name)
|
||||||
|
if b.NeedsStackObjects {
|
||||||
|
b.trackPointer(ptr)
|
||||||
|
}
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) copyIndirectAggregate(dst, src llvm.Value, typ llvm.Type) {
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false)
|
||||||
|
b.createMemCopy("memcpy", dst, src, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) storeValue(dst llvm.Value, value ssa.Value) {
|
||||||
|
typ := b.getLLVMType(value.Type())
|
||||||
|
if src, ok := b.indirectValues[value]; ok {
|
||||||
|
b.copyIndirectAggregate(dst, src, typ)
|
||||||
|
} else {
|
||||||
|
b.CreateStore(b.getValue(value, getPos(value)), dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) copyToIndirectStorage(src llvm.Value, typ llvm.Type, name string) llvm.Value {
|
||||||
|
dst := b.createIndirectStorage(typ, name)
|
||||||
|
b.copyIndirectAggregate(dst, src, typ)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) loadFromStorage(ptr llvm.Value, typ types.Type, name string) llvm.Value {
|
||||||
|
llvmType := b.getLLVMType(typ)
|
||||||
|
if b.isIndirectAggregate(llvmType) {
|
||||||
|
return b.copyToIndirectStorage(ptr, llvmType, name)
|
||||||
|
}
|
||||||
|
return b.CreateLoad(llvmType, ptr, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) getValueField(value ssa.Value, index int, resultType types.Type, name string) (llvm.Value, bool) {
|
||||||
|
if !b.isAggregateValue(value.Type()) {
|
||||||
|
return llvm.Value{}, false
|
||||||
|
}
|
||||||
|
valueType := b.getLLVMType(value.Type())
|
||||||
|
if _, indirect := b.indirectValues[value]; !indirect && !b.isIndirectAggregate(valueType) {
|
||||||
|
return llvm.Value{}, false
|
||||||
|
}
|
||||||
|
fieldPtr := b.CreateStructGEP(valueType, b.getValuePointer(value), index, "")
|
||||||
|
return b.loadFromStorage(fieldPtr, resultType, name), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) zeroIndirectStorage(ptr llvm.Value, typ llvm.Type) {
|
||||||
|
memset := b.getMemsetFunc()
|
||||||
|
b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
|
||||||
|
ptr,
|
||||||
|
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||||
|
llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false),
|
||||||
|
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||||
|
}, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueStorage struct {
|
||||||
|
ptr, size llvm.Value
|
||||||
|
temporary bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) getValueStorage(value ssa.Value, name string) valueStorage {
|
||||||
|
typ := b.getLLVMType(value.Type())
|
||||||
|
if b.isIndirectAggregate(typ) {
|
||||||
|
return valueStorage{ptr: b.getValuePointer(value)}
|
||||||
|
}
|
||||||
|
ptr, size := b.createTemporaryAlloca(typ, name)
|
||||||
|
b.storeValue(ptr, value)
|
||||||
|
return valueStorage{ptr: ptr, size: size, temporary: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) endValueStorage(storage valueStorage) {
|
||||||
|
if storage.temporary {
|
||||||
|
b.emitLifetimeEnd(storage.ptr, storage.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type runtimeValueResult struct {
|
||||||
|
valueType llvm.Type
|
||||||
|
resultType llvm.Type
|
||||||
|
result llvm.Value
|
||||||
|
valuePtr llvm.Value
|
||||||
|
valueSize llvm.Value
|
||||||
|
temporary bool
|
||||||
|
zero bool
|
||||||
|
commaOk bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) createRuntimeValueResult(valueType llvm.Type, commaOk, zeroAsNull bool, name string) runtimeValueResult {
|
||||||
|
result := runtimeValueResult{
|
||||||
|
valueType: valueType,
|
||||||
|
resultType: valueType,
|
||||||
|
valueSize: llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(valueType), false),
|
||||||
|
commaOk: commaOk,
|
||||||
|
}
|
||||||
|
if commaOk {
|
||||||
|
result.resultType = b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)
|
||||||
|
}
|
||||||
|
if b.isIndirectAggregate(result.resultType) {
|
||||||
|
result.result = b.createIndirectStorage(result.resultType, name+".result")
|
||||||
|
result.valuePtr = result.result
|
||||||
|
if commaOk {
|
||||||
|
result.valuePtr = b.CreateStructGEP(result.resultType, result.result, 0, "")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if zeroAsNull && b.targetData.TypeAllocSize(valueType) == 0 {
|
||||||
|
result.valuePtr = llvm.ConstNull(b.dataPtrType)
|
||||||
|
result.zero = true
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
result.valuePtr, result.valueSize = b.createTemporaryAlloca(valueType, name+".value")
|
||||||
|
result.temporary = true
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r runtimeValueResult) finish(b *builder, commaOk llvm.Value, name string) llvm.Value {
|
||||||
|
if !r.result.IsNil() {
|
||||||
|
if r.commaOk {
|
||||||
|
b.CreateStore(commaOk, b.CreateStructGEP(r.resultType, r.result, 1, ""))
|
||||||
|
}
|
||||||
|
return r.result
|
||||||
|
}
|
||||||
|
|
||||||
|
var value llvm.Value
|
||||||
|
if r.zero {
|
||||||
|
value = llvm.ConstNull(r.valueType)
|
||||||
|
} else {
|
||||||
|
value = b.CreateLoad(r.valueType, r.valuePtr, name)
|
||||||
|
}
|
||||||
|
if r.temporary {
|
||||||
|
b.emitLifetimeEnd(r.valuePtr, r.valueSize)
|
||||||
|
}
|
||||||
|
if !r.commaOk {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
result := llvm.Undef(r.resultType)
|
||||||
|
result = b.CreateInsertValue(result, value, 0, "")
|
||||||
|
return b.CreateInsertValue(result, commaOk, 1, "")
|
||||||
|
}
|
||||||
|
|
||||||
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
|
// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
|
||||||
// LLVM IR. It uses runtime calls for some builtins.
|
// LLVM IR. It uses runtime calls for some builtins.
|
||||||
func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, callName string, pos token.Pos) (llvm.Value, error) {
|
func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, callName string, pos token.Pos) (llvm.Value, error) {
|
||||||
@@ -1570,7 +1845,8 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
||||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
elemLayout := b.createObjectLayout(elemType, pos)
|
||||||
|
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize, elemLayout}, "append.new")
|
||||||
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
||||||
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
||||||
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
||||||
@@ -1652,13 +1928,41 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
case "copy":
|
case "copy":
|
||||||
dst := argValues[0]
|
dst := argValues[0]
|
||||||
src := argValues[1]
|
src := argValues[1]
|
||||||
|
// Fetch the lengths.
|
||||||
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
||||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
// Find the minimum of the lengths.
|
||||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
minFuncName := "llvm.umin.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
|
minFunc := b.mod.NamedFunction(minFuncName)
|
||||||
|
if minFunc.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||||
|
minFunc = llvm.AddFunction(b.mod, minFuncName, fnType)
|
||||||
|
}
|
||||||
|
minLen := b.CreateCall(minFunc.GlobalValueType(), minFunc, []llvm.Value{dstLen, srcLen}, "copy.n")
|
||||||
|
// Multiply the length by the element size.
|
||||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
// NOTE: This is also NSW when uintptr is int, but we can only choose one through the C API?
|
||||||
|
size := b.CreateNUWMul(minLen, elemSize, "copy.size")
|
||||||
|
// Fetch the pointers.
|
||||||
|
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstPtr")
|
||||||
|
srcBuf := b.CreateExtractValue(src, 0, "copy.srcPtr")
|
||||||
|
// Create a memcpy.
|
||||||
|
call := b.createMemCopy("memmove", dstBuf, srcBuf, size)
|
||||||
|
align := b.targetData.ABITypeAlignment(elemType)
|
||||||
|
if align > 1 {
|
||||||
|
// Apply the type's alignment to the arguments.
|
||||||
|
// LLVM sometimes turns constant-length moves into loads and stores.
|
||||||
|
// It may use this alignment for the created loads and stores.
|
||||||
|
alignAttr := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align))
|
||||||
|
call.AddCallSiteAttribute(1, alignAttr)
|
||||||
|
call.AddCallSiteAttribute(2, alignAttr)
|
||||||
|
}
|
||||||
|
// Extend and return the copied length.
|
||||||
|
if b.targetData.TypeAllocSize(minLen.Type()) < b.targetData.TypeAllocSize(b.intType) {
|
||||||
|
minLen = b.CreateZExt(minLen, b.intType, "copy.n.zext")
|
||||||
|
}
|
||||||
|
return minLen, nil
|
||||||
case "delete":
|
case "delete":
|
||||||
m := argValues[0]
|
m := argValues[0]
|
||||||
key := argValues[1]
|
key := argValues[1]
|
||||||
@@ -1686,20 +1990,66 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
return llvmLen, nil
|
return llvmLen, nil
|
||||||
case "min", "max":
|
case "min", "max":
|
||||||
// min and max builtins, added in Go 1.21.
|
// min and max builtins, added in Go 1.21.
|
||||||
// We can simply reuse the existing binop comparison code, which has all
|
// Find the corresponding intrinsic name.
|
||||||
// the edge cases figured out already.
|
ty := argTypes[0].Underlying().(*types.Basic)
|
||||||
tok := token.LSS
|
llvmType := b.getLLVMType(ty)
|
||||||
if callName == "max" {
|
info := ty.Info()
|
||||||
tok = token.GTR
|
var prefix, delimeter, typeName string
|
||||||
}
|
if info&types.IsInteger != 0 {
|
||||||
result := argValues[0]
|
// This is an integer value.
|
||||||
typ := argTypes[0]
|
// Use the LLVM int min/max intrinsics.
|
||||||
for _, arg := range argValues[1:] {
|
prefix = "llvm.s"
|
||||||
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
if info&types.IsUnsigned != 0 {
|
||||||
if err != nil {
|
prefix = "llvm.u"
|
||||||
return result, err
|
|
||||||
}
|
}
|
||||||
result = b.CreateSelect(cmp, result, arg, "")
|
delimeter = ".i"
|
||||||
|
typeName = strconv.Itoa(llvmType.IntTypeWidth())
|
||||||
|
} else {
|
||||||
|
switch ty.Kind() {
|
||||||
|
case types.String:
|
||||||
|
// Strings do not have an equivalent intrinsic.
|
||||||
|
// Implement with compares and selects.
|
||||||
|
tok := token.LSS
|
||||||
|
if callName == "max" {
|
||||||
|
tok = token.GTR
|
||||||
|
}
|
||||||
|
result := argValues[0]
|
||||||
|
typ := argTypes[0]
|
||||||
|
for _, arg := range argValues[1:] {
|
||||||
|
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = b.CreateSelect(cmp, result, arg, "")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
case types.Float32:
|
||||||
|
typeName = "f32"
|
||||||
|
case types.Float64:
|
||||||
|
typeName = "f64"
|
||||||
|
default:
|
||||||
|
return llvm.Value{}, b.makeError(pos, "todo: min/max: unknown type")
|
||||||
|
}
|
||||||
|
// There are a few edge cases with floating point min/max:
|
||||||
|
// min(-0.0, +0.0) = -0.0
|
||||||
|
// min(NaN, number) = NaN
|
||||||
|
// The llvm.minimum.*/llvm.maximum.* intrinsics match this behavior.
|
||||||
|
// Neither Go nor LLVM defines the bit representation of resulting NaNs.
|
||||||
|
prefix = "llvm."
|
||||||
|
delimeter = "imum."
|
||||||
|
}
|
||||||
|
intrinsicName := prefix + callName + delimeter + typeName
|
||||||
|
// Find or create the intrinsic.
|
||||||
|
llvmFn := b.mod.NamedFunction(intrinsicName)
|
||||||
|
if llvmFn.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(llvmType, []llvm.Type{llvmType, llvmType}, false)
|
||||||
|
llvmFn = llvm.AddFunction(b.mod, intrinsicName, fnType)
|
||||||
|
}
|
||||||
|
// Call the intrinsic repeatedly to merge the arguments.
|
||||||
|
callType := llvmFn.GlobalValueType()
|
||||||
|
result := argValues[0]
|
||||||
|
for _, arg := range argValues[1:] {
|
||||||
|
result = b.CreateCall(callType, llvmFn, []llvm.Value{result, arg}, "")
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
case "panic":
|
case "panic":
|
||||||
@@ -1780,6 +2130,12 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
// not of the current function.
|
// not of the current function.
|
||||||
useParentFrame = 1
|
useParentFrame = 1
|
||||||
}
|
}
|
||||||
|
// Prevent inlining of functions that call recover(), matching the
|
||||||
|
// Go compiler's behavior. If this function were inlined into a
|
||||||
|
// deferred function, recover() would incorrectly succeed because
|
||||||
|
// the inlined code runs in the deferred function's context.
|
||||||
|
noinline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
|
||||||
|
b.llvmFn.AddFunctionAttr(noinline)
|
||||||
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
|
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
|
||||||
case "ssa:wrapnilchk":
|
case "ssa:wrapnilchk":
|
||||||
// TODO: do an actual nil check?
|
// TODO: do an actual nil check?
|
||||||
@@ -1860,7 +2216,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
if fn := instr.StaticCallee(); fn != nil {
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
// Direct function call, either to a named or anonymous (directly
|
// Direct function call, either to a named or anonymous (directly
|
||||||
// applied) function call. If it is anonymous, it may be a closure.
|
// applied) function call. If it is anonymous, it may be a closure.
|
||||||
name := fn.RelString(nil)
|
name := b.getFunctionInfo(fn).linkName
|
||||||
switch {
|
switch {
|
||||||
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||||
return b.createInlineAsm(instr.Args)
|
return b.createInlineAsm(instr.Args)
|
||||||
@@ -1872,10 +2228,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return b.emitSV64Call(instr.Args, getPos(instr))
|
return b.emitSV64Call(instr.Args, getPos(instr))
|
||||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||||
return b.emitCSROperation(instr)
|
return b.emitCSROperation(instr)
|
||||||
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall"):
|
case (strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall")) && name != "syscall.SyscallN":
|
||||||
if b.GOOS != "darwin" {
|
if b.GOOS != "darwin" {
|
||||||
return b.createSyscall(instr)
|
return b.createSyscall(instr)
|
||||||
}
|
}
|
||||||
|
case name == "syscall.syscalln":
|
||||||
|
if b.GOOS == "windows" {
|
||||||
|
return b.createSyscalln(instr)
|
||||||
|
}
|
||||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
|
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
|
||||||
return b.createRawSyscallNoError(instr)
|
return b.createRawSyscallNoError(instr)
|
||||||
case name == "runtime.supportsRecover":
|
case name == "runtime.supportsRecover":
|
||||||
@@ -1905,14 +2265,10 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var params []llvm.Value
|
|
||||||
for _, param := range instr.Args {
|
|
||||||
params = append(params, b.getValue(param, getPos(instr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
// Try to call the function directly for trivially static calls.
|
||||||
var callee, context llvm.Value
|
var callee, context llvm.Value
|
||||||
var calleeType llvm.Type
|
var calleeType llvm.Type
|
||||||
|
var invokeTypecode, invokeReceiver llvm.Value
|
||||||
exported := false
|
exported := false
|
||||||
if fn := instr.StaticCallee(); fn != nil {
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
calleeType, callee = b.getFunction(fn)
|
calleeType, callee = b.getFunction(fn)
|
||||||
@@ -1942,19 +2298,18 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
exported = info.exported
|
exported = info.exported
|
||||||
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||||
// Builtin function (append, close, delete, etc.).)
|
// Builtin function (append, close, delete, etc.).)
|
||||||
|
var params []llvm.Value
|
||||||
var argTypes []types.Type
|
var argTypes []types.Type
|
||||||
for _, arg := range instr.Args {
|
for _, arg := range instr.Args {
|
||||||
argTypes = append(argTypes, arg.Type())
|
argTypes = append(argTypes, arg.Type())
|
||||||
|
params = append(params, b.getValue(arg, getPos(instr)))
|
||||||
}
|
}
|
||||||
return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
|
return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
|
||||||
} else if instr.IsInvoke() {
|
} else if instr.IsInvoke() {
|
||||||
// Interface method call (aka invoke call).
|
// Interface method call (aka invoke call).
|
||||||
itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface)
|
itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface)
|
||||||
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
invokeTypecode = b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||||
value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver
|
invokeReceiver = b.CreateExtractValue(itf, 1, "invoke.func.value")
|
||||||
// Prefix the params with receiver value and suffix with typecode.
|
|
||||||
params = append([]llvm.Value{value}, params...)
|
|
||||||
params = append(params, typecode)
|
|
||||||
callee = b.getInvokeFunction(instr)
|
callee = b.getInvokeFunction(instr)
|
||||||
calleeType = callee.GlobalValueType()
|
calleeType = callee.GlobalValueType()
|
||||||
context = llvm.Undef(b.dataPtrType)
|
context = llvm.Undef(b.dataPtrType)
|
||||||
@@ -1968,7 +2323,23 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
b.createNilCheck(instr.Value, callee, "fpcall")
|
b.createNilCheck(instr.Value, callee, "fpcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var params []llvm.Value
|
||||||
|
for _, param := range instr.Args {
|
||||||
|
params = append(params, b.getCallArgument(param, exported))
|
||||||
|
}
|
||||||
|
if instr.IsInvoke() {
|
||||||
|
params = append([]llvm.Value{invokeReceiver}, params...)
|
||||||
|
params = append(params, invokeTypecode)
|
||||||
|
}
|
||||||
|
|
||||||
if !exported {
|
if !exported {
|
||||||
|
if resultType, indirectResult := b.hasIndirectResult(instr.Signature()); indirectResult {
|
||||||
|
result := b.createIndirectStorage(resultType, "call.result")
|
||||||
|
params = append([]llvm.Value{result}, params...)
|
||||||
|
params = append(params, context)
|
||||||
|
b.createInvoke(calleeType, callee, params, "")
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
// This function takes a context parameter.
|
// This function takes a context parameter.
|
||||||
// Add it to the end of the parameter list.
|
// Add it to the end of the parameter list.
|
||||||
params = append(params, context)
|
params = append(params, context)
|
||||||
@@ -2007,6 +2378,9 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
|
|||||||
return value
|
return value
|
||||||
default:
|
default:
|
||||||
// other (local) SSA value
|
// other (local) SSA value
|
||||||
|
if value, ok := b.indirectValues[expr]; ok {
|
||||||
|
return b.CreateLoad(b.getLLVMType(expr.Type()), value, "")
|
||||||
|
}
|
||||||
if value, ok := b.locals[expr]; ok {
|
if value, ok := b.locals[expr]; ok {
|
||||||
return value
|
return value
|
||||||
} else {
|
} else {
|
||||||
@@ -2031,13 +2405,10 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
|||||||
if elementSize == 0 {
|
if elementSize == 0 {
|
||||||
elementSize = 1
|
elementSize = 1
|
||||||
}
|
}
|
||||||
maxSize := maxPointerValue / elementSize
|
maxSize := min(
|
||||||
|
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
// an int.
|
||||||
// an int.
|
maxPointerValue/elementSize, maxIntegerValue)
|
||||||
if maxSize > maxIntegerValue {
|
|
||||||
maxSize = maxIntegerValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxSize
|
return maxSize
|
||||||
}
|
}
|
||||||
@@ -2064,9 +2435,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
||||||
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
|
||||||
align := b.targetData.ABITypeAlignment(typ)
|
align := b.targetData.ABITypeAlignment(typ)
|
||||||
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
buf := b.createAlloc(sizeValue, layoutValue, align, expr.Comment)
|
||||||
return buf, nil
|
return buf, nil
|
||||||
} else {
|
} else {
|
||||||
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
|
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
|
||||||
@@ -2093,8 +2463,15 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
// This instruction changes the type, but the underlying value remains
|
// This instruction changes the type, but the underlying value remains
|
||||||
// the same. This is often a no-op, but sometimes we have to change the
|
// the same. This is often a no-op, but sometimes we have to change the
|
||||||
// LLVM type as well.
|
// LLVM type as well.
|
||||||
x := b.getValue(expr.X, getPos(expr))
|
|
||||||
llvmType := b.getLLVMType(expr.Type())
|
llvmType := b.getLLVMType(expr.Type())
|
||||||
|
if b.isIndirectAggregate(llvmType) {
|
||||||
|
sourceType := b.getLLVMType(expr.X.Type())
|
||||||
|
if !b.isIndirectAggregate(sourceType) || b.targetData.TypeAllocSize(sourceType) != b.targetData.TypeAllocSize(llvmType) {
|
||||||
|
return llvm.Value{}, errors.New("todo: indirect aggregate ChangeType with different layout")
|
||||||
|
}
|
||||||
|
return b.getValuePointer(expr.X), nil
|
||||||
|
}
|
||||||
|
x := b.getValue(expr.X, getPos(expr))
|
||||||
if x.Type() == llvmType {
|
if x.Type() == llvmType {
|
||||||
// Different Go type but same LLVM type (for example, named int).
|
// Different Go type but same LLVM type (for example, named int).
|
||||||
// This is the common case.
|
// This is the common case.
|
||||||
@@ -2124,9 +2501,15 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
if _, ok := expr.Tuple.(*ssa.Select); ok {
|
if _, ok := expr.Tuple.(*ssa.Select); ok {
|
||||||
return b.getChanSelectResult(expr), nil
|
return b.getChanSelectResult(expr), nil
|
||||||
}
|
}
|
||||||
|
if value, ok := b.getValueField(expr.Tuple, expr.Index, expr.Type(), expr.Name()); ok {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
value := b.getValue(expr.Tuple, getPos(expr))
|
value := b.getValue(expr.Tuple, getPos(expr))
|
||||||
return b.CreateExtractValue(value, expr.Index, ""), nil
|
return b.CreateExtractValue(value, expr.Index, ""), nil
|
||||||
case *ssa.Field:
|
case *ssa.Field:
|
||||||
|
if value, ok := b.getValueField(expr.X, expr.Field, expr.Type(), expr.Name()); ok {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
value := b.getValue(expr.X, getPos(expr))
|
value := b.getValue(expr.X, getPos(expr))
|
||||||
result := b.CreateExtractValue(value, expr.Field, "")
|
result := b.CreateExtractValue(value, expr.Field, "")
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -2149,11 +2532,11 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
panic("global is not an expression")
|
panic("global is not an expression")
|
||||||
case *ssa.Index:
|
case *ssa.Index:
|
||||||
collection := b.getValue(expr.X, getPos(expr))
|
|
||||||
index := b.getValue(expr.Index, getPos(expr))
|
index := b.getValue(expr.Index, getPos(expr))
|
||||||
|
|
||||||
switch xType := expr.X.Type().Underlying().(type) {
|
switch xType := expr.X.Type().Underlying().(type) {
|
||||||
case *types.Basic: // extract byte from string
|
case *types.Basic: // extract byte from string
|
||||||
|
collection := b.getValue(expr.X, getPos(expr))
|
||||||
// Value type must be a string, which is a basic type.
|
// Value type must be a string, which is a basic type.
|
||||||
if xType.Info()&types.IsString == 0 {
|
if xType.Info()&types.IsString == 0 {
|
||||||
panic("lookup on non-string?")
|
panic("lookup on non-string?")
|
||||||
@@ -2187,13 +2570,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
|
|
||||||
// Can't load directly from array (as index is non-constant), so
|
// Can't load directly from array (as index is non-constant), so
|
||||||
// have to do it using an alloca+gep+load.
|
// have to do it using an alloca+gep+load.
|
||||||
arrayType := collection.Type()
|
arrayType := b.getLLVMType(expr.X.Type())
|
||||||
alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
|
storage := b.getValueStorage(expr.X, "index.alloca")
|
||||||
b.CreateStore(collection, alloca)
|
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
|
ptr := b.CreateInBoundsGEP(arrayType, storage.ptr, []llvm.Value{zero, index}, "index.gep")
|
||||||
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
|
result := b.loadFromStorage(ptr, expr.Type(), "index.load")
|
||||||
b.emitLifetimeEnd(alloca, allocaSize)
|
b.endValueStorage(storage)
|
||||||
return result, nil
|
return result, nil
|
||||||
default:
|
default:
|
||||||
panic("unknown *ssa.Index type")
|
panic("unknown *ssa.Index type")
|
||||||
@@ -2252,17 +2634,21 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
case *ssa.Lookup: // map lookup
|
case *ssa.Lookup: // map lookup
|
||||||
value := b.getValue(expr.X, getPos(expr))
|
value := b.getValue(expr.X, getPos(expr))
|
||||||
index := b.getValue(expr.Index, getPos(expr))
|
|
||||||
valueType := expr.Type()
|
valueType := expr.Type()
|
||||||
if expr.CommaOk {
|
if expr.CommaOk {
|
||||||
valueType = valueType.(*types.Tuple).At(0).Type()
|
valueType = valueType.(*types.Tuple).At(0).Type()
|
||||||
}
|
}
|
||||||
return b.createMapLookup(expr.X.Type().Underlying().(*types.Map).Key(), valueType, value, index, expr.CommaOk, expr.Pos())
|
return b.createMapLookup(expr.X.Type().Underlying().(*types.Map).Key(), valueType, value, expr.Index, expr.CommaOk, expr.Pos())
|
||||||
case *ssa.MakeChan:
|
case *ssa.MakeChan:
|
||||||
return b.createMakeChan(expr), nil
|
return b.createMakeChan(expr), nil
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
return b.parseMakeClosure(expr)
|
return b.parseMakeClosure(expr)
|
||||||
case *ssa.MakeInterface:
|
case *ssa.MakeInterface:
|
||||||
|
if b.isOversizedAggregate(expr.X.Type()) {
|
||||||
|
typ := b.getLLVMType(expr.X.Type())
|
||||||
|
ptr := b.copyToIndirectStorage(b.getValuePointer(expr.X), typ, "interface.value")
|
||||||
|
return b.createMakeInterfaceFromPointer(ptr, expr.X.Type()), nil
|
||||||
|
}
|
||||||
val := b.getValue(expr.X, getPos(expr))
|
val := b.getValue(expr.X, getPos(expr))
|
||||||
return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||||
case *ssa.MakeMap:
|
case *ssa.MakeMap:
|
||||||
@@ -2296,8 +2682,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
||||||
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
||||||
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
slicePtr := b.createAlloc(sliceSize, layoutValue, elemAlign, "makeslice.buf")
|
||||||
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
|
|
||||||
|
|
||||||
// Extend or truncate if necessary. This is safe as we've already done
|
// Extend or truncate if necessary. This is safe as we've already done
|
||||||
// the bounds check.
|
// the bounds check.
|
||||||
@@ -2330,7 +2715,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil
|
return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil
|
||||||
}
|
}
|
||||||
case *ssa.Phi:
|
case *ssa.Phi:
|
||||||
phi := b.CreatePHI(b.getLLVMType(expr.Type()), "")
|
phiType := b.storedParamType(b.getLLVMType(expr.Type()), false)
|
||||||
|
phi := b.CreatePHI(phiType, "")
|
||||||
b.phis = append(b.phis, phiNode{expr, phi})
|
b.phis = append(b.phis, phiNode{expr, phi})
|
||||||
return phi, nil
|
return phi, nil
|
||||||
case *ssa.Range:
|
case *ssa.Range:
|
||||||
@@ -3333,8 +3719,7 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
return fn, nil
|
return fn, nil
|
||||||
} else {
|
} else {
|
||||||
b.createNilCheck(unop.X, x, "deref")
|
b.createNilCheck(unop.X, x, "deref")
|
||||||
load := b.CreateLoad(valueType, x, "")
|
return b.loadFromStorage(x, unop.Type(), ""), nil
|
||||||
return load, nil
|
|
||||||
}
|
}
|
||||||
case token.XOR: // ^x, toggle all bits in integer
|
case token.XOR: // ^x, toggle all bits in integer
|
||||||
return b.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil
|
return b.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil
|
||||||
|
|||||||
+179
-21
@@ -4,6 +4,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -50,6 +51,8 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"channel.go", "", ""},
|
{"channel.go", "", ""},
|
||||||
{"gc.go", "", ""},
|
{"gc.go", "", ""},
|
||||||
{"zeromap.go", "", ""},
|
{"zeromap.go", "", ""},
|
||||||
|
{"generics.go", "", ""},
|
||||||
|
{"large.go", "", ""},
|
||||||
}
|
}
|
||||||
if goMinor >= 20 {
|
if goMinor >= 20 {
|
||||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||||
@@ -57,6 +60,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
if goMinor >= 21 {
|
if goMinor >= 21 {
|
||||||
tests = append(tests, testCase{"go1.21.go", "", ""})
|
tests = append(tests, testCase{"go1.21.go", "", ""})
|
||||||
}
|
}
|
||||||
|
if goMinor >= 27 {
|
||||||
|
tests = append(tests, testCase{"go1.27.go", "", ""})
|
||||||
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
name := tc.file
|
name := tc.file
|
||||||
@@ -109,7 +115,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
// Update test if needed. Do not check the result.
|
// Update test if needed. Do not check the result.
|
||||||
if *flagUpdate {
|
if *flagUpdate {
|
||||||
err := os.WriteFile(outPath, []byte(mod.String()), 0666)
|
err := os.WriteFile(outPath, []byte(normalizeIR(mod.String())), 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to write updated output file:", err)
|
t.Error("failed to write updated output file:", err)
|
||||||
}
|
}
|
||||||
@@ -121,30 +127,137 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Fatal("failed to read golden file:", err)
|
t.Fatal("failed to read golden file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fuzzyEqualIR(mod.String(), string(expected)) {
|
if diff := diffIR(string(expected), mod.String()); diff != "" {
|
||||||
t.Errorf("output does not match expected output:\n%s", mod.String())
|
t.Errorf("output does not match expected output (re-run with -update to regenerate):\n%s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
func TestOptimizedLargeAggregateABI(t *testing.T) {
|
||||||
// equal. That means, only relevant lines are compared (excluding comments
|
options := &compileopts.Options{Target: "wasm"}
|
||||||
// etc.).
|
mod, errs := testCompilePackage(t, options, "large-optimized.go")
|
||||||
func fuzzyEqualIR(s1, s2 string) bool {
|
if len(errs) != 0 {
|
||||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
for _, err := range errs {
|
||||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
t.Error(err)
|
||||||
if len(lines1) != len(lines2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, line1 := range lines1 {
|
|
||||||
line2 := lines2[i]
|
|
||||||
if line1 != line2 {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer mod.Dispose()
|
||||||
|
|
||||||
|
passOptions := llvm.NewPassBuilderOptions()
|
||||||
|
defer passOptions.Dispose()
|
||||||
|
if err := mod.RunPasses("default<O2>", llvm.TargetMachine{}, passOptions); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
resultFn := mod.NamedFunction("main.makeLargeOptimizedValue")
|
||||||
|
if resultFn.IsNil() {
|
||||||
|
t.Fatal("missing function main.makeLargeOptimizedValue")
|
||||||
|
}
|
||||||
|
if resultType := resultFn.GlobalValueType().ReturnType(); resultType.TypeKind() != llvm.VoidTypeKind {
|
||||||
|
t.Errorf("large aggregate result was promoted to %s", resultType)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range []string{
|
||||||
|
"main.makeLargeOptimizedValue",
|
||||||
|
"main.readLargeOptimizedValue",
|
||||||
|
"main.readMixedLargeOptimizedValue",
|
||||||
|
} {
|
||||||
|
fn := mod.NamedFunction(name)
|
||||||
|
if fn.IsNil() {
|
||||||
|
t.Fatalf("missing function %s", name)
|
||||||
|
}
|
||||||
|
if paramType := fn.GlobalValueType().ParamTypes()[0]; paramType.TypeKind() != llvm.PointerTypeKind {
|
||||||
|
t.Errorf("%s aggregate parameter was promoted to %s", name, paramType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeIR canonicalizes LLVM-version-specific IR spellings for comparison
|
||||||
|
// and when regenerating golden files.
|
||||||
|
func normalizeIR(s string) string {
|
||||||
|
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
||||||
|
// which LLVM printed before any co-occurring attribute such as
|
||||||
|
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
||||||
|
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
||||||
|
// "ptr readonly captures(none)"). Normalize both name and position back
|
||||||
|
// to the old spelling.
|
||||||
|
s = normalizeCapturesAttr(s)
|
||||||
|
|
||||||
|
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
||||||
|
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
||||||
|
// assumed not to create undef/poison before. It's unrelated to the
|
||||||
|
// behavior under test, so ignore it for comparison.
|
||||||
|
s = strings.ReplaceAll(s, "nocreateundeforpoison ", "")
|
||||||
|
|
||||||
|
// LLVM 22 dropped the (redundant) i64 size argument from
|
||||||
|
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
||||||
|
// written against the two-argument form still match.
|
||||||
|
s = lifetimeSizeArgRe.ReplaceAllString(s, "$1")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffIR compares two LLVM IR strings, ignoring irrelevant lines (comments,
|
||||||
|
// empty lines, etc.) and normalizing LLVM-version-specific spellings via
|
||||||
|
// normalizeIR. It returns "" when they are equal. Otherwise it returns a
|
||||||
|
// compact diff of only the region that differs: the common prefix and suffix
|
||||||
|
// are trimmed, then the differing expected lines (prefixed "-") are shown
|
||||||
|
// followed by the differing actual lines (prefixed "+").
|
||||||
|
func diffIR(expected, actual string) string {
|
||||||
|
exp := filterIrrelevantIRLines(strings.Split(normalizeIR(expected), "\n"))
|
||||||
|
act := filterIrrelevantIRLines(strings.Split(normalizeIR(actual), "\n"))
|
||||||
|
|
||||||
|
// Trim the common prefix.
|
||||||
|
start := 0
|
||||||
|
for start < len(exp) && start < len(act) && exp[start] == act[start] {
|
||||||
|
start++
|
||||||
|
}
|
||||||
|
// Trim the common suffix.
|
||||||
|
e, a := len(exp), len(act)
|
||||||
|
for e > start && a > start && exp[e-1] == act[a-1] {
|
||||||
|
e--
|
||||||
|
a--
|
||||||
|
}
|
||||||
|
if start == e && start == a {
|
||||||
|
return "" // equal
|
||||||
|
}
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("first difference at relevant line ")
|
||||||
|
b.WriteString(strconv.Itoa(start + 1))
|
||||||
|
b.WriteString(":\n")
|
||||||
|
for _, line := range exp[start:e] {
|
||||||
|
b.WriteString("- ")
|
||||||
|
b.WriteString(line)
|
||||||
|
b.WriteByte('\n')
|
||||||
|
}
|
||||||
|
for _, line := range act[start:a] {
|
||||||
|
b.WriteString("+ ")
|
||||||
|
b.WriteString(line)
|
||||||
|
b.WriteByte('\n')
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
||||||
|
// 'captures(none)', which is how LLVM 21+ orders these two attributes when
|
||||||
|
// printing IR (the pre-LLVM21 'nocapture' attribute printed the other way
|
||||||
|
// around).
|
||||||
|
var capturesNoneAttrRe = regexp.MustCompile(`\b(readonly|readnone|writeonly|nonnull)\s+captures\(none\)`)
|
||||||
|
|
||||||
|
// lifetimeSizeArgRe matches the i64 size argument of an
|
||||||
|
// llvm.lifetime.start/end call or declaration, which LLVM 22 removed.
|
||||||
|
var lifetimeSizeArgRe = regexp.MustCompile(`(@llvm\.lifetime\.(?:start|end)\.p0\()i64(?: immarg| \d+), `)
|
||||||
|
|
||||||
|
// normalizeCapturesAttr rewrites LLVM 21+'s 'captures(none)' attribute back
|
||||||
|
// to the pre-LLVM21 'nocapture' spelling and position, so golden IR files
|
||||||
|
// written against LLVM <21 keep matching.
|
||||||
|
func normalizeCapturesAttr(s string) string {
|
||||||
|
s = capturesNoneAttrRe.ReplaceAllString(s, "nocapture $1")
|
||||||
|
s = strings.ReplaceAll(s, "captures(none)", "nocapture")
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterIrrelevantIRLines removes lines from the input slice of strings that
|
// filterIrrelevantIRLines removes lines from the input slice of strings that
|
||||||
@@ -187,9 +300,9 @@ func TestCompilerErrors(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
errorsFileString := strings.ReplaceAll(string(errorsFile), "\r\n", "\n")
|
errorsFileString := strings.ReplaceAll(string(errorsFile), "\r\n", "\n")
|
||||||
for _, line := range strings.Split(errorsFileString, "\n") {
|
for line := range strings.SplitSeq(errorsFileString, "\n") {
|
||||||
if strings.HasPrefix(line, "// ERROR: ") {
|
if after, ok := strings.CutPrefix(line, "// ERROR: "); ok {
|
||||||
expectedErrors = append(expectedErrors, strings.TrimPrefix(line, "// ERROR: "))
|
expectedErrors = append(expectedErrors, after)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,6 +326,49 @@ func TestCompilerErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAggregateValueCount(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
defer ctx.Dispose()
|
||||||
|
|
||||||
|
byteType := ctx.Int8Type()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
typ llvm.Type
|
||||||
|
count uint64
|
||||||
|
exceeded bool
|
||||||
|
}{
|
||||||
|
{"empty", llvm.ArrayType(byteType, 0), 0, false},
|
||||||
|
{"limit", llvm.ArrayType(byteType, 1024), 1024, false},
|
||||||
|
{"over limit", llvm.ArrayType(byteType, 1025), 0, true},
|
||||||
|
{"combined limit", ctx.StructType([]llvm.Type{
|
||||||
|
llvm.ArrayType(byteType, 512),
|
||||||
|
llvm.ArrayType(byteType, 512),
|
||||||
|
}, false), 1024, false},
|
||||||
|
{"combined over limit", ctx.StructType([]llvm.Type{
|
||||||
|
llvm.ArrayType(byteType, 1000),
|
||||||
|
llvm.ArrayType(byteType, 1000),
|
||||||
|
}, false), 0, true},
|
||||||
|
{"comma-ok over limit", ctx.StructType([]llvm.Type{
|
||||||
|
llvm.ArrayType(byteType, 1024),
|
||||||
|
ctx.Int1Type(),
|
||||||
|
}, false), 0, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
count, exceeded := aggregateValueCount(test.typ, 0)
|
||||||
|
if exceeded != test.exceeded {
|
||||||
|
t.Errorf("expected exceeded=%t, got %t", test.exceeded, exceeded)
|
||||||
|
}
|
||||||
|
if !exceeded && count != test.count {
|
||||||
|
t.Errorf("expected count=%d, got %d", test.count, count)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build a package given a number of compiler options and a file.
|
// 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) {
|
func testCompilePackage(t *testing.T, options *compileopts.Options, file string) (llvm.Module, []error) {
|
||||||
target, err := compileopts.LoadTarget(options)
|
target, err := compileopts.LoadTarget(options)
|
||||||
@@ -257,5 +413,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string)
|
|||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
return CompilePackage(file, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
ssaPkg := program.Package(pkg.Pkg)
|
||||||
|
ssaPkg.Build()
|
||||||
|
return CompilePackage(file, pkg, ssaPkg, machine, compilerConfig, false)
|
||||||
}
|
}
|
||||||
|
|||||||
+216
-122
@@ -32,7 +32,7 @@ func (b *builder) supportsRecover() bool {
|
|||||||
// proposal of WebAssembly:
|
// proposal of WebAssembly:
|
||||||
// https://github.com/WebAssembly/exception-handling
|
// https://github.com/WebAssembly/exception-handling
|
||||||
return false
|
return false
|
||||||
case "riscv64", "xtensa":
|
case "xtensa":
|
||||||
// TODO: add support for these architectures
|
// TODO: add support for these architectures
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
@@ -60,10 +60,6 @@ func (b *builder) deferInitFunc() {
|
|||||||
b.deferExprFuncs = make(map[ssa.Value]int)
|
b.deferExprFuncs = make(map[ssa.Value]int)
|
||||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||||
|
|
||||||
// Create defer list pointer.
|
|
||||||
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
|
||||||
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
|
||||||
|
|
||||||
if b.hasDeferFrame() {
|
if b.hasDeferFrame() {
|
||||||
// Set up the defer frame with the current stack pointer.
|
// Set up the defer frame with the current stack pointer.
|
||||||
// This assumes that the stack pointer doesn't move outside of the
|
// This assumes that the stack pointer doesn't move outside of the
|
||||||
@@ -73,12 +69,22 @@ func (b *builder) deferInitFunc() {
|
|||||||
// in the setjmp-like inline assembly.
|
// in the setjmp-like inline assembly.
|
||||||
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||||
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||||
|
// The field index must match the DeferPtr field in runtime.deferFrame,
|
||||||
|
// defined in src/runtime/panic.go.
|
||||||
|
b.deferPtr = b.CreateInBoundsGEP(deferFrameType, b.deferFrame, []llvm.Value{
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), 6, false), // DeferPtr field
|
||||||
|
}, "deferPtr")
|
||||||
stackPointer := b.readStackPointer()
|
stackPointer := b.readStackPointer()
|
||||||
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
||||||
|
|
||||||
// Create the landing pad block, which is where control transfers after
|
// Create the landing pad block, which is where control transfers after
|
||||||
// a panic.
|
// a panic.
|
||||||
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
||||||
|
} else {
|
||||||
|
// Create defer list pointer.
|
||||||
|
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||||
|
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +106,7 @@ func (b *builder) createLandingPad() {
|
|||||||
|
|
||||||
// Continue at the 'recover' block, which returns to the parent in an
|
// Continue at the 'recover' block, which returns to the parent in an
|
||||||
// appropriate way.
|
// appropriate way.
|
||||||
b.CreateBr(b.blockEntries[b.fn.Recover])
|
b.CreateBr(b.blockInfo[b.fn.Recover.Index].entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a checkpoint (similar to setjmp). This emits inline assembly that
|
// Create a checkpoint (similar to setjmp). This emits inline assembly that
|
||||||
@@ -110,8 +116,8 @@ func (b *builder) createLandingPad() {
|
|||||||
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
||||||
// Construct inline assembly equivalents of setjmp.
|
// Construct inline assembly equivalents of setjmp.
|
||||||
// The assembly works as follows:
|
// The assembly works as follows:
|
||||||
// * All registers (both callee-saved and caller saved) are clobbered
|
// * Registers are either clobbered or, on 386, saved for longjmp to
|
||||||
// after the inline assembly returns.
|
// restore if the ABI requires them to survive calls.
|
||||||
// * The assembly stores the address just past the end of the assembly
|
// * The assembly stores the address just past the end of the assembly
|
||||||
// into the jump buffer.
|
// into the jump buffer.
|
||||||
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||||
@@ -124,8 +130,12 @@ func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
|||||||
asmString = `
|
asmString = `
|
||||||
xorl %eax, %eax
|
xorl %eax, %eax
|
||||||
movl $$1f, 4(%ebx)
|
movl $$1f, 4(%ebx)
|
||||||
|
movl %ebx, 8(%ebx)
|
||||||
|
movl %esi, 12(%ebx)
|
||||||
|
movl %edi, 16(%ebx)
|
||||||
|
movl %ebp, 20(%ebx)
|
||||||
1:`
|
1:`
|
||||||
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
constraints = "={eax},{ebx},~{ecx},~{edx},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||||
// This doesn't include the floating point stack because TinyGo uses
|
// This doesn't include the floating point stack because TinyGo uses
|
||||||
// newer floating point instructions.
|
// newer floating point instructions.
|
||||||
case "x86_64":
|
case "x86_64":
|
||||||
@@ -207,12 +217,20 @@ sw $$ra, 4($$5)
|
|||||||
// So only add them when using hardfloat.
|
// 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}"
|
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":
|
case "riscv32", "riscv64":
|
||||||
asmString = `
|
if b.archFamily() == "riscv32" {
|
||||||
|
asmString = `
|
||||||
la a2, 1f
|
la a2, 1f
|
||||||
sw a2, 4(a1)
|
sw a2, 4(a1)
|
||||||
li a0, 0
|
li a0, 0
|
||||||
1:`
|
1:`
|
||||||
|
} else {
|
||||||
|
asmString = `
|
||||||
|
la a2, 1f
|
||||||
|
sd a2, 8(a1)
|
||||||
|
li a0, 0
|
||||||
|
1:`
|
||||||
|
}
|
||||||
constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
|
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:
|
default:
|
||||||
// This case should have been handled by b.supportsRecover().
|
// This case should have been handled by b.supportsRecover().
|
||||||
@@ -234,41 +252,157 @@ func (b *builder) createInvokeCheckpoint() {
|
|||||||
continueBB := b.insertBasicBlock("")
|
continueBB := b.insertBasicBlock("")
|
||||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||||
b.SetInsertPointAtEnd(continueBB)
|
b.SetInsertPointAtEnd(continueBB)
|
||||||
b.blockExits[b.currentBlock] = continueBB
|
b.currentBlockInfo.exit = continueBB
|
||||||
}
|
}
|
||||||
|
|
||||||
// isInLoop checks if there is a path from a basic block to itself.
|
// createFaultCheckpoint is like createInvokeCheckpoint but for use in fault
|
||||||
func isInLoop(start *ssa.BasicBlock) bool {
|
// blocks (e.g., bounds check failures). Unlike createInvokeCheckpoint, it does
|
||||||
// Use a breadth-first search to scan backwards through the block graph.
|
// not update currentBlockInfo.exit because the fault block is a dead-end that
|
||||||
queue := []*ssa.BasicBlock{start}
|
// does not participate in phi node resolution.
|
||||||
checked := map[*ssa.BasicBlock]struct{}{}
|
func (b *builder) createFaultCheckpoint() {
|
||||||
|
isZero := b.createCheckpoint(b.deferFrame)
|
||||||
|
continueBB := b.insertBasicBlock("")
|
||||||
|
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||||
|
b.SetInsertPointAtEnd(continueBB)
|
||||||
|
}
|
||||||
|
|
||||||
for len(queue) > 0 {
|
// isInLoop checks if there is a path from the current block to itself.
|
||||||
// pop a block off of the queue
|
// Use Tarjan's strongly connected components algorithm to search for cycles.
|
||||||
block := queue[len(queue)-1]
|
// A one-node SCC is a cycle iff there is an edge from the node to itself.
|
||||||
queue = queue[:len(queue)-1]
|
// A multi-node SCC is always a cycle.
|
||||||
|
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||||
|
func (b *builder) isInLoop() bool {
|
||||||
|
if b.currentBlockInfo.tarjan.lowLink == 0 {
|
||||||
|
b.strongConnect(b.currentBlock)
|
||||||
|
}
|
||||||
|
return b.currentBlockInfo.tarjan.cyclic
|
||||||
|
}
|
||||||
|
|
||||||
// Search through predecessors.
|
func (b *builder) strongConnect(block *ssa.BasicBlock) {
|
||||||
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
// Assign a new index.
|
||||||
// Defers are often placed near the start of the function.
|
// Indices start from 1 so that 0 can be used as a sentinel.
|
||||||
for _, pred := range block.Preds {
|
assignedIndex := b.tarjanIndex + 1
|
||||||
if pred == start {
|
b.tarjanIndex = assignedIndex
|
||||||
// cycle found
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := checked[pred]; ok {
|
// Apply the new index.
|
||||||
// block already checked
|
blockIndex := block.Index
|
||||||
continue
|
node := &b.blockInfo[blockIndex].tarjan
|
||||||
}
|
node.lowLink = assignedIndex
|
||||||
|
|
||||||
// add to queue and checked map
|
// Push the node onto the stack.
|
||||||
queue = append(queue, pred)
|
node.onStack = true
|
||||||
checked[pred] = struct{}{}
|
b.tarjanStack = append(b.tarjanStack, uint(blockIndex))
|
||||||
|
|
||||||
|
// Process the successors.
|
||||||
|
for _, successor := range block.Succs {
|
||||||
|
// Look up the successor's state.
|
||||||
|
successorIndex := successor.Index
|
||||||
|
if successorIndex == blockIndex {
|
||||||
|
// Handle a self-cycle specially.
|
||||||
|
node.cyclic = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
successorNode := &b.blockInfo[successorIndex].tarjan
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case successorNode.lowLink == 0:
|
||||||
|
// This node has not yet been visisted.
|
||||||
|
b.strongConnect(successor)
|
||||||
|
|
||||||
|
case !successorNode.onStack:
|
||||||
|
// This node has been visited, but is in a different SCC.
|
||||||
|
// Ignore it, and do not update lowLink.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the lowLink index.
|
||||||
|
// This always uses the min-of-lowlink instead of using index in the on-stack case.
|
||||||
|
// This is done for two reasons:
|
||||||
|
// 1. The lowLink update can be shared between the new-node and on-stack cases.
|
||||||
|
// 2. The assigned index does not need to be saved - it is only needed for root node detection.
|
||||||
|
if successorNode.lowLink < node.lowLink {
|
||||||
|
node.lowLink = successorNode.lowLink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
if node.lowLink == assignedIndex {
|
||||||
|
// This is a root node.
|
||||||
|
// Pop the SCC off the stack.
|
||||||
|
stack := b.tarjanStack
|
||||||
|
top := stack[len(stack)-1]
|
||||||
|
stack = stack[:len(stack)-1]
|
||||||
|
blocks := b.blockInfo
|
||||||
|
topNode := &blocks[top].tarjan
|
||||||
|
topNode.onStack = false
|
||||||
|
|
||||||
|
if top != uint(blockIndex) {
|
||||||
|
// The root node is not the only node in the SCC.
|
||||||
|
// Mark all nodes in this SCC as cyclic.
|
||||||
|
topNode.cyclic = true
|
||||||
|
for top != uint(blockIndex) {
|
||||||
|
top = stack[len(stack)-1]
|
||||||
|
stack = stack[:len(stack)-1]
|
||||||
|
topNode = &blocks[top].tarjan
|
||||||
|
topNode.onStack = false
|
||||||
|
topNode.cyclic = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.tarjanStack = stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tarjanNode holds per-block state for isInLoop and strongConnect.
|
||||||
|
type tarjanNode struct {
|
||||||
|
// lowLink is the index of the first visited node that is reachable from this block.
|
||||||
|
// The lowLink indices are assigned by the SCC search, and do not correspond to b.Index.
|
||||||
|
// A lowLink of 0 is used as a sentinel to mark a node which has not yet been visited.
|
||||||
|
lowLink uint
|
||||||
|
|
||||||
|
// onStack tracks whether this node is currently on the SCC search stack.
|
||||||
|
onStack bool
|
||||||
|
|
||||||
|
// cyclic indicates whether this block is in a loop.
|
||||||
|
// If lowLink is 0, strongConnect must be called before reading this field.
|
||||||
|
cyclic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type llvmValueList struct {
|
||||||
|
values []llvm.Value
|
||||||
|
types []llvm.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLLVMValueList(values ...llvm.Value) llvmValueList {
|
||||||
|
var list llvmValueList
|
||||||
|
list.append(values...)
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *llvmValueList) append(values ...llvm.Value) {
|
||||||
|
for _, value := range values {
|
||||||
|
l.values = append(l.values, value)
|
||||||
|
l.types = append(l.types, value.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *llvmValueList) appendSSAValues(values []ssa.Value, lower func(ssa.Value) llvm.Value) {
|
||||||
|
for _, value := range values {
|
||||||
|
l.append(lower(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) loadDeferredCallParams(structType llvm.Type, ptr llvm.Value) []llvm.Value {
|
||||||
|
fieldTypes := structType.StructElementTypes()
|
||||||
|
values := make([]llvm.Value, 0, len(fieldTypes)-2)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 2; i < len(fieldTypes); i++ {
|
||||||
|
fieldPtr := b.CreateInBoundsGEP(structType, ptr, []llvm.Value{
|
||||||
|
zero,
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
|
}, "gep")
|
||||||
|
values = append(values, b.CreateLoad(fieldTypes[i], fieldPtr, "param"))
|
||||||
|
}
|
||||||
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
// createDefer emits a single defer instruction, to be run when this function
|
// createDefer emits a single defer instruction, to be run when this function
|
||||||
@@ -278,31 +412,28 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
// make a linked list.
|
// make a linked list.
|
||||||
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
|
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
|
||||||
|
|
||||||
var values []llvm.Value
|
var values llvmValueList
|
||||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
lowerArgument := func(value ssa.Value) llvm.Value {
|
||||||
|
return b.getCallArgument(value, false)
|
||||||
|
}
|
||||||
if instr.Call.IsInvoke() {
|
if instr.Call.IsInvoke() {
|
||||||
// Method call on an interface.
|
// Method call on an interface.
|
||||||
|
|
||||||
// Get callback type number.
|
// Get callback type number.
|
||||||
methodName := instr.Call.Method.FullName()
|
key := b.getInvokeFunctionName(&instr.Call)
|
||||||
if _, ok := b.deferInvokeFuncs[methodName]; !ok {
|
if _, ok := b.deferInvokeFuncs[key]; !ok {
|
||||||
b.deferInvokeFuncs[methodName] = len(b.allDeferFuncs)
|
b.deferInvokeFuncs[key] = len(b.allDeferFuncs)
|
||||||
b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
|
b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
|
||||||
}
|
}
|
||||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[methodName]), false)
|
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[key]), false)
|
||||||
|
|
||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields, followed by the call parameters).
|
// runtime._defer fields, followed by the call parameters).
|
||||||
itf := b.getValue(instr.Call.Value, getPos(instr)) // interface
|
itf := b.getValue(instr.Call.Value, getPos(instr)) // interface
|
||||||
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
values = []llvm.Value{callback, next, typecode, receiverValue}
|
values = newLLVMValueList(callback, next, typecode, receiverValue)
|
||||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||||
for _, arg := range instr.Call.Args {
|
|
||||||
val := b.getValue(arg, getPos(instr))
|
|
||||||
values = append(values, val)
|
|
||||||
valueTypes = append(valueTypes, val.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
|
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
|
||||||
// Regular function call.
|
// Regular function call.
|
||||||
@@ -314,12 +445,11 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
|
|
||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields).
|
// runtime._defer fields).
|
||||||
values = []llvm.Value{callback, next}
|
values = newLLVMValueList(callback, next)
|
||||||
for _, param := range instr.Call.Args {
|
exported := b.getFunctionInfo(callee).exported
|
||||||
llvmParam := b.getValue(param, getPos(instr))
|
values.appendSSAValues(instr.Call.Args, func(value ssa.Value) llvm.Value {
|
||||||
values = append(values, llvmParam)
|
return b.getCallArgument(value, exported)
|
||||||
valueTypes = append(valueTypes, llvmParam.Type())
|
})
|
||||||
}
|
|
||||||
|
|
||||||
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
|
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
|
||||||
// Immediately applied function literal with free variables.
|
// Immediately applied function literal with free variables.
|
||||||
@@ -342,14 +472,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields, followed by all parameters including the
|
// runtime._defer fields, followed by all parameters including the
|
||||||
// context pointer).
|
// context pointer).
|
||||||
values = []llvm.Value{callback, next}
|
values = newLLVMValueList(callback, next)
|
||||||
for _, param := range instr.Call.Args {
|
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||||
llvmParam := b.getValue(param, getPos(instr))
|
values.append(context)
|
||||||
values = append(values, llvmParam)
|
|
||||||
valueTypes = append(valueTypes, llvmParam.Type())
|
|
||||||
}
|
|
||||||
values = append(values, context)
|
|
||||||
valueTypes = append(valueTypes, context.Type())
|
|
||||||
|
|
||||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||||
var argTypes []types.Type
|
var argTypes []types.Type
|
||||||
@@ -372,11 +497,8 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
|
|
||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields).
|
// runtime._defer fields).
|
||||||
values = []llvm.Value{callback, next}
|
values = newLLVMValueList(callback, next)
|
||||||
for _, param := range argValues {
|
values.append(argValues...)
|
||||||
values = append(values, param)
|
|
||||||
valueTypes = append(valueTypes, param.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
funcValue := b.getValue(instr.Call.Value, getPos(instr))
|
funcValue := b.getValue(instr.Call.Value, getPos(instr))
|
||||||
@@ -391,34 +513,33 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields, followed by all parameters including the
|
// runtime._defer fields, followed by all parameters including the
|
||||||
// context pointer).
|
// context pointer).
|
||||||
values = []llvm.Value{callback, next, funcValue}
|
values = newLLVMValueList(callback, next, funcValue)
|
||||||
valueTypes = append(valueTypes, funcValue.Type())
|
values.appendSSAValues(instr.Call.Args, lowerArgument)
|
||||||
for _, param := range instr.Call.Args {
|
|
||||||
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 deferred call
|
// Make a struct out of the collected values to put in the deferred call
|
||||||
// struct.
|
// struct.
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(values.types, false)
|
||||||
deferredCall := llvm.ConstNull(deferredCallType)
|
deferredCall := llvm.ConstNull(deferredCallType)
|
||||||
for i, value := range values {
|
for i, value := range values.values {
|
||||||
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put this struct in an allocation.
|
// Put this struct in an allocation.
|
||||||
var alloca llvm.Value
|
var alloca llvm.Value
|
||||||
if !isInLoop(instr.Block()) {
|
if instr.Block() != b.currentBlock {
|
||||||
|
panic("block mismatch")
|
||||||
|
}
|
||||||
|
if !b.isInLoop() {
|
||||||
// This can safely use a stack allocation.
|
// This can safely use a stack allocation.
|
||||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||||
} else {
|
} else {
|
||||||
// This may be hit a variable number of times, so use a heap allocation.
|
// This may be hit a variable number of times, so use a heap allocation.
|
||||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
nilPtr := llvm.ConstNull(b.dataPtrType)
|
layoutValue := b.createObjectLayout(deferredCallType, instr.Pos())
|
||||||
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
align := b.targetData.ABITypeAlignment(deferredCallType)
|
||||||
|
alloca = b.createAlloc(sizeValue, layoutValue, align, "defer.alloc.call")
|
||||||
}
|
}
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(alloca)
|
b.trackPointer(alloca)
|
||||||
@@ -502,19 +623,11 @@ func (b *builder) createRunDefers() {
|
|||||||
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, arg := range callback.Args {
|
valueTypes = b.appendStoredValueTypes(valueTypes, callback.Args, false)
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the params from the struct (including receiver).
|
// 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)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||||
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 fnPtr llvm.Value
|
||||||
var fnType llvm.Type
|
var fnType llvm.Type
|
||||||
@@ -543,6 +656,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// with a strict calling convention.
|
// with a strict calling convention.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||||
}
|
}
|
||||||
|
forwardParams = b.prependIndirectResult(callback.Signature(), false, forwardParams, "defer.result")
|
||||||
|
|
||||||
b.createCall(fnType, fnPtr, forwardParams, "")
|
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||||
|
|
||||||
@@ -551,27 +665,21 @@ func (b *builder) createRunDefers() {
|
|||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
for _, param := range getParams(callback.Signature) {
|
exported := b.getFunctionInfo(callback).exported
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(callback.Signature), exported)
|
||||||
}
|
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
|
||||||
for i := range getParams(callback.Signature) {
|
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
|
||||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plain TinyGo functions add some extra parameters to implement async functionality and function receivers.
|
// 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.
|
// These parameters should not be supplied when calling into an external C/ASM function.
|
||||||
if !b.getFunctionInfo(callback).exported {
|
if !exported {
|
||||||
// Add the context parameter. We know it is ignored by the receiving
|
// Add the context parameter. We know it is ignored by the receiving
|
||||||
// function, but we have to pass one anyway.
|
// function, but we have to pass one anyway.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||||
}
|
}
|
||||||
|
forwardParams = b.prependIndirectResult(callback.Signature, exported, forwardParams, "defer.result")
|
||||||
|
|
||||||
// Call real function.
|
// Call real function.
|
||||||
fnType, fn := b.getFunction(callback)
|
fnType, fn := b.getFunction(callback)
|
||||||
@@ -581,24 +689,16 @@ func (b *builder) createRunDefers() {
|
|||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
fn := callback.Fn.(*ssa.Function)
|
fn := callback.Fn.(*ssa.Function)
|
||||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
params := fn.Signature.Params()
|
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(fn.Signature), false)
|
||||||
for i := 0; i < params.Len(); i++ {
|
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
|
||||||
}
|
|
||||||
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
|
||||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call deferred function.
|
// Call deferred function.
|
||||||
fnType, llvmFn := b.getFunction(fn)
|
fnType, llvmFn := b.getFunction(fn)
|
||||||
|
forwardParams = b.prependIndirectResult(fn.Signature, false, forwardParams, "defer.result")
|
||||||
b.createCall(fnType, llvmFn, forwardParams, "")
|
b.createCall(fnType, llvmFn, forwardParams, "")
|
||||||
case *ssa.Builtin:
|
case *ssa.Builtin:
|
||||||
db := b.deferBuiltinFuncs[callback]
|
db := b.deferBuiltinFuncs[callback]
|
||||||
@@ -608,20 +708,14 @@ func (b *builder) createRunDefers() {
|
|||||||
|
|
||||||
//Get signature from call results
|
//Get signature from call results
|
||||||
params := callback.Type().Underlying().(*types.Signature).Params()
|
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||||
for i := 0; i < params.Len(); i++ {
|
for v := range params.Variables() {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
var argValues []llvm.Value
|
argValues := b.loadDeferredCallParams(deferredCallType, deferData)
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
|
||||||
for i := 0; i < params.Len(); i++ {
|
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
|
||||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
|
||||||
argValues = append(argValues, forwardParam)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
|
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+97
-22
@@ -10,6 +10,91 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LLVM recursively expands each struct field and array element in parameters
|
||||||
|
// and results into separate values. It gets very slow with too many values, so
|
||||||
|
// pass larger aggregates indirectly before LLVM expands them.
|
||||||
|
const maxDirectAggregateValues = 1024
|
||||||
|
|
||||||
|
func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
|
||||||
|
switch sig.Results().Len() {
|
||||||
|
case 0:
|
||||||
|
return c.ctx.VoidType()
|
||||||
|
case 1:
|
||||||
|
return c.getLLVMType(sig.Results().At(0).Type())
|
||||||
|
default:
|
||||||
|
results := make([]llvm.Type, sig.Results().Len())
|
||||||
|
for i := range results {
|
||||||
|
results[i] = c.getLLVMType(sig.Results().At(i).Type())
|
||||||
|
}
|
||||||
|
return c.ctx.StructType(results, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) hasIndirectResult(sig *types.Signature) (llvm.Type, bool) {
|
||||||
|
resultType := c.getLLVMResultType(sig)
|
||||||
|
return resultType, c.isIndirectAggregate(resultType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) isIndirectAggregate(typ llvm.Type) bool {
|
||||||
|
switch typ.TypeKind() {
|
||||||
|
case llvm.ArrayTypeKind, llvm.StructTypeKind:
|
||||||
|
_, exceeded := aggregateValueCount(typ, 0)
|
||||||
|
return exceeded
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func aggregateValueCount(typ llvm.Type, count uint64) (uint64, bool) {
|
||||||
|
switch typ.TypeKind() {
|
||||||
|
case llvm.ArrayTypeKind:
|
||||||
|
length := uint64(typ.ArrayLength())
|
||||||
|
if length == 0 {
|
||||||
|
return count, false
|
||||||
|
}
|
||||||
|
elementCount, exceeded := aggregateValueCount(typ.ElementType(), 0)
|
||||||
|
if exceeded {
|
||||||
|
return count, true
|
||||||
|
}
|
||||||
|
if elementCount != 0 && length > (maxDirectAggregateValues-count)/elementCount {
|
||||||
|
return count, true
|
||||||
|
}
|
||||||
|
return count + length*elementCount, false
|
||||||
|
case llvm.StructTypeKind:
|
||||||
|
for _, field := range typ.StructElementTypes() {
|
||||||
|
var exceeded bool
|
||||||
|
count, exceeded = aggregateValueCount(field, count)
|
||||||
|
if exceeded {
|
||||||
|
return count, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count, false
|
||||||
|
default:
|
||||||
|
count++
|
||||||
|
return count, count > maxDirectAggregateValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLLVMValueType(typ types.Type) bool {
|
||||||
|
switch typ := typ.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return typ.Kind() != types.Invalid
|
||||||
|
case *types.Array:
|
||||||
|
return isLLVMValueType(typ.Elem())
|
||||||
|
case *types.Struct:
|
||||||
|
for field := range typ.Fields() {
|
||||||
|
if !isLLVMValueType(field.Type()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createFuncValue creates a function value from a raw function pointer with no
|
// createFuncValue creates a function value from a raw function pointer with no
|
||||||
// context.
|
// context.
|
||||||
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||||
@@ -48,28 +133,18 @@ func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
|||||||
|
|
||||||
// getLLVMFunctionType returns a LLVM function type for a given signature.
|
// getLLVMFunctionType returns a LLVM function type for a given signature.
|
||||||
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
||||||
// Get the return type.
|
returnType, indirectResult := c.hasIndirectResult(typ)
|
||||||
var returnType llvm.Type
|
|
||||||
switch typ.Results().Len() {
|
|
||||||
case 0:
|
|
||||||
// No return values.
|
|
||||||
returnType = c.ctx.VoidType()
|
|
||||||
case 1:
|
|
||||||
// Just one return value.
|
|
||||||
returnType = c.getLLVMType(typ.Results().At(0).Type())
|
|
||||||
default:
|
|
||||||
// Multiple return values. Put them together in a struct.
|
|
||||||
// This appears to be the common way to handle multiple return values in
|
|
||||||
// LLVM.
|
|
||||||
members := make([]llvm.Type, typ.Results().Len())
|
|
||||||
for i := 0; i < typ.Results().Len(); i++ {
|
|
||||||
members[i] = c.getLLVMType(typ.Results().At(i).Type())
|
|
||||||
}
|
|
||||||
returnType = c.ctx.StructType(members, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the parameter types.
|
// Get the parameter types.
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
|
if indirectResult {
|
||||||
|
// LLVM expands aggregate returns into scalar leaves before deciding
|
||||||
|
// whether to pass them indirectly, so a large IR return can exhaust
|
||||||
|
// memory. Returning void avoids that expansion and cannot be demoted
|
||||||
|
// again. Keep the result pointer first so the context remains last.
|
||||||
|
paramTypes = append(paramTypes, c.dataPtrType)
|
||||||
|
returnType = c.ctx.VoidType()
|
||||||
|
}
|
||||||
if typ.Recv() != nil {
|
if typ.Recv() != nil {
|
||||||
recv := c.getLLVMType(typ.Recv().Type())
|
recv := c.getLLVMType(typ.Recv().Type())
|
||||||
if recv.StructName() == "runtime._interface" {
|
if recv.StructName() == "runtime._interface" {
|
||||||
@@ -81,8 +156,8 @@ func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
|||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for v := range typ.Params().Variables() {
|
||||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(v.Type())
|
||||||
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
@@ -112,7 +187,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
|||||||
|
|
||||||
// Store the bound variables in a single object, allocating it on the heap
|
// Store the bound variables in a single object, allocating it on the heap
|
||||||
// if necessary.
|
// if necessary.
|
||||||
context := b.emitPointerPack(boundVars)
|
context := b.emitPointerPack(boundVars, expr.Pos())
|
||||||
|
|
||||||
// Create the closure.
|
// Create the closure.
|
||||||
_, fn := b.getFunction(f)
|
_, fn := b.getFunction(f)
|
||||||
|
|||||||
+33
-8
@@ -5,11 +5,38 @@ package compiler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Heap-allocate a buffer of the given size. This will typically call
|
||||||
|
// runtime.alloc.
|
||||||
|
func (b *builder) createAlloc(sizeValue, layoutValue llvm.Value, align int, comment string) llvm.Value {
|
||||||
|
// Normally allocate using "runtime.alloc", but use "runtime.alloc_noheap"
|
||||||
|
// if the //go:noheap pragma is used.
|
||||||
|
allocFunc := "alloc"
|
||||||
|
if b.info.noheap {
|
||||||
|
allocFunc = "alloc_noheap"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocs that don't allocate anything can return an architecture-specific
|
||||||
|
// sentinel value.
|
||||||
|
if !sizeValue.IsAConstantInt().IsNil() && sizeValue.ZExtValue() == 0 {
|
||||||
|
allocFunc = "alloc_zero"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the runtime call.
|
||||||
|
call := b.createRuntimeCall(allocFunc, []llvm.Value{sizeValue, layoutValue}, comment)
|
||||||
|
if align == 0 || align&(align-1) != 0 {
|
||||||
|
panic("invalid alignment")
|
||||||
|
}
|
||||||
|
call.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
|
||||||
|
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
// trackExpr inserts pointer tracking intrinsics for the GC if the expression is
|
// trackExpr inserts pointer tracking intrinsics for the GC if the expression is
|
||||||
// one of the expressions that need this.
|
// one of the expressions that need this.
|
||||||
func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) {
|
func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) {
|
||||||
@@ -62,7 +89,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
numElements := typ.StructElementTypesCount()
|
numElements := typ.StructElementTypesCount()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
subValue := b.CreateExtractValue(value, i, "")
|
subValue := b.CreateExtractValue(value, i, "")
|
||||||
b.trackValue(subValue)
|
b.trackValue(subValue)
|
||||||
}
|
}
|
||||||
@@ -71,7 +98,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
numElements := typ.ArrayLength()
|
numElements := typ.ArrayLength()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
subValue := b.CreateExtractValue(value, i, "")
|
subValue := b.CreateExtractValue(value, i, "")
|
||||||
b.trackValue(subValue)
|
b.trackValue(subValue)
|
||||||
}
|
}
|
||||||
@@ -92,13 +119,11 @@ func typeHasPointers(t llvm.Type) bool {
|
|||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
return true
|
return true
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
for _, subType := range t.StructElementTypes() {
|
return slices.ContainsFunc(t.StructElementTypes(), typeHasPointers)
|
||||||
if typeHasPointers(subType) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
|
if t.ArrayLength() == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if typeHasPointers(t.ElementType()) {
|
if typeHasPointers(t.ElementType()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-21
@@ -47,20 +47,16 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all function parameters to pass to the goroutine.
|
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
for _, param := range instr.Call.Args {
|
|
||||||
params = append(params, b.expandFormalParam(b.getValue(param, getPos(instr)))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var prefix string
|
var prefix string
|
||||||
var funcPtr llvm.Value
|
var funcPtr llvm.Value
|
||||||
var funcType llvm.Type
|
var funcType llvm.Type
|
||||||
|
var context llvm.Value
|
||||||
hasContext := false
|
hasContext := false
|
||||||
|
exported := false
|
||||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||||
// Static callee is known. This makes it easier to start a new
|
// Static callee is known. This makes it easier to start a new
|
||||||
// goroutine.
|
// goroutine.
|
||||||
var context llvm.Value
|
|
||||||
switch value := instr.Call.Value.(type) {
|
switch value := instr.Call.Value.(type) {
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
// Goroutine call is regular function call. No context is necessary.
|
// Goroutine call is regular function call. No context is necessary.
|
||||||
@@ -73,10 +69,10 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
panic("StaticCallee returned an unexpected value")
|
panic("StaticCallee returned an unexpected value")
|
||||||
}
|
}
|
||||||
if !context.IsNil() {
|
if !context.IsNil() {
|
||||||
params = append(params, context) // context parameter
|
|
||||||
hasContext = true
|
hasContext = true
|
||||||
}
|
}
|
||||||
funcType, funcPtr = b.getFunction(callee)
|
funcType, funcPtr = b.getFunction(callee)
|
||||||
|
exported = b.getFunctionInfo(callee).exported
|
||||||
} else if instr.Call.IsInvoke() {
|
} else if instr.Call.IsInvoke() {
|
||||||
// This is a method call on an interface value.
|
// This is a method call on an interface value.
|
||||||
itf := b.getValue(instr.Call.Value, getPos(instr))
|
itf := b.getValue(instr.Call.Value, getPos(instr))
|
||||||
@@ -84,23 +80,32 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||||
funcType = funcPtr.GlobalValueType()
|
funcType = funcPtr.GlobalValueType()
|
||||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
params = append(params, itfValue)
|
||||||
params = append(params, itfTypeCode) // end with typecode
|
context = itfTypeCode
|
||||||
} else {
|
} else {
|
||||||
// This is a function pointer.
|
// This is a function pointer.
|
||||||
// At the moment, two extra params are passed to the newly started
|
// At the moment, two extra params are passed to the newly started
|
||||||
// goroutine:
|
// goroutine:
|
||||||
// * The function context, for closures.
|
// * The function context, for closures.
|
||||||
// * The function pointer (for tasks).
|
// * The function pointer (for tasks).
|
||||||
var context llvm.Value
|
|
||||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
|
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
|
||||||
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||||
params = append(params, context, funcPtr)
|
|
||||||
hasContext = true
|
hasContext = true
|
||||||
prefix = b.fn.RelString(nil)
|
prefix = b.getFunctionInfo(b.fn).linkName
|
||||||
}
|
}
|
||||||
|
|
||||||
paramBundle := b.emitPointerPack(params)
|
for _, param := range instr.Call.Args {
|
||||||
|
params = append(params, b.getGoroutineCallArgument(param, exported)...)
|
||||||
|
}
|
||||||
|
if !context.IsNil() {
|
||||||
|
params = append(params, context)
|
||||||
|
}
|
||||||
|
if hasContext && instr.Call.StaticCallee() == nil {
|
||||||
|
params = append(params, funcPtr)
|
||||||
|
}
|
||||||
|
params = b.prependIndirectResult(instr.Call.Signature(), exported, params, "go.result")
|
||||||
|
|
||||||
|
paramBundle := b.emitPointerPack(params, instr.Pos())
|
||||||
var stackSize llvm.Value
|
var stackSize llvm.Value
|
||||||
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos())
|
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos())
|
||||||
if b.AutomaticStackSize {
|
if b.AutomaticStackSize {
|
||||||
@@ -122,6 +127,15 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) getGoroutineCallArgument(value ssa.Value, exported bool) []llvm.Value {
|
||||||
|
typ := b.getLLVMType(value.Type())
|
||||||
|
arg := b.getCallArgument(value, exported)
|
||||||
|
if b.isIndirectParam(typ, exported) {
|
||||||
|
return []llvm.Value{b.copyToIndirectStorage(arg, typ, "go.param")}
|
||||||
|
}
|
||||||
|
return b.expandFormalParam(arg)
|
||||||
|
}
|
||||||
|
|
||||||
// Create an exported wrapper function for functions with the //go:wasmexport
|
// Create an exported wrapper function for functions with the //go:wasmexport
|
||||||
// pragma. This wrapper function is quite complex when the scheduler is enabled:
|
// 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.
|
// it needs to start a new goroutine each time the exported function is called.
|
||||||
@@ -139,7 +153,7 @@ func (b *builder) createWasmExport() {
|
|||||||
// Declare the exported function.
|
// Declare the exported function.
|
||||||
paramTypes := b.llvmFnType.ParamTypes()
|
paramTypes := b.llvmFnType.ParamTypes()
|
||||||
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
|
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
|
||||||
exportedFn := llvm.AddFunction(b.mod, b.fn.RelString(nil)+suffix, exportedFnType)
|
exportedFn := llvm.AddFunction(b.mod, b.getFunctionInfo(b.fn).linkName+suffix, exportedFnType)
|
||||||
b.addStandardAttributes(exportedFn)
|
b.addStandardAttributes(exportedFn)
|
||||||
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
|
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
|
||||||
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
|
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
|
||||||
@@ -295,10 +309,10 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
}
|
}
|
||||||
defer b.Dispose()
|
defer b.Dispose()
|
||||||
|
|
||||||
var deadlock llvm.Value
|
var exitGoroutine llvm.Value
|
||||||
var deadlockType llvm.Type
|
var exitGoroutineType llvm.Type
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
deadlockType, deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
exitGoroutineType, exitGoroutine = c.getFunction(c.program.ImportedPackage("runtime").Members["exitGoroutine"].(*ssa.Function))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fn.IsAFunction().IsNil() {
|
if !fn.IsAFunction().IsNil() {
|
||||||
@@ -363,7 +377,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
b.CreateCall(fnType, fn, params, "")
|
b.CreateCall(fnType, fn, params, "")
|
||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
b.CreateCall(exitGoroutineType, exitGoroutine, []llvm.Value{
|
||||||
llvm.Undef(c.dataPtrType),
|
llvm.Undef(c.dataPtrType),
|
||||||
}, "")
|
}, "")
|
||||||
}
|
}
|
||||||
@@ -414,7 +428,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
// Extract parameters from the state object, and call the function
|
// Extract parameters from the state object, and call the function
|
||||||
// that's being wrapped.
|
// that's being wrapped.
|
||||||
var callParams []llvm.Value
|
var callParams []llvm.Value
|
||||||
for i := 0; i < numParams; i++ {
|
for i := range numParams {
|
||||||
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
@@ -514,14 +528,13 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
b.CreateCall(fnType, fnPtr, params, "")
|
b.CreateCall(fnType, fnPtr, params, "")
|
||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
b.CreateCall(exitGoroutineType, exitGoroutine, []llvm.Value{
|
||||||
llvm.Undef(c.dataPtrType),
|
llvm.Undef(c.dataPtrType),
|
||||||
}, "")
|
}, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
// The goroutine was terminated via deadlock.
|
|
||||||
b.CreateUnreachable()
|
b.CreateUnreachable()
|
||||||
} else {
|
} else {
|
||||||
// Finish the function. Every basic block must end in a terminator, and
|
// Finish the function. Every basic block must end in a terminator, and
|
||||||
|
|||||||
+15
-13
@@ -38,10 +38,10 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
|||||||
// provided immediately. For example:
|
// provided immediately. For example:
|
||||||
//
|
//
|
||||||
// arm.AsmFull(
|
// arm.AsmFull(
|
||||||
// "str {value}, {result}",
|
// "str {value}, [{result}]",
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "value": 1
|
// "value": 1,
|
||||||
// "result": &dest,
|
// "result": uintptr(unsafe.Pointer(&dest)),
|
||||||
// })
|
// })
|
||||||
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||||
@@ -146,13 +146,14 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
|||||||
llvmArgs := []llvm.Value{}
|
llvmArgs := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||||
constraints := "={r0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={r0}")
|
||||||
for i, arg := range args[1:] {
|
for i, arg := range args[1:] {
|
||||||
arg = arg.(*ssa.MakeInterface).X
|
arg = arg.(*ssa.MakeInterface).X
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
constraints += ",0"
|
constraints.WriteString(",0")
|
||||||
} else {
|
} else {
|
||||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",{r" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
llvmValue := b.getValue(arg, pos)
|
llvmValue := b.getValue(arg, pos)
|
||||||
llvmArgs = append(llvmArgs, llvmValue)
|
llvmArgs = append(llvmArgs, llvmValue)
|
||||||
@@ -161,9 +162,9 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
|||||||
// Implement the ARM calling convention by marking r1-r3 as
|
// Implement the ARM calling convention by marking r1-r3 as
|
||||||
// clobbered. r0 is used as an output register so doesn't have to be
|
// clobbered. r0 is used as an output register so doesn't have to be
|
||||||
// marked as clobbered.
|
// marked as clobbered.
|
||||||
constraints += ",~{r1},~{r2},~{r3}"
|
constraints.WriteString(",~{r1},~{r2},~{r3}")
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,13 +185,14 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
|||||||
llvmArgs := []llvm.Value{}
|
llvmArgs := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||||
constraints := "={x0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={x0}")
|
||||||
for i, arg := range args[1:] {
|
for i, arg := range args[1:] {
|
||||||
arg = arg.(*ssa.MakeInterface).X
|
arg = arg.(*ssa.MakeInterface).X
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
constraints += ",0"
|
constraints.WriteString(",0")
|
||||||
} else {
|
} else {
|
||||||
constraints += ",{x" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",{x" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
llvmValue := b.getValue(arg, pos)
|
llvmValue := b.getValue(arg, pos)
|
||||||
llvmArgs = append(llvmArgs, llvmValue)
|
llvmArgs = append(llvmArgs, llvmValue)
|
||||||
@@ -199,9 +201,9 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
|||||||
// Implement the ARM64 calling convention by marking x1-x7 as
|
// Implement the ARM64 calling convention by marking x1-x7 as
|
||||||
// clobbered. x0 is used as an output register so doesn't have to be
|
// clobbered. x0 is used as an output register so doesn't have to be
|
||||||
// marked as clobbered.
|
// marked as clobbered.
|
||||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
constraints.WriteString(",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}")
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+583
-208
File diff suppressed because it is too large
Load Diff
+88
-16
@@ -7,6 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
b.createStackSaveImpl()
|
b.createStackSaveImpl()
|
||||||
case name == "runtime.KeepAlive":
|
case name == "runtime.KeepAlive":
|
||||||
b.createKeepAliveImpl()
|
b.createKeepAliveImpl()
|
||||||
|
case name == "machine.keepAliveNoEscape":
|
||||||
|
b.createMachineKeepAliveImpl()
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||||
b.createVolatileLoad()
|
b.createVolatileLoad()
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
@@ -48,19 +51,24 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||||
func (b *builder) createMemoryCopyImpl() {
|
func (b *builder) createMemoryCopyImpl() {
|
||||||
b.createFunctionStart(true)
|
b.createFunctionStart(true)
|
||||||
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
params := b.fn.Params[0:3]
|
||||||
|
b.createMemCopy(
|
||||||
|
b.fn.Name(),
|
||||||
|
b.getValue(params[0], getPos(b.fn)),
|
||||||
|
b.getValue(params[1], getPos(b.fn)),
|
||||||
|
b.getValue(params[2], getPos(b.fn)),
|
||||||
|
)
|
||||||
|
b.CreateRetVoid()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) createMemCopy(kind string, dst, src, len llvm.Value) llvm.Value {
|
||||||
|
fnName := "llvm." + kind + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, 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)
|
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
var params []llvm.Value
|
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{dst, src, len, llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "")
|
||||||
for _, param := range b.fn.Params {
|
|
||||||
params = append(params, b.getValue(param, getPos(b.fn)))
|
|
||||||
}
|
|
||||||
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
|
||||||
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
|
||||||
b.CreateRetVoid()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||||
@@ -144,13 +152,37 @@ func (b *builder) createAbiEscapeImpl() {
|
|||||||
b.CreateRet(result)
|
b.CreateRet(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement machine.keepAliveNoEscape, which makes sure the compiler keeps the
|
||||||
|
// pointer parameter alive until this point (for GC).
|
||||||
|
func (b *builder) createMachineKeepAliveImpl() {
|
||||||
|
b.createFunctionStart(true)
|
||||||
|
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||||
|
|
||||||
|
// See createKeepAliveImpl for details.
|
||||||
|
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
|
||||||
|
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
|
||||||
|
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
||||||
|
|
||||||
|
b.CreateRetVoid()
|
||||||
|
}
|
||||||
|
|
||||||
var mathToLLVMMapping = map[string]string{
|
var mathToLLVMMapping = map[string]string{
|
||||||
|
"math.Acos": "llvm.acos.f64",
|
||||||
|
"math.Asin": "llvm.asin.f64",
|
||||||
|
"math.Atan": "llvm.atan.f64",
|
||||||
|
"math.Atan2": "llvm.atan2.f64",
|
||||||
"math.Ceil": "llvm.ceil.f64",
|
"math.Ceil": "llvm.ceil.f64",
|
||||||
|
"math.Cos": "llvm.cos.f64",
|
||||||
|
"math.Cosh": "llvm.cosh.f64",
|
||||||
"math.Exp": "llvm.exp.f64",
|
"math.Exp": "llvm.exp.f64",
|
||||||
"math.Exp2": "llvm.exp2.f64",
|
"math.Exp2": "llvm.exp2.f64",
|
||||||
"math.Floor": "llvm.floor.f64",
|
"math.Floor": "llvm.floor.f64",
|
||||||
"math.Log": "llvm.log.f64",
|
"math.Log": "llvm.log.f64",
|
||||||
|
"math.Sin": "llvm.sin.f64",
|
||||||
|
"math.Sinh": "llvm.sinh.f64",
|
||||||
"math.Sqrt": "llvm.sqrt.f64",
|
"math.Sqrt": "llvm.sqrt.f64",
|
||||||
|
"math.Tan": "llvm.tan.f64",
|
||||||
|
"math.Tanh": "llvm.tanh.f64",
|
||||||
"math.Trunc": "llvm.trunc.f64",
|
"math.Trunc": "llvm.trunc.f64",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,18 +196,40 @@ var mathToLLVMMapping = map[string]string{
|
|||||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
// 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)
|
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||||
// more expensive than 32-bit ones.
|
// more expensive than 32-bit ones.
|
||||||
func (b *builder) defineMathOp() {
|
func (b *builder) defineMathOp() bool {
|
||||||
b.createFunctionStart(true)
|
llvmName, ok := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||||
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
|
if !ok {
|
||||||
if llvmName == "" {
|
return false
|
||||||
panic("unreachable: unknown math operation") // sanity check
|
|
||||||
}
|
}
|
||||||
|
if strings.HasSuffix(b.Triple, "-wasi") || llvmutil.Version() < 19 {
|
||||||
|
// We don't have a real libc for wasip2. Until that is fixed, we need to
|
||||||
|
// limit math intrinsics on WASI to a subset supported natively in
|
||||||
|
// WebAssembly.
|
||||||
|
// Also, since we don't know the specific libc we will target, disallow
|
||||||
|
// these for all WASI targets.
|
||||||
|
//
|
||||||
|
// We also need to limit ourselves to LLVM 19 and above for the extended
|
||||||
|
// set of math intrinsics, see:
|
||||||
|
// https://discourse.llvm.org/t/rfc-all-the-math-intrinsics/78294
|
||||||
|
switch b.fn.Name() {
|
||||||
|
case "Ceil", "Exp", "Exp2", "Floor", "Log", "Sqrt", "Trunc":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.createFunctionStart(true)
|
||||||
llvmFn := b.mod.NamedFunction(llvmName)
|
llvmFn := b.mod.NamedFunction(llvmName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
// The intrinsic doesn't exist yet, so declare it.
|
// The intrinsic doesn't exist yet, so declare it.
|
||||||
// At the moment, all supported intrinsics have the form "double
|
var llvmType llvm.Type
|
||||||
// foo(double %x)" so we can hardcode the signature here.
|
switch b.fn.Name() {
|
||||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
case "Atan2":
|
||||||
|
// double atan2(double %y, double %x)
|
||||||
|
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)
|
||||||
|
default:
|
||||||
|
// double foo(double %x)
|
||||||
|
llvmType = llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||||
|
}
|
||||||
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
||||||
}
|
}
|
||||||
// Create a call to the intrinsic.
|
// Create a call to the intrinsic.
|
||||||
@@ -185,6 +239,24 @@ func (b *builder) defineMathOp() {
|
|||||||
}
|
}
|
||||||
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||||
b.CreateRet(result)
|
b.CreateRet(result)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) defineCryptoIntrinsic() bool {
|
||||||
|
if b.fn.Pkg.Pkg.Path() != "crypto/internal/constanttime" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch b.fn.Name() {
|
||||||
|
case "boolToUint8":
|
||||||
|
b.createFunctionStart(true)
|
||||||
|
param := b.getValue(b.fn.Params[0], b.fn.Pos())
|
||||||
|
result := b.CreateZExt(param, b.ctx.Int8Type(), "")
|
||||||
|
b.CreateRet(result)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement most math/bits functions.
|
// Implement most math/bits functions.
|
||||||
|
|||||||
+95
-100
@@ -1,10 +1,10 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"math/big"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -54,7 +54,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
|||||||
// pointer value directly. It returns the pointer with the packed data.
|
// 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
|
// If the values are all constants, they are be stored in a constant global and
|
||||||
// deduplicated.
|
// deduplicated.
|
||||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
func (b *builder) emitPointerPack(values []llvm.Value, pos token.Pos) llvm.Value {
|
||||||
valueTypes := make([]llvm.Type, len(values))
|
valueTypes := make([]llvm.Type, len(values))
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
valueTypes[i] = value.Type()
|
valueTypes[i] = value.Type()
|
||||||
@@ -128,14 +128,9 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
|
|
||||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
|
layoutValue := b.createObjectLayout(packedType, pos)
|
||||||
align := b.targetData.ABITypeAlignment(packedType)
|
align := b.targetData.ABITypeAlignment(packedType)
|
||||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
packedAlloc := b.createAlloc(sizeValue, layoutValue, align, "")
|
||||||
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 {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(packedAlloc)
|
b.trackPointer(packedAlloc)
|
||||||
}
|
}
|
||||||
@@ -212,7 +207,7 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
globalType := llvm.ArrayType(elementType, len(buf))
|
globalType := llvm.ArrayType(elementType, len(buf))
|
||||||
global := llvm.AddGlobal(c.mod, globalType, name)
|
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||||
value := llvm.Undef(globalType)
|
value := llvm.Undef(globalType)
|
||||||
for i := 0; i < len(buf); i++ {
|
for i := range buf {
|
||||||
ch := uint64(buf[i])
|
ch := uint64(buf[i])
|
||||||
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||||
}
|
}
|
||||||
@@ -231,6 +226,12 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
//
|
//
|
||||||
// For details on what's in this value, see src/runtime/gc_precise.go.
|
// 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 {
|
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||||
|
if !typeHasPointers(t) {
|
||||||
|
// There are no pointers in this type, so we can simplify the layout.
|
||||||
|
layout := (uint64(1) << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
|
}
|
||||||
|
|
||||||
// Use the element type for arrays. This works even for nested arrays.
|
// Use the element type for arrays. This works even for nested arrays.
|
||||||
for {
|
for {
|
||||||
kind := t.TypeKind()
|
kind := t.TypeKind()
|
||||||
@@ -248,54 +249,29 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a few checks to see whether we need to generate any object layout
|
// Create the pointer bitmap.
|
||||||
// information at all.
|
|
||||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||||
|
pointerAlignment := uint64(c.targetData.PrefTypeAlignment(c.dataPtrType))
|
||||||
|
bitmapLen := objectSizeBytes / pointerAlignment
|
||||||
|
bitmapBytes := (bitmapLen + 7) / 8
|
||||||
|
bitmap := make([]byte, bitmapBytes, max(bitmapBytes, 8))
|
||||||
|
c.buildPointerBitmap(bitmap, pointerAlignment, pos, t, 0)
|
||||||
|
|
||||||
|
// Try to encode the layout inline.
|
||||||
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||||
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
|
||||||
if objectSizeBytes < pointerSize {
|
|
||||||
// Too small to contain a pointer.
|
|
||||||
layout := (uint64(1) << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
|
||||||
bitmap := c.getPointerBitmap(t, pos)
|
|
||||||
if bitmap.BitLen() == 0 {
|
|
||||||
// There are no pointers in this type, so we can simplify the layout.
|
|
||||||
// TODO: this can be done in many other cases, e.g. when allocating an
|
|
||||||
// array (like [4][]byte, which repeats a slice 4 times).
|
|
||||||
layout := (uint64(1) << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
|
||||||
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
|
||||||
// This shouldn't happen except for packed structs, which aren't
|
|
||||||
// currently used.
|
|
||||||
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
|
||||||
return llvm.ConstNull(c.dataPtrType)
|
|
||||||
}
|
|
||||||
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
|
||||||
|
|
||||||
pointerBits := pointerSize * 8
|
pointerBits := pointerSize * 8
|
||||||
var sizeFieldBits uint64
|
if bitmapLen < pointerBits {
|
||||||
switch pointerBits {
|
rawMask := binary.LittleEndian.Uint64(bitmap[0:8])
|
||||||
case 16:
|
layout := rawMask*pointerBits + bitmapLen
|
||||||
sizeFieldBits = 4
|
layout <<= 1
|
||||||
case 32:
|
layout |= 1
|
||||||
sizeFieldBits = 5
|
|
||||||
case 64:
|
|
||||||
sizeFieldBits = 6
|
|
||||||
default:
|
|
||||||
panic("unknown pointer size")
|
|
||||||
}
|
|
||||||
layoutFieldBits := pointerBits - 1 - sizeFieldBits
|
|
||||||
|
|
||||||
// Try to emit the value as an inline integer. This is possible in most
|
// Check if the layout fits.
|
||||||
// cases.
|
layout &= 1<<pointerBits - 1
|
||||||
if objectSizeWords < layoutFieldBits {
|
if (layout>>1)/pointerBits == rawMask {
|
||||||
// If it can be stored directly in the pointer value, do so.
|
// No set bits were shifted off.
|
||||||
// The runtime knows that if the least significant bit of the pointer is
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
// set, the pointer contains the value itself.
|
}
|
||||||
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||||
@@ -303,25 +279,24 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
|
|
||||||
// Try first whether the global already exists. All objects with a
|
// Try first whether the global already exists. All objects with a
|
||||||
// particular name have the same type, so this is possible.
|
// particular name have the same type, so this is possible.
|
||||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", bitmapLen, (bitmapLen+15)/16, bitmap)
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if !global.IsNil() {
|
if !global.IsNil() {
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the global initializer.
|
// Create the global initializer.
|
||||||
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
bitmapByteValues := make([]llvm.Value, bitmapBytes)
|
||||||
bitmap.FillBytes(bitmapBytes)
|
i8 := c.ctx.Int8Type()
|
||||||
reverseBytes(bitmapBytes) // big-endian to little-endian
|
for i, b := range bitmap {
|
||||||
var bitmapByteValues []llvm.Value
|
bitmapByteValues[i] = llvm.ConstInt(i8, uint64(b), false)
|
||||||
for _, b := range bitmapBytes {
|
|
||||||
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
|
||||||
}
|
}
|
||||||
initializer := c.ctx.ConstStruct([]llvm.Value{
|
initializer := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, objectSizeWords, false),
|
llvm.ConstInt(c.uintptrType, bitmapLen, false),
|
||||||
llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
|
llvm.ConstArray(i8, bitmapByteValues),
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
|
// Create the actual global.
|
||||||
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
@@ -329,6 +304,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
||||||
// AVR doesn't have alignment by default.
|
// AVR doesn't have alignment by default.
|
||||||
|
// The lowest bit must be unset to distinguish this from an inline layout.
|
||||||
global.SetAlignment(2)
|
global.SetAlignment(2)
|
||||||
}
|
}
|
||||||
if c.Debug && pos != token.NoPos {
|
if c.Debug && pos != token.NoPos {
|
||||||
@@ -360,52 +336,71 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
// buildPointerBitmap 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.
|
// bitmap at the word offset that contains a pointer. This scan is recursive.
|
||||||
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
func (c *compilerContext) buildPointerBitmap(
|
||||||
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
dst []byte,
|
||||||
switch typ.TypeKind() {
|
ptrAlign uint64,
|
||||||
|
pos token.Pos,
|
||||||
|
t llvm.Type,
|
||||||
|
offset uint64,
|
||||||
|
) {
|
||||||
|
switch t.TypeKind() {
|
||||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
return big.NewInt(0)
|
// These types do not contain pointers.
|
||||||
|
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
return big.NewInt(1)
|
// Set the corresponding position in the bitmap.
|
||||||
|
dst[offset/8] |= 1 << (offset % 8)
|
||||||
|
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
ptrs := big.NewInt(0)
|
// Recurse over struct elements.
|
||||||
for i, subtyp := range typ.StructElementTypes() {
|
for i, et := range t.StructElementTypes() {
|
||||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
eo := c.targetData.ElementOffset(t, i)
|
||||||
if subptrs.BitLen() == 0 {
|
if eo%uint64(ptrAlign) != 0 {
|
||||||
|
if typeHasPointers(et) {
|
||||||
|
// This error will let the compilation fail, but by continuing
|
||||||
|
// the error can still easily be shown.
|
||||||
|
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
offset := c.targetData.ElementOffset(typ, i)
|
c.buildPointerBitmap(
|
||||||
if offset%uint64(alignment) != 0 {
|
dst,
|
||||||
// This error will let the compilation fail, but by continuing
|
ptrAlign,
|
||||||
// the error can still easily be shown.
|
pos,
|
||||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
et,
|
||||||
continue
|
offset+(eo/ptrAlign),
|
||||||
}
|
)
|
||||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
|
||||||
ptrs.Or(ptrs, subptrs)
|
|
||||||
}
|
}
|
||||||
return ptrs
|
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
subtyp := typ.ElementType()
|
// Recurse over array elements.
|
||||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
len := t.ArrayLength()
|
||||||
ptrs := big.NewInt(0)
|
if len <= 0 {
|
||||||
if subptrs.BitLen() == 0 {
|
return
|
||||||
return ptrs
|
|
||||||
}
|
}
|
||||||
elementSize := c.targetData.TypeAllocSize(subtyp)
|
et := t.ElementType()
|
||||||
if elementSize%uint64(alignment) != 0 {
|
elementSize := c.targetData.TypeAllocSize(et)
|
||||||
// This error will let the compilation fail (but continues so that
|
if elementSize%ptrAlign != 0 {
|
||||||
// other errors can be shown).
|
if typeHasPointers(et) {
|
||||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
// This error will let the compilation fail (but continues so that
|
||||||
return ptrs
|
// other errors can be shown).
|
||||||
|
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.ArrayLength(); i++ {
|
elementSize /= ptrAlign
|
||||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
for i := range len {
|
||||||
ptrs.Or(ptrs, subptrs)
|
c.buildPointerBitmap(
|
||||||
|
dst,
|
||||||
|
ptrAlign,
|
||||||
|
pos,
|
||||||
|
et,
|
||||||
|
offset+uint64(i)*elementSize,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return ptrs
|
|
||||||
default:
|
default:
|
||||||
// Should not happen.
|
// Should not happen.
|
||||||
panic("unknown LLVM type")
|
panic("unknown LLVM type")
|
||||||
@@ -423,7 +418,7 @@ func (c *compilerContext) archFamily() string {
|
|||||||
// features string is not one for an ARM architecture.
|
// features string is not one for an ARM architecture.
|
||||||
func (c *compilerContext) isThumb() bool {
|
func (c *compilerContext) isThumb() bool {
|
||||||
var isThumb, isNotThumb bool
|
var isThumb, isNotThumb bool
|
||||||
for _, feature := range strings.Split(c.Features, ",") {
|
for feature := range strings.SplitSeq(c.Features, ",") {
|
||||||
if feature == "+thumb-mode" {
|
if feature == "+thumb-mode" {
|
||||||
isThumb = true
|
isThumb = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
|||||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||||
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||||
fnType, fn := getLifetimeStartFunc(mod)
|
fnType, fn := getLifetimeStartFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, alloca), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,14 +58,14 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
|||||||
builder.SetInsertPointBefore(inst)
|
builder.SetInsertPointBefore(inst)
|
||||||
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||||
fnType, fn := getLifetimeStartFunc(mod)
|
fnType, fn := getLifetimeStartFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, alloca), "")
|
||||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||||
builder.SetInsertPointBefore(next)
|
builder.SetInsertPointBefore(next)
|
||||||
} else {
|
} else {
|
||||||
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||||
}
|
}
|
||||||
fnType, fn = getLifetimeEndFunc(mod)
|
fnType, fn = getLifetimeEndFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, alloca), "")
|
||||||
return alloca
|
return alloca
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,27 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
|||||||
// createTemporaryAlloca.
|
// createTemporaryAlloca.
|
||||||
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
|
func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
|
||||||
fnType, fn := getLifetimeEndFunc(mod)
|
fnType, fn := getLifetimeEndFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
|
builder.CreateCall(fnType, fn, lifetimeCallArgs(size, ptr), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// lifetimeCallArgs returns the arguments to pass to a call of the
|
||||||
|
// llvm.lifetime.start/end intrinsics. LLVM 22 dropped the (redundant,
|
||||||
|
// already required to match the alloca size) i64 size argument, so the
|
||||||
|
// intrinsic now only takes the pointer.
|
||||||
|
func lifetimeCallArgs(size, ptr llvm.Value) []llvm.Value {
|
||||||
|
if Version() >= 22 {
|
||||||
|
return []llvm.Value{ptr}
|
||||||
|
}
|
||||||
|
return []llvm.Value{size, ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lifetimeFuncType returns the function type of the llvm.lifetime.start/end
|
||||||
|
// intrinsics, which lost their i64 size parameter in LLVM 22.
|
||||||
|
func lifetimeFuncType(ctx llvm.Context, ptrType llvm.Type) llvm.Type {
|
||||||
|
if Version() >= 22 {
|
||||||
|
return llvm.FunctionType(ctx.VoidType(), []llvm.Type{ptrType}, false)
|
||||||
|
}
|
||||||
|
return llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||||
@@ -84,7 +104,7 @@ func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
|||||||
fn := mod.NamedFunction(fnName)
|
fn := mod.NamedFunction(fnName)
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
fnType := lifetimeFuncType(ctx, ptrType)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
@@ -98,7 +118,7 @@ func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
|||||||
fn := mod.NamedFunction(fnName)
|
fn := mod.NamedFunction(fnName)
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
fnType := lifetimeFuncType(ctx, ptrType)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
@@ -218,7 +238,37 @@ func Version() int {
|
|||||||
return major
|
return major
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the byte order for the given target triple. Most targets are little
|
// NoCaptureAttrName returns the name of the LLVM attribute kind that marks a
|
||||||
|
// pointer parameter as guaranteed not to escape by capture.
|
||||||
|
//
|
||||||
|
// LLVM 21 removed the old boolean 'nocapture' enum attribute in favor of the
|
||||||
|
// more expressive 'captures' int attribute, where 'captures(none)' (encoded
|
||||||
|
// as the value 0) is the equivalent of the old 'nocapture'. LLVM 20 supports
|
||||||
|
// both, but its own optimizer still emits 'nocapture', so the cutoff for
|
||||||
|
// reading/writing the new name is LLVM 21.
|
||||||
|
func NoCaptureAttrName() string {
|
||||||
|
if Version() >= 21 {
|
||||||
|
return "captures"
|
||||||
|
}
|
||||||
|
return "nocapture"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNoCapture reports whether attr (looked up using the kind returned by
|
||||||
|
// NoCaptureAttrName) indicates that the pointer it is attached to does not
|
||||||
|
// escape by capture. It returns false for a nil attribute.
|
||||||
|
func IsNoCapture(attr llvm.Attribute) bool {
|
||||||
|
if attr.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if Version() >= 21 {
|
||||||
|
// captures(none) is encoded as the value 0; any other value permits
|
||||||
|
// some form of capture.
|
||||||
|
return attr.GetEnumValue() == 0
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByteOrder returns the byte order for the given target triple. Most targets are little
|
||||||
// endian, but for example MIPS can be big-endian.
|
// endian, but for example MIPS can be big-endian.
|
||||||
func ByteOrder(target string) binary.ByteOrder {
|
func ByteOrder(target string) binary.ByteOrder {
|
||||||
if strings.HasPrefix(target, "mips-") {
|
if strings.HasPrefix(target, "mips-") {
|
||||||
|
|||||||
+469
-190
@@ -3,42 +3,29 @@ package compiler
|
|||||||
// This file emits the correct map intrinsics for map operations.
|
// This file emits the correct map intrinsics for map operations.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/src/tinygo"
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
|
"strings"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const hashArrayUnrollLimit = 4
|
||||||
|
|
||||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||||
// initializing an appropriately sized object.
|
// initializing an appropriately sized object.
|
||||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
mapType := expr.Type().Underlying().(*types.Map)
|
mapType := expr.Type().Underlying().(*types.Map)
|
||||||
keyType := mapType.Key().Underlying()
|
keyType := mapType.Key().Underlying()
|
||||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||||
var llvmKeyType llvm.Type
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
var alg uint64
|
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
|
||||||
// String keys.
|
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
|
||||||
alg = uint64(tinygo.HashmapAlgorithmString)
|
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
|
||||||
// Trivially comparable keys.
|
|
||||||
llvmKeyType = b.getLLVMType(keyType)
|
|
||||||
alg = uint64(tinygo.HashmapAlgorithmBinary)
|
|
||||||
} else {
|
|
||||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
|
||||||
// implementation.
|
|
||||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
|
||||||
alg = uint64(tinygo.HashmapAlgorithmInterface)
|
|
||||||
}
|
|
||||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
|
||||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||||
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
|
||||||
if expr.Reserve != nil {
|
if expr.Reserve != nil {
|
||||||
sizeHint = b.getValue(expr.Reserve, getPos(expr))
|
sizeHint = b.getValue(expr.Reserve, getPos(expr))
|
||||||
var err error
|
var err error
|
||||||
@@ -47,134 +34,129 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
|
||||||
|
// Resolve hash and equal functions for this key type. For string and
|
||||||
|
// binary key types, reference the corresponding runtime functions
|
||||||
|
// directly. For composite types, generate type-specific functions.
|
||||||
|
var hashFn, equalFn llvm.Value
|
||||||
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
|
hashFn = b.getRuntimeFunctionValue("hashmapStringPtrHash", hashmapKeyHashSignature())
|
||||||
|
equalFn = b.getRuntimeFunctionValue("hashmapStringEqual", hashmapKeyEqualSignature())
|
||||||
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
|
hashFn = b.getRuntimeFunctionValue("hash32", hashmapKeyHashSignature())
|
||||||
|
equalFn = b.getRuntimeFunctionValue("memequal", hashmapKeyEqualSignature())
|
||||||
|
} else {
|
||||||
|
fn := b.getOrGenerateKeyHashFunc(keyType)
|
||||||
|
hashFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyHashSignature())
|
||||||
|
fn = b.getOrGenerateKeyEqualFunc(keyType)
|
||||||
|
equalFn = b.createFuncValue(fn, llvm.ConstNull(b.dataPtrType), hashmapKeyEqualSignature())
|
||||||
|
}
|
||||||
|
|
||||||
|
hashmap := b.createRuntimeCall("hashmapMakeGeneric", []llvm.Value{
|
||||||
|
llvmKeySize, llvmValueSize, sizeHint,
|
||||||
|
hashFn, equalFn,
|
||||||
|
}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRuntimeFunctionValue returns a TinyGo function value (with nil context)
|
||||||
|
// for the named runtime function.
|
||||||
|
func (b *builder) getRuntimeFunctionValue(name string, sig *types.Signature) llvm.Value {
|
||||||
|
member := b.program.ImportedPackage("runtime").Members[name]
|
||||||
|
if member == nil {
|
||||||
|
panic("unknown runtime function: " + name)
|
||||||
|
}
|
||||||
|
_, llvmFn := b.getFunction(member.(*ssa.Function))
|
||||||
|
return b.createFuncValue(llvmFn, llvm.ConstNull(b.dataPtrType), sig)
|
||||||
|
}
|
||||||
|
|
||||||
// createMapLookup returns the value in a map. It calls a runtime function
|
// createMapLookup returns the value in a map. It calls a runtime function
|
||||||
// depending on the map key type to load the map value and its comma-ok value.
|
// depending on the map key type to load the map value and its comma-ok value.
|
||||||
func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, key ssa.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
llvmValueType := b.getLLVMType(valueType)
|
llvmValueType := b.getLLVMType(valueType)
|
||||||
|
|
||||||
// Allocate the memory for the resulting type. Do not zero this memory: it
|
// Allocate the memory for the resulting type. Do not zero this memory: it
|
||||||
// will be zeroed by the hashmap get implementation if the key is not
|
// will be zeroed by the hashmap get implementation if the key is not
|
||||||
// present in the map.
|
// present in the map.
|
||||||
mapValueAlloca, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
result := b.createRuntimeValueResult(llvmValueType, commaOk, false, "hashmap")
|
||||||
|
mapValueAlloca := result.valuePtr
|
||||||
|
|
||||||
// We need the map size (with type uintptr) to pass to the hashmap*Get
|
// We need the map size (with type uintptr) to pass to the hashmap*Get
|
||||||
// functions. This is necessary because those *Get functions are valid on
|
// functions. This is necessary because those *Get functions are valid on
|
||||||
// nil maps, and they'll need to zero the value pointer by that number of
|
// nil maps, and they'll need to zero the value pointer by that number of
|
||||||
// bytes.
|
// bytes.
|
||||||
mapValueSize := mapValueAllocaSize
|
mapValueSize := result.valueSize
|
||||||
if mapValueSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
if mapValueSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||||
mapValueSize = llvm.ConstTrunc(mapValueSize, b.uintptrType)
|
mapValueSize = llvm.ConstTrunc(mapValueSize, b.uintptrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the lookup. How it is done depends on the key type.
|
// Do the lookup. How it is done depends on the key type.
|
||||||
var commaOkValue llvm.Value
|
var commaOkValue llvm.Value
|
||||||
origKeyType := keyType
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
|
params := []llvm.Value{m, b.getValue(key, getPos(key)), mapValueAlloca, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
|
||||||
// key can be compared with runtime.memequal
|
|
||||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
|
||||||
// growth.
|
|
||||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
|
||||||
b.CreateStore(key, mapKeyAlloca)
|
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
|
||||||
// Fetch the value from the hashmap.
|
|
||||||
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
|
|
||||||
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
|
||||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
|
||||||
} else {
|
} else {
|
||||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
// Key stored at actual type: either binary-comparable or with
|
||||||
itfKey := key
|
// compiler-generated hash/equal.
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
mapKey := b.getValueStorage(key, "hashmap.key")
|
||||||
// Not already an interface, so convert it to an interface now.
|
params := []llvm.Value{m, mapKey.ptr, mapValueAlloca, mapValueSize}
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
fnName := "hashmapBinaryGet"
|
||||||
|
if !hashmapIsBinaryKey(keyType) {
|
||||||
|
fnName = "hashmapGenericGet"
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
|
commaOkValue = b.createRuntimeCall(fnName, params, "")
|
||||||
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
b.endValueStorage(mapKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
// The value is set to the zero value if the key doesn't exist.
|
||||||
// value if the key doesn't exist in the hashmap.
|
return result.finish(b, commaOkValue, ""), nil
|
||||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
|
||||||
b.emitLifetimeEnd(mapValueAlloca, mapValueAllocaSize)
|
|
||||||
|
|
||||||
if commaOk {
|
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false))
|
|
||||||
tuple = b.CreateInsertValue(tuple, mapValue, 0, "")
|
|
||||||
tuple = b.CreateInsertValue(tuple, commaOkValue, 1, "")
|
|
||||||
return tuple, nil
|
|
||||||
} else {
|
|
||||||
return mapValue, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMapUpdate updates a map key to a given value, by creating an
|
// createMapUpdate updates a map key to a given value, by creating an
|
||||||
// appropriate runtime call.
|
// appropriate runtime call.
|
||||||
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
func (b *builder) createMapUpdate(keyType types.Type, m llvm.Value, key, value ssa.Value, pos token.Pos) {
|
||||||
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
storedValue := b.getValueStorage(value, "hashmap.value")
|
||||||
b.CreateStore(value, valueAlloca)
|
|
||||||
origKeyType := keyType
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, valueAlloca}
|
params := []llvm.Value{m, b.getValue(key, getPos(key)), storedValue.ptr}
|
||||||
b.createRuntimeCall("hashmapStringSet", params, "")
|
b.createRuntimeInvoke("hashmapStringSet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
|
||||||
// key can be compared with runtime.memequal
|
|
||||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
|
||||||
b.CreateStore(key, keyAlloca)
|
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
|
||||||
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
|
||||||
b.createRuntimeCall("hashmapBinarySet", params, "")
|
|
||||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
|
||||||
} else {
|
} else {
|
||||||
// Key is not trivially comparable, so compare it as an interface instead.
|
// Key stored at actual type.
|
||||||
itfKey := key
|
keyStorage := b.getValueStorage(key, "hashmap.key")
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
fnName := "hashmapBinarySet"
|
||||||
// Not already an interface, so convert it to an interface first.
|
if !hashmapIsBinaryKey(keyType) {
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
fnName = "hashmapGenericSet"
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, valueAlloca}
|
params := []llvm.Value{m, keyStorage.ptr, storedValue.ptr}
|
||||||
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
b.createRuntimeInvoke(fnName, params, "")
|
||||||
|
b.endValueStorage(keyStorage)
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(valueAlloca, valueSize)
|
b.endValueStorage(storedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
||||||
// function. It is the implementation of the Go delete() builtin.
|
// function. It is the implementation of the Go delete() builtin.
|
||||||
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||||
origKeyType := keyType
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key}
|
params := []llvm.Value{m, key}
|
||||||
b.createRuntimeCall("hashmapStringDelete", params, "")
|
b.createRuntimeCall("hashmapStringDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else {
|
||||||
|
// Key stored at actual type.
|
||||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
fnName := "hashmapBinaryDelete"
|
||||||
params := []llvm.Value{m, keyAlloca}
|
if !hashmapIsBinaryKey(keyType) {
|
||||||
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
fnName = "hashmapGenericDelete"
|
||||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
// Key is not trivially comparable, so compare it as an interface
|
|
||||||
// instead.
|
|
||||||
itfKey := key
|
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
|
||||||
// Not already an interface, so convert it to an interface first.
|
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey}
|
params := []llvm.Value{m, keyAlloca}
|
||||||
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
b.createRuntimeCall(fnName, params, "")
|
||||||
|
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,42 +177,15 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
llvmKeyType := b.getLLVMType(keyType)
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
llvmValueType := b.getLLVMType(valueType)
|
llvmValueType := b.getLLVMType(valueType)
|
||||||
|
|
||||||
// There is a special case in which keys are stored as an interface value
|
// All key types are now stored at their declared type (no interface wrapping).
|
||||||
// instead of the value they normally are. This happens for non-trivially
|
|
||||||
// comparable types such as float32 or some structs.
|
|
||||||
isKeyStoredAsInterface := false
|
|
||||||
if t, ok := keyType.Underlying().(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
|
||||||
// key is a string
|
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
|
||||||
// key can be compared with runtime.memequal
|
|
||||||
} else {
|
|
||||||
// The key is stored as an interface value, and may or may not be an
|
|
||||||
// interface type (for example, float32 keys are stored as an interface
|
|
||||||
// value).
|
|
||||||
if _, ok := keyType.Underlying().(*types.Interface); !ok {
|
|
||||||
isKeyStoredAsInterface = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the type of the key as stored in the map.
|
|
||||||
llvmStoredKeyType := llvmKeyType
|
|
||||||
if isKeyStoredAsInterface {
|
|
||||||
llvmStoredKeyType = b.getLLVMRuntimeType("_interface")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the key and value from the map.
|
// Extract the key and value from the map.
|
||||||
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key")
|
||||||
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
||||||
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
mapKey := b.CreateLoad(llvmKeyType, mapKeyAlloca, "")
|
||||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||||
|
|
||||||
if isKeyStoredAsInterface {
|
|
||||||
// The key is stored as an interface but it isn't of interface type.
|
|
||||||
// Extract the underlying value.
|
|
||||||
mapKey = b.extractValueFromInterface(mapKey, llvmKeyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// End the lifetimes of the allocas, because we're done with them.
|
// End the lifetimes of the allocas, because we're done with them.
|
||||||
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||||
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
||||||
@@ -250,17 +205,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||||
switch keyType := keyType.Underlying().(type) {
|
switch keyType := keyType.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 || keyType.Kind() == types.UnsafePointer
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
return true
|
return true
|
||||||
case *types.Struct:
|
|
||||||
for i := 0; i < keyType.NumFields(); i++ {
|
|
||||||
fieldType := keyType.Field(i).Type().Underlying()
|
|
||||||
if !hashmapIsBinaryKey(fieldType) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return hashmapIsBinaryKey(keyType.Elem())
|
return hashmapIsBinaryKey(keyType.Elem())
|
||||||
default:
|
default:
|
||||||
@@ -268,68 +215,400 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
|
// hashmapKeyHashSignature returns the Go type signature for hashmap key hash
|
||||||
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
|
// functions: func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||||
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
|
func hashmapKeyHashSignature() *types.Signature {
|
||||||
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
|
return types.NewSignatureType(nil, nil, nil,
|
||||||
// we handle nested types. Next, we determine if there are any padding bytes before the next
|
types.NewTuple(
|
||||||
// element and zero those as well.
|
types.NewVar(token.NoPos, nil, "key", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uintptr]),
|
||||||
|
types.NewVar(token.NoPos, nil, "seed", types.Typ[types.Uintptr]),
|
||||||
|
),
|
||||||
|
types.NewTuple(
|
||||||
|
types.NewVar(token.NoPos, nil, "", types.Typ[types.Uint32]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
// hashmapKeyEqualSignature returns the Go type signature for hashmap key equal
|
||||||
|
// functions: func(x, y unsafe.Pointer, n uintptr) bool
|
||||||
|
func hashmapKeyEqualSignature() *types.Signature {
|
||||||
|
return types.NewSignatureType(nil, nil, nil,
|
||||||
|
types.NewTuple(
|
||||||
|
types.NewVar(token.NoPos, nil, "x", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "y", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "n", types.Typ[types.Uintptr]),
|
||||||
|
),
|
||||||
|
types.NewTuple(
|
||||||
|
types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
switch llvmType.TypeKind() {
|
// hashmapKeyFuncName returns a canonical name for a generated hash or equal
|
||||||
case llvm.IntegerTypeKind:
|
// function based on the key type's underlying structure. Named types are
|
||||||
// no padding bytes
|
// replaced with their underlying types so that structurally identical key
|
||||||
return nil
|
// types (e.g., struct{i1; str1} and struct{i2; str2} where both i1, i2 are
|
||||||
case llvm.PointerTypeKind:
|
// int and str1, str2 are string) share the same generated function.
|
||||||
// mo padding bytes
|
func hashmapKeyFuncName(prefix string, keyType types.Type) string {
|
||||||
return nil
|
return prefix + "." + hashmapCanonicalTypeName(keyType)
|
||||||
case llvm.ArrayTypeKind:
|
}
|
||||||
llvmArrayType := llvmType
|
|
||||||
llvmElemType := llvmType.ElementType()
|
|
||||||
|
|
||||||
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
|
// hashmapCanonicalTypeName returns a string representation of the hash/equal
|
||||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
// operations needed for a type, stripping named types where the operation does
|
||||||
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
|
// not depend on the name. Pointer and channel names do not include the element
|
||||||
|
// type because their hash/equal operations only use the pointer word.
|
||||||
// zero any padding bytes in this element
|
func hashmapCanonicalTypeName(t types.Type) string {
|
||||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
switch t := t.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return t.Name()
|
||||||
|
case *types.Pointer:
|
||||||
|
return "*"
|
||||||
|
case *types.Chan:
|
||||||
|
switch t.Dir() {
|
||||||
|
case types.SendRecv:
|
||||||
|
return "chan"
|
||||||
|
case types.SendOnly:
|
||||||
|
return "chan<-"
|
||||||
|
case types.RecvOnly:
|
||||||
|
return "<-chan"
|
||||||
}
|
}
|
||||||
|
case *types.Interface:
|
||||||
case llvm.StructTypeKind:
|
if t.NumMethods() == 0 {
|
||||||
llvmStructType := llvmType
|
return "interface{}"
|
||||||
numFields := llvmStructType.StructElementTypesCount()
|
|
||||||
llvmElementTypes := llvmStructType.StructElementTypes()
|
|
||||||
|
|
||||||
for i := 0; i < numFields; i++ {
|
|
||||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
|
||||||
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
|
|
||||||
|
|
||||||
// zero any padding bytes in this field
|
|
||||||
llvmElemType := llvmElementTypes[i]
|
|
||||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
|
||||||
|
|
||||||
// zero any padding bytes before the next field, if any
|
|
||||||
offset := b.targetData.ElementOffset(llvmStructType, i)
|
|
||||||
storeSize := b.targetData.TypeStoreSize(llvmElemType)
|
|
||||||
fieldEndOffset := offset + storeSize
|
|
||||||
|
|
||||||
var nextOffset uint64
|
|
||||||
if i < numFields-1 {
|
|
||||||
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
|
|
||||||
} else {
|
|
||||||
// Last field? Next offset is the total size of the allocate struct.
|
|
||||||
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fieldEndOffset != nextOffset {
|
|
||||||
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
|
||||||
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
|
||||||
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
|
|
||||||
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return t.String()
|
||||||
|
case *types.Struct:
|
||||||
|
var s strings.Builder
|
||||||
|
s.WriteString("struct{")
|
||||||
|
for i := 0; i < t.NumFields(); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
s.WriteString("; ")
|
||||||
|
}
|
||||||
|
s.WriteString(hashmapCanonicalTypeName(t.Field(i).Type()))
|
||||||
|
}
|
||||||
|
return s.String() + "}"
|
||||||
|
case *types.Array:
|
||||||
|
return fmt.Sprintf("[%d]%s", t.Len(), hashmapCanonicalTypeName(t.Elem()))
|
||||||
|
}
|
||||||
|
return t.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrGenerateKeyHashFunc returns an LLVM function that computes the hash
|
||||||
|
// of a key of the given type. The function is generated on first call and
|
||||||
|
// cached in the module.
|
||||||
|
func (b *builder) getOrGenerateKeyHashFunc(keyType types.Type) llvm.Value {
|
||||||
|
name := hashmapKeyFuncName("hashmapKeyHash", keyType)
|
||||||
|
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||||
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// Create the LLVM function type:
|
||||||
|
// (key ptr, size uintptr, seed uintptr, context ptr) -> i32
|
||||||
|
fnType := llvm.FunctionType(b.ctx.Int32Type(), []llvm.Type{
|
||||||
|
b.dataPtrType, b.uintptrType, b.uintptrType, b.dataPtrType,
|
||||||
|
}, false)
|
||||||
|
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||||
|
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
fn.SetUnnamedAddr(true)
|
||||||
|
b.addStandardAttributes(fn)
|
||||||
|
|
||||||
|
// Generate the function body.
|
||||||
|
savedBlock := b.GetInsertBlock()
|
||||||
|
defer b.SetInsertPointAtEnd(savedBlock)
|
||||||
|
|
||||||
|
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||||
|
b.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
keyPtr := fn.Param(0)
|
||||||
|
seed := fn.Param(2)
|
||||||
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
|
hash := b.generateKeyHash(keyType, llvmKeyType, keyPtr, seed)
|
||||||
|
b.CreateRet(hash)
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrGenerateKeyEqualFunc returns an LLVM function that compares two keys
|
||||||
|
// of the given type for equality. The function is generated on first call
|
||||||
|
// and cached in the module.
|
||||||
|
func (b *builder) getOrGenerateKeyEqualFunc(keyType types.Type) llvm.Value {
|
||||||
|
name := hashmapKeyFuncName("hashmapKeyEqual", keyType)
|
||||||
|
if fn := b.mod.NamedFunction(name); !fn.IsNil() {
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the LLVM function type:
|
||||||
|
// (x ptr, y ptr, n uintptr, context ptr) -> i1
|
||||||
|
fnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{
|
||||||
|
b.dataPtrType, b.dataPtrType, b.uintptrType, b.dataPtrType,
|
||||||
|
}, false)
|
||||||
|
fn := llvm.AddFunction(b.mod, name, fnType)
|
||||||
|
fn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
fn.SetUnnamedAddr(true)
|
||||||
|
b.addStandardAttributes(fn)
|
||||||
|
|
||||||
|
// Generate the function body.
|
||||||
|
savedBlock := b.GetInsertBlock()
|
||||||
|
defer b.SetInsertPointAtEnd(savedBlock)
|
||||||
|
|
||||||
|
entry := b.ctx.AddBasicBlock(fn, "entry")
|
||||||
|
b.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
xPtr := fn.Param(0)
|
||||||
|
yPtr := fn.Param(1)
|
||||||
|
llvmKeyType := b.getLLVMType(keyType)
|
||||||
|
result := b.generateKeyEqual(keyType, llvmKeyType, xPtr, yPtr, fn)
|
||||||
|
b.CreateRet(result)
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateKeyHash generates IR that hashes a key value. Returns the i32 hash.
|
||||||
|
func (b *builder) generateKeyHash(keyType types.Type, llvmKeyType llvm.Type, keyPtr llvm.Value, seed llvm.Value) llvm.Value {
|
||||||
|
switch keyType := keyType.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
if keyType.Info()&types.IsString != 0 {
|
||||||
|
// Hash the string contents. The size parameter is unused by
|
||||||
|
// hashmapStringPtrHash (it dereferences the string header to
|
||||||
|
// get the actual length), but we pass it for signature
|
||||||
|
// consistency with other hash functions.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hashmapStringPtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsFloat != 0 {
|
||||||
|
// Float hash: normalizes -0 to +0 before hashing.
|
||||||
|
if keyType.Kind() == types.Float32 {
|
||||||
|
return b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||||
|
}
|
||||||
|
return b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{keyPtr, seed}, "hash")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsComplex != 0 {
|
||||||
|
// Complex hash: hash real and imaginary parts as floats.
|
||||||
|
if keyType.Kind() == types.Complex64 {
|
||||||
|
realPtr := keyPtr
|
||||||
|
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||||
|
llvm.ConstInt(b.uintptrType, 4, false),
|
||||||
|
}, "")
|
||||||
|
realHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||||
|
imagHash := b.createRuntimeCall("hashmapFloat32Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||||
|
return b.CreateXor(realHash, imagHash, "")
|
||||||
|
}
|
||||||
|
realPtr := keyPtr
|
||||||
|
imagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), keyPtr, []llvm.Value{
|
||||||
|
llvm.ConstInt(b.uintptrType, 8, false),
|
||||||
|
}, "")
|
||||||
|
realHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{realPtr, seed}, "hash.real")
|
||||||
|
imagHash := b.createRuntimeCall("hashmapFloat64Hash", []llvm.Value{imagPtr, seed}, "hash.imag")
|
||||||
|
return b.CreateXor(realHash, imagHash, "")
|
||||||
|
}
|
||||||
|
// Integer/boolean: hash the raw bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
case *types.Pointer, *types.Chan:
|
||||||
|
// Pointers and channels: hash as raw pointer-sized bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
case *types.Interface:
|
||||||
|
// Interface: use runtime reflection-based hash.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hashmapInterfacePtrHash", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
case *types.Struct:
|
||||||
|
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < keyType.NumFields(); i++ {
|
||||||
|
if keyType.Field(i).Name() == "_" {
|
||||||
|
continue // blank fields are ignored in Go equality
|
||||||
|
}
|
||||||
|
fieldType := keyType.Field(i).Type()
|
||||||
|
llvmFieldType := b.getLLVMType(fieldType)
|
||||||
|
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||||
|
continue // skip zero-sized fields
|
||||||
|
}
|
||||||
|
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||||
|
fieldPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
fieldHash := b.generateKeyHash(fieldType, llvmFieldType, fieldPtr, seed)
|
||||||
|
hash = b.CreateXor(hash, fieldHash, "")
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
case *types.Array:
|
||||||
|
elemType := keyType.Elem()
|
||||||
|
llvmElemType := b.getLLVMType(elemType)
|
||||||
|
arrayLen := keyType.Len()
|
||||||
|
if hashmapIsBinaryKey(elemType) {
|
||||||
|
// All elements are binary-comparable; hash the entire array as raw bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hash32", []llvm.Value{keyPtr, size, seed}, "hash")
|
||||||
|
}
|
||||||
|
if arrayLen == 0 {
|
||||||
|
return llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
}
|
||||||
|
if arrayLen <= hashArrayUnrollLimit {
|
||||||
|
hash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < int(arrayLen); i++ {
|
||||||
|
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||||
|
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||||
|
hash = b.CreateXor(hash, elemHash, "")
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
initHash := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
|
||||||
|
loopEntry := b.GetInsertBlock()
|
||||||
|
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.body")
|
||||||
|
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "hash.array.done")
|
||||||
|
|
||||||
|
b.CreateBr(loopBody)
|
||||||
|
b.SetInsertPointAtEnd(loopBody)
|
||||||
|
|
||||||
|
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||||
|
phiHash := b.CreatePHI(b.ctx.Int32Type(), "hash.acc")
|
||||||
|
|
||||||
|
elemPtr := b.CreateInBoundsGEP(llvmKeyType, keyPtr, []llvm.Value{zero, phiI}, "")
|
||||||
|
elemHash := b.generateKeyHash(elemType, llvmElemType, elemPtr, seed)
|
||||||
|
newHash := b.CreateXor(phiHash, elemHash, "")
|
||||||
|
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||||
|
cond := b.CreateICmp(llvm.IntULT, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||||
|
b.CreateCondBr(cond, loopBody, loopDone)
|
||||||
|
|
||||||
|
bodyEnd := b.GetInsertBlock()
|
||||||
|
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||||
|
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||||
|
phiHash.AddIncoming([]llvm.Value{initHash, newHash},
|
||||||
|
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||||
|
|
||||||
|
b.SetInsertPointAtEnd(loopDone)
|
||||||
|
return newHash
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled key type for hash generation: %T", keyType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateKeyEqual generates IR that compares two key values for equality.
|
||||||
|
// Returns an i1 result.
|
||||||
|
func (b *builder) generateKeyEqual(keyType types.Type, llvmKeyType llvm.Type, xPtr, yPtr llvm.Value, fn llvm.Value) llvm.Value {
|
||||||
|
switch keyType := keyType.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
if keyType.Info()&types.IsString != 0 {
|
||||||
|
// Compare strings: load both string headers and compare.
|
||||||
|
xStr := b.CreateLoad(llvmKeyType, xPtr, "x.str")
|
||||||
|
yStr := b.CreateLoad(llvmKeyType, yPtr, "y.str")
|
||||||
|
return b.createRuntimeCall("stringEqual", []llvm.Value{xStr, yStr}, "eq")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsFloat != 0 {
|
||||||
|
// Float equality: fcmp oeq handles -0==+0 (true) and NaN==NaN (false).
|
||||||
|
xVal := b.CreateLoad(llvmKeyType, xPtr, "x.float")
|
||||||
|
yVal := b.CreateLoad(llvmKeyType, yPtr, "y.float")
|
||||||
|
return b.CreateFCmp(llvm.FloatOEQ, xVal, yVal, "eq")
|
||||||
|
}
|
||||||
|
if keyType.Info()&types.IsComplex != 0 {
|
||||||
|
// Complex equality: both real and imaginary parts must be equal.
|
||||||
|
var floatType llvm.Type
|
||||||
|
if keyType.Kind() == types.Complex64 {
|
||||||
|
floatType = b.ctx.FloatType()
|
||||||
|
} else {
|
||||||
|
floatType = b.ctx.DoubleType()
|
||||||
|
}
|
||||||
|
floatSize := b.targetData.TypeAllocSize(floatType)
|
||||||
|
imagOffset := llvm.ConstInt(b.uintptrType, floatSize, false)
|
||||||
|
// Real parts
|
||||||
|
xReal := b.CreateLoad(floatType, xPtr, "x.real")
|
||||||
|
yReal := b.CreateLoad(floatType, yPtr, "y.real")
|
||||||
|
realEq := b.CreateFCmp(llvm.FloatOEQ, xReal, yReal, "eq.real")
|
||||||
|
// Imaginary parts
|
||||||
|
xImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), xPtr, []llvm.Value{imagOffset}, "")
|
||||||
|
yImagPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), yPtr, []llvm.Value{imagOffset}, "")
|
||||||
|
xImag := b.CreateLoad(floatType, xImagPtr, "x.imag")
|
||||||
|
yImag := b.CreateLoad(floatType, yImagPtr, "y.imag")
|
||||||
|
imagEq := b.CreateFCmp(llvm.FloatOEQ, xImag, yImag, "eq.imag")
|
||||||
|
return b.CreateAnd(realEq, imagEq, "")
|
||||||
|
}
|
||||||
|
// Integer/boolean: compare raw bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
case *types.Pointer, *types.Chan:
|
||||||
|
// Pointers and channels: compare as raw pointer-sized bytes.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
case *types.Interface:
|
||||||
|
// Interface: use runtime interface equality.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("hashmapInterfaceEqual", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
case *types.Struct:
|
||||||
|
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false) // start with true
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < keyType.NumFields(); i++ {
|
||||||
|
if keyType.Field(i).Name() == "_" {
|
||||||
|
continue // blank fields are ignored in Go equality
|
||||||
|
}
|
||||||
|
fieldType := keyType.Field(i).Type()
|
||||||
|
llvmFieldType := b.getLLVMType(fieldType)
|
||||||
|
if b.targetData.TypeAllocSize(llvmFieldType) == 0 {
|
||||||
|
continue // skip zero-sized fields
|
||||||
|
}
|
||||||
|
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
||||||
|
xFieldPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
yFieldPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
fieldEq := b.generateKeyEqual(fieldType, llvmFieldType, xFieldPtr, yFieldPtr, fn)
|
||||||
|
result = b.CreateAnd(result, fieldEq, "")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
case *types.Array:
|
||||||
|
elemType := keyType.Elem()
|
||||||
|
llvmElemType := b.getLLVMType(elemType)
|
||||||
|
arrayLen := keyType.Len()
|
||||||
|
if hashmapIsBinaryKey(elemType) {
|
||||||
|
// All elements are binary-comparable; compare the entire array.
|
||||||
|
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(llvmKeyType), false)
|
||||||
|
return b.createRuntimeCall("memequal", []llvm.Value{xPtr, yPtr, size}, "eq")
|
||||||
|
}
|
||||||
|
if arrayLen == 0 {
|
||||||
|
return llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||||
|
}
|
||||||
|
if arrayLen <= hashArrayUnrollLimit {
|
||||||
|
result := llvm.ConstInt(b.ctx.Int1Type(), 1, false)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < int(arrayLen); i++ {
|
||||||
|
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
||||||
|
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, idx}, "")
|
||||||
|
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||||
|
result = b.CreateAnd(result, elemEq, "")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
|
||||||
|
loopEntry := b.GetInsertBlock()
|
||||||
|
loopBody := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.body")
|
||||||
|
loopDone := b.ctx.AddBasicBlock(loopEntry.Parent(), "eq.array.done")
|
||||||
|
|
||||||
|
b.CreateBr(loopBody)
|
||||||
|
b.SetInsertPointAtEnd(loopBody)
|
||||||
|
|
||||||
|
phiI := b.CreatePHI(b.uintptrType, "i")
|
||||||
|
|
||||||
|
xElemPtr := b.CreateInBoundsGEP(llvmKeyType, xPtr, []llvm.Value{zero, phiI}, "")
|
||||||
|
yElemPtr := b.CreateInBoundsGEP(llvmKeyType, yPtr, []llvm.Value{zero, phiI}, "")
|
||||||
|
elemEq := b.generateKeyEqual(elemType, llvmElemType, xElemPtr, yElemPtr, fn)
|
||||||
|
|
||||||
|
nextI := b.CreateAdd(phiI, llvm.ConstInt(b.uintptrType, 1, false), "")
|
||||||
|
atEnd := b.CreateICmp(llvm.IntUGE, nextI, llvm.ConstInt(b.uintptrType, uint64(arrayLen), false), "")
|
||||||
|
exitLoop := b.CreateOr(atEnd, b.CreateNot(elemEq, ""), "")
|
||||||
|
b.CreateCondBr(exitLoop, loopDone, loopBody)
|
||||||
|
|
||||||
|
bodyEnd := b.GetInsertBlock()
|
||||||
|
phiI.AddIncoming([]llvm.Value{llvm.ConstInt(b.uintptrType, 0, false), nextI},
|
||||||
|
[]llvm.BasicBlock{loopEntry, bodyEnd})
|
||||||
|
|
||||||
|
b.SetInsertPointAtEnd(loopDone)
|
||||||
|
return elemEq
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled key type for equal generation: %T", keyType))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -29,8 +29,7 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
|||||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
// field f of x, but at least 1."
|
// field f of x, but at least 1."
|
||||||
max := int64(1)
|
max := int64(1)
|
||||||
for i := 0; i < t.NumFields(); i++ {
|
for f := range t.Fields() {
|
||||||
f := t.Field(i)
|
|
||||||
if a := s.Alignof(f.Type()); a > max {
|
if a := s.Alignof(f.Type()); a > max {
|
||||||
max = a
|
max = a
|
||||||
}
|
}
|
||||||
|
|||||||
+181
-56
@@ -8,6 +8,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ type functionInfo struct {
|
|||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
noescape bool // go:noescape
|
noescape bool // go:noescape
|
||||||
|
noheap bool // go:noheap
|
||||||
variadic bool // go:variadic (CGo only)
|
variadic bool // go:variadic (CGo only)
|
||||||
inline inlineType // go:inline
|
inline inlineType // go:inline
|
||||||
}
|
}
|
||||||
@@ -77,24 +79,27 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
return llvmFn.GlobalValueType(), llvmFn
|
return llvmFn.GlobalValueType(), llvmFn
|
||||||
}
|
}
|
||||||
|
|
||||||
var retType llvm.Type
|
retType, indirectResult := c.hasIndirectResult(fn.Signature)
|
||||||
if fn.Signature.Results() == nil {
|
if info.exported {
|
||||||
retType = c.ctx.VoidType()
|
indirectResult = false
|
||||||
} else if fn.Signature.Results().Len() == 1 {
|
|
||||||
retType = c.getLLVMType(fn.Signature.Results().At(0).Type())
|
|
||||||
} else {
|
|
||||||
results := make([]llvm.Type, 0, fn.Signature.Results().Len())
|
|
||||||
for i := 0; i < fn.Signature.Results().Len(); i++ {
|
|
||||||
results = append(results, c.getLLVMType(fn.Signature.Results().At(i).Type()))
|
|
||||||
}
|
|
||||||
retType = c.ctx.StructType(results, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramInfos []paramInfo
|
var paramInfos []paramInfo
|
||||||
|
if indirectResult {
|
||||||
|
paramInfos = append(paramInfos, paramInfo{
|
||||||
|
llvmType: c.dataPtrType,
|
||||||
|
name: "return",
|
||||||
|
elemSize: c.targetData.TypeAllocSize(retType),
|
||||||
|
})
|
||||||
|
retType = c.ctx.VoidType()
|
||||||
|
}
|
||||||
for _, param := range getParams(fn.Signature) {
|
for _, param := range getParams(fn.Signature) {
|
||||||
paramType := c.getLLVMType(param.Type())
|
paramType := c.getLLVMType(param.Type())
|
||||||
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type())
|
if info.exported {
|
||||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
paramInfos = append(paramInfos, c.expandDirectFormalParamType(paramType, param.Name(), param.Type())...)
|
||||||
|
} else {
|
||||||
|
paramInfos = append(paramInfos, c.expandFormalParamType(paramType, param.Name(), param.Type())...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an extra parameter as the function context. This context is used in
|
// Add an extra parameter as the function context. This context is used in
|
||||||
@@ -104,12 +109,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
|
hasIndirectABI := indirectResult
|
||||||
for _, info := range paramInfos {
|
for _, info := range paramInfos {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
|
hasIndirectABI = hasIndirectABI || info.flags¶mIsIndirect != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
|
fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
|
||||||
llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType)
|
llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType)
|
||||||
|
if hasIndirectABI {
|
||||||
|
// Argument promotion only rewrites functions whose uses are all direct
|
||||||
|
// calls. Keep an address use so LLVM cannot reconstruct the large
|
||||||
|
// aggregate signature that this ABI exists to avoid.
|
||||||
|
llvmutil.AppendToGlobal(c.mod, "llvm.used", llvmFn)
|
||||||
|
}
|
||||||
if strings.HasPrefix(c.Triple, "wasm") {
|
if strings.HasPrefix(c.Triple, "wasm") {
|
||||||
// C functions without prototypes like this:
|
// C functions without prototypes like this:
|
||||||
// void foo();
|
// void foo();
|
||||||
@@ -139,9 +152,14 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// not.
|
// not.
|
||||||
// (It may be safe to add the nocapture parameter to the context
|
// (It may be safe to add the nocapture parameter to the context
|
||||||
// parameter, but I'd like to stay on the safe side here).
|
// parameter, but I'd like to stay on the safe side here).
|
||||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0)
|
||||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||||
}
|
}
|
||||||
|
if paramInfo.flags¶mIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||||
|
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
|
||||||
|
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
|
||||||
|
llvmFn.AddAttributeAtIndex(i+1, readonly)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a number of function or parameter attributes, depending on the
|
// Set a number of function or parameter attributes, depending on the
|
||||||
@@ -153,8 +171,10 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// Mark it as noreturn so LLVM can optimize away code.
|
// Mark it as noreturn so LLVM can optimize away code.
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
||||||
case "internal/abi.NoEscape":
|
case "internal/abi.NoEscape":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
case "runtime.alloc":
|
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
|
||||||
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
case "runtime.alloc", "runtime.alloc_noheap", "runtime.alloc_zero":
|
||||||
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
||||||
// returns values that are never null and never alias to an existing value.
|
// returns values that are never null and never alias to an existing value.
|
||||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||||
@@ -175,25 +195,44 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
case "runtime.sliceAppend":
|
case "runtime.sliceAppend":
|
||||||
// Appending a slice will only read the to-be-appended slice, it won't
|
// Appending a slice will only read the to-be-appended slice, it won't
|
||||||
// be modified.
|
// be modified.
|
||||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
case "runtime.sliceCopy":
|
|
||||||
// Copying a slice won't capture any of the parameters.
|
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
|
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
|
||||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
|
||||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
|
||||||
case "runtime.stringFromBytes":
|
case "runtime.stringFromBytes":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
case "runtime.stringFromRunes":
|
case "runtime.stringFromRunes":
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
|
case "runtime.hashmapSet":
|
||||||
|
// The key (param 2) and value (param 3) pointers are only read via
|
||||||
|
// memcpy/hash/equal and are never captured. The indirect calls
|
||||||
|
// through m.keyHash and m.keyEqual function pointers prevent LLVM's
|
||||||
|
// functionattrs pass from inferring this automatically.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
case "runtime.hashmapGet":
|
||||||
|
// The key (param 2) is read-only and never captured.
|
||||||
|
// The value (param 3) is written to (receives the result) but never captured.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
case "runtime.hashmapDelete":
|
||||||
|
// The key (param 2) is read-only and never captured.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
case "runtime.hashmapGenericSet":
|
||||||
|
// Same as hashmapBinarySet: key (param 2) and value (param 3) are
|
||||||
|
// not captured.
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
case "runtime.hashmapGenericGet":
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
llvmFn.AddAttributeAtIndex(3, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
|
case "runtime.hashmapGenericDelete":
|
||||||
|
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
case "runtime.trackPointer":
|
case "runtime.trackPointer":
|
||||||
// This function is necessary for tracking pointers on the stack in a
|
// This function is necessary for tracking pointers on the stack in a
|
||||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||||
// that the only thing we'll do is read the pointer.
|
// that the only thing we'll do is read the pointer.
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(llvmutil.NoCaptureAttrName()), 0))
|
||||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||||
case "__mulsi3", "__divmodsi4", "__udivmodsi4":
|
case "__mulsi3", "__divmodsi4", "__udivmodsi4":
|
||||||
if strings.Split(c.Triple, "-")[0] == "avr" {
|
if strings.Split(c.Triple, "-")[0] == "avr" {
|
||||||
@@ -228,7 +267,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
|
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
|
||||||
}
|
}
|
||||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
nocaptureKind := llvm.AttributeKindID(llvmutil.NoCaptureAttrName())
|
||||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||||
for i, typ := range paramTypes {
|
for i, typ := range paramTypes {
|
||||||
if typ.TypeKind() == llvm.PointerTypeKind {
|
if typ.TypeKind() == llvm.PointerTypeKind {
|
||||||
@@ -292,7 +331,7 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
|||||||
}
|
}
|
||||||
info := functionInfo{
|
info := functionInfo{
|
||||||
// Pick the default linkName.
|
// Pick the default linkName.
|
||||||
linkName: f.RelString(nil),
|
linkName: c.canonicalFunctionName(f),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a few runtime functions that are treated specially.
|
// Check for a few runtime functions that are treated specially.
|
||||||
@@ -319,6 +358,18 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) canonicalFunctionName(f *ssa.Function) string {
|
||||||
|
typeArgs := f.TypeArgs()
|
||||||
|
if len(typeArgs) == 0 {
|
||||||
|
return f.RelString(nil)
|
||||||
|
}
|
||||||
|
parts := make([]string, len(typeArgs))
|
||||||
|
for i, ta := range typeArgs {
|
||||||
|
parts[i], _ = c.getTypeCodeName(ta)
|
||||||
|
}
|
||||||
|
return f.Origin().RelString(nil) + "[" + strings.Join(parts, ",") + "]"
|
||||||
|
}
|
||||||
|
|
||||||
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
||||||
// //export or //go:noinline.
|
// //export or //go:noinline.
|
||||||
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||||
@@ -345,6 +396,51 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also scan file-level //go:linkname directives. These appear as
|
||||||
|
// free-standing comments in *ast.File.Comments (not attached to any
|
||||||
|
// declaration), and are used by modern golang.org/x/sys/unix and others.
|
||||||
|
// Function-attached directives (above) take precedence — we only add
|
||||||
|
// file-level ones if no doc-comment linkname was found for this function.
|
||||||
|
//
|
||||||
|
// TODO: the hasUnsafeImport gate enforced downstream (see the
|
||||||
|
// //go:linkname case below) is package-level. gc enforces it per
|
||||||
|
// file, on the file containing the directive. For file-level
|
||||||
|
// linknames this is more important than for function-attached ones,
|
||||||
|
// because the directive can live in a file separate from the
|
||||||
|
// function. A stricter implementation would check whether the file
|
||||||
|
// returned by fileForFunc imports "unsafe", not whether any file in
|
||||||
|
// the package does.
|
||||||
|
hasFunctionLinkname := false
|
||||||
|
for _, comment := range pragmas {
|
||||||
|
if strings.HasPrefix(comment.Text, "//go:linkname ") {
|
||||||
|
parts := strings.Fields(comment.Text)
|
||||||
|
if len(parts) == 3 && parts[1] == f.Name() {
|
||||||
|
hasFunctionLinkname = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasFunctionLinkname {
|
||||||
|
if file := c.fileForFunc(f); file != nil {
|
||||||
|
for _, group := range file.Comments {
|
||||||
|
// Skip the function's own doc comment — already handled above.
|
||||||
|
if decl, ok := syntax.(*ast.FuncDecl); ok && group == decl.Doc {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, comment := range group.List {
|
||||||
|
if !strings.HasPrefix(comment.Text, "//go:linkname ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Fields(comment.Text)
|
||||||
|
if len(parts) != 3 || parts[1] != f.Name() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pragmas = append(pragmas, comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse each pragma.
|
// Parse each pragma.
|
||||||
for _, comment := range pragmas {
|
for _, comment := range pragmas {
|
||||||
parts := strings.Fields(comment.Text)
|
parts := strings.Fields(comment.Text)
|
||||||
@@ -362,7 +458,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
info.wasmName = info.linkName
|
info.wasmName = info.linkName
|
||||||
info.exported = true
|
info.exported = true
|
||||||
case "//go:interrupt":
|
case "//go:interrupt":
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.interrupt = true
|
info.interrupt = true
|
||||||
}
|
}
|
||||||
case "//go:wasm-module":
|
case "//go:wasm-module":
|
||||||
@@ -416,7 +512,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
info.inline = inlineHint
|
info.inline = inlineHint
|
||||||
case "//go:noinline":
|
case "//go:noinline":
|
||||||
info.inline = inlineNone
|
info.inline = inlineNone
|
||||||
case "//go:linkname":
|
case "//go:linkname", "//go:linknamestd":
|
||||||
if len(parts) != 3 || parts[1] != f.Name() {
|
if len(parts) != 3 || parts[1] != f.Name() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -424,14 +520,14 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
// This is a slightly looser requirement than what gc uses: gc
|
// This is a slightly looser requirement than what gc uses: gc
|
||||||
// requires the file to import "unsafe", not the package as a
|
// requires the file to import "unsafe", not the package as a
|
||||||
// whole.
|
// whole.
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.linkName = parts[2]
|
info.linkName = parts[2]
|
||||||
}
|
}
|
||||||
case "//go:section":
|
case "//go:section":
|
||||||
// Only enable go:section when the package imports "unsafe".
|
// Only enable go:section when the package imports "unsafe".
|
||||||
// go:section also implies go:noinline since inlining could
|
// go:section also implies go:noinline since inlining could
|
||||||
// move the code to a different section than that requested.
|
// move the code to a different section than that requested.
|
||||||
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
|
if len(parts) == 2 && slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.section = parts[1]
|
info.section = parts[1]
|
||||||
info.inline = inlineNone
|
info.inline = inlineNone
|
||||||
}
|
}
|
||||||
@@ -440,7 +536,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
// runtime functions.
|
// runtime functions.
|
||||||
// This is somewhat dangerous and thus only imported in packages
|
// This is somewhat dangerous and thus only imported in packages
|
||||||
// that import unsafe.
|
// that import unsafe.
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.nobounds = true
|
info.nobounds = true
|
||||||
}
|
}
|
||||||
case "//go:noescape":
|
case "//go:noescape":
|
||||||
@@ -450,6 +546,9 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
if len(f.Blocks) == 0 {
|
if len(f.Blocks) == 0 {
|
||||||
info.noescape = true
|
info.noescape = true
|
||||||
}
|
}
|
||||||
|
case "//go:noheap":
|
||||||
|
// Ensure this function does not allocate on the heap.
|
||||||
|
info.noheap = true
|
||||||
case "//go:variadic":
|
case "//go:variadic":
|
||||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||||
// pass for C variadic functions. This includes both explicit
|
// pass for C variadic functions. This includes both explicit
|
||||||
@@ -537,9 +636,9 @@ func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool {
|
|||||||
hasHostLayout = false // package structs added in go1.23
|
hasHostLayout = false // package structs added in go1.23
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.NumFields(); i++ {
|
for field := range typ.Fields() {
|
||||||
ftyp := typ.Field(i).Type()
|
ftyp := field.Type()
|
||||||
if ftyp.String() == "structs.HostLayout" {
|
if types.Unalias(ftyp).String() == "structs.HostLayout" {
|
||||||
hasHostLayout = true
|
hasHostLayout = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -570,8 +669,8 @@ func getParams(sig *types.Signature) []*types.Var {
|
|||||||
if sig.Recv() != nil {
|
if sig.Recv() != nil {
|
||||||
params = append(params, sig.Recv())
|
params = append(params, sig.Recv())
|
||||||
}
|
}
|
||||||
for i := 0; i < sig.Params().Len(); i++ {
|
for v := range sig.Params().Variables() {
|
||||||
params = append(params, sig.Params().At(i))
|
params = append(params, v)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
@@ -630,12 +729,40 @@ func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) {
|
|||||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||||
// some symbols this is different (due to //go:extern for example).
|
// some symbols this is different (due to //go:extern for example).
|
||||||
type globalInfo struct {
|
type globalInfo struct {
|
||||||
linkName string // go:extern
|
linkName string // go:extern, go:linkname
|
||||||
extern bool // go:extern
|
extern bool // go:extern
|
||||||
align int // go:align
|
align int // go:align
|
||||||
section string // go:section
|
section string // go:section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fileForFunc returns the *ast.File that contains the declaration of f, or
|
||||||
|
// nil if it cannot be determined. File-level pragmas are only consulted for
|
||||||
|
// functions in the package currently being compiled — functions imported from
|
||||||
|
// other packages have their file-level pragmas processed when those packages
|
||||||
|
// are compiled.
|
||||||
|
func (c *compilerContext) fileForFunc(f *ssa.Function) *ast.File {
|
||||||
|
if c.loaderPkg == nil || f.Pkg == nil || f.Pkg.Pkg != c.loaderPkg.Pkg {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
syntax := f.Syntax()
|
||||||
|
if f.Origin() != nil {
|
||||||
|
syntax = f.Origin().Syntax()
|
||||||
|
}
|
||||||
|
if syntax == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pos := syntax.Pos()
|
||||||
|
if !pos.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, file := range c.loaderPkg.Files {
|
||||||
|
if file.FileStart <= pos && pos < file.FileEnd {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||||
// program. In particular, they are required for //go:extern pragmas on globals.
|
// program. In particular, they are required for //go:extern pragmas on globals.
|
||||||
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
||||||
@@ -674,10 +801,7 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||||
|
|
||||||
// Set alignment from the //go:align comment.
|
// Set alignment from the //go:align comment.
|
||||||
alignment := c.targetData.ABITypeAlignment(llvmType)
|
alignment := max(info.align, c.targetData.ABITypeAlignment(llvmType))
|
||||||
if info.align > alignment {
|
|
||||||
alignment = info.align
|
|
||||||
}
|
|
||||||
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
||||||
// Check for power-of-two (or 0).
|
// Check for power-of-two (or 0).
|
||||||
// See: https://stackoverflow.com/a/108360
|
// See: https://stackoverflow.com/a/108360
|
||||||
@@ -715,14 +839,14 @@ func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
|
|||||||
// Check for //go: pragmas, which may change the link name (among others).
|
// Check for //go: pragmas, which may change the link name (among others).
|
||||||
doc := c.astComments[info.linkName]
|
doc := c.astComments[info.linkName]
|
||||||
if doc != nil {
|
if doc != nil {
|
||||||
info.parsePragmas(doc)
|
info.parsePragmas(doc, c, g)
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse //go: pragma comments from the source. In particular, it parses the
|
// Parse //go: pragma comments from the source. In particular, it parses the
|
||||||
// //go:extern pragma on globals.
|
// //go:extern and //go:linkname pragmas on globals.
|
||||||
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup, c *compilerContext, g *ssa.Global) {
|
||||||
for _, comment := range doc.List {
|
for _, comment := range doc.List {
|
||||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||||
continue
|
continue
|
||||||
@@ -743,6 +867,17 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
|||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
info.section = parts[1]
|
info.section = parts[1]
|
||||||
}
|
}
|
||||||
|
case "//go:linkname":
|
||||||
|
if len(parts) != 3 || parts[1] != g.Name() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Only enable go:linkname when the package imports "unsafe".
|
||||||
|
// This is a slightly looser requirement than what gc uses: gc
|
||||||
|
// requires the file to import "unsafe", not the package as a
|
||||||
|
// whole.
|
||||||
|
if slices.Contains(g.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
|
info.linkName = parts[2]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,13 +891,3 @@ func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
|||||||
}
|
}
|
||||||
return methods
|
return methods
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if this package imports "unsafe", false otherwise.
|
|
||||||
func hasUnsafeImport(pkg *types.Package) bool {
|
|
||||||
for _, imp := range pkg.Imports() {
|
|
||||||
if imp == types.Unsafe {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
+164
-27
@@ -26,24 +26,25 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{b.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||||
constraints := "={rax},0"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={rax},0")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"{rdi}",
|
"{rdi}",
|
||||||
"{rsi}",
|
"{rsi}",
|
||||||
"{rdx}",
|
"{rdx}",
|
||||||
"{r10}",
|
"{r10}",
|
||||||
"{r8}",
|
"{r8}",
|
||||||
"{r9}",
|
"{r9}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
// rcx and r11 are clobbered by the syscall, so make sure they are not used
|
// rcx and r11 are clobbered by the syscall, so make sure they are not used
|
||||||
constraints += ",~{rcx},~{r11}"
|
constraints.WriteString(",~{rcx},~{r11}")
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
target := llvm.InlineAsm(fnType, "syscall", constraints.String(), true, false, llvm.InlineAsmDialectIntel, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||||
@@ -55,34 +56,44 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{b.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
||||||
constraints := "={eax},0"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={eax},0")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"{ebx}",
|
"{ebx}",
|
||||||
"{ecx}",
|
"{ecx}",
|
||||||
"{edx}",
|
"{edx}",
|
||||||
"{esi}",
|
"{esi}",
|
||||||
"{edi}",
|
"{edi}",
|
||||||
"{ebp}",
|
"{ebp}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
target := llvm.InlineAsm(fnType, "int 0x80", constraints.String(), true, false, llvm.InlineAsmDialectIntel, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||||
|
if arch := b.archFamily(); arch != "arm" {
|
||||||
|
// Some targets pretend to be linux/arm for compatibility but aren't
|
||||||
|
// actually such a system. Make sure we emit an error instead of
|
||||||
|
// creating inline assembly that will fail to compile.
|
||||||
|
// See: https://github.com/tinygo-org/tinygo/issues/4959
|
||||||
|
return llvm.Value{}, b.makeError(call.Pos(), "system calls are not supported: target emulates a linux/arm system on "+arch)
|
||||||
|
}
|
||||||
|
|
||||||
// Implement the EABI system call convention for Linux.
|
// Implement the EABI system call convention for Linux.
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// ={r0},0,{r1},{r2},{r7},~{r3}
|
// ={r0},0,{r1},{r2},{r7},~{r3}
|
||||||
constraints := "={r0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={r0}")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"0", // tie to output
|
"0", // tie to output
|
||||||
"{r1}",
|
"{r1}",
|
||||||
"{r2}",
|
"{r2}",
|
||||||
@@ -90,20 +101,20 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
"{r4}",
|
"{r4}",
|
||||||
"{r5}",
|
"{r5}",
|
||||||
"{r6}",
|
"{r6}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, num)
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, b.uintptrType)
|
argTypes = append(argTypes, b.uintptrType)
|
||||||
constraints += ",{r7}" // syscall number
|
constraints.WriteString(",{r7}") // syscall number
|
||||||
for i := len(call.Args) - 1; i < 4; i++ {
|
for i := len(call.Args) - 1; i < 4; i++ {
|
||||||
// r0-r3 get clobbered after the syscall returns
|
// r0-r3 get clobbered after the syscall returns
|
||||||
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",~{r" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||||
@@ -112,31 +123,32 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17}
|
// ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17}
|
||||||
constraints := "={x0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={x0}")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"0", // tie to output
|
"0", // tie to output
|
||||||
"{x1}",
|
"{x1}",
|
||||||
"{x2}",
|
"{x2}",
|
||||||
"{x3}",
|
"{x3}",
|
||||||
"{x4}",
|
"{x4}",
|
||||||
"{x5}",
|
"{x5}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, num)
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, b.uintptrType)
|
argTypes = append(argTypes, b.uintptrType)
|
||||||
constraints += ",{x8}" // syscall number
|
constraints.WriteString(",{x8}") // syscall number
|
||||||
for i := len(call.Args) - 1; i < 8; i++ {
|
for i := len(call.Args) - 1; i < 8; i++ {
|
||||||
// x0-x7 may get clobbered during the syscall following the aarch64
|
// x0-x7 may get clobbered during the syscall following the aarch64
|
||||||
// calling convention.
|
// calling convention.
|
||||||
constraints += ",~{x" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",~{x" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
constraints += ",~{x16},~{x17}" // scratch registers
|
constraints.WriteString(",~{x16},~{x17}") // scratch registers
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
||||||
@@ -155,7 +167,8 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// faster and smaller code.
|
// faster and smaller code.
|
||||||
args := []llvm.Value{num}
|
args := []llvm.Value{num}
|
||||||
argTypes := []llvm.Type{b.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
constraints := "={$2},={$7},0"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={$2},={$7},0")
|
||||||
syscallParams := call.Args[1:]
|
syscallParams := call.Args[1:]
|
||||||
if len(syscallParams) > 7 {
|
if len(syscallParams) > 7 {
|
||||||
// There is one syscall that uses 7 parameters: sync_file_range.
|
// There is one syscall that uses 7 parameters: sync_file_range.
|
||||||
@@ -164,7 +177,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
syscallParams = syscallParams[:7]
|
syscallParams = syscallParams[:7]
|
||||||
}
|
}
|
||||||
for i, arg := range syscallParams {
|
for i, arg := range syscallParams {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"{$4}", // arg1
|
"{$4}", // arg1
|
||||||
"{$5}", // arg2
|
"{$5}", // arg2
|
||||||
"{$6}", // arg3
|
"{$6}", // arg3
|
||||||
@@ -172,7 +185,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
"r", // arg5 on the stack
|
"r", // arg5 on the stack
|
||||||
"r", // arg6 on the stack
|
"r", // arg6 on the stack
|
||||||
"r", // arg7 on the stack
|
"r", // arg7 on the stack
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
@@ -213,10 +226,10 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
"addu $$sp, $$sp, 32\n" +
|
"addu $$sp, $$sp, 32\n" +
|
||||||
".set at\n"
|
".set at\n"
|
||||||
}
|
}
|
||||||
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
|
constraints.WriteString(",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}")
|
||||||
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||||
fnType := llvm.FunctionType(returnType, argTypes, false)
|
fnType := llvm.FunctionType(returnType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
|
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, true, 0, false)
|
||||||
call := b.CreateCall(fnType, target, args, "")
|
call := b.CreateCall(fnType, target, args, "")
|
||||||
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
||||||
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
||||||
@@ -341,6 +354,130 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createSyscalln emits instructions for the syscall.syscalln function on
|
||||||
|
// Windows. This handles the variadic calling convention used in Go 1.26+:
|
||||||
|
//
|
||||||
|
// func syscalln(fn, n uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
|
||||||
|
//
|
||||||
|
// The function generates a switch on n to dispatch to the correct fixed-argument
|
||||||
|
// function pointer call with SetLastError(0)/GetLastError() wrapping.
|
||||||
|
func (b *builder) createSyscalln(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
const maxArgs = 18 // Windows syscalls support up to 18 args
|
||||||
|
|
||||||
|
isI386 := strings.HasPrefix(b.Triple, "i386-")
|
||||||
|
|
||||||
|
// Get the function pointer (call.Args[0]) and n (call.Args[1]).
|
||||||
|
fn := b.getValue(call.Args[0], getPos(call))
|
||||||
|
fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
|
||||||
|
n := b.getValue(call.Args[1], getPos(call))
|
||||||
|
|
||||||
|
// Get the variadic args slice (call.Args[2]).
|
||||||
|
// In SSA, the variadic slice is the third argument.
|
||||||
|
var argsPtr llvm.Value
|
||||||
|
if len(call.Args) > 2 {
|
||||||
|
argsSlice := b.getValue(call.Args[2], getPos(call))
|
||||||
|
argsPtr = b.CreateExtractValue(argsSlice, 0, "args.data")
|
||||||
|
} else {
|
||||||
|
argsPtr = llvm.ConstNull(b.dataPtrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare SetLastError and GetLastError.
|
||||||
|
setLastError := b.mod.NamedFunction("SetLastError")
|
||||||
|
if setLastError.IsNil() {
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||||
|
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||||
|
if isI386 {
|
||||||
|
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getLastError := b.mod.NamedFunction("GetLastError")
|
||||||
|
if getLastError.IsNil() {
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||||
|
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||||
|
if isI386 {
|
||||||
|
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)
|
||||||
|
|
||||||
|
// Create the merge block where all cases converge.
|
||||||
|
mergeBB := b.insertBasicBlock("syscalln.merge")
|
||||||
|
|
||||||
|
// Create the default (panic) block.
|
||||||
|
panicBB := b.insertBasicBlock("syscalln.panic")
|
||||||
|
|
||||||
|
// Create the switch on n.
|
||||||
|
sw := b.CreateSwitch(n, panicBB, maxArgs+1)
|
||||||
|
|
||||||
|
// We'll collect blocks and values for the PHI node.
|
||||||
|
var incomingVals []llvm.Value
|
||||||
|
var incomingBlocks []llvm.BasicBlock
|
||||||
|
|
||||||
|
// Generate a case for each arg count 0..maxArgs.
|
||||||
|
for i := 0; i <= maxArgs; i++ {
|
||||||
|
caseBB := b.insertBasicBlock("syscalln.case" + strconv.Itoa(i))
|
||||||
|
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), caseBB)
|
||||||
|
b.SetInsertPointAtEnd(caseBB)
|
||||||
|
|
||||||
|
// Load args[0] through args[i-1] from the slice data pointer.
|
||||||
|
var params []llvm.Value
|
||||||
|
var paramTypes []llvm.Type
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
gep := b.CreateInBoundsGEP(b.uintptrType, argsPtr, []llvm.Value{
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(j), false),
|
||||||
|
}, "")
|
||||||
|
arg := b.CreateLoad(b.uintptrType, gep, "")
|
||||||
|
params = append(params, arg)
|
||||||
|
paramTypes = append(paramTypes, b.uintptrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastError(0)
|
||||||
|
setCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||||
|
var sp llvm.Value
|
||||||
|
if isI386 {
|
||||||
|
setCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
sp = b.readStackPointer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call fn(args...)
|
||||||
|
fnType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
||||||
|
syscallResult := b.CreateCall(fnType, fnPtr, params, "")
|
||||||
|
if isI386 {
|
||||||
|
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
b.writeStackPointer(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// err = GetLastError()
|
||||||
|
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||||
|
if isI386 {
|
||||||
|
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
|
if b.uintptrType != b.ctx.Int32Type() {
|
||||||
|
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build {r1, 0, err}
|
||||||
|
result := llvm.ConstNull(retType)
|
||||||
|
result = b.CreateInsertValue(result, syscallResult, 0, "")
|
||||||
|
result = b.CreateInsertValue(result, errResult, 2, "")
|
||||||
|
|
||||||
|
incomingVals = append(incomingVals, result)
|
||||||
|
incomingBlocks = append(incomingBlocks, b.Builder.GetInsertBlock())
|
||||||
|
b.CreateBr(mergeBB)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic block for n > maxArgs.
|
||||||
|
b.SetInsertPointAtEnd(panicBB)
|
||||||
|
b.CreateUnreachable()
|
||||||
|
|
||||||
|
// Merge block: PHI node to select the result.
|
||||||
|
b.SetInsertPointAtEnd(mergeBB)
|
||||||
|
phi := b.CreatePHI(retType, "syscalln.result")
|
||||||
|
phi.AddIncoming(incomingVals, incomingBlocks)
|
||||||
|
return phi, nil
|
||||||
|
}
|
||||||
|
|
||||||
// createRawSyscallNoError emits instructions for the Linux-specific
|
// createRawSyscallNoError emits instructions for the Linux-specific
|
||||||
// syscall.rawSyscallNoError function.
|
// syscall.rawSyscallNoError function.
|
||||||
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
|||||||
Vendored
+5
@@ -66,6 +66,11 @@ func complexSub(x, y complex64) complex64 {
|
|||||||
return x - y
|
return x - y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shiftNested(x uint64) uint64 {
|
||||||
|
k := 3
|
||||||
|
return x >> (1 << k) // https://github.com/tinygo-org/tinygo/issues/5496
|
||||||
|
}
|
||||||
|
|
||||||
func complexMul(x, y complex64) complex64 {
|
func complexMul(x, y complex64) complex64 {
|
||||||
return x * y
|
return x * y
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+37
-34
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'basic.go'
|
; ModuleID = 'basic.go'
|
||||||
source_filename = "basic.go"
|
source_filename = "basic.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%main.kv = type { float, i32, i32, i32 }
|
%main.kv = type { float, i32, i32, i32 }
|
||||||
@@ -10,33 +10,30 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = add i32 %x, %y
|
%0 = add i32 %x, %y
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %x, %y
|
%0 = icmp eq i32 %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -50,14 +47,14 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.divideByZeroPanic(ptr) #1
|
declare void @runtime.divideByZeroPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -67,12 +64,12 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -86,12 +83,12 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
@@ -101,66 +98,66 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(ptr undef) #3
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oeq float %x, %y
|
%0 = fcmp oeq float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp une float %x, %y
|
%0 = fcmp une float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt float %x, %y
|
%0 = fcmp olt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ole float %x, %y
|
%0 = fcmp ole float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ogt float %x, %y
|
%0 = fcmp ogt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oge float %x, %y
|
%0 = fcmp oge float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
|
define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.r
|
ret float %x.r
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #2 {
|
define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.i
|
ret float %x.i
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fadd float %x.r, %y.r
|
%0 = fadd float %x.r, %y.r
|
||||||
%1 = fadd float %x.i, %y.i
|
%1 = fadd float %x.i, %y.i
|
||||||
@@ -170,7 +167,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fsub float %x.r, %y.r
|
%0 = fsub float %x.r, %y.r
|
||||||
%1 = fsub float %x.i, %y.i
|
%1 = fsub float %x.i, %y.i
|
||||||
@@ -180,7 +177,14 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 {
|
define hidden i64 @main.shiftNested(i64 %x, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = lshr i64 %x, 8
|
||||||
|
ret i64 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fmul float %x.r, %y.r
|
%0 = fmul float %x.r, %y.r
|
||||||
%1 = fmul float %x.i, %y.i
|
%1 = fmul float %x.i, %y.i
|
||||||
@@ -194,19 +198,18 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
|
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
|
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+39
-43
@@ -1,95 +1,92 @@
|
|||||||
; ModuleID = 'channel.go'
|
; ModuleID = 'channel.go'
|
||||||
source_filename = "channel.go"
|
source_filename = "channel.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
||||||
%runtime.chanSelectState = type { ptr, ptr }
|
%runtime.chanSelectState = type { ptr, ptr }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
|
||||||
store i32 3, ptr %chan.value, align 4
|
store i32 3, ptr %chan.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
declare void @llvm.lifetime.start.p0(ptr nocapture) #2
|
||||||
|
|
||||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
declare void @llvm.lifetime.end.p0(ptr nocapture) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.op = alloca %runtime.channelOp, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
|
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||||
%select.send.value = alloca i32, align 4
|
%select.send.value = alloca i32, align 4
|
||||||
store i32 1, ptr %select.send.value, align 4
|
store i32 1, ptr %select.send.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
call void @llvm.lifetime.start.p0(ptr nonnull %select.states.alloca)
|
||||||
store ptr %ch1, ptr %select.states.alloca, align 4
|
store ptr %ch1, ptr %select.states.alloca, align 4
|
||||||
%select.states.alloca.repack1 = getelementptr inbounds i8, ptr %select.states.alloca, i32 4
|
%select.states.alloca.repack1 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4
|
||||||
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
||||||
%0 = getelementptr inbounds i8, ptr %select.states.alloca, i32 8
|
%0 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 8
|
||||||
store ptr %ch2, ptr %0, align 4
|
store ptr %ch2, ptr %0, align 4
|
||||||
%.repack3 = getelementptr inbounds i8, ptr %select.states.alloca, i32 12
|
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
|
||||||
store ptr null, ptr %.repack3, align 4
|
store ptr null, ptr %.repack3, align 4
|
||||||
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4
|
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #3
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
call void @llvm.lifetime.end.p0(ptr nonnull %select.states.alloca)
|
||||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||||
%2 = icmp eq i32 %1, 0
|
%2 = icmp eq i32 %1, 0
|
||||||
br i1 %2, label %select.done, label %select.next
|
br i1 %2, label %select.done, label %select.next
|
||||||
@@ -105,10 +102,9 @@ select.body: ; preds = %select.next
|
|||||||
br label %select.done
|
br label %select.done
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
|
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #0
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
attributes #3 = { nounwind }
|
||||||
attributes #4 = { nounwind }
|
|
||||||
|
|||||||
+267
-35
@@ -3,32 +3,29 @@ source_filename = "defer.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||||
|
|
||||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
|
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr }
|
||||||
%runtime._interface = type { ptr, ptr }
|
%runtime._interface = type { ptr, ptr }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.external(ptr) #2
|
declare void @main.external(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferSimple(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca = alloca { i32, ptr }, align 4
|
%defer.alloca = alloca { i32, ptr }, align 4
|
||||||
%deferPtr = alloca ptr, align 4
|
|
||||||
store ptr null, ptr %deferPtr, align 4
|
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||||
%0 = call ptr @llvm.stacksave.p0()
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
|
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||||
store i32 0, ptr %defer.alloca, align 4
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
%defer.alloca.repack15 = getelementptr inbounds i8, ptr %defer.alloca, i32 4
|
%defer.alloca.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||||
store ptr null, ptr %defer.alloca.repack15, align 4
|
store ptr %defer.next, ptr %defer.alloca.repack15, align 4
|
||||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
||||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||||
@@ -51,7 +48,7 @@ rundefers.loophead: ; preds = %3, %rundefers.block
|
|||||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
%stack.next.gep = getelementptr inbounds i8, ptr %2, i32 4
|
%stack.next.gep = getelementptr inbounds nuw i8, ptr %2, i32 4
|
||||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
store ptr %stack.next, ptr %deferPtr, align 4
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
%callback = load i32, ptr %2, align 4
|
%callback = load i32, ptr %2, align 4
|
||||||
@@ -87,7 +84,7 @@ rundefers.loophead6: ; preds = %5, %lpad
|
|||||||
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
||||||
|
|
||||||
rundefers.loop5: ; preds = %rundefers.loophead6
|
rundefers.loop5: ; preds = %rundefers.loophead6
|
||||||
%stack.next.gep8 = getelementptr inbounds i8, ptr %4, i32 4
|
%stack.next.gep8 = getelementptr inbounds nuw i8, ptr %4, i32 4
|
||||||
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
|
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
|
||||||
store ptr %stack.next9, ptr %deferPtr, align 4
|
store ptr %stack.next9, ptr %deferPtr, align 4
|
||||||
%callback11 = load i32, ptr %4, align 4
|
%callback11 = load i32, ptr %4, align 4
|
||||||
@@ -112,14 +109,14 @@ rundefers.end3: ; preds = %rundefers.loophead6
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||||
declare ptr @llvm.stacksave.p0() #3
|
declare ptr @llvm.stacksave.p0() #2
|
||||||
|
|
||||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
|
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(28), ptr, ptr) #1
|
||||||
|
|
||||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
|
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(28), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printlock(ptr undef) #4
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||||
@@ -127,29 +124,29 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printlock(ptr) #2
|
declare void @runtime.printlock(ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #2
|
declare void @runtime.printint32(i32, ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printunlock(ptr) #2
|
declare void @runtime.printunlock(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca2 = alloca { i32, ptr }, align 4
|
%defer.alloca2 = alloca { i32, ptr }, align 4
|
||||||
%defer.alloca = alloca { i32, ptr }, align 4
|
%defer.alloca = alloca { i32, ptr }, align 4
|
||||||
%deferPtr = alloca ptr, align 4
|
|
||||||
store ptr null, ptr %deferPtr, align 4
|
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||||
%0 = call ptr @llvm.stacksave.p0()
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
|
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||||
store i32 0, ptr %defer.alloca, align 4
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
%defer.alloca.repack22 = getelementptr inbounds i8, ptr %defer.alloca, i32 4
|
%defer.alloca.repack22 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||||
store ptr null, ptr %defer.alloca.repack22, align 4
|
store ptr %defer.next, ptr %defer.alloca.repack22, align 4
|
||||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
store i32 1, ptr %defer.alloca2, align 4
|
store i32 1, ptr %defer.alloca2, align 4
|
||||||
%defer.alloca2.repack23 = getelementptr inbounds i8, ptr %defer.alloca2, i32 4
|
%defer.alloca2.repack24 = getelementptr inbounds nuw i8, ptr %defer.alloca2, i32 4
|
||||||
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
|
store ptr %defer.alloca, ptr %defer.alloca2.repack24, align 4
|
||||||
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
||||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
||||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||||
@@ -172,7 +169,7 @@ rundefers.loophead: ; preds = %4, %3, %rundefers.b
|
|||||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
%stack.next.gep = getelementptr inbounds i8, ptr %2, i32 4
|
%stack.next.gep = getelementptr inbounds nuw i8, ptr %2, i32 4
|
||||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
store ptr %stack.next, ptr %deferPtr, align 4
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
%callback = load i32, ptr %2, align 4
|
%callback = load i32, ptr %2, align 4
|
||||||
@@ -218,7 +215,7 @@ rundefers.loophead10: ; preds = %7, %6, %lpad
|
|||||||
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
|
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
|
||||||
|
|
||||||
rundefers.loop9: ; preds = %rundefers.loophead10
|
rundefers.loop9: ; preds = %rundefers.loophead10
|
||||||
%stack.next.gep12 = getelementptr inbounds i8, ptr %5, i32 4
|
%stack.next.gep12 = getelementptr inbounds nuw i8, ptr %5, i32 4
|
||||||
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
|
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
|
||||||
store ptr %stack.next13, ptr %deferPtr, align 4
|
store ptr %stack.next13, ptr %deferPtr, align 4
|
||||||
%callback15 = load i32, ptr %5, align 4
|
%callback15 = load i32, ptr %5, align 4
|
||||||
@@ -253,7 +250,7 @@ rundefers.end7: ; preds = %rundefers.loophead1
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printlock(ptr undef) #4
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||||
@@ -262,7 +259,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printlock(ptr undef) #4
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 5, ptr undef) #4
|
call void @runtime.printint32(i32 5, ptr undef) #4
|
||||||
@@ -270,9 +267,244 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
; Function Attrs: nounwind
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #0 {
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
entry:
|
||||||
attributes #3 = { nocallback nofree nosync nounwind willreturn }
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||||
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
|
br label %for.body
|
||||||
|
|
||||||
|
for.body: ; preds = %for.body, %entry
|
||||||
|
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||||
|
%defer.alloc.call = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 135 to ptr), ptr undef) #4
|
||||||
|
store i32 0, ptr %defer.alloc.call, align 4
|
||||||
|
%defer.alloc.call.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
|
||||||
|
store ptr %defer.next, ptr %defer.alloc.call.repack1, align 4
|
||||||
|
%defer.alloc.call.repack3 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
|
||||||
|
store i32 8, ptr %defer.alloc.call.repack3, align 4
|
||||||
|
store ptr %defer.alloc.call, ptr %deferPtr, align 4
|
||||||
|
br label %for.body
|
||||||
|
|
||||||
|
recover: ; preds = %rundefers.end
|
||||||
|
ret void
|
||||||
|
|
||||||
|
lpad: ; No predecessors!
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.loophead: ; preds = %rundefers.callback0, %lpad
|
||||||
|
br i1 poison, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
|
switch i32 poison, label %rundefers.default [
|
||||||
|
i32 0, label %rundefers.callback0
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
|
br label %recover
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.deferLoop(ptr %context) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||||
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
|
br label %for.loop
|
||||||
|
|
||||||
|
for.loop: ; preds = %for.body, %entry
|
||||||
|
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
|
||||||
|
%2 = icmp slt i32 %1, 10
|
||||||
|
br i1 %2, label %for.body, label %for.done
|
||||||
|
|
||||||
|
for.body: ; preds = %for.loop
|
||||||
|
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||||
|
%defer.alloc.call = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 135 to ptr), ptr undef) #4
|
||||||
|
store i32 0, ptr %defer.alloc.call, align 4
|
||||||
|
%defer.alloc.call.repack13 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
|
||||||
|
store ptr %defer.next, ptr %defer.alloc.call.repack13, align 4
|
||||||
|
%defer.alloc.call.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
|
||||||
|
store i32 %1, ptr %defer.alloc.call.repack15, align 4
|
||||||
|
store ptr %defer.alloc.call, ptr %deferPtr, align 4
|
||||||
|
%3 = add i32 %1, 1
|
||||||
|
br label %for.loop
|
||||||
|
|
||||||
|
for.done: ; preds = %for.loop
|
||||||
|
br label %rundefers.block
|
||||||
|
|
||||||
|
rundefers.after: ; preds = %rundefers.end
|
||||||
|
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
|
||||||
|
ret void
|
||||||
|
|
||||||
|
rundefers.block: ; preds = %for.done
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
|
||||||
|
%4 = load ptr, ptr %deferPtr, align 4
|
||||||
|
%stackIsNil = icmp eq ptr %4, null
|
||||||
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
|
%stack.next.gep = getelementptr inbounds nuw i8, ptr %4, i32 4
|
||||||
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
|
%callback = load i32, ptr %4, align 4
|
||||||
|
switch i32 %callback, label %rundefers.default [
|
||||||
|
i32 0, label %rundefers.callback0
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
|
%gep = getelementptr inbounds nuw i8, ptr %4, i32 8
|
||||||
|
%param = load i32, ptr %gep, align 4
|
||||||
|
call void @runtime.printlock(ptr undef) #4
|
||||||
|
call void @runtime.printint32(i32 %param, ptr undef) #4
|
||||||
|
call void @runtime.printunlock(ptr undef) #4
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
|
br label %rundefers.after
|
||||||
|
|
||||||
|
recover: ; preds = %rundefers.end1
|
||||||
|
ret void
|
||||||
|
|
||||||
|
lpad: ; No predecessors!
|
||||||
|
br label %rundefers.loophead4
|
||||||
|
|
||||||
|
rundefers.loophead4: ; preds = %rundefers.callback010, %lpad
|
||||||
|
br i1 poison, label %rundefers.end1, label %rundefers.loop3
|
||||||
|
|
||||||
|
rundefers.loop3: ; preds = %rundefers.loophead4
|
||||||
|
switch i32 poison, label %rundefers.default2 [
|
||||||
|
i32 0, label %rundefers.callback010
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback010: ; preds = %rundefers.loop3
|
||||||
|
br label %rundefers.loophead4
|
||||||
|
|
||||||
|
rundefers.default2: ; preds = %rundefers.loop3
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end1: ; preds = %rundefers.loophead4
|
||||||
|
br label %recover
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%defer.alloca = alloca { i32, ptr, i32 }, align 4
|
||||||
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%deferPtr = getelementptr inbounds nuw i8, ptr %deferframe.buf, i32 24
|
||||||
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
|
br label %for.loop
|
||||||
|
|
||||||
|
for.loop: ; preds = %for.body, %entry
|
||||||
|
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
|
||||||
|
%2 = icmp slt i32 %1, 10
|
||||||
|
br i1 %2, label %for.body, label %for.done
|
||||||
|
|
||||||
|
for.body: ; preds = %for.loop
|
||||||
|
%3 = add i32 %1, 1
|
||||||
|
br label %for.loop
|
||||||
|
|
||||||
|
for.done: ; preds = %for.loop
|
||||||
|
%defer.next = load ptr, ptr %deferPtr, align 4
|
||||||
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
|
%defer.alloca.repack16 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
|
||||||
|
store ptr %defer.next, ptr %defer.alloca.repack16, align 4
|
||||||
|
%defer.alloca.repack18 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 8
|
||||||
|
store i32 1, ptr %defer.alloca.repack18, align 4
|
||||||
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
|
br label %for.loop1
|
||||||
|
|
||||||
|
for.loop1: ; preds = %for.body2, %for.done
|
||||||
|
%4 = phi i32 [ 0, %for.done ], [ %6, %for.body2 ]
|
||||||
|
%5 = icmp slt i32 %4, 10
|
||||||
|
br i1 %5, label %for.body2, label %for.done3
|
||||||
|
|
||||||
|
for.body2: ; preds = %for.loop1
|
||||||
|
%6 = add i32 %4, 1
|
||||||
|
br label %for.loop1
|
||||||
|
|
||||||
|
for.done3: ; preds = %for.loop1
|
||||||
|
br label %rundefers.block
|
||||||
|
|
||||||
|
rundefers.after: ; preds = %rundefers.end
|
||||||
|
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
|
||||||
|
ret void
|
||||||
|
|
||||||
|
rundefers.block: ; preds = %for.done3
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
|
||||||
|
%7 = load ptr, ptr %deferPtr, align 4
|
||||||
|
%stackIsNil = icmp eq ptr %7, null
|
||||||
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
|
%stack.next.gep = getelementptr inbounds nuw i8, ptr %7, i32 4
|
||||||
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
|
%callback = load i32, ptr %7, align 4
|
||||||
|
switch i32 %callback, label %rundefers.default [
|
||||||
|
i32 0, label %rundefers.callback0
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
|
%gep = getelementptr inbounds nuw i8, ptr %7, i32 8
|
||||||
|
%param = load i32, ptr %gep, align 4
|
||||||
|
call void @runtime.printlock(ptr undef) #4
|
||||||
|
call void @runtime.printint32(i32 %param, ptr undef) #4
|
||||||
|
call void @runtime.printunlock(ptr undef) #4
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
|
br label %rundefers.after
|
||||||
|
|
||||||
|
recover: ; preds = %rundefers.end4
|
||||||
|
ret void
|
||||||
|
|
||||||
|
lpad: ; No predecessors!
|
||||||
|
br label %rundefers.loophead7
|
||||||
|
|
||||||
|
rundefers.loophead7: ; preds = %rundefers.callback013, %lpad
|
||||||
|
br i1 poison, label %rundefers.end4, label %rundefers.loop6
|
||||||
|
|
||||||
|
rundefers.loop6: ; preds = %rundefers.loophead7
|
||||||
|
switch i32 poison, label %rundefers.default5 [
|
||||||
|
i32 0, label %rundefers.callback013
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback013: ; preds = %rundefers.loop6
|
||||||
|
br label %rundefers.loophead7
|
||||||
|
|
||||||
|
rundefers.default5: ; preds = %rundefers.loop6
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end4: ; preds = %rundefers.loophead7
|
||||||
|
br label %recover
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
attributes #2 = { nocallback nofree nosync nounwind willreturn }
|
||||||
|
attributes #3 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #4 = { nounwind }
|
attributes #4 = { nounwind }
|
||||||
attributes #5 = { nounwind returns_twice }
|
attributes #5 = { nounwind returns_twice }
|
||||||
|
|||||||
Vendored
+20
@@ -18,3 +18,23 @@ func deferMultiple() {
|
|||||||
}()
|
}()
|
||||||
external()
|
external()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deferInfiniteLoop() {
|
||||||
|
for {
|
||||||
|
defer print(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deferLoop() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
defer print(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deferBetweenLoops() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
}
|
||||||
|
defer print(1)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+13
-17
@@ -1,21 +1,18 @@
|
|||||||
; ModuleID = 'float.go'
|
; ModuleID = 'float.go'
|
||||||
source_filename = "float.go"
|
source_filename = "float.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -27,25 +24,25 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 {
|
define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float 0x41F0000000000000
|
ret float 0x41F0000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 -1
|
ret i32 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #2 {
|
define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = uitofp i32 %v to float
|
%0 = uitofp i32 %v to float
|
||||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||||
@@ -55,7 +52,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 {
|
define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -68,7 +65,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||||
@@ -80,7 +77,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||||
@@ -93,6 +90,5 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
|
||||||
|
|||||||
Vendored
+12
-16
@@ -1,50 +1,46 @@
|
|||||||
; ModuleID = 'func.go'
|
; ModuleID = 'func.go'
|
||||||
source_filename = "func.go"
|
source_filename = "func.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 {
|
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq ptr %callback.funcptr, null
|
%0 = icmp eq ptr %callback.funcptr, null
|
||||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||||
|
|
||||||
fpcall.next: ; preds = %entry
|
fpcall.next: ; preds = %entry
|
||||||
call void %callback.funcptr(i32 3, ptr %callback.context) #3
|
call void %callback.funcptr(i32 3, ptr %callback.context) #2
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
fpcall.throw: ; preds = %entry
|
fpcall.throw: ; preds = %entry
|
||||||
call void @runtime.nilPanic(ptr undef) #3
|
call void @runtime.nilPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.nilPanic(ptr) #1
|
declare void @runtime.nilPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.bar(ptr %context) unnamed_addr #2 {
|
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 {
|
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+8
@@ -24,6 +24,10 @@ var (
|
|||||||
x *byte
|
x *byte
|
||||||
y [61]uintptr
|
y [61]uintptr
|
||||||
}
|
}
|
||||||
|
struct5 *struct {
|
||||||
|
x *byte
|
||||||
|
y [30]uintptr
|
||||||
|
}
|
||||||
|
|
||||||
slice1 []byte
|
slice1 []byte
|
||||||
slice2 []*int
|
slice2 []*int
|
||||||
@@ -58,6 +62,10 @@ func newStruct() {
|
|||||||
x *byte
|
x *byte
|
||||||
y [61]uintptr
|
y [61]uintptr
|
||||||
})
|
})
|
||||||
|
struct5 = new(struct {
|
||||||
|
x *byte
|
||||||
|
y [30]uintptr
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFuncValue() *func() {
|
func newFuncValue() *func() {
|
||||||
|
|||||||
Vendored
+35
-28
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'gc.go'
|
; ModuleID = 'gc.go'
|
||||||
source_filename = "gc.go"
|
source_filename = "gc.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime._interface = type { ptr, ptr }
|
%runtime._interface = type { ptr, ptr }
|
||||||
@@ -16,27 +16,25 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@main.struct2 = hidden global ptr null, align 4
|
@main.struct2 = hidden global ptr null, align 4
|
||||||
@main.struct3 = hidden global ptr null, align 4
|
@main.struct3 = hidden global ptr null, align 4
|
||||||
@main.struct4 = hidden global ptr null, align 4
|
@main.struct4 = hidden global ptr null, align 4
|
||||||
|
@main.struct5 = hidden global ptr null, align 4
|
||||||
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
@"runtime/gc.layout:62-0100000000000020" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
@"runtime/gc.layout:62-0100000000000000" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
|
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
@@ -54,8 +52,11 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
|
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
@@ -71,26 +72,32 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
|
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new = call align 1 ptr @runtime.alloc_zero(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new, ptr @main.struct1, align 4
|
store ptr %new, ptr @main.struct1, align 4
|
||||||
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new1, ptr @main.struct2, align 4
|
store ptr %new1, ptr @main.struct2, align 4
|
||||||
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
|
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new2, ptr @main.struct3, align 4
|
store ptr %new2, ptr @main.struct3, align 4
|
||||||
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
|
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new3, ptr @main.struct4, align 4
|
store ptr %new3, ptr @main.struct4, align 4
|
||||||
|
%new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #3
|
||||||
|
call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
|
store ptr %new4, ptr @main.struct5, align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc_zero(i32, ptr, ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
|
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
|
||||||
@@ -99,35 +106,35 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
|
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %makeslice, ptr @main.slice1, align 4
|
store ptr %makeslice, ptr @main.slice1, align 4
|
||||||
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice1, i32 4), align 4
|
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice1, i32 4), align 4
|
||||||
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice1, i32 8), align 4
|
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice1, i32 8), align 4
|
||||||
%makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
|
%makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %makeslice1, ptr @main.slice2, align 4
|
store ptr %makeslice1, ptr @main.slice2, align 4
|
||||||
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice2, i32 4), align 4
|
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice2, i32 4), align 4
|
||||||
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice2, i32 8), align 4
|
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice2, i32 8), align 4
|
||||||
%makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
|
%makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %makeslice3, ptr @main.slice3, align 4
|
store ptr %makeslice3, ptr @main.slice3, align 4
|
||||||
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice3, i32 4), align 4
|
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice3, i32 4), align 4
|
||||||
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice3, i32 8), align 4
|
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice3, i32 8), align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store double %v.r, ptr %0, align 8
|
store double %v.r, ptr %0, align 8
|
||||||
%.repack1 = getelementptr inbounds i8, ptr %0, i32 8
|
%.repack1 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store double %v.i, ptr %.repack1, align 8
|
store double %v.i, ptr %.repack1, align 8
|
||||||
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
|
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3
|
||||||
@@ -135,7 +142,7 @@ entry:
|
|||||||
ret %runtime._interface %1
|
ret %runtime._interface %1
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+37
@@ -19,12 +19,49 @@ func Add[T Coord](a, b Point[T]) Point[T] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func aliasSize[F float32 | float64]() uintptr {
|
||||||
|
return unsafe.Sizeof(F(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func aliasSize32() uintptr {
|
||||||
|
type F = float32
|
||||||
|
return aliasSize[F]()
|
||||||
|
}
|
||||||
|
|
||||||
|
func aliasSize64() uintptr {
|
||||||
|
type F = float64
|
||||||
|
return aliasSize[F]()
|
||||||
|
}
|
||||||
|
|
||||||
|
type aliasMethodResult[F float32 | float64] struct {
|
||||||
|
Value F
|
||||||
|
}
|
||||||
|
|
||||||
|
type aliasMethodValue[F float32 | float64] struct{}
|
||||||
|
|
||||||
|
func (aliasMethodValue[F]) Get() aliasMethodResult[F] {
|
||||||
|
return aliasMethodResult[F]{}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var af, bf Point[float32]
|
var af, bf Point[float32]
|
||||||
Add(af, bf)
|
Add(af, bf)
|
||||||
|
|
||||||
var ai, bi Point[int]
|
var ai, bi Point[int]
|
||||||
Add(ai, bi)
|
Add(ai, bi)
|
||||||
|
|
||||||
|
checkSize(aliasSize32())
|
||||||
|
checkSize(aliasSize64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSize(uintptr)
|
func checkSize(uintptr)
|
||||||
|
|
||||||
|
func checkBool(bool)
|
||||||
|
|
||||||
|
func aliasMethod32(x any) {
|
||||||
|
type F = float32
|
||||||
|
_, ok := x.(interface {
|
||||||
|
Get() aliasMethodResult[F]
|
||||||
|
})
|
||||||
|
checkBool(ok)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+149
-212
@@ -1,259 +1,196 @@
|
|||||||
; ModuleID = 'generics.go'
|
; ModuleID = 'generics.go'
|
||||||
source_filename = "generics.go"
|
source_filename = "generics.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%"main.Point[int]" = type { i32, i32 }
|
|
||||||
%"main.Point[float32]" = type { float, float }
|
%"main.Point[float32]" = type { float, float }
|
||||||
|
%"main.Point[int]" = type { i32, i32 }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.main(i8* %context) unnamed_addr #1 {
|
define hidden i32 @main.aliasSize32(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%bi = alloca %"main.Point[int]", align 8
|
%0 = call i32 @"main.aliasSize[basic:float32]"(ptr undef)
|
||||||
%ai = alloca %"main.Point[int]", align 8
|
ret i32 %0
|
||||||
%bf = alloca %"main.Point[float32]", align 8
|
}
|
||||||
%af = alloca %"main.Point[float32]", align 8
|
|
||||||
%af.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
; Function Attrs: nounwind
|
||||||
store float 0.000000e+00, float* %af.repack, align 8
|
define linkonce_odr hidden i32 @"main.aliasSize[basic:float32]"(ptr %context) unnamed_addr #1 {
|
||||||
%af.repack1 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
entry:
|
||||||
store float 0.000000e+00, float* %af.repack1, align 4
|
ret i32 4
|
||||||
%0 = bitcast %"main.Point[float32]"* %af to i8*
|
}
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
|
||||||
%bf.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
; Function Attrs: nounwind
|
||||||
store float 0.000000e+00, float* %bf.repack, align 8
|
define hidden i32 @main.aliasSize64(ptr %context) unnamed_addr #1 {
|
||||||
%bf.repack2 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
entry:
|
||||||
store float 0.000000e+00, float* %bf.repack2, align 4
|
%0 = call i32 @"main.aliasSize[basic:float64]"(ptr undef)
|
||||||
%1 = bitcast %"main.Point[float32]"* %bf to i8*
|
ret i32 %0
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
}
|
||||||
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
|
||||||
%.unpack = load float, float* %.elt, align 8
|
; Function Attrs: nounwind
|
||||||
%.elt3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
define linkonce_odr hidden i32 @"main.aliasSize[basic:float64]"(ptr %context) unnamed_addr #1 {
|
||||||
%.unpack4 = load float, float* %.elt3, align 4
|
entry:
|
||||||
%.elt5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
ret i32 8
|
||||||
%.unpack6 = load float, float* %.elt5, align 8
|
}
|
||||||
%.elt7 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
|
||||||
%.unpack8 = load float, float* %.elt7, align 4
|
; Function Attrs: nounwind
|
||||||
%2 = call %"main.Point[float32]" @"main.Add[float32]"(float %.unpack, float %.unpack4, float %.unpack6, float %.unpack8, i8* undef)
|
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||||
%ai.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
entry:
|
||||||
store i32 0, i32* %ai.repack, align 8
|
%0 = call %"main.Point[float32]" @"main.Add[basic:float32]"(float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, ptr undef)
|
||||||
%ai.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
%1 = call %"main.Point[int]" @"main.Add[basic:int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
|
||||||
store i32 0, i32* %ai.repack9, align 4
|
%2 = call i32 @main.aliasSize32(ptr undef)
|
||||||
%3 = bitcast %"main.Point[int]"* %ai to i8*
|
call void @main.checkSize(i32 %2, ptr undef) #4
|
||||||
call void @runtime.trackPointer(i8* nonnull %3, i8* undef) #2
|
%3 = call i32 @main.aliasSize64(ptr undef)
|
||||||
%bi.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
call void @main.checkSize(i32 %3, ptr undef) #4
|
||||||
store i32 0, i32* %bi.repack, align 8
|
|
||||||
%bi.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
|
||||||
store i32 0, i32* %bi.repack10, align 4
|
|
||||||
%4 = bitcast %"main.Point[int]"* %bi to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %4, i8* undef) #2
|
|
||||||
%.elt11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
|
||||||
%.unpack12 = load i32, i32* %.elt11, align 8
|
|
||||||
%.elt13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
|
||||||
%.unpack14 = load i32, i32* %.elt13, align 4
|
|
||||||
%.elt15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
|
||||||
%.unpack16 = load i32, i32* %.elt15, align 8
|
|
||||||
%.elt17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
|
||||||
%.unpack18 = load i32, i32* %.elt17, align 4
|
|
||||||
%5 = call %"main.Point[int]" @"main.Add[int]"(i32 %.unpack12, i32 %.unpack14, i32 %.unpack16, i32 %.unpack18, i8* undef)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
|
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[basic:float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca %"main.Point[float32]", align 8
|
%stackalloc = alloca i8, align 1
|
||||||
%b = alloca %"main.Point[float32]", align 8
|
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
%a = alloca %"main.Point[float32]", align 8
|
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
%a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
store float %a.X, ptr %a, align 4
|
||||||
store float 0.000000e+00, float* %a.repack, align 8
|
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
store float %a.Y, ptr %a.repack5, align 4
|
||||||
store float 0.000000e+00, float* %a.repack9, align 4
|
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
%0 = bitcast %"main.Point[float32]"* %a to i8*
|
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
store float %b.X, ptr %b, align 4
|
||||||
%a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
store float %a.X, float* %a.repack10, align 8
|
store float %b.Y, ptr %b.repack7, align 4
|
||||||
%a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
call void @main.checkSize(i32 4, ptr undef) #4
|
||||||
store float %a.Y, float* %a.repack11, align 4
|
call void @main.checkSize(i32 8, ptr undef) #4
|
||||||
%b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
store float 0.000000e+00, float* %b.repack, align 8
|
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
%b.repack13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
|
||||||
store float 0.000000e+00, float* %b.repack13, align 4
|
|
||||||
%1 = bitcast %"main.Point[float32]"* %b to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
|
||||||
%b.repack14 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
|
||||||
store float %b.X, float* %b.repack14, align 8
|
|
||||||
%b.repack15 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
|
||||||
store float %b.Y, float* %b.repack15, align 4
|
|
||||||
call void @main.checkSize(i32 4, i8* undef) #2
|
|
||||||
call void @main.checkSize(i32 8, i8* undef) #2
|
|
||||||
%complit.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
|
||||||
store float 0.000000e+00, float* %complit.repack, align 8
|
|
||||||
%complit.repack17 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
|
||||||
store float 0.000000e+00, float* %complit.repack17, align 4
|
|
||||||
%2 = bitcast %"main.Point[float32]"* %complit to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
|
||||||
%3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
|
||||||
br i1 false, label %deref.throw, label %deref.next
|
br i1 false, label %deref.throw, label %deref.next
|
||||||
|
|
||||||
deref.next: ; preds = %entry
|
deref.next: ; preds = %entry
|
||||||
br i1 false, label %deref.throw1, label %deref.next2
|
br i1 false, label %deref.throw, label %deref.next1
|
||||||
|
|
||||||
deref.next2: ; preds = %deref.next
|
deref.next1: ; preds = %deref.next
|
||||||
%4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
%0 = load float, ptr %a, align 4
|
||||||
%5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
%1 = load float, ptr %b, align 4
|
||||||
%6 = load float, float* %5, align 8
|
%2 = fadd float %0, %1
|
||||||
%7 = load float, float* %4, align 8
|
br i1 false, label %deref.throw, label %deref.next2
|
||||||
%8 = fadd float %6, %7
|
|
||||||
br i1 false, label %deref.throw3, label %deref.next4
|
|
||||||
|
|
||||||
deref.next4: ; preds = %deref.next2
|
deref.next2: ; preds = %deref.next1
|
||||||
br i1 false, label %deref.throw5, label %deref.next6
|
br i1 false, label %deref.throw, label %deref.next3
|
||||||
|
|
||||||
deref.next6: ; preds = %deref.next4
|
deref.next3: ; preds = %deref.next2
|
||||||
%9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
%10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%11 = load float, float* %10, align 4
|
%5 = load float, ptr %4, align 4
|
||||||
%12 = load float, float* %9, align 4
|
%6 = load float, ptr %3, align 4
|
||||||
br i1 false, label %store.throw, label %store.next
|
br i1 false, label %deref.throw, label %store.next
|
||||||
|
|
||||||
store.next: ; preds = %deref.next6
|
store.next: ; preds = %deref.next3
|
||||||
store float %8, float* %3, align 8
|
store float %2, ptr %complit, align 4
|
||||||
br i1 false, label %store.throw7, label %store.next8
|
br i1 false, label %deref.throw, label %store.next4
|
||||||
|
|
||||||
store.next8: ; preds = %store.next
|
store.next4: ; preds = %store.next
|
||||||
%13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||||
%14 = fadd float %11, %12
|
%8 = fadd float %5, %6
|
||||||
store float %14, float* %13, align 4
|
store float %8, ptr %7, align 4
|
||||||
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
%.unpack = load float, ptr %complit, align 4
|
||||||
%.unpack = load float, float* %.elt, align 8
|
%9 = insertvalue %"main.Point[float32]" poison, float %.unpack, 0
|
||||||
%15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
|
%10 = insertvalue %"main.Point[float32]" %9, float %8, 1
|
||||||
%16 = insertvalue %"main.Point[float32]" %15, float %14, 1
|
ret %"main.Point[float32]" %10
|
||||||
ret %"main.Point[float32]" %16
|
|
||||||
|
|
||||||
deref.throw: ; preds = %entry
|
deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
|
||||||
unreachable
|
|
||||||
|
|
||||||
deref.throw1: ; preds = %deref.next
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
deref.throw3: ; preds = %deref.next2
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
deref.throw5: ; preds = %deref.next4
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
store.throw: ; preds = %deref.next6
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
store.throw7: ; preds = %store.next
|
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.checkSize(i32, i8*) #0
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2
|
||||||
|
|
||||||
declare void @runtime.nilPanic(i8*) #0
|
declare void @main.checkSize(i32, ptr) #0
|
||||||
|
|
||||||
|
declare void @runtime.nilPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
|
define linkonce_odr hidden %"main.Point[int]" @"main.Add[basic:int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca %"main.Point[int]", align 8
|
%stackalloc = alloca i8, align 1
|
||||||
%b = alloca %"main.Point[int]", align 8
|
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
%a = alloca %"main.Point[int]", align 8
|
call void @runtime.trackPointer(ptr nonnull %a, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
%a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
store i32 %a.X, ptr %a, align 4
|
||||||
store i32 0, i32* %a.repack, align 8
|
%a.repack5 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
store i32 %a.Y, ptr %a.repack5, align 4
|
||||||
store i32 0, i32* %a.repack9, align 4
|
%b = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
%0 = bitcast %"main.Point[int]"* %a to i8*
|
call void @runtime.trackPointer(ptr nonnull %b, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
store i32 %b.X, ptr %b, align 4
|
||||||
%a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
%b.repack7 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
store i32 %a.X, i32* %a.repack10, align 8
|
store i32 %b.Y, ptr %b.repack7, align 4
|
||||||
%a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
call void @main.checkSize(i32 4, ptr undef) #4
|
||||||
store i32 %a.Y, i32* %a.repack11, align 4
|
call void @main.checkSize(i32 8, ptr undef) #4
|
||||||
%b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
%complit = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
store i32 0, i32* %b.repack, align 8
|
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
%b.repack13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
|
||||||
store i32 0, i32* %b.repack13, align 4
|
|
||||||
%1 = bitcast %"main.Point[int]"* %b to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
|
||||||
%b.repack14 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
|
||||||
store i32 %b.X, i32* %b.repack14, align 8
|
|
||||||
%b.repack15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
|
||||||
store i32 %b.Y, i32* %b.repack15, align 4
|
|
||||||
call void @main.checkSize(i32 4, i8* undef) #2
|
|
||||||
call void @main.checkSize(i32 8, i8* undef) #2
|
|
||||||
%complit.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
|
||||||
store i32 0, i32* %complit.repack, align 8
|
|
||||||
%complit.repack17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
|
||||||
store i32 0, i32* %complit.repack17, align 4
|
|
||||||
%2 = bitcast %"main.Point[int]"* %complit to i8*
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
|
||||||
%3 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
|
||||||
br i1 false, label %deref.throw, label %deref.next
|
br i1 false, label %deref.throw, label %deref.next
|
||||||
|
|
||||||
deref.next: ; preds = %entry
|
deref.next: ; preds = %entry
|
||||||
br i1 false, label %deref.throw1, label %deref.next2
|
br i1 false, label %deref.throw, label %deref.next1
|
||||||
|
|
||||||
deref.next2: ; preds = %deref.next
|
deref.next1: ; preds = %deref.next
|
||||||
%4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
%0 = load i32, ptr %a, align 4
|
||||||
%5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
%1 = load i32, ptr %b, align 4
|
||||||
%6 = load i32, i32* %5, align 8
|
%2 = add i32 %0, %1
|
||||||
%7 = load i32, i32* %4, align 8
|
br i1 false, label %deref.throw, label %deref.next2
|
||||||
%8 = add i32 %6, %7
|
|
||||||
br i1 false, label %deref.throw3, label %deref.next4
|
|
||||||
|
|
||||||
deref.next4: ; preds = %deref.next2
|
deref.next2: ; preds = %deref.next1
|
||||||
br i1 false, label %deref.throw5, label %deref.next6
|
br i1 false, label %deref.throw, label %deref.next3
|
||||||
|
|
||||||
deref.next6: ; preds = %deref.next4
|
deref.next3: ; preds = %deref.next2
|
||||||
%9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
%3 = getelementptr inbounds nuw i8, ptr %b, i32 4
|
||||||
%10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
%4 = getelementptr inbounds nuw i8, ptr %a, i32 4
|
||||||
%11 = load i32, i32* %10, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%12 = load i32, i32* %9, align 4
|
%6 = load i32, ptr %3, align 4
|
||||||
br i1 false, label %store.throw, label %store.next
|
br i1 false, label %deref.throw, label %store.next
|
||||||
|
|
||||||
store.next: ; preds = %deref.next6
|
store.next: ; preds = %deref.next3
|
||||||
store i32 %8, i32* %3, align 8
|
store i32 %2, ptr %complit, align 4
|
||||||
br i1 false, label %store.throw7, label %store.next8
|
br i1 false, label %deref.throw, label %store.next4
|
||||||
|
|
||||||
store.next8: ; preds = %store.next
|
store.next4: ; preds = %store.next
|
||||||
%13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
%7 = getelementptr inbounds nuw i8, ptr %complit, i32 4
|
||||||
%14 = add i32 %11, %12
|
%8 = add i32 %5, %6
|
||||||
store i32 %14, i32* %13, align 4
|
store i32 %8, ptr %7, align 4
|
||||||
%.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
%.unpack = load i32, ptr %complit, align 4
|
||||||
%.unpack = load i32, i32* %.elt, align 8
|
%9 = insertvalue %"main.Point[int]" poison, i32 %.unpack, 0
|
||||||
%15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
|
%10 = insertvalue %"main.Point[int]" %9, i32 %8, 1
|
||||||
%16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
|
ret %"main.Point[int]" %10
|
||||||
ret %"main.Point[int]" %16
|
|
||||||
|
|
||||||
deref.throw: ; preds = %entry
|
deref.throw: ; preds = %store.next, %deref.next3, %deref.next2, %deref.next1, %deref.next, %entry
|
||||||
unreachable
|
|
||||||
|
|
||||||
deref.throw1: ; preds = %deref.next
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
deref.throw3: ; preds = %deref.next2
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
deref.throw5: ; preds = %deref.next4
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
store.throw: ; preds = %deref.next6
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
store.throw7: ; preds = %store.next
|
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
declare void @main.checkBool(i1, ptr) #0
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #2 = { nounwind }
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.aliasMethod32(ptr %x.typecode, ptr %x.value, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}}.$typeassert"(ptr %x.typecode) #4
|
||||||
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
|
call void @main.checkBool(i1 %0, ptr undef) #4
|
||||||
|
ret void
|
||||||
|
|
||||||
|
typeassert.ok: ; preds = %entry
|
||||||
|
br label %typeassert.next
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}}.$typeassert"(ptr) #3
|
||||||
|
|
||||||
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Get:func:{}{named:main.aliasMethodResult[basic:float32]}" }
|
||||||
|
attributes #4 = { nounwind }
|
||||||
|
|||||||
Vendored
+14
-18
@@ -1,31 +1,28 @@
|
|||||||
; ModuleID = 'go1.20.go'
|
; ModuleID = 'go1.20.go'
|
||||||
source_filename = "go1.20.go"
|
source_filename = "go1.20.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime._string = type { ptr, i32 }
|
%runtime._string = type { ptr, i32 }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %s.data
|
ret ptr %s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = icmp slt i16 %len, 0
|
%0 = icmp slt i16 %len, 0
|
||||||
@@ -39,25 +36,24 @@ unsafe.String.next: ; preds = %entry
|
|||||||
%5 = zext nneg i16 %len to i32
|
%5 = zext nneg i16 %len to i32
|
||||||
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
||||||
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret %runtime._string %7
|
ret %runtime._string %7
|
||||||
|
|
||||||
unsafe.String.throw: ; preds = %entry
|
unsafe.String.throw: ; preds = %entry
|
||||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
|
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret ptr %s.data
|
ret ptr %s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind }
|
||||||
attributes #3 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+68
-66
@@ -1,36 +1,36 @@
|
|||||||
; ModuleID = 'go1.21.go'
|
; ModuleID = 'go1.21.go'
|
||||||
source_filename = "go1.21.go"
|
source_filename = "go1.21.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime._string = type { ptr, i32 }
|
%runtime._string = type { ptr, i32 }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %a
|
ret i32 %a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.smin.i32(i32, i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||||
@@ -38,7 +38,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||||
@@ -47,91 +47,109 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i8 @llvm.umin.i8(i8, i8) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
declare i32 @llvm.umin.i32(i32, i32) #2
|
||||||
entry:
|
|
||||||
%0 = fcmp olt float %a, %b
|
|
||||||
%1 = select i1 %0, float %a, float %b
|
|
||||||
ret float %1
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt double %a, %b
|
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
||||||
%1 = select i1 %0, double %a, double %b
|
ret float %0
|
||||||
ret double %1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare float @llvm.minimum.f32(float, float) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare double @llvm.minimum.f64(double, double) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #4
|
||||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||||
%6 = extractvalue %runtime._string %5, 0
|
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
ret %runtime._string %5
|
ret %runtime._string %5
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.smax.i32(i32, i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
declare i32 @llvm.umax.i32(i32, i32) #2
|
||||||
entry:
|
|
||||||
%0 = fcmp ogt float %a, %b
|
|
||||||
%1 = select i1 %0, float %a, float %b
|
|
||||||
ret float %1
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
||||||
|
ret float %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare float @llvm.maximum.f32(float, float) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||||
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #4
|
||||||
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||||
%6 = extractvalue %runtime._string %5, 0
|
%6 = select i1 %4, ptr %a.data, ptr %b.data
|
||||||
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
|
||||||
ret %runtime._string %5
|
ret %runtime._string %5
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = shl i32 %s.len, 2
|
%0 = shl i32 %s.len, 2
|
||||||
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||||
@@ -142,38 +160,22 @@ entry:
|
|||||||
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
|
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
|
call void @runtime.hashmapClear(ptr %m, ptr undef) #4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
declare i32 @llvm.smin.i32(i32, i32) #4
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i8 @llvm.umin.i8(i8, i8) #4
|
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.umin.i32(i32, i32) #4
|
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.smax.i32(i32, i32) #4
|
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
|
||||||
declare i32 @llvm.umax.i32(i32, i32) #4
|
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
|
||||||
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||||
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #4 = { nounwind }
|
||||||
attributes #5 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// genericMethod has both a regular method and a "generic method": a method
|
||||||
|
// with its own type parameter, independent of any type parameter on the
|
||||||
|
// receiver. This is a Go 1.27 feature (see math/rand/v2.Rand.N for a
|
||||||
|
// real-world example). Boxing such a value into an interface must not try to
|
||||||
|
// encode the generic method's type-parameterized signature into a type code.
|
||||||
|
type genericMethod struct{}
|
||||||
|
|
||||||
|
func (t genericMethod) Regular(n int) int { return n }
|
||||||
|
|
||||||
|
func (t genericMethod) GenericParam[X int | int64](n X) X { return n }
|
||||||
|
|
||||||
|
// onlyGenericMethod's only method is generic, so its runtime type must have
|
||||||
|
// an empty method set (hasMethodSet must become false, not just numMethods).
|
||||||
|
type onlyGenericMethod struct{}
|
||||||
|
|
||||||
|
func (t onlyGenericMethod) GenericParam[X int | int64](n X) X { return n }
|
||||||
|
|
||||||
|
func useGenericMethods() (any, any) {
|
||||||
|
var g genericMethod
|
||||||
|
var o onlyGenericMethod
|
||||||
|
return any(g), any(o)
|
||||||
|
}
|
||||||
Vendored
+67
@@ -0,0 +1,67 @@
|
|||||||
|
; ModuleID = 'go1.27.go'
|
||||||
|
source_filename = "go1.27.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%runtime.structField = type { ptr, ptr }
|
||||||
|
%runtime._interface = type { ptr, ptr }
|
||||||
|
|
||||||
|
@"reflect/types.signature:Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
|
||||||
|
@"reflect/types.type:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] } { ptr @"named:main.genericMethod$methodset", i8 122, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, { i32, [1 x ptr] } }, ptr @"reflect/types.type:pointer:named:main.genericMethod", i32 0, i32 1), ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] }, [19 x i8] c"main.genericMethod\00" }, align 4
|
||||||
|
@"reflect/types.type.pkgpath:main" = linkonce_odr unnamed_addr constant [5 x i8] c"main\00", align 1
|
||||||
|
@"reflect/types.type:pointer:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, { i32, [1 x ptr] } } { ptr @"pointer:named:main.genericMethod$methodset", i8 -43, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] } }, align 4
|
||||||
|
@"reflect/methods.Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
|
||||||
|
@"main$string" = internal unnamed_addr constant [18 x i8] c"main.genericMethod", align 1
|
||||||
|
@"main$string.1" = internal unnamed_addr constant [7 x i8] c"Regular", align 1
|
||||||
|
@"pointer:named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(*main.genericMethod).Regular" } }
|
||||||
|
@"reflect/types.type:struct:{}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [0 x %runtime.structField] } { i8 90, i16 0, ptr @"reflect/types.type:pointer:struct:{}", ptr @"reflect/types.type.pkgpath.empty", i32 0, i16 0, [0 x %runtime.structField] zeroinitializer }, align 4
|
||||||
|
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||||
|
@"reflect/types.type:pointer:struct:{}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{}" }, align 4
|
||||||
|
@"named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(main.genericMethod).Regular$invoke" } }
|
||||||
|
@"reflect/types.type:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [23 x i8] } { i8 122, i16 0, ptr @"reflect/types.type:pointer:named:main.onlyGenericMethod", ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", [23 x i8] c"main.onlyGenericMethod\00" }, align 4
|
||||||
|
@"reflect/types.type:pointer:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.onlyGenericMethod" }, align 4
|
||||||
|
|
||||||
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @"(main.genericMethod).Regular"(i32 %n, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret i32 %n
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { %runtime._interface, %runtime._interface } @main.useGenericMethods(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
call void @runtime.trackPointer(ptr nonnull getelementptr inbounds nuw (i8, ptr @"reflect/types.type:named:main.genericMethod", i32 4), ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:named:main.onlyGenericMethod", ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret { %runtime._interface, %runtime._interface } { %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), ptr null }, %runtime._interface { ptr @"reflect/types.type:named:main.onlyGenericMethod", ptr null } }
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr hidden i32 @"(*main.genericMethod).Regular"(ptr %t, i32 %n, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
call void @runtime.trackPointer(ptr %t, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
%0 = call i32 @"(main.genericMethod).Regular"(i32 %n, ptr undef)
|
||||||
|
ret i32 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr i32 @"(main.genericMethod).Regular$invoke"(ptr %0, i32 %1, ptr %2) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%ret = call i32 @"(main.genericMethod).Regular"(i32 %1, ptr %2)
|
||||||
|
ret i32 %ret
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
+75
-68
@@ -5,53 +5,50 @@ target triple = "thumbv7m-unknown-unknown-eabi"
|
|||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
define hidden void @main.init(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, ptr) #2
|
declare void @main.regularFunction(i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #2
|
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #1
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
|
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
@@ -59,25 +56,28 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||||
store i32 3, ptr %n, align 4
|
store i32 3, ptr %n, align 4
|
||||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 133 to ptr), ptr undef) #11
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
call void @runtime.printlock(ptr undef) #9
|
call void @runtime.printlock(ptr undef) #11
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
call void @runtime.printint32(i32 %2, ptr undef) #11
|
||||||
call void @runtime.printunlock(ptr undef) #9
|
call void @runtime.printunlock(ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
store i32 7, ptr %context, align 4
|
store i32 7, ptr %context, align 4
|
||||||
ret void
|
ret void
|
||||||
@@ -87,29 +87,29 @@ entry:
|
|||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printlock(ptr) #2
|
declare void @runtime.printlock(ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #2
|
declare void @runtime.printint32(i32, ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printunlock(ptr) #2
|
declare void @runtime.printunlock(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 391 to ptr), ptr undef) #11
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %fn.context, ptr %1, align 4
|
store ptr %fn.context, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 8
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,77 +117,84 @@ entry:
|
|||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
%5 = load ptr, ptr %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
call void %5(i32 %1, ptr %3) #9
|
call void %5(i32 %1, ptr %3) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
|
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||||
|
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.umin.i32(i32, i32) #7
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||||
|
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 713 to ptr), ptr undef) #11
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr @"main$string", ptr %1, align 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 8
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store i32 4, ptr %2, align 4
|
store i32 4, ptr %2, align 4
|
||||||
%3 = getelementptr inbounds i8, ptr %0, i32 12
|
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
store ptr %itf.typecode, ptr %3, align 4
|
store ptr %itf.typecode, ptr %3, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #11
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load ptr, ptr %0, align 4
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
%5 = load i32, ptr %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds i8, ptr %0, i32 12
|
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
%7 = load ptr, ptr %6, align 4
|
%7 = load ptr, ptr %6, align 4
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #2 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||||
attributes #7 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #8 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #9 = { nounwind }
|
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
|
||||||
|
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
|
attributes #11 = { nounwind }
|
||||||
|
|||||||
+85
-78
@@ -1,89 +1,89 @@
|
|||||||
; ModuleID = 'goroutine.go'
|
; ModuleID = 'goroutine.go'
|
||||||
source_filename = "goroutine.go"
|
source_filename = "goroutine.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, ptr) #1
|
declare void @main.regularFunction(i32, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.deadlock(ptr) #1
|
declare void @runtime.exitGoroutine(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.exitGoroutine(ptr undef) #11
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint ptr %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.exitGoroutine(ptr undef) #11
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
|
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
|
||||||
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11
|
||||||
store i32 3, ptr %n, align 4
|
store i32 3, ptr %n, align 4
|
||||||
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11
|
||||||
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #11
|
||||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 133 to ptr), ptr undef) #11
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
call void @runtime.printlock(ptr undef) #9
|
call void @runtime.printlock(ptr undef) #11
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
call void @runtime.printint32(i32 %2, ptr undef) #11
|
||||||
call void @runtime.printunlock(ptr undef) #9
|
call void @runtime.printunlock(ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 {
|
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
store i32 7, ptr %context, align 4
|
store i32 7, ptr %context, align 4
|
||||||
ret void
|
ret void
|
||||||
@@ -93,31 +93,31 @@ entry:
|
|||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.exitGoroutine(ptr undef) #11
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printlock(ptr) #1
|
declare void @runtime.printlock(ptr) #0
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #1
|
declare void @runtime.printint32(i32, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.printunlock(ptr) #1
|
declare void @runtime.printunlock(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 391 to ptr), ptr undef) #11
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr %fn.context, ptr %1, align 4
|
store ptr %fn.context, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 8
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,80 +125,87 @@ entry:
|
|||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
%5 = load ptr, ptr %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
call void %5(i32 %1, ptr %3) #9
|
call void %5(i32 %1, ptr %3) #11
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.exitGoroutine(ptr undef) #11
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
|
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
|
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||||
|
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.umin.i32(i32, i32) #7
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
|
||||||
|
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 713 to ptr), ptr undef) #11
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
store ptr @"main$string", ptr %1, align 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 8
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
store i32 4, ptr %2, align 4
|
store i32 4, ptr %2, align 4
|
||||||
%3 = getelementptr inbounds i8, ptr %0, i32 12
|
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
store ptr %itf.typecode, ptr %3, align 4
|
store ptr %itf.typecode, ptr %3, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load ptr, ptr %0, align 4
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||||
%5 = load i32, ptr %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds i8, ptr %0, i32 12
|
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||||
%7 = load ptr, ptr %6, align 4
|
%7 = load ptr, ptr %6, align 4
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.exitGoroutine(ptr undef) #11
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
||||||
attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #9 = { nounwind }
|
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
|
||||||
|
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
|
attributes #11 = { nounwind }
|
||||||
|
|||||||
Vendored
+43
-45
@@ -1,6 +1,6 @@
|
|||||||
; ModuleID = 'interface.go'
|
; ModuleID = 'interface.go'
|
||||||
source_filename = "interface.go"
|
source_filename = "interface.go"
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime._interface = type { ptr, ptr }
|
%runtime._interface = type { ptr, ptr }
|
||||||
@@ -9,65 +9,64 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||||
|
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 -32767, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
|
||||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
|
||||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
|
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||||
|
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||||
@"reflect/types.typeid:basic:int" = external constant i8
|
@"reflect/types.typeid:basic:int" = external constant i8
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
|
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
|
||||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
|
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
|
||||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -77,12 +76,12 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
|
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -92,12 +91,12 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
|
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -107,34 +106,33 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
|
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
|
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #6
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
|
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
|
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6
|
||||||
%1 = extractvalue %runtime._string %0, 0
|
%1 = extractvalue %runtime._string %0, 0
|
||||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
|
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
|
||||||
ret %runtime._string %0
|
ret %runtime._string %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #2 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error:func:{}{basic:string}" }
|
||||||
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String:func:{}{basic:string}" }
|
||||||
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo:func:{basic:int}{basic:uint8}" "tinygo-methods"="reflect/methods.String:func:{}{basic:string}; main.$methods.foo:func:{basic:int}{basic:uint8}" }
|
||||||
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error:func:{}{basic:string}" "tinygo-methods"="reflect/methods.Error:func:{}{basic:string}" }
|
||||||
attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #6 = { nounwind }
|
||||||
attributes #7 = { nounwind }
|
|
||||||
|
|||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type largeOptimizedValue [1025]byte
|
||||||
|
|
||||||
|
type mixedLargeOptimizedValue struct {
|
||||||
|
value [1025]byte
|
||||||
|
any any
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLargeOptimizedValue() largeOptimizedValue {
|
||||||
|
return largeOptimizedValue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLargeOptimizedValue(value largeOptimizedValue) byte {
|
||||||
|
return value[len(value)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func readMixedLargeOptimizedValue(value mixedLargeOptimizedValue) byte {
|
||||||
|
return value.value[len(value.value)-1]
|
||||||
|
}
|
||||||
Vendored
+120
@@ -0,0 +1,120 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type largeValue [1025]byte
|
||||||
|
|
||||||
|
type largeStruct struct {
|
||||||
|
value [1025]byte
|
||||||
|
ptr *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type largeInterface interface {
|
||||||
|
makeLargeValue() largeValue
|
||||||
|
readLargeValue(largeValue) byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type largeReceiver largeValue
|
||||||
|
|
||||||
|
func makeLargeValue(value byte) largeValue {
|
||||||
|
var result largeValue
|
||||||
|
result[len(result)-1] = value
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeZeroLargeValue() largeValue {
|
||||||
|
return largeValue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func passZeroLargeValue() byte {
|
||||||
|
return readLargeValue(largeValue{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLargeValue(value largeValue) byte {
|
||||||
|
return value[len(value)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func useLargeValue() byte {
|
||||||
|
return readLargeValue(makeLargeValue(42))
|
||||||
|
}
|
||||||
|
|
||||||
|
func useLargeFunctionValue(fn func(largeValue) byte) byte {
|
||||||
|
return fn(makeLargeValue(42))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (receiver largeReceiver) makeLargeValue() largeValue {
|
||||||
|
return largeValue(receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (receiver largeReceiver) readLargeValue(value largeValue) byte {
|
||||||
|
return value[len(value)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func useLargeInterface(value largeInterface) byte {
|
||||||
|
return value.readLargeValue(value.makeLargeValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
func deferLargeValue(value largeValue) {
|
||||||
|
defer readLargeValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func goLargeValue(value largeValue) {
|
||||||
|
go readLargeValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLargeResults(value byte) (largeValue, byte) {
|
||||||
|
return makeLargeValue(value), value
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTwoLargeResults(value byte) (largeValue, largeValue) {
|
||||||
|
return makeLargeValue(value), makeLargeValue(value + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMixedLargeResults(value byte) (largeValue, byte, largeValue) {
|
||||||
|
return makeLargeValue(value), value + 1, makeLargeValue(value + 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func chooseLargeValue(flag bool) largeValue {
|
||||||
|
value := makeLargeValue(1)
|
||||||
|
if flag {
|
||||||
|
value = makeLargeValue(42)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePointerLargeValue(value *byte) largeStruct {
|
||||||
|
return largeStruct{ptr: value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertLargeValue(value any) byte {
|
||||||
|
large, ok := value.(largeValue)
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return large[len(large)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func useLargeMap(key, value largeValue) byte {
|
||||||
|
values := map[largeValue]largeValue{key: value}
|
||||||
|
result, ok := values[key]
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result[len(result)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func useLargeChannel(ch chan largeValue, value largeValue) byte {
|
||||||
|
ch <- value
|
||||||
|
result, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result[len(result)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectLargeChannel(ch chan largeValue, value largeValue) byte {
|
||||||
|
select {
|
||||||
|
case ch <- value:
|
||||||
|
return 0
|
||||||
|
case result := <-ch:
|
||||||
|
return result[len(result)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user