mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
Compare commits
290 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b7fa93fcb | |||
| cce9c6d5a1 | |||
| f41b6a3b96 | |||
| e066e67baf | |||
| cacb452aa6 | |||
| 9296332151 | |||
| 1125d42149 | |||
| 7982d26db0 | |||
| f6758d22d8 | |||
| 87cfe6a538 | |||
| c02cc339c5 | |||
| 361ecf9ea4 | |||
| e21ab04fad | |||
| 4e8453167f | |||
| ebb410afd9 | |||
| 012bdfae80 | |||
| f6df276118 | |||
| e0a5fc2555 | |||
| cecb80b8bf | |||
| d899895e32 | |||
| 0d56dee00f | |||
| 1f0bf9ba63 | |||
| 0b0ae19656 | |||
| 9027c50405 | |||
| 4ce54ede15 | |||
| d8b1fac690 | |||
| 1996fad075 | |||
| 7fe996e7ba | |||
| 47ca1c037b | |||
| df0f5ae1da | |||
| 45c8817ddd | |||
| eebd2f648b | |||
| 13698b17f7 | |||
| dd9c2273e4 | |||
| 1adfdf8fa5 | |||
| da362b8a24 | |||
| 7fa13ed0a6 | |||
| eec915170d | |||
| 334a12818d | |||
| 0504e4a201 | |||
| 530ca0838d | |||
| c41a212712 | |||
| 27824e3c80 | |||
| 159317a5fe | |||
| 655075e5e0 | |||
| f9d0ff3bec | |||
| 17176a2cea | |||
| fb73074325 | |||
| 187d9c6aca | |||
| 35f427c8cc | |||
| 26b6d0b06d | |||
| 38252e338f | |||
| e7ba07dd5a | |||
| 0d646d8e95 | |||
| 3177591b31 | |||
| 19db0144ae | |||
| d639e01650 | |||
| c43958972c | |||
| 33489d6344 | |||
| 80077ef276 | |||
| 911ce3a4bc | |||
| 776dabb2c8 | |||
| e11df5c212 | |||
| 656805d91f | |||
| 05ec4e6bd0 | |||
| ae65b37762 | |||
| aa12579864 | |||
| 715b269f78 | |||
| 5f3534fe72 | |||
| a7ff2731b9 | |||
| 0566bbfeb4 | |||
| 70c4e1cf96 | |||
| 9fd0567fb5 | |||
| 481aba6536 | |||
| a700f58581 | |||
| e71e289e8b | |||
| 6cdc718bfb | |||
| 70c94c6d01 | |||
| 762a6f1256 | |||
| 8d4d3c6201 | |||
| 146e2cd376 | |||
| d304e6706b | |||
| c5c6464175 | |||
| c90ff1e1cf | |||
| 0f6dfedcfd | |||
| 69a284bd36 | |||
| 92be61534e | |||
| 675b8e3f3c | |||
| f3d0195d35 | |||
| 398c284480 | |||
| 86f125cf72 | |||
| 5293d3e5f0 | |||
| 7aca814954 | |||
| 1d52e6be29 | |||
| acb2391439 | |||
| 49007891c8 | |||
| 54aec042b7 | |||
| baca8a643e | |||
| b731919f97 | |||
| c759e6fc2d | |||
| a329f56ec2 | |||
| 6e503f5ab9 | |||
| 217449df07 | |||
| a7fc65861d | |||
| f0a271bd21 | |||
| 4d14d3cd54 | |||
| 5c622cfc43 | |||
| 783c6a813a | |||
| 67841207e8 | |||
| d92e16e6dc | |||
| 4daf4fa0a0 | |||
| 0b3a7280fa | |||
| bb5050a50d | |||
| 73b368270c | |||
| 2001c44ed3 | |||
| 666ead3996 | |||
| df888acd5e | |||
| b9bb605257 | |||
| e1281b0128 | |||
| 81dbbea1c8 | |||
| a834359079 | |||
| 29f8d22a2d | |||
| 9e34ca9e5f | |||
| 268140ae40 | |||
| 8906584fb9 | |||
| aaa860f154 | |||
| bce0516394 | |||
| 726d74ad1b | |||
| ad544b5133 | |||
| 580fd490a5 | |||
| c0d505d13a | |||
| 12a41dc791 | |||
| dd3ad57efb | |||
| 89facf834f | |||
| 62594004c6 | |||
| 3dbc4d5210 | |||
| 42e4a75319 | |||
| 2b7f562202 | |||
| 08a51535d4 | |||
| 0ddcf4af96 | |||
| d435fc868b | |||
| 62df1d7490 | |||
| 229746b71e | |||
| 09ec846c9f | |||
| f57cffce2d | |||
| 7b6a9fab42 | |||
| 6bc6de8f82 | |||
| b79bf29c11 | |||
| 65d65c1313 | |||
| 6b46ae261a | |||
| b734b3c7a1 | |||
| 5937f03a07 | |||
| bf9282a3a0 | |||
| cad5b79a2d | |||
| d56c9f5533 | |||
| 3d7f24e26c | |||
| c32490935b | |||
| 2b04006b1e | |||
| 9de757205f | |||
| f866d5cc38 | |||
| c153239682 | |||
| 8e88f3e76a | |||
| a62044d89b | |||
| 5548b5999b | |||
| 8f33721b88 | |||
| 7e9d84777e | |||
| 2072d1bb5c | |||
| 636a151ffe | |||
| e7d02cd51b | |||
| f136eb6adf | |||
| 2688d9d733 | |||
| b5ad81c884 | |||
| c2fb1e776a | |||
| 895c542076 | |||
| e91fae5756 | |||
| f52ecf3054 | |||
| 725864d8dc | |||
| fca2de21b1 | |||
| 107b1c2906 | |||
| 2409bbef69 | |||
| 8b078a9e8f | |||
| 9e24441978 | |||
| 1e6c14b3e4 | |||
| 25f6be1488 | |||
| 9a9106a297 | |||
| a68f7e4ce3 | |||
| 70b3ece6ec | |||
| 86ea216e7d | |||
| 5a92c35536 | |||
| 7513fa2c96 | |||
| bc946f346d | |||
| 6e6666519c | |||
| cecae8084c | |||
| 9720ccacbb | |||
| a5aa777c7b | |||
| aca1ad187d | |||
| 5a41ed6329 | |||
| 91e9c84d85 | |||
| 4af530f238 | |||
| d3863f337d | |||
| 5551ec7a1e | |||
| 91104b2f27 | |||
| 80f38e8449 | |||
| 22893c5180 | |||
| 9e4e182615 | |||
| 5f96d2b784 | |||
| bd1d93b705 | |||
| 03d1c44265 | |||
| dc0346d968 | |||
| dadae95448 | |||
| f5fc2fc072 | |||
| 4ba76a5df9 | |||
| ea1e08f53f | |||
| bba0dc9575 | |||
| 617c7586fe | |||
| 7366d5e185 | |||
| 96e3ecd949 | |||
| 8bbfb1ee68 | |||
| 07cdcc61f7 | |||
| 4ca9b34133 | |||
| d5e4b16911 | |||
| fd05254683 | |||
| c579e7e509 | |||
| d463528a72 | |||
| 49b0086f8f | |||
| d7814ff50b | |||
| 02160aa8d8 | |||
| c199dd03c7 | |||
| 472aecf07a | |||
| 09350e5719 | |||
| 623dd6a815 | |||
| e09bd5abb3 | |||
| e955aa1941 | |||
| edaf13f951 | |||
| 5f6cf665f5 | |||
| c6db89ff05 | |||
| 61d651c947 | |||
| 227a55d891 | |||
| b485e8bfbd | |||
| 9e8739bb47 | |||
| 20a7a6fd54 | |||
| 4695da83b7 | |||
| f094e895c5 | |||
| 55573c6729 | |||
| b8a6a1f62b | |||
| ef912a132d | |||
| fb603a471c | |||
| f439514703 | |||
| 303410d3fc | |||
| aa13b5d83b | |||
| 12d63d9642 | |||
| 24b45555bd | |||
| 80c17c0f32 | |||
| f6e6aca8d9 | |||
| c4d99e5297 | |||
| 697e8c725b | |||
| a87e5cdbf0 | |||
| b56baa7aad | |||
| ee94f92ede | |||
| 0b77e92c50 | |||
| a4ee98e0e1 | |||
| d0808c93f6 | |||
| a0407be7b7 | |||
| e70dfa4dd6 | |||
| f34a0d44ca | |||
| df52b500bf | |||
| c2f437e0b7 | |||
| 2365c7cfec | |||
| a5d28bdcca | |||
| f12ddfe164 | |||
| f9ba99344a | |||
| 1784bcd728 | |||
| a2704f1435 | |||
| edbbca5614 | |||
| 13f21477b1 | |||
| 9d73e6cfe4 | |||
| 9e7667ffae | |||
| b86467f9c5 | |||
| 13a16afc2a | |||
| a107b4d459 | |||
| 5c176f80d5 | |||
| c4392d9472 | |||
| b6d6efde07 | |||
| 8b67282f91 | |||
| c7a23183e8 | |||
| f936125658 | |||
| 3cfaceeb16 | |||
| f0391eac25 | |||
| 05cdde162c | |||
| 25c8d3ec3a |
+4
-27
@@ -6,18 +6,6 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Pull submodules"
|
name: "Pull submodules"
|
||||||
command: git submodule update --init
|
command: git submodule update --init
|
||||||
install-xtensa-toolchain:
|
|
||||||
parameters:
|
|
||||||
variant:
|
|
||||||
type: string
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Install Xtensa toolchain"
|
|
||||||
command: |
|
|
||||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
|
||||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
|
||||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
|
||||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
|
||||||
llvm-source-linux:
|
llvm-source-linux:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -75,8 +63,6 @@ commands:
|
|||||||
clang-<<parameters.llvm>> \
|
clang-<<parameters.llvm>> \
|
||||||
libclang-<<parameters.llvm>>-dev \
|
libclang-<<parameters.llvm>>-dev \
|
||||||
lld-<<parameters.llvm>> \
|
lld-<<parameters.llvm>> \
|
||||||
gcc-avr \
|
|
||||||
avr-libc \
|
|
||||||
cmake \
|
cmake \
|
||||||
ninja-build
|
ninja-build
|
||||||
- hack-ninja-jobs
|
- hack-ninja-jobs
|
||||||
@@ -112,26 +98,17 @@ commands:
|
|||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm13-go116:
|
test-llvm14-go118:
|
||||||
docker:
|
docker:
|
||||||
- image: golang:1.16-buster
|
- image: golang:1.18-buster
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "13"
|
|
||||||
test-llvm14-go119:
|
|
||||||
docker:
|
|
||||||
- image: golang:1.19beta1-buster
|
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "14"
|
llvm: "14"
|
||||||
fmt-check: false
|
resource_class: large
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
test-all:
|
test-all:
|
||||||
jobs:
|
jobs:
|
||||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||||
# least the smoke tests still pass.
|
# least the smoke tests still pass.
|
||||||
- test-llvm13-go116
|
- test-llvm14-go118
|
||||||
# This tests a beta version of Go. It should be removed once regular
|
|
||||||
# release builds are built using this version.
|
|
||||||
- test-llvm14-go119
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
build/
|
build/
|
||||||
llvm-*/
|
llvm-*/
|
||||||
|
.github
|
||||||
|
.circleci
|
||||||
|
|
||||||
|
|||||||
@@ -16,30 +16,24 @@ jobs:
|
|||||||
name: build-macos
|
name: build-macos
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '1.18.1'
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||||
- name: Install Xtensa toolchain
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
|
|
||||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
|
|
||||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
|
||||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-macos.tar.gz
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Cache LLVM source
|
- name: Install Go
|
||||||
uses: actions/cache@v2
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
|
- name: Restore LLVM source cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-macos-v1
|
key: llvm-source-15-macos-v3
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -49,11 +43,22 @@ jobs:
|
|||||||
- 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 LLVM build
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Restore LLVM build cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-macos-v1
|
key: llvm-build-15-macos-v4
|
||||||
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'
|
||||||
@@ -67,8 +72,14 @@ jobs:
|
|||||||
# 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
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
|
path: llvm-build
|
||||||
- name: Cache wasi-libc sysroot
|
- name: Cache wasi-libc sysroot
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v4
|
key: wasi-libc-sysroot-v4
|
||||||
@@ -99,4 +110,23 @@ jobs:
|
|||||||
path: build/tinygo.darwin-amd64.tar.gz
|
path: build/tinygo.darwin-amd64.tar.gz
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0
|
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||||
|
test-macos-homebrew:
|
||||||
|
name: homebrew-install
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: Install LLVM
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
|
- name: Build TinyGo
|
||||||
|
run: go install
|
||||||
|
- name: Check binary
|
||||||
|
run: tinygo version
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v3
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/tinygo-dev
|
tinygo/tinygo-dev
|
||||||
@@ -36,23 +36,24 @@ 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@v1
|
uses: docker/login-action@v2
|
||||||
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@v1
|
uses: docker/login-action@v2
|
||||||
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@v2
|
uses: docker/build-push-action@v3
|
||||||
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 }}
|
||||||
|
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-15
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
- name: Trigger Drivers repo build on Github Actions
|
- name: Trigger Drivers repo build on Github Actions
|
||||||
@@ -69,21 +70,31 @@ jobs:
|
|||||||
-H "Accept: application/vnd.github.v3+json" \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
|
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
|
||||||
-d '{"ref": "dev"}'
|
-d '{"ref": "dev"}'
|
||||||
- name: Trigger TinyFS repo build on CircleCI
|
- name: Trigger TinyFS repo build on Github Actions
|
||||||
run: |
|
run: |
|
||||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
|
curl -X POST \
|
||||||
--header 'Content-Type: application/json' \
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
-d '{"branch": "dev"}' \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
|
||||||
- name: Trigger TinyFont repo build on CircleCI
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger TinyFont repo build on Github Actions
|
||||||
run: |
|
run: |
|
||||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfont/pipeline' \
|
curl -X POST \
|
||||||
--header 'Content-Type: application/json' \
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
-d '{"branch": "dev"}' \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
|
||||||
- name: Trigger TinyDraw repo build on CircleCI
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger TinyDraw repo build on Github Actions
|
||||||
run: |
|
run: |
|
||||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinydraw/pipeline' \
|
curl -X POST \
|
||||||
--header 'Content-Type: application/json' \
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
-d '{"branch": "dev"}' \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
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"}'
|
||||||
|
|||||||
+135
-101
@@ -18,37 +18,32 @@ jobs:
|
|||||||
# statically linked binary.
|
# statically linked binary.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: alpine:3.16
|
image: golang:1.20-alpine
|
||||||
steps:
|
steps:
|
||||||
- name: Install apk dependencies
|
- name: Install apk dependencies
|
||||||
# tar: needed for actions/cache@v2
|
# tar: needed for actions/cache@v3
|
||||||
# git+openssh: needed for checkout (I think?)
|
# git+openssh: needed for checkout (I think?)
|
||||||
# gcompat: needed for go binary
|
|
||||||
# ruby: needed to install fpm
|
# ruby: needed to install fpm
|
||||||
run: apk add tar git openssh gcompat make g++ ruby
|
run: apk add tar git openssh make g++ ruby
|
||||||
- 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@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '1.18.1'
|
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
- name: Cache LLVM source
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-alpine-v1
|
key: llvm-source-15-linux-alpine-v3
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -58,11 +53,22 @@ jobs:
|
|||||||
- 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 LLVM build
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Restore LLVM build cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-linux-alpine-v1
|
key: llvm-build-15-linux-alpine-v4
|
||||||
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'
|
||||||
@@ -76,8 +82,14 @@ jobs:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
# 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
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-alpine-v1
|
key: binaryen-linux-alpine-v1
|
||||||
@@ -88,7 +100,7 @@ jobs:
|
|||||||
apk add cmake samurai python3
|
apk add cmake samurai python3
|
||||||
make binaryen STATIC=1
|
make binaryen STATIC=1
|
||||||
- name: Cache wasi-libc
|
- name: Cache wasi-libc
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-alpine-v1
|
key: wasi-libc-sysroot-linux-alpine-v1
|
||||||
@@ -98,6 +110,7 @@ jobs:
|
|||||||
run: make wasi-libc
|
run: make wasi-libc
|
||||||
- name: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
|
gem install --version 4.0.7 public_suffix
|
||||||
gem install --version 2.7.6 dotenv
|
gem install --version 2.7.6 dotenv
|
||||||
gem install --no-document fpm
|
gem install --no-document fpm
|
||||||
- name: Build TinyGo release
|
- name: Build TinyGo release
|
||||||
@@ -106,7 +119,7 @@ jobs:
|
|||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
path: |
|
path: |
|
||||||
@@ -118,17 +131,20 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.18.1'
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||||
|
curl https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
|
||||||
|
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
|
||||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||||
- name: Download release artifact
|
- name: Download release artifact
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
- name: Extract release tarball
|
- name: Extract release tarball
|
||||||
@@ -136,17 +152,6 @@ jobs:
|
|||||||
mkdir -p ~/lib
|
mkdir -p ~/lib
|
||||||
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
||||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||||
- name: Install apt dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get install --no-install-recommends \
|
|
||||||
gcc-avr \
|
|
||||||
avr-libc
|
|
||||||
- name: "Install Xtensa toolchain"
|
|
||||||
run: |
|
|
||||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
|
||||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
|
||||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
|
||||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
|
||||||
- run: make tinygo-test-wasi-fast
|
- run: make tinygo-test-wasi-fast
|
||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
assert-test-linux:
|
assert-test-linux:
|
||||||
@@ -155,7 +160,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
@@ -167,34 +172,28 @@ jobs:
|
|||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-system-riscv32 \
|
qemu-system-riscv32 \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
gcc-avr \
|
|
||||||
avr-libc \
|
|
||||||
simavr \
|
simavr \
|
||||||
ninja-build
|
ninja-build
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.18.1'
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '14'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||||
|
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
|
||||||
|
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
|
||||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||||
- name: Cache Go
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/restore@v3
|
||||||
with:
|
|
||||||
key: go-cache-linux-asserts-v1-${{ hashFiles('go.mod') }}
|
|
||||||
path: |
|
|
||||||
~/.cache/go-build
|
|
||||||
~/go/pkg/mod
|
|
||||||
- name: Cache LLVM source
|
|
||||||
uses: actions/cache@v2
|
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-asserts-v2
|
key: llvm-source-15-linux-asserts-v3
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -204,11 +203,22 @@ jobs:
|
|||||||
- 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 LLVM build
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Restore LLVM build cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-linux-asserts-v1
|
key: llvm-build-15-linux-asserts-v4
|
||||||
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'
|
||||||
@@ -220,8 +230,14 @@ jobs:
|
|||||||
make llvm-build ASSERT=1
|
make llvm-build ASSERT=1
|
||||||
# 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
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-asserts-v1
|
key: binaryen-linux-asserts-v1
|
||||||
@@ -230,7 +246,7 @@ jobs:
|
|||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
run: make binaryen
|
run: make binaryen
|
||||||
- name: Cache wasi-libc
|
- name: Cache wasi-libc
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-asserts-v5
|
key: wasi-libc-sysroot-linux-asserts-v5
|
||||||
@@ -247,12 +263,6 @@ 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
|
||||||
- name: Install Xtensa toolchain
|
|
||||||
run: |
|
|
||||||
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
|
||||||
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
|
||||||
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
|
||||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
|
||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
- run: make wasmtest
|
- run: make wasmtest
|
||||||
- run: make tinygo-baremetal
|
- run: make tinygo-baremetal
|
||||||
@@ -269,7 +279,7 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@@ -278,21 +288,15 @@ jobs:
|
|||||||
g++-arm-linux-gnueabihf \
|
g++-arm-linux-gnueabihf \
|
||||||
libc6-dev-armhf-cross
|
libc6-dev-armhf-cross
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.18.1'
|
go-version: '1.20'
|
||||||
- name: Cache Go
|
cache: true
|
||||||
uses: actions/cache@v2
|
- name: Restore LLVM source cache
|
||||||
with:
|
uses: actions/cache/restore@v3
|
||||||
key: go-cache-linux-arm-v2-${{ hashFiles('go.mod') }}
|
|
||||||
path: |
|
|
||||||
~/.cache/go-build
|
|
||||||
~/go/pkg/mod
|
|
||||||
- name: Cache LLVM source
|
|
||||||
uses: actions/cache@v2
|
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-v2
|
key: llvm-source-15-linux-v3
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -302,11 +306,22 @@ jobs:
|
|||||||
- 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 LLVM build
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Restore LLVM build cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-linux-arm-v1
|
key: llvm-build-15-linux-arm-v4
|
||||||
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'
|
||||||
@@ -320,8 +335,14 @@ jobs:
|
|||||||
make llvm-build CROSS=arm-linux-gnueabihf
|
make llvm-build CROSS=arm-linux-gnueabihf
|
||||||
# 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
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-arm-v1
|
key: binaryen-linux-arm-v1
|
||||||
@@ -334,13 +355,14 @@ jobs:
|
|||||||
make CROSS=arm-linux-gnueabihf binaryen
|
make CROSS=arm-linux-gnueabihf binaryen
|
||||||
- name: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
|
sudo gem install --version 4.0.7 public_suffix
|
||||||
sudo gem install --version 2.7.6 dotenv
|
sudo gem install --version 2.7.6 dotenv
|
||||||
sudo gem install --no-document fpm
|
sudo gem install --no-document fpm
|
||||||
- name: Build TinyGo binary
|
- name: Build TinyGo binary
|
||||||
run: |
|
run: |
|
||||||
make CROSS=arm-linux-gnueabihf
|
make CROSS=arm-linux-gnueabihf
|
||||||
- name: Download amd64 release
|
- name: Download amd64 release
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
- name: Extract amd64 release
|
- name: Extract amd64 release
|
||||||
@@ -357,7 +379,7 @@ jobs:
|
|||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_armhf.deb
|
cp -p build/release.deb /tmp/tinygo_armhf.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-arm-double-zipped
|
name: linux-arm-double-zipped
|
||||||
path: |
|
path: |
|
||||||
@@ -373,7 +395,7 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@@ -383,21 +405,15 @@ jobs:
|
|||||||
libc6-dev-arm64-cross \
|
libc6-dev-arm64-cross \
|
||||||
ninja-build
|
ninja-build
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.18.1'
|
go-version: '1.20'
|
||||||
- name: Cache Go
|
cache: true
|
||||||
uses: actions/cache@v2
|
- name: Restore LLVM source cache
|
||||||
with:
|
uses: actions/cache/restore@v3
|
||||||
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
|
|
||||||
path: |
|
|
||||||
~/.cache/go-build
|
|
||||||
~/go/pkg/mod
|
|
||||||
- name: Cache LLVM source
|
|
||||||
uses: actions/cache@v2
|
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-v1
|
key: llvm-source-15-linux-v3
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -407,11 +423,22 @@ jobs:
|
|||||||
- 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 LLVM build
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Restore LLVM build cache
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-linux-arm64-v1
|
key: llvm-build-15-linux-arm64-v4
|
||||||
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'
|
||||||
@@ -423,8 +450,14 @@ jobs:
|
|||||||
make llvm-build CROSS=aarch64-linux-gnu
|
make llvm-build CROSS=aarch64-linux-gnu
|
||||||
# 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
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-arm64-v1
|
key: binaryen-linux-arm64-v1
|
||||||
@@ -436,13 +469,14 @@ jobs:
|
|||||||
make CROSS=aarch64-linux-gnu binaryen
|
make CROSS=aarch64-linux-gnu binaryen
|
||||||
- name: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
|
sudo gem install --version 4.0.7 public_suffix
|
||||||
sudo gem install --version 2.7.6 dotenv
|
sudo gem install --version 2.7.6 dotenv
|
||||||
sudo gem install --no-document fpm
|
sudo gem install --no-document fpm
|
||||||
- name: Build TinyGo binary
|
- name: Build TinyGo binary
|
||||||
run: |
|
run: |
|
||||||
make CROSS=aarch64-linux-gnu
|
make CROSS=aarch64-linux-gnu
|
||||||
- name: Download amd64 release
|
- name: Download amd64 release
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
- name: Extract amd64 release
|
- name: Extract amd64 release
|
||||||
@@ -459,7 +493,7 @@ jobs:
|
|||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-arm64-double-zipped
|
name: linux-arm64-double-zipped
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
+133
-21
@@ -15,10 +15,12 @@ jobs:
|
|||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Configure pagefile
|
||||||
uses: actions/setup-go@v2
|
uses: al-cheb/configure-pagefile-action@v1.3
|
||||||
with:
|
with:
|
||||||
go-version: '1.18.1'
|
minimum-size: 8GB
|
||||||
|
maximum-size: 24GB
|
||||||
|
disk-root: "C:"
|
||||||
- uses: brechtm/setup-scoop@v2
|
- uses: brechtm/setup-scoop@v2
|
||||||
with:
|
with:
|
||||||
scoop_update: 'false'
|
scoop_update: 'false'
|
||||||
@@ -27,21 +29,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install ninja binaryen
|
scoop install ninja binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Cache Go
|
- name: Install Go
|
||||||
uses: actions/cache@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
|
go-version: '1.20'
|
||||||
path: |
|
cache: true
|
||||||
~/AppData/Local/go-build
|
- name: Restore cached LLVM source
|
||||||
~/go/pkg/mod
|
uses: actions/cache/restore@v3
|
||||||
- name: Cache LLVM source
|
|
||||||
uses: actions/cache@v2
|
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-windows-v2
|
key: llvm-source-15-windows-v4
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -51,11 +51,22 @@ jobs:
|
|||||||
- 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 LLVM build
|
- name: Save cached LLVM source
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Restore cached LLVM build
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-windows-v2
|
key: llvm-build-15-windows-v5
|
||||||
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'
|
||||||
@@ -68,8 +79,14 @@ jobs:
|
|||||||
make llvm-build CCACHE=OFF
|
make llvm-build CCACHE=OFF
|
||||||
# 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
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
|
path: llvm-build
|
||||||
- name: Cache wasi-libc sysroot
|
- name: Cache wasi-libc sysroot
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v4
|
key: wasi-libc-sysroot-v4
|
||||||
@@ -97,14 +114,109 @@ jobs:
|
|||||||
# - have a dobule-zipped artifact when downloaded from the UI
|
# - have a dobule-zipped artifact when downloaded from the UI
|
||||||
# - have a very slow artifact upload
|
# - have a very slow artifact upload
|
||||||
# We're doing the former here, to keep artifact uploads fast.
|
# We're doing the former here, to keep artifact uploads fast.
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: release-double-zipped
|
||||||
path: build/release/release.zip
|
path: build/release/release.zip
|
||||||
|
|
||||||
|
smoke-test-windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
needs: build-windows
|
||||||
|
steps:
|
||||||
|
- name: Configure pagefile
|
||||||
|
uses: al-cheb/configure-pagefile-action@v1.3
|
||||||
|
with:
|
||||||
|
minimum-size: 8GB
|
||||||
|
maximum-size: 24GB
|
||||||
|
disk-root: "C:"
|
||||||
|
- uses: brechtm/setup-scoop@v2
|
||||||
|
with:
|
||||||
|
scoop_update: 'false'
|
||||||
|
- name: Install Dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
scoop install binaryen
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
|
- name: Download TinyGo build
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: release-double-zipped
|
||||||
|
path: build/
|
||||||
|
- name: Unzip TinyGo build
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: 7z x release.zip -r
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
|
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||||
|
|
||||||
|
stdlib-test-windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
needs: build-windows
|
||||||
|
steps:
|
||||||
|
- name: Configure pagefile
|
||||||
|
uses: al-cheb/configure-pagefile-action@v1.3
|
||||||
|
with:
|
||||||
|
minimum-size: 8GB
|
||||||
|
maximum-size: 24GB
|
||||||
|
disk-root: "C:"
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
|
- name: Download TinyGo build
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: release-double-zipped
|
||||||
|
path: build/
|
||||||
|
- name: Unzip TinyGo build
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: 7z x release.zip -r
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
run: make tinygo-test
|
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||||
|
|
||||||
|
stdlib-wasi-test-windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
needs: build-windows
|
||||||
|
steps:
|
||||||
|
- name: Configure pagefile
|
||||||
|
uses: al-cheb/configure-pagefile-action@v1.3
|
||||||
|
with:
|
||||||
|
minimum-size: 8GB
|
||||||
|
maximum-size: 24GB
|
||||||
|
disk-root: "C:"
|
||||||
|
- uses: brechtm/setup-scoop@v2
|
||||||
|
with:
|
||||||
|
scoop_update: 'false'
|
||||||
|
- name: Install Dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
scoop install binaryen wasmtime
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.20'
|
||||||
|
cache: true
|
||||||
|
- name: Download TinyGo build
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: release-double-zipped
|
||||||
|
path: build/
|
||||||
|
- name: Unzip TinyGo build
|
||||||
|
shell: bash
|
||||||
|
working-directory: build
|
||||||
|
run: 7z x release.zip -r
|
||||||
- name: Test stdlib packages on wasi
|
- name: Test stdlib packages on wasi
|
||||||
run: make tinygo-test-wasi-fast
|
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
|
|||||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||||
|
|
||||||
* Go (1.16+)
|
* Go (1.18+)
|
||||||
* Standard build tools (gcc/clang)
|
* Standard build tools (gcc/clang)
|
||||||
* git
|
* git
|
||||||
* CMake
|
* CMake
|
||||||
|
|||||||
+236
@@ -1,3 +1,239 @@
|
|||||||
|
0.27.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **general**
|
||||||
|
- all: update musl
|
||||||
|
- all: remove "acm:"` prefix for USB vid/pid pair
|
||||||
|
- all: add support for LLVM 15
|
||||||
|
- all: use DWARF version 4
|
||||||
|
- all: add initial (incomplete) support for Go 1.20
|
||||||
|
- all: add `-gc=custom` option
|
||||||
|
- `main`: print ldflags including ThinLTO flags with -x
|
||||||
|
- `main`: fix error message when a serial port can't be accessed
|
||||||
|
- `main`: add `-timeout` flag to allow setting how long TinyGo will try looking for a MSD volume for flashing
|
||||||
|
- `test`: print PASS on pass when running standalone test binaries
|
||||||
|
- `test`: fix printing of benchmark output
|
||||||
|
- `test`: print package name when compilation failed (not just when the test failed)
|
||||||
|
* **compiler**
|
||||||
|
- refactor to support LLVM 15
|
||||||
|
- `builder`: print compiler commands while building a library
|
||||||
|
- `compiler`: fix stack overflow when creating recursive pointer types (fix for LLVM 15+ only)
|
||||||
|
- `compiler`: allow map keys and values of ≥256 bytes
|
||||||
|
- `cgo`: add support for `C.float` and `C.double`
|
||||||
|
- `cgo`: support anonymous enums included in multiple Go files
|
||||||
|
- `cgo`: add support for bitwise operators
|
||||||
|
- `interp`: add support for constant icmp instructions
|
||||||
|
- `transform`: fix memory corruption issues
|
||||||
|
* **standard library**
|
||||||
|
- `machine/usb`: remove allocs in USB ISR
|
||||||
|
- `machine/usb`: add `Port()` and deprecate `New()` to have the API better match the singleton that is actually being returned
|
||||||
|
- `machine/usb`: change HID usage-maximum to 0xFF
|
||||||
|
- `machine/usb`: add USB HID joystick support
|
||||||
|
- `machine/usb`: change to not send before endpoint initialization
|
||||||
|
- `net`: implement `Pipe`
|
||||||
|
- `os`: add stub for `os.Chtimes`
|
||||||
|
- `reflect`: stub out `Type.FieldByIndex`
|
||||||
|
- `reflect`: add `Value.IsZero` method
|
||||||
|
- `reflect`: fix bug in `.Field` method when the field fits in a pointer but the parent doesn't
|
||||||
|
- `runtime`: switch some `panic()` calls in the gc to `runtimePanic()` for consistency
|
||||||
|
- `runtime`: add xorshift-based fastrand64
|
||||||
|
- `runtime`: fix alignment for arm64, arm, xtensa, riscv
|
||||||
|
- `runtime`: implement precise GC
|
||||||
|
- `runtime/debug`: stub `PrintStack`
|
||||||
|
- `sync`: implement simple pooling in `sync.Pool`
|
||||||
|
- `syscall`: stubbed `Setuid`, Exec and friends
|
||||||
|
- `syscall`: add more stubs as needed for Go 1.20 support
|
||||||
|
- `testing`: implement `t.Setenv`
|
||||||
|
- `unsafe`: add support for Go 1.20 slice/string functions
|
||||||
|
* **targets**
|
||||||
|
- `all`: do not set stack size per board
|
||||||
|
- `all`: update picolibc to v1.7.9
|
||||||
|
- `atsame5x`: fix CAN extendedID handling
|
||||||
|
- `atsame5x`: reduce heap allocation
|
||||||
|
- `avr`: drop GNU toolchain dependency
|
||||||
|
- `avr`: fix .data initialization for binaries over 64kB
|
||||||
|
- `avr`: support ThinLTO
|
||||||
|
- `baremetal`: implements calloc
|
||||||
|
- `darwin`: fix `syscall.Open` on darwin/arm64
|
||||||
|
- `darwin`: fix error with `tinygo lldb`
|
||||||
|
- `esp`: use LLVM Xtensa linker instead of Espressif toolchain
|
||||||
|
- `esp`: use ThinLTO for Xtensa
|
||||||
|
- `esp32c3`: add SPI support
|
||||||
|
- `linux`: include musl `getpagesize` function in release
|
||||||
|
- `nrf51`: add ADC implementation
|
||||||
|
- `nrf52840`: add PDM support
|
||||||
|
- `riscv`: add "target-abi" metadata flag
|
||||||
|
- `rp2040`: remove mem allocation in GPIO ISR
|
||||||
|
- `rp2040`: avoid allocating clock on heap
|
||||||
|
- `rp2040`: add basic GPIO support for PIO
|
||||||
|
- `rp2040`: fix USB interrupt issue
|
||||||
|
- `rp2040`: fix RP2040-E5 USB errata
|
||||||
|
- `stm32`: always set ADC pins to pullups floating
|
||||||
|
- `stm32f1`, `stm32f4`: fix ADC by clearing the correct bit for rank after each read
|
||||||
|
- `stm32wl`: Fix incomplete RNG initialisation
|
||||||
|
- `stm32wlx`: change order for init so clock speeds are set before peripheral start
|
||||||
|
- `wasi`: makes wasmtime "run" explicit
|
||||||
|
- `wasm`: fix GC scanning of allocas
|
||||||
|
- `wasm`: allow custom malloc implementation
|
||||||
|
- `wasm`: remove `-wasm-abi=` flag (use `-target` instead)
|
||||||
|
- `wasm`: fix scanning of the stack
|
||||||
|
- `wasm`: fix panic when allocating 0 bytes using malloc
|
||||||
|
- `wasm`: always run wasm-opt even with `-scheduler=none`
|
||||||
|
- `wasm`: avoid miscompile with ThinLTO
|
||||||
|
- `wasm`: allow the emulator to expand `{tmpDir}`
|
||||||
|
- `wasm`: support ThinLTO
|
||||||
|
- `windows`: update mingw-w64 version to avoid linker warning
|
||||||
|
- `windows`: add ARM64 support
|
||||||
|
* **boards**
|
||||||
|
- Add Waveshare RP2040 Zero
|
||||||
|
- Add Arduino Leonardo support
|
||||||
|
- Add Adafruit KB2040
|
||||||
|
- Add Adafruit Feather M0 Express
|
||||||
|
- Add Makerfabs ESP32C3SPI35 TFT Touchscreen board
|
||||||
|
- Add Espressif ESP32-C3-DevKit-RUST-1 board
|
||||||
|
- `lgt92`: fix OpenOCD configuration
|
||||||
|
- `xiao-rp2040`: fix D9 and D10 constants
|
||||||
|
- `xiao-rp2040`: add pin definitions
|
||||||
|
|
||||||
|
0.26.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **general**
|
||||||
|
- remove support for LLVM 13
|
||||||
|
- remove calls to deprecated ioutil package
|
||||||
|
- move from `os.IsFoo` to `errors.Is(err, ErrFoo)`
|
||||||
|
- fix for builds using an Android host
|
||||||
|
- make interp timeout configurable from command line
|
||||||
|
- ignore ports with VID/PID if there is no candidates
|
||||||
|
- drop support for Go 1.16 and Go 1.17
|
||||||
|
- update serial package to v1.3.5 for latest bugfixes
|
||||||
|
- remove GOARM from `tinygo info`
|
||||||
|
- add flag for setting the goroutine stack size
|
||||||
|
- add serial port monitoring functionality
|
||||||
|
* **compiler**
|
||||||
|
- `cgo`: implement support for static functions
|
||||||
|
- `cgo`: fix panic when FuncType.Results is nil
|
||||||
|
- `compiler`: add aliases for `edwards25519/field.feMul` and `field.feSquare`
|
||||||
|
- `compiler`: fix incorrect DWARF type in some generic parameters
|
||||||
|
- `compiler`: use LLVM math builtins everywhere
|
||||||
|
- `compiler`: replace some math operation bodies with LLVM intrinsics
|
||||||
|
- `compiler`: replace math aliases with intrinsics
|
||||||
|
- `compiler`: fix `unsafe.Sizeof` for chan and map values
|
||||||
|
- `compileopts`: use tags parser from buildutil
|
||||||
|
- `compileopts`: use backticks for regexp to avoid extra escapes
|
||||||
|
- `compileopts`: fail fast on duplicate values in target field slices
|
||||||
|
- `compileopts`: fix windows/arm target triple
|
||||||
|
- `compileopts`: improve error handling when loading target/*.json
|
||||||
|
- `compileopts`: add support for stlink-dap programmer
|
||||||
|
- `compileopts`: do not complain about `-no-debug` on MacOS
|
||||||
|
- `goenv`: support `GOOS=android`
|
||||||
|
- `interp`: fix reading from external global
|
||||||
|
- `loader`: fix link error for `crypto/internal/boring/sig.StandardCrypto`
|
||||||
|
* **standard library**
|
||||||
|
- rename assembly files to .S extension
|
||||||
|
- `machine`: add PWM peripheral comments to pins
|
||||||
|
- `machine`: improve UARTParity slightly
|
||||||
|
- `machine`: do not export DFU_MAGIC_* constants on nrf52840
|
||||||
|
- `machine`: rename `PinInputPullUp`/`PinInputPullDown`
|
||||||
|
- `machine`: add `KHz`, `MHz`, `GHz` constants, deprecate `TWI_FREQ_*` constants
|
||||||
|
- `machine`: remove level triggered pin interrupts
|
||||||
|
- `machine`: do not expose `RESET_MAGIC_VALUE`
|
||||||
|
- `machine`: use `NoPin` constant where appropriate (instead of `0` for example)
|
||||||
|
- `net`: sync net.go with Go 1.18 stdlib
|
||||||
|
- `os`: add `SyscallError.Timeout`
|
||||||
|
- `os`: add `ErrProcessDone` error
|
||||||
|
- `reflect`: implement `CanInterface` and fix string `Index`
|
||||||
|
- `runtime`: make `MemStats` available to leaking collector
|
||||||
|
- `runtime`: add `MemStats.TotalAlloc`
|
||||||
|
- `runtime`: add `MemStats.Mallocs` and `Frees`
|
||||||
|
- `runtime`: add support for `time.NewTimer` and `time.NewTicker`
|
||||||
|
- `runtime`: implement `resetTimer`
|
||||||
|
- `runtime`: ensure some headroom for the GC to run
|
||||||
|
- `runtime`: make gc and scheduler asserts settable with build tags
|
||||||
|
- `runtime/pprof`: add `WriteHeapProfile`
|
||||||
|
- `runtime/pprof`: `runtime/trace`: stub some additional functions
|
||||||
|
- `sync`: implement `Map.LoadAndDelete`
|
||||||
|
- `syscall`: group WASI consts by purpose
|
||||||
|
- `syscall`: add WASI `{D,R}SYNC`, `NONBLOCK` FD flags
|
||||||
|
- `syscall`: add ENOTCONN on darwin
|
||||||
|
- `testing`: add support for -benchmem
|
||||||
|
* **targets**
|
||||||
|
- remove USB vid/pid pair of bootloader
|
||||||
|
- `esp32c3`: remove unused `UARTStopBits` constants
|
||||||
|
- `nrf`: implement `GetRNG` function
|
||||||
|
- `nrf`: `rp2040`: add `machine.ReadTemperature`
|
||||||
|
- `nrf52`: cleanup s140v6 and s140v7 uf2 targets
|
||||||
|
- `rp2040`: implement semi-random RNG based on ROSC based on pico-sdk
|
||||||
|
- `wasm`: add summary of wasm examples and fix callback bug
|
||||||
|
- `wasm`: do not allow undefined symbols (`--allow-undefined`)
|
||||||
|
- `wasm`: make sure buffers returned by `malloc` are kept until `free` is called
|
||||||
|
- `windows`: save and restore xmm registers when switching goroutines
|
||||||
|
* **boards**
|
||||||
|
- add Pimoroni's Tufty2040
|
||||||
|
- add XIAO ESP32C3
|
||||||
|
- add Adafruit QT2040
|
||||||
|
- add Adafruit QT Py RP2040
|
||||||
|
- `esp32c3-12f`: `matrixportal-m4`: `p1am-100`: remove duplicate build tags
|
||||||
|
- `hifive1-qemu`: remove this emulated board
|
||||||
|
- `wioterminal`: add UART3 for RTL8720DN
|
||||||
|
- `xiao-ble`: fix usbpid
|
||||||
|
|
||||||
|
|
||||||
|
0.25.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **command line**
|
||||||
|
- change to ignore PortReset failures
|
||||||
|
* **compiler**
|
||||||
|
- `compiler`: darwin/arm64 is aarch64, not arm
|
||||||
|
- `compiler`: don't clobber X18 and FP registers on darwin/arm64
|
||||||
|
- `compiler`: fix issue with methods on generic structs
|
||||||
|
- `compiler`: do not try to build generic functions
|
||||||
|
- `compiler`: fix type names for generic named structs
|
||||||
|
- `compiler`: fix multiple defined function issue for generic functions
|
||||||
|
- `compiler`: implement `unsafe.Alignof` and `unsafe.Sizeof` for generic code
|
||||||
|
* **standard library**
|
||||||
|
- `machine`: add DTR and RTS to Serialer interface
|
||||||
|
- `machine`: reorder pin definitions to improve pin list on tinygo.org
|
||||||
|
- `machine/usb`: add support for MIDI
|
||||||
|
- `machine/usb`: adjust buffer alignment (samd21, samd51, nrf52840)
|
||||||
|
- `machine/usb/midi`: add `NoteOn`, `NoteOff`, and `SendCC` methods
|
||||||
|
- `machine/usb/midi`: add definition of MIDI note number
|
||||||
|
- `runtime`: add benchmarks for memhash
|
||||||
|
- `runtime`: add support for printing slices via print/println
|
||||||
|
* **targets**
|
||||||
|
- `avr`: fix some apparent mistake in atmega1280/atmega2560 pin constants
|
||||||
|
- `esp32`: provide hardware pin constants
|
||||||
|
- `esp32`: fix WDT reset on the MCH2022 badge
|
||||||
|
- `esp32`: optimize SPI transmit
|
||||||
|
- `esp32c3`: provide hardware pin constants
|
||||||
|
- `esp8266`: provide hardware pin constants like `GPIO2`
|
||||||
|
- `nrf51`: define and use `P0_xx` constants
|
||||||
|
- `nrf52840`, `samd21`, `samd51`: unify bootloader entry process
|
||||||
|
- `nrf52840`, `samd21`, `samd51`: change usbSetup and sendZlp to public
|
||||||
|
- `nrf52840`, `samd21`, `samd51`: refactor handleStandardSetup and initEndpoint
|
||||||
|
- `nrf52840`, `samd21`, `samd51`: improve usb-device initialization
|
||||||
|
- `nrf52840`, `samd21`, `samd51`: move usbcdc to machine/usb/cdc
|
||||||
|
- `rp2040`: add usb serial vendor/product ID
|
||||||
|
- `rp2040`: add support for usb
|
||||||
|
- `rp2040`: change default for serial to usb
|
||||||
|
- `rp2040`: add support for `machine.EnterBootloader`
|
||||||
|
- `rp2040`: turn off pullup/down when input type is not specified
|
||||||
|
- `rp2040`: make picoprobe default openocd interface
|
||||||
|
- `samd51`: add support for `DAC1`
|
||||||
|
- `samd51`: improve TRNG
|
||||||
|
- `wasm`: stub `runtime.buffered`, `runtime.getchar`
|
||||||
|
- `wasi`: make leveldb runtime hash the default
|
||||||
|
* **boards**
|
||||||
|
- add Challenger RP2040 LoRa
|
||||||
|
- add MCH2022 badge
|
||||||
|
- add XIAO RP2040
|
||||||
|
- `clue`: remove pins `D21`..`D28`
|
||||||
|
- `feather-rp2040`, `macropad-rp2040`: fix qspi-flash settings
|
||||||
|
- `xiao-ble`: add support for flash-1200-bps-reset
|
||||||
|
- `gopherbot`, `gopherbot2`: add these aliases to simplify for newer users
|
||||||
|
|
||||||
|
|
||||||
0.24.0
|
0.24.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+3
-13
@@ -1,8 +1,8 @@
|
|||||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||||
FROM golang:1.18 AS tinygo-llvm
|
FROM golang:1.20 AS tinygo-llvm
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
|
apt-get install -y apt-utils make cmake clang-11 ninja-build
|
||||||
|
|
||||||
COPY ./Makefile /tinygo/Makefile
|
COPY ./Makefile /tinygo/Makefile
|
||||||
|
|
||||||
@@ -15,18 +15,8 @@ FROM tinygo-llvm AS tinygo-llvm-build
|
|||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
make llvm-build
|
make llvm-build
|
||||||
|
|
||||||
# tinygo-xtensa stage installs tools needed for ESP32
|
|
||||||
FROM tinygo-llvm-build AS tinygo-xtensa
|
|
||||||
|
|
||||||
ARG xtensa_version="1.22.0-80-g6c4433a-5.2.0"
|
|
||||||
RUN cd /tmp/ && \
|
|
||||||
wget -q https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-${xtensa_version}.tar.gz && \
|
|
||||||
tar xzf xtensa-esp32-elf-linux64-${xtensa_version}.tar.gz && \
|
|
||||||
cp ./xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/ && \
|
|
||||||
rm -rf /tmp/xtensa*
|
|
||||||
|
|
||||||
# tinygo-compiler stage builds the compiler itself
|
# tinygo-compiler stage builds the compiler itself
|
||||||
FROM tinygo-xtensa AS tinygo-compiler
|
FROM tinygo-llvm-build AS tinygo-compiler
|
||||||
|
|
||||||
COPY . /tinygo
|
COPY . /tinygo
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
|||||||
|
|
||||||
# Try to autodetect LLVM build tools.
|
# Try to autodetect LLVM build tools.
|
||||||
# Versions are listed here in descending priority order.
|
# Versions are listed here in descending priority order.
|
||||||
LLVM_VERSIONS = 14 13 12 11
|
LLVM_VERSIONS = 15 14 13 12 11
|
||||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||||
toolSearchPathsVersion = $(1)-$(2)
|
toolSearchPathsVersion = $(1)-$(2)
|
||||||
@@ -56,6 +56,12 @@ else
|
|||||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Enable AddressSanitizer
|
||||||
|
ifeq (1, $(ASAN))
|
||||||
|
LLVM_OPTION += -DLLVM_USE_SANITIZER=Address
|
||||||
|
CGO_LDFLAGS += -fsanitize=address
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq (1, $(STATIC))
|
ifeq (1, $(STATIC))
|
||||||
# Build TinyGo as a fully statically linked binary (no dynamically loaded
|
# Build TinyGo as a fully statically linked binary (no dynamically loaded
|
||||||
# libraries such as a libc). This is not supported with glibc which is used
|
# libraries such as a libc). This is not supported with glibc which is used
|
||||||
@@ -107,7 +113,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||||
|
|
||||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
@@ -142,7 +148,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# Libraries that should be linked in for the statically linked Clang.
|
# Libraries that should be linked in for the statically linked Clang.
|
||||||
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||||
|
|
||||||
# Libraries that should be linked in for the statically linked LLD.
|
# Libraries that should be linked in for the statically linked LLD.
|
||||||
@@ -230,13 +236,13 @@ gen-device-rp: build/gen-device-svd
|
|||||||
|
|
||||||
# Get LLVM sources.
|
# Get LLVM sources.
|
||||||
$(LLVM_PROJECTDIR)/llvm:
|
$(LLVM_PROJECTDIR)/llvm:
|
||||||
git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||||
$(LLVM_BUILDDIR)/build.ninja:
|
$(LLVM_BUILDDIR)/build.ninja:
|
||||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||||
@@ -257,7 +263,7 @@ endif
|
|||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
cd lib/wasi-libc && make -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||||
|
|
||||||
|
|
||||||
# Build the Go compiler.
|
# Build the Go compiler.
|
||||||
@@ -280,8 +286,6 @@ TEST_PACKAGES_FAST = \
|
|||||||
container/list \
|
container/list \
|
||||||
container/ring \
|
container/ring \
|
||||||
crypto/des \
|
crypto/des \
|
||||||
crypto/elliptic/internal/fiat \
|
|
||||||
crypto/internal/subtle \
|
|
||||||
crypto/md5 \
|
crypto/md5 \
|
||||||
crypto/rc4 \
|
crypto/rc4 \
|
||||||
crypto/sha1 \
|
crypto/sha1 \
|
||||||
@@ -292,6 +296,7 @@ TEST_PACKAGES_FAST = \
|
|||||||
encoding \
|
encoding \
|
||||||
encoding/ascii85 \
|
encoding/ascii85 \
|
||||||
encoding/base32 \
|
encoding/base32 \
|
||||||
|
encoding/base64 \
|
||||||
encoding/csv \
|
encoding/csv \
|
||||||
encoding/hex \
|
encoding/hex \
|
||||||
go/scanner \
|
go/scanner \
|
||||||
@@ -317,32 +322,55 @@ TEST_PACKAGES_FAST = \
|
|||||||
unicode \
|
unicode \
|
||||||
unicode/utf16 \
|
unicode/utf16 \
|
||||||
unicode/utf8 \
|
unicode/utf8 \
|
||||||
|
$(nil)
|
||||||
|
|
||||||
|
# Assume this will go away before Go2, so only check minor version.
|
||||||
|
ifeq ($(filter $(shell $(GO) env GOVERSION | cut -f 2 -d.), 16 17 18), )
|
||||||
|
TEST_PACKAGES_FAST += crypto/internal/nistec/fiat
|
||||||
|
else
|
||||||
|
TEST_PACKAGES_FAST += crypto/elliptic/internal/fiat
|
||||||
|
endif
|
||||||
|
|
||||||
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
||||||
|
# bytes requires mmap
|
||||||
|
# compress/flate appears to hang on wasi
|
||||||
|
# compress/lzw appears to hang on wasi
|
||||||
|
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
|
||||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||||
# io/fs requires os.ReadDir, which is not yet supported on windows or wasi
|
# image requires recover(), which is not yet supported on wasi
|
||||||
|
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||||
|
# mime/quotedprintable requires syscall.Faccessat
|
||||||
|
# strconv requires recover() which is not yet supported on wasi
|
||||||
|
# text/tabwriter requries recover(), which is not yet supported on wasi
|
||||||
|
# text/template/parse requires recover(), which is not yet supported on wasi
|
||||||
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
|
||||||
# compress/flate fails windows go 1.18, https://github.com/tinygo-org/tinygo/issues/2762
|
|
||||||
# compress/lzw fails windows go 1.18 wasi, https://github.com/tinygo-org/tinygo/issues/2762
|
|
||||||
|
|
||||||
# Additional standard library packages that pass tests on individual platforms
|
# Additional standard library packages that pass tests on individual platforms
|
||||||
TEST_PACKAGES_LINUX := \
|
TEST_PACKAGES_LINUX := \
|
||||||
archive/zip \
|
archive/zip \
|
||||||
|
bytes \
|
||||||
compress/flate \
|
compress/flate \
|
||||||
compress/lzw \
|
compress/lzw \
|
||||||
crypto/hmac \
|
crypto/hmac \
|
||||||
debug/dwarf \
|
debug/dwarf \
|
||||||
debug/plan9obj \
|
debug/plan9obj \
|
||||||
io/fs \
|
image \
|
||||||
io/ioutil \
|
io/ioutil \
|
||||||
|
mime/quotedprintable \
|
||||||
strconv \
|
strconv \
|
||||||
testing/fstest \
|
testing/fstest \
|
||||||
|
text/tabwriter \
|
||||||
text/template/parse
|
text/template/parse
|
||||||
|
|
||||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||||
|
|
||||||
TEST_PACKAGES_WINDOWS := \
|
TEST_PACKAGES_WINDOWS := \
|
||||||
compress/lzw
|
compress/flate \
|
||||||
|
compress/lzw \
|
||||||
|
crypto/hmac \
|
||||||
|
strconv \
|
||||||
|
text/template/parse \
|
||||||
|
$(nil)
|
||||||
|
|
||||||
# Report platforms on which each standard library package is known to pass tests
|
# Report platforms on which each standard library package is known to pass tests
|
||||||
jointmp := $(shell echo /tmp/join.$$$$)
|
jointmp := $(shell echo /tmp/join.$$$$)
|
||||||
@@ -357,12 +385,15 @@ report-stdlib-tests-pass:
|
|||||||
# Standard library packages that pass tests quickly on the current platform
|
# Standard library packages that pass tests quickly on the current platform
|
||||||
ifeq ($(shell uname),Darwin)
|
ifeq ($(shell uname),Darwin)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||||
|
TEST_IOFS := true
|
||||||
endif
|
endif
|
||||||
ifeq ($(shell uname),Linux)
|
ifeq ($(shell uname),Linux)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||||
|
TEST_IOFS := true
|
||||||
endif
|
endif
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||||
|
TEST_IOFS := false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
@@ -370,6 +401,12 @@ endif
|
|||||||
.PHONY: tinygo-test
|
.PHONY: tinygo-test
|
||||||
tinygo-test:
|
tinygo-test:
|
||||||
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||||
|
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||||
|
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||||
|
@# For more details, see the comments on issue #3143.
|
||||||
|
ifeq ($(TEST_IOFS),true)
|
||||||
|
$(TINYGO) test -stack-size=6MB io/fs
|
||||||
|
endif
|
||||||
tinygo-test-fast:
|
tinygo-test-fast:
|
||||||
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
||||||
tinygo-bench:
|
tinygo-bench:
|
||||||
@@ -379,9 +416,9 @@ tinygo-bench-fast:
|
|||||||
|
|
||||||
# Same thing, except for wasi rather than the current platform.
|
# Same thing, except for wasi rather than the current platform.
|
||||||
tinygo-test-wasi:
|
tinygo-test-wasi:
|
||||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||||
tinygo-test-wasi-fast:
|
tinygo-test-wasi-fast:
|
||||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST)
|
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||||
tinygo-bench-wasi:
|
tinygo-bench-wasi:
|
||||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||||
tinygo-bench-wasi-fast:
|
tinygo-bench-wasi-fast:
|
||||||
@@ -403,6 +440,7 @@ tinygo-baremetal:
|
|||||||
.PHONY: smoketest
|
.PHONY: smoketest
|
||||||
smoketest:
|
smoketest:
|
||||||
$(TINYGO) version
|
$(TINYGO) version
|
||||||
|
$(TINYGO) targets > /dev/null
|
||||||
# regression test for #2892
|
# regression test for #2892
|
||||||
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||||
# compile-only platform-independent examples
|
# compile-only platform-independent examples
|
||||||
@@ -512,6 +550,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=matrixportal-m4 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||||
@@ -578,16 +618,26 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=kb2040 examples/echo
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=tufty2040 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-zero examples/echo
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
# test pwm
|
# test pwm
|
||||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -636,11 +686,12 @@ ifneq ($(STM32), 0)
|
|||||||
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
endif
|
||||||
ifneq ($(AVR), 0)
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||||
@@ -655,7 +706,6 @@ ifneq ($(AVR), 0)
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
|
||||||
ifneq ($(XTENSA), 0)
|
ifneq ($(XTENSA), 0)
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
@@ -668,14 +718,16 @@ ifneq ($(XTENSA), 0)
|
|||||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
endif
|
endif
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
|
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/serial
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
@@ -695,6 +747,7 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||||
|
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
@@ -746,8 +799,10 @@ endif
|
|||||||
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
||||||
@@ -766,6 +821,7 @@ endif
|
|||||||
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
|
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||||
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||||
|
|||||||
@@ -43,12 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
|||||||
|
|
||||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||||
|
|
||||||
The following 88 microcontroller boards are currently supported:
|
The following 94 microcontroller boards are currently supported:
|
||||||
|
|
||||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||||
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
||||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||||
|
* [Adafruit Feather M0 Express](https://www.adafruit.com/product/3403)
|
||||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||||
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
||||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||||
@@ -59,6 +60,7 @@ The following 88 microcontroller boards are currently supported:
|
|||||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||||
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||||
|
* [Adafruit KB2040](https://www.adafruit.com/product/5302)
|
||||||
* [Adafruit MacroPad RP2040](https://www.adafruit.com/product/5100)
|
* [Adafruit MacroPad RP2040](https://www.adafruit.com/product/5100)
|
||||||
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
|
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
|
||||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||||
@@ -66,7 +68,9 @@ The following 88 microcontroller boards are currently supported:
|
|||||||
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
||||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||||
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
||||||
|
* [Adafruit QT Py RP2040](https://www.adafruit.com/product/4900)
|
||||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||||
|
* [Adafruit Trinkey QT2040](https://adafruit.com/product/5056)
|
||||||
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
||||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||||
@@ -108,6 +112,7 @@ The following 88 microcontroller boards are currently supported:
|
|||||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||||
* [Pimoroni Badger2040](https://shop.pimoroni.com/products/badger-2040)
|
* [Pimoroni Badger2040](https://shop.pimoroni.com/products/badger-2040)
|
||||||
|
* [Pimoroni Tufty2040](https://shop.pimoroni.com/products/tufty-2040)
|
||||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||||
@@ -117,11 +122,12 @@ The following 88 microcontroller boards are currently supported:
|
|||||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||||
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
||||||
|
* [Seeed XIAO ESP32C3](https://www.seeedstudio.com/Seeed-XIAO-ESP32C3-p-5431.html)
|
||||||
* [Seeed XIAO RP2040](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html)
|
* [Seeed XIAO RP2040](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html)
|
||||||
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
||||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||||
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
|
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1-rev-b)
|
||||||
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
|
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
|
||||||
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||||
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||||
@@ -132,8 +138,10 @@ The following 88 microcontroller boards are currently supported:
|
|||||||
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
||||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||||
* [ST Micro STM32F469 "Discovery"](https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html)
|
* [ST Micro STM32F469 "Discovery"](https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html)
|
||||||
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
|
|
||||||
* [The Things Industries Generic Node Sensor Edition](https://www.genericnode.com/docs/sensor-edition/)
|
* [The Things Industries Generic Node Sensor Edition](https://www.genericnode.com/docs/sensor-edition/)
|
||||||
|
* [Waveshare RP2040-Zero](https://www.waveshare.com/wiki/RP2040-Zero)
|
||||||
|
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
|
||||||
|
|
||||||
|
|
||||||
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
|
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import (
|
|||||||
// makeArchive creates an arcive for static linking from a list of object files
|
// makeArchive creates an arcive for static linking from a list of object files
|
||||||
// given as a parameter. It is equivalent to the following command:
|
// given as a parameter. It is equivalent to the following command:
|
||||||
//
|
//
|
||||||
// ar -rcs <archivePath> <objs...>
|
// ar -rcs <archivePath> <objs...>
|
||||||
func makeArchive(arfile *os.File, objs []string) error {
|
func makeArchive(arfile *os.File, objs []string) error {
|
||||||
// Open the archive file.
|
// Open the archive file.
|
||||||
arwriter := ar.NewWriter(arfile)
|
arwriter := ar.NewWriter(arfile)
|
||||||
|
|||||||
+126
-140
@@ -14,7 +14,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io/ioutil"
|
"io/fs"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -42,8 +42,8 @@ type BuildResult struct {
|
|||||||
// information. Used for GDB for example.
|
// information. Used for GDB for example.
|
||||||
Executable string
|
Executable string
|
||||||
|
|
||||||
// A path to the output binary. It will be removed after Build returns, so
|
// A path to the output binary. It is stored in the tmpdir directory of the
|
||||||
// if it should be kept it must be copied or moved away.
|
// Build function, so if it should be kept it must be copied or moved away.
|
||||||
// It is often the same as Executable, but differs if the output format is
|
// It is often the same as Executable, but differs if the output format is
|
||||||
// .hex for example (instead of the usual ELF).
|
// .hex for example (instead of the usual ELF).
|
||||||
Binary string
|
Binary string
|
||||||
@@ -94,23 +94,16 @@ type packageAction struct {
|
|||||||
//
|
//
|
||||||
// The error value may be of type *MultiError. Callers will likely want to check
|
// The error value may be of type *MultiError. Callers will likely want to check
|
||||||
// for this case and print such errors individually.
|
// for this case and print such errors individually.
|
||||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildResult, error) {
|
||||||
// Read the build ID of the tinygo binary.
|
// Read the build ID of the tinygo binary.
|
||||||
// Used as a cache key for package builds.
|
// Used as a cache key for package builds.
|
||||||
compilerBuildID, err := ReadBuildID()
|
compilerBuildID, err := ReadBuildID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary directory for intermediary files.
|
|
||||||
dir, err := ioutil.TempDir("", "tinygo")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if config.Options.Work {
|
if config.Options.Work {
|
||||||
fmt.Printf("WORK=%s\n", dir)
|
fmt.Printf("WORK=%s\n", tmpdir)
|
||||||
} else {
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up the build cache directory, which is used to speed up incremental
|
// Look up the build cache directory, which is used to speed up incremental
|
||||||
@@ -119,7 +112,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if cacheDir == "off" {
|
if cacheDir == "off" {
|
||||||
// Use temporary build directory instead, effectively disabling the
|
// Use temporary build directory instead, effectively disabling the
|
||||||
// build cache.
|
// build cache.
|
||||||
cacheDir = dir
|
cacheDir = tmpdir
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a libc dependency.
|
// Check for a libc dependency.
|
||||||
@@ -129,40 +122,40 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
var libcDependencies []*compileJob
|
var libcDependencies []*compileJob
|
||||||
switch config.Target.Libc {
|
switch config.Target.Libc {
|
||||||
case "darwin-libSystem":
|
case "darwin-libSystem":
|
||||||
job := makeDarwinLibSystemJob(config, dir)
|
job := makeDarwinLibSystemJob(config, tmpdir)
|
||||||
libcDependencies = append(libcDependencies, job)
|
libcDependencies = append(libcDependencies, job)
|
||||||
case "musl":
|
case "musl":
|
||||||
job, unlock, err := Musl.load(config, dir)
|
job, unlock, err := Musl.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
|
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
|
||||||
libcDependencies = append(libcDependencies, job)
|
libcDependencies = append(libcDependencies, job)
|
||||||
case "picolibc":
|
case "picolibc":
|
||||||
libcJob, unlock, err := Picolibc.load(config, dir)
|
libcJob, unlock, err := Picolibc.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
libcDependencies = append(libcDependencies, libcJob)
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
|
||||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||||
}
|
}
|
||||||
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
||||||
case "mingw-w64":
|
case "mingw-w64":
|
||||||
_, unlock, err := MinGW.load(config, dir)
|
_, unlock, err := MinGW.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
unlock()
|
unlock()
|
||||||
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...)
|
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
|
||||||
case "":
|
case "":
|
||||||
// no library specified, so nothing to do
|
// no library specified, so nothing to do
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||||
}
|
}
|
||||||
|
|
||||||
optLevel, sizeLevel, _ := config.OptLevels()
|
optLevel, sizeLevel, _ := config.OptLevels()
|
||||||
@@ -170,6 +163,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
CPU: config.CPU(),
|
CPU: config.CPU(),
|
||||||
Features: config.Features(),
|
Features: config.Features(),
|
||||||
|
ABI: config.ABI(),
|
||||||
GOOS: config.GOOS(),
|
GOOS: config.GOOS(),
|
||||||
GOARCH: config.GOARCH(),
|
GOARCH: config.GOARCH(),
|
||||||
CodeModel: config.CodeModel(),
|
CodeModel: config.CodeModel(),
|
||||||
@@ -178,7 +172,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
|
|
||||||
Scheduler: config.Scheduler(),
|
Scheduler: config.Scheduler(),
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.Target.DefaultStackSize,
|
DefaultStackSize: config.StackSize(),
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: true,
|
Debug: true,
|
||||||
}
|
}
|
||||||
@@ -188,7 +182,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// address spaces, etc).
|
// address spaces, etc).
|
||||||
machine, err := compiler.NewTargetMachine(compilerConfig)
|
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
defer machine.Dispose()
|
defer machine.Dispose()
|
||||||
|
|
||||||
@@ -196,12 +190,21 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||||
Sizes: compiler.Sizes(machine),
|
Sizes: compiler.Sizes(machine),
|
||||||
})
|
})
|
||||||
if err != nil {
|
result := BuildResult{
|
||||||
return err
|
ModuleRoot: lprogram.MainPkg().Module.Dir,
|
||||||
|
MainDir: lprogram.MainPkg().Dir,
|
||||||
|
ImportPath: lprogram.MainPkg().ImportPath,
|
||||||
|
}
|
||||||
|
if result.ModuleRoot == "" {
|
||||||
|
// If there is no module root, just the regular root.
|
||||||
|
result.ModuleRoot = lprogram.MainPkg().Root
|
||||||
|
}
|
||||||
|
if err != nil { // failed to load AST
|
||||||
|
return result, err
|
||||||
}
|
}
|
||||||
err = lprogram.Parse()
|
err = lprogram.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -270,7 +273,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
|
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), tmpdir, compilerConfig)
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -284,7 +287,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
for _, imported := range pkg.Pkg.Imports() {
|
for _, imported := range pkg.Pkg.Imports() {
|
||||||
job, ok := packageActionIDJobs[imported.Path()]
|
job, ok := packageActionIDJobs[imported.Path()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
return result, fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||||
}
|
}
|
||||||
importedPackages = append(importedPackages, job)
|
importedPackages = append(importedPackages, job)
|
||||||
actionIDDependencies = append(actionIDDependencies, job)
|
actionIDDependencies = append(actionIDDependencies, job)
|
||||||
@@ -370,7 +373,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Packages are compiled independently anyway.
|
// Packages are compiled independently anyway.
|
||||||
for _, cgoHeader := range pkg.CGoHeaders {
|
for _, cgoHeader := range pkg.CGoHeaders {
|
||||||
// Store the header text in a temporary file.
|
// Store the header text in a temporary file.
|
||||||
f, err := ioutil.TempFile(dir, "cgosnippet-*.c")
|
f, err := os.CreateTemp(tmpdir, "cgosnippet-*.c")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -418,7 +421,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return errors.New("global not found: " + globalName)
|
return errors.New("global not found: " + globalName)
|
||||||
}
|
}
|
||||||
name := global.Name()
|
name := global.Name()
|
||||||
newGlobal := llvm.AddGlobal(mod, global.Type().ElementType(), name+".tmp")
|
newGlobal := llvm.AddGlobal(mod, global.GlobalValueType(), name+".tmp")
|
||||||
global.ReplaceAllUsesWith(newGlobal)
|
global.ReplaceAllUsesWith(newGlobal)
|
||||||
global.EraseFromParentAsGlobal()
|
global.EraseFromParentAsGlobal()
|
||||||
newGlobal.SetName(name)
|
newGlobal.SetName(name)
|
||||||
@@ -431,7 +434,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
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.DumpSSA())
|
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -445,7 +448,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Write to a temporary path that is renamed to the destination
|
// Write to a temporary path that is renamed to the destination
|
||||||
// file to avoid race conditions with other TinyGo invocatiosn
|
// file to avoid race conditions with other TinyGo invocatiosn
|
||||||
// that might also be compiling this package at the same time.
|
// that might also be compiling this package at the same time.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
|
f, err := os.CreateTemp(filepath.Dir(job.result), filepath.Base(job.result))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -523,7 +526,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if pkgInit.IsNil() {
|
if pkgInit.IsNil() {
|
||||||
panic("init not found for " + pkg.Pkg.Path())
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
}
|
}
|
||||||
irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
||||||
}
|
}
|
||||||
irbuilder.CreateRetVoid()
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
@@ -579,17 +582,17 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Run jobs to produce the LLVM module.
|
// Run jobs to produce the LLVM module.
|
||||||
err := runJobs(programJob, config.Options.Semaphore)
|
err := runJobs(programJob, config.Options.Semaphore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
// Generate output.
|
// Generate output.
|
||||||
switch outext {
|
switch outext {
|
||||||
case ".o":
|
case ".o":
|
||||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
defer llvmBuf.Dispose()
|
defer llvmBuf.Dispose()
|
||||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||||
case ".bc":
|
case ".bc":
|
||||||
var buf llvm.MemoryBuffer
|
var buf llvm.MemoryBuffer
|
||||||
if config.UseThinLTO() {
|
if config.UseThinLTO() {
|
||||||
@@ -598,10 +601,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
|
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||||
}
|
}
|
||||||
defer buf.Dispose()
|
defer buf.Dispose()
|
||||||
return ioutil.WriteFile(outpath, buf.Bytes(), 0666)
|
return result, os.WriteFile(outpath, buf.Bytes(), 0666)
|
||||||
case ".ll":
|
case ".ll":
|
||||||
data := []byte(mod.String())
|
data := []byte(mod.String())
|
||||||
return ioutil.WriteFile(outpath, data, 0666)
|
return result, os.WriteFile(outpath, data, 0666)
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
@@ -612,7 +615,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// run all jobs in parallel as far as possible.
|
// run all jobs in parallel as far as possible.
|
||||||
|
|
||||||
// Add job to write the output object file.
|
// Add job to write the output object file.
|
||||||
objfile := filepath.Join(dir, "main.o")
|
objfile := filepath.Join(tmpdir, "main.o")
|
||||||
outputObjectFileJob := &compileJob{
|
outputObjectFileJob := &compileJob{
|
||||||
description: "generate output file",
|
description: "generate output file",
|
||||||
dependencies: []*compileJob{programJob},
|
dependencies: []*compileJob{programJob},
|
||||||
@@ -629,25 +632,25 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer llvmBuf.Dispose()
|
defer llvmBuf.Dispose()
|
||||||
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
executable := filepath.Join(dir, "main")
|
result.Executable = filepath.Join(tmpdir, "main")
|
||||||
if config.GOOS() == "windows" {
|
if config.GOOS() == "windows" {
|
||||||
executable += ".exe"
|
result.Executable += ".exe"
|
||||||
}
|
}
|
||||||
tmppath := executable // final file
|
result.Binary = result.Executable // final file
|
||||||
ldflags := append(config.LDFlags(), "-o", executable)
|
ldflags := append(config.LDFlags(), "-o", result.Executable)
|
||||||
|
|
||||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||||
// a cache.
|
// a cache.
|
||||||
if config.Target.RTLib == "compiler-rt" {
|
if config.Target.RTLib == "compiler-rt" {
|
||||||
job, unlock, err := CompilerRT.load(config, dir)
|
job, unlock, err := CompilerRT.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
@@ -661,7 +664,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile extra file " + path,
|
description: "compile extra file " + path,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -679,7 +682,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile CGo file " + abspath,
|
description: "compile CGo file " + abspath,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -700,21 +703,18 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Add embedded files.
|
// Add embedded files.
|
||||||
linkerDependencies = append(linkerDependencies, embedFileObjects...)
|
linkerDependencies = append(linkerDependencies, embedFileObjects...)
|
||||||
|
|
||||||
|
// Determine whether the compilation configuration would result in debug
|
||||||
|
// (DWARF) information in the object files.
|
||||||
|
var hasDebug = true
|
||||||
|
if config.GOOS() == "darwin" {
|
||||||
|
// Debug information isn't stored in the binary itself on MacOS but
|
||||||
|
// is left in the object files by default. The binary does store the
|
||||||
|
// path to these object files though.
|
||||||
|
hasDebug = false
|
||||||
|
}
|
||||||
|
|
||||||
// Strip debug information with -no-debug.
|
// Strip debug information with -no-debug.
|
||||||
if !config.Debug() {
|
if hasDebug && !config.Debug() {
|
||||||
for _, tag := range config.BuildTags() {
|
|
||||||
if tag == "baremetal" {
|
|
||||||
// Don't use -no-debug on baremetal targets. It makes no sense:
|
|
||||||
// the debug information isn't flashed to the device anyway.
|
|
||||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if config.GOOS() == "darwin" {
|
|
||||||
// Debug information isn't stored in the binary itself on MacOS but
|
|
||||||
// is left in the object files by default. The binary does store the
|
|
||||||
// path to these object files though.
|
|
||||||
return errors.New("cannot remove debug information: MacOS doesn't store debug info in the executable by default")
|
|
||||||
}
|
|
||||||
if config.Target.Linker == "wasm-ld" {
|
if config.Target.Linker == "wasm-ld" {
|
||||||
// Don't just strip debug information, also compress relocations
|
// Don't just strip debug information, also compress relocations
|
||||||
// while we're at it. Relocations can only be compressed when debug
|
// while we're at it. Relocations can only be compressed when debug
|
||||||
@@ -725,7 +725,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
ldflags = append(ldflags, "--strip-debug")
|
ldflags = append(ldflags, "--strip-debug")
|
||||||
} else {
|
} else {
|
||||||
// Other linkers may have different flags.
|
// Other linkers may have different flags.
|
||||||
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
|
return result, errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,9 +741,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
ldflags = append(ldflags, dependency.result)
|
ldflags = append(ldflags, dependency.result)
|
||||||
}
|
}
|
||||||
if config.Options.PrintCommands != nil {
|
|
||||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
|
||||||
}
|
|
||||||
if config.UseThinLTO() {
|
if config.UseThinLTO() {
|
||||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
if config.GOOS() == "windows" {
|
if config.GOOS() == "windows" {
|
||||||
@@ -775,9 +772,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
"-mllvm", "--rotation-max-header-size=0")
|
"-mllvm", "--rotation-max-header-size=0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if config.Options.PrintCommands != nil {
|
||||||
|
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||||
|
}
|
||||||
err = link(config.Target.Linker, ldflags...)
|
err = link(config.Target.Linker, ldflags...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to link", executable, err}
|
return &commandError{"failed to link", result.Executable, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
var calculatedStacks []string
|
var calculatedStacks []string
|
||||||
@@ -786,7 +786,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Try to determine stack sizes at compile time.
|
// Try to determine stack sizes at compile time.
|
||||||
// Don't do this by default as it usually doesn't work on
|
// Don't do this by default as it usually doesn't work on
|
||||||
// unsupported architectures.
|
// unsupported architectures.
|
||||||
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
|
calculatedStacks, stackSizes, err = determineStackSizes(mod, result.Executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -796,41 +796,51 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if config.AutomaticStackSize() {
|
if config.AutomaticStackSize() {
|
||||||
// Modify the .tinygo_stacksizes section that contains a stack size
|
// Modify the .tinygo_stacksizes section that contains a stack size
|
||||||
// for each goroutine.
|
// for each goroutine.
|
||||||
err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
|
err = modifyStackSizes(result.Executable, stackSizeLoads, stackSizes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not modify stack sizes: %w", err)
|
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.RP2040BootPatch() {
|
if config.RP2040BootPatch() {
|
||||||
// Patch the second stage bootloader CRC into the .boot2 section
|
// Patch the second stage bootloader CRC into the .boot2 section
|
||||||
err = patchRP2040BootCRC(executable)
|
err = patchRP2040BootCRC(result.Executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
|
return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run wasm-opt if necessary.
|
// Run wasm-opt for wasm binaries
|
||||||
if config.Scheduler() == "asyncify" {
|
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
||||||
var optLevel, shrinkLevel int
|
var opt string
|
||||||
switch config.Options.Opt {
|
switch config.Options.Opt {
|
||||||
case "none", "0":
|
case "none", "0":
|
||||||
|
opt = "-O0"
|
||||||
case "1":
|
case "1":
|
||||||
optLevel = 1
|
opt = "-O1"
|
||||||
case "2":
|
case "2":
|
||||||
optLevel = 2
|
opt = "-O2"
|
||||||
case "s":
|
case "s":
|
||||||
optLevel = 2
|
opt = "-Os"
|
||||||
shrinkLevel = 1
|
|
||||||
case "z":
|
case "z":
|
||||||
optLevel = 2
|
opt = "-Oz"
|
||||||
shrinkLevel = 2
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
|
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
|
||||||
}
|
}
|
||||||
cmd := exec.Command(goenv.Get("WASMOPT"), "--asyncify", "-g",
|
|
||||||
"--optimize-level", strconv.Itoa(optLevel),
|
var args []string
|
||||||
"--shrink-level", strconv.Itoa(shrinkLevel),
|
|
||||||
executable, "--output", executable)
|
if config.Scheduler() == "asyncify" {
|
||||||
|
args = append(args, "--asyncify")
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args,
|
||||||
|
opt,
|
||||||
|
"-g",
|
||||||
|
result.Executable,
|
||||||
|
"--output", result.Executable,
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := exec.Command(goenv.Get("WASMOPT"), args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
@@ -846,7 +856,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||||
}
|
}
|
||||||
sizes, err := loadProgramSize(executable, packagePathMap)
|
sizes, err := loadProgramSize(result.Executable, packagePathMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -882,7 +892,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// is simpler and cannot be parallelized.
|
// is simpler and cannot be parallelized.
|
||||||
err = runJobs(linkJob, config.Options.Semaphore)
|
err = runJobs(linkJob, config.Options.Semaphore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an Intel .hex file or .bin file from the .elf file.
|
// Get an Intel .hex file or .bin file from the .elf file.
|
||||||
@@ -893,56 +903,43 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
case "hex", "bin":
|
case "hex", "bin":
|
||||||
// Extract raw binary, either encoding it as a hex file or as a raw
|
// Extract raw binary, either encoding it as a hex file or as a raw
|
||||||
// firmware file.
|
// firmware file.
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||||
err := objcopy(executable, tmppath, outputBinaryFormat)
|
err := objcopy(result.Executable, result.Binary, outputBinaryFormat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
case "uf2":
|
case "uf2":
|
||||||
// Get UF2 from the .elf file.
|
// Get UF2 from the .elf file.
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||||
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
|
err := convertELFFileToUF2File(result.Executable, result.Binary, config.Target.UF2FamilyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
case "esp32", "esp32-img", "esp32c3", "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).
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||||
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
|
err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
case "nrf-dfu":
|
case "nrf-dfu":
|
||||||
// special format for nrfutil for Nordic chips
|
// special format for nrfutil for Nordic chips
|
||||||
tmphexpath := filepath.Join(dir, "main.hex")
|
tmphexpath := filepath.Join(tmpdir, "main.hex")
|
||||||
err := objcopy(executable, tmphexpath, "hex")
|
err := objcopy(result.Executable, tmphexpath, "hex")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||||
err = makeDFUFirmwareImage(config.Options, tmphexpath, tmppath)
|
err = makeDFUFirmwareImage(config.Options, tmphexpath, result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return result, err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
return result, fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's a module root, use that.
|
return result, nil
|
||||||
moduleroot := lprogram.MainPkg().Module.Dir
|
|
||||||
if moduleroot == "" {
|
|
||||||
// if not, just the regular root
|
|
||||||
moduleroot = lprogram.MainPkg().Root
|
|
||||||
}
|
|
||||||
|
|
||||||
return action(BuildResult{
|
|
||||||
Executable: executable,
|
|
||||||
Binary: tmppath,
|
|
||||||
MainDir: lprogram.MainPkg().Dir,
|
|
||||||
ModuleRoot: moduleroot,
|
|
||||||
ImportPath: lprogram.MainPkg().ImportPath,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createEmbedObjectFile creates a new object file with the given contents, for
|
// createEmbedObjectFile creates a new object file with the given contents, for
|
||||||
@@ -1055,7 +1052,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.DumpSSA())
|
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1086,7 +1083,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
// cannot be represented exactly in JavaScript (JS only has doubles). To
|
// cannot be represented exactly in JavaScript (JS only has doubles). To
|
||||||
// keep functions interoperable, pass int64 types as pointers to
|
// keep functions interoperable, pass int64 types as pointers to
|
||||||
// stack-allocated values.
|
// stack-allocated values.
|
||||||
// Use -wasm-abi=generic to disable this behaviour.
|
|
||||||
if config.WasmAbi() == "js" {
|
if config.WasmAbi() == "js" {
|
||||||
err := transform.ExternalInt64AsPtr(mod, config)
|
err := transform.ExternalInt64AsPtr(mod, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1137,7 +1133,7 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
|
|||||||
|
|
||||||
// A strin is a {ptr, len} pair. We need these types to build the
|
// A strin is a {ptr, len} pair. We need these types to build the
|
||||||
// initializer.
|
// initializer.
|
||||||
initializerType := global.Type().ElementType()
|
initializerType := global.GlobalValueType()
|
||||||
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
|
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
|
||||||
return fmt.Errorf("%s: not a string", globalName)
|
return fmt.Errorf("%s: not a string", globalName)
|
||||||
}
|
}
|
||||||
@@ -1156,7 +1152,7 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
|
|||||||
|
|
||||||
// Create the string value, which is a {ptr, len} pair.
|
// Create the string value, which is a {ptr, len} pair.
|
||||||
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
|
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
|
||||||
ptr := llvm.ConstGEP(buf, []llvm.Value{zero, zero})
|
ptr := llvm.ConstGEP(bufInitializer.Type(), buf, []llvm.Value{zero, zero})
|
||||||
if ptr.Type() != elementTypes[0] {
|
if ptr.Type() != elementTypes[0] {
|
||||||
return fmt.Errorf("%s: not a string", globalName)
|
return fmt.Errorf("%s: not a string", globalName)
|
||||||
}
|
}
|
||||||
@@ -1228,15 +1224,10 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
|||||||
// Goroutines need to be started and finished and take up some stack space
|
// Goroutines need to be started and finished and take up some stack space
|
||||||
// that way. This can be measured by measuing the stack size of
|
// that way. This can be measured by measuing the stack size of
|
||||||
// tinygo_startTask.
|
// tinygo_startTask.
|
||||||
var baseStackSize uint64
|
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
||||||
var baseStackSizeType stacksize.SizeType
|
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
||||||
var baseStackSizeFailedAt *stacksize.CallNode
|
|
||||||
if len(gowrappers) != 0 {
|
|
||||||
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
|
||||||
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
|
||||||
}
|
|
||||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt = functions["tinygo_startTask"][0].StackSize()
|
|
||||||
}
|
}
|
||||||
|
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||||
|
|
||||||
sizes := make(map[string]functionStackSize)
|
sizes := make(map[string]functionStackSize)
|
||||||
|
|
||||||
@@ -1308,11 +1299,6 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if data == nil {
|
|
||||||
// The .tinygo_stacksizes section doesn't exist, so assume this
|
|
||||||
// modification isn't needed.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(stackSizeLoads)*4 != len(data) {
|
if len(stackSizeLoads)*4 != len(data) {
|
||||||
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
|
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
|
||||||
@@ -1377,10 +1363,10 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
|||||||
//
|
//
|
||||||
// It might print something like the following:
|
// It might print something like the following:
|
||||||
//
|
//
|
||||||
// function stack usage (in bytes)
|
// function stack usage (in bytes)
|
||||||
// Reset_Handler 316
|
// Reset_Handler 316
|
||||||
// examples/blinky2.led1 92
|
// examples/blinky2.led1 92
|
||||||
// runtime.run$1 300
|
// runtime.run$1 300
|
||||||
func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
|
func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
|
||||||
// Print the sizes of all stacks.
|
// Print the sizes of all stacks.
|
||||||
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
|
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -60,6 +59,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
{GOOS: "windows", GOARCH: "amd64"},
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
|
{GOOS: "windows", GOARCH: "arm64"},
|
||||||
} {
|
} {
|
||||||
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
||||||
if options.GOARCH == "arm" {
|
if options.GOARCH == "arm" {
|
||||||
@@ -90,7 +90,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|||||||
|
|
||||||
// Create a very simple C input file.
|
// Create a very simple C input file.
|
||||||
srcpath := filepath.Join(testDir, "test.c")
|
srcpath := filepath.Join(testDir, "test.c")
|
||||||
err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
err = os.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not write target file %s: %s", srcpath, err)
|
t.Fatalf("could not write target file %s: %s", srcpath, err)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ func ReadBuildID() ([]byte, error) {
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "linux", "freebsd":
|
case "linux", "freebsd", "android":
|
||||||
// Read the GNU build id section. (Not sure about FreeBSD though...)
|
// Read the GNU build id section. (Not sure about FreeBSD though...)
|
||||||
file, err := elf.NewFile(f)
|
file, err := elf.NewFile(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+14
-2
@@ -162,6 +162,15 @@ var aeabiBuiltins = []string{
|
|||||||
"udivmodsi4.c",
|
"udivmodsi4.c",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var avrBuiltins = []string{
|
||||||
|
"avr/divmodhi4.S",
|
||||||
|
"avr/divmodqi4.S",
|
||||||
|
"avr/mulhi3.S",
|
||||||
|
"avr/mulqi3.S",
|
||||||
|
"avr/udivmodhi4.S",
|
||||||
|
"avr/udivmodqi4.S",
|
||||||
|
}
|
||||||
|
|
||||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||||
// These symbols are for operations that cannot be emitted with a single
|
// These symbols are for operations that cannot be emitted with a single
|
||||||
// instruction or a short sequence of instructions for that target.
|
// instruction or a short sequence of instructions for that target.
|
||||||
@@ -181,11 +190,14 @@ var CompilerRT = Library{
|
|||||||
// Development build.
|
// Development build.
|
||||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||||
},
|
},
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) ([]string, error) {
|
||||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||||
builtins = append(builtins, aeabiBuiltins...)
|
builtins = append(builtins, aeabiBuiltins...)
|
||||||
}
|
}
|
||||||
return builtins
|
if strings.HasPrefix(target, "avr") {
|
||||||
|
builtins = append(builtins, avrBuiltins...)
|
||||||
|
}
|
||||||
|
return builtins, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-36
@@ -10,7 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -33,29 +33,29 @@ import (
|
|||||||
// Because of this complexity, every file has in fact two cached build outputs:
|
// Because of this complexity, every file has in fact two cached build outputs:
|
||||||
// the file itself, and the list of dependencies. Its operation is as follows:
|
// the file itself, and the list of dependencies. Its operation is as follows:
|
||||||
//
|
//
|
||||||
// depfile = hash(path, compiler, cflags, ...)
|
// depfile = hash(path, compiler, cflags, ...)
|
||||||
// if depfile exists:
|
// if depfile exists:
|
||||||
// outfile = hash of all files and depfile name
|
// outfile = hash of all files and depfile name
|
||||||
// if outfile exists:
|
// if outfile exists:
|
||||||
// # cache hit
|
// # cache hit
|
||||||
// return outfile
|
// return outfile
|
||||||
// # cache miss
|
// # cache miss
|
||||||
// tmpfile = compile file
|
// tmpfile = compile file
|
||||||
// read dependencies (side effect of compile)
|
// read dependencies (side effect of compile)
|
||||||
// write depfile
|
// write depfile
|
||||||
// outfile = hash of all files and depfile name
|
// outfile = hash of all files and depfile name
|
||||||
// rename tmpfile to outfile
|
// rename tmpfile to outfile
|
||||||
//
|
//
|
||||||
// There are a few edge cases that are not handled:
|
// There are a few edge cases that are not handled:
|
||||||
// - If a file is added to an include path, that file may be included instead of
|
// - If a file is added to an include path, that file may be included instead of
|
||||||
// some other file. This would be fixed by also including lookup failures in the
|
// some other file. This would be fixed by also including lookup failures in the
|
||||||
// dependencies file, but I'm not aware of a compiler which does that.
|
// dependencies file, but I'm not aware of a compiler which does that.
|
||||||
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
// - The Makefile syntax that compilers output has issues, see readDepFile for
|
||||||
// details.
|
// details.
|
||||||
// - A header file may be changed to add/remove an include. This invalidates the
|
// - A header file may be changed to add/remove an include. This invalidates the
|
||||||
// depfile but without invalidating its name. For this reason, the depfile is
|
// depfile but without invalidating its name. For this reason, the depfile is
|
||||||
// written on each new compilation (even when it seems unnecessary). However, it
|
// written on each new compilation (even when it seems unnecessary). However, it
|
||||||
// could in rare cases lead to a stale file fetched from the cache.
|
// could in rare cases lead to a stale file fetched from the cache.
|
||||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
|
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
|
||||||
// Hash input file.
|
// Hash input file.
|
||||||
fileHash, err := hashFile(abspath)
|
fileHash, err := hashFile(abspath)
|
||||||
@@ -93,7 +93,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
// Load dependencies file, if possible.
|
// Load dependencies file, if possible.
|
||||||
depfileName := "dep-" + depfileNameHash + ".json"
|
depfileName := "dep-" + depfileNameHash + ".json"
|
||||||
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
|
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
|
||||||
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
|
depfileBuf, err := os.ReadFile(depfileCachePath)
|
||||||
var dependencies []string // sorted list of dependency paths
|
var dependencies []string // sorted list of dependency paths
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// There is a dependency file, that's great!
|
// There is a dependency file, that's great!
|
||||||
@@ -108,21 +108,21 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
if _, err := os.Stat(outpath); err == nil {
|
if _, err := os.Stat(outpath); err == nil {
|
||||||
return outpath, nil
|
return outpath, nil
|
||||||
} else if !os.IsNotExist(err) {
|
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !os.IsNotExist(err) {
|
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||||
// expected either nil or IsNotExist
|
// expected either nil or IsNotExist
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*"+ext)
|
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
objTmpFile.Close()
|
objTmpFile.Close()
|
||||||
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
|
depTmpFile, err := os.CreateTemp(tmpdir, "dep-*.d")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
sort.Strings(dependencySlice)
|
sort.Strings(dependencySlice)
|
||||||
|
|
||||||
// Write dependencies file.
|
// Write dependencies file.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
f, err := os.CreateTemp(filepath.Dir(depfileCachePath), depfileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -252,13 +252,14 @@ func hashFile(path string) (string, error) {
|
|||||||
// file is assumed to have a single target named deps.
|
// file is assumed to have a single target named deps.
|
||||||
//
|
//
|
||||||
// There are roughly three make syntax variants:
|
// There are roughly three make syntax variants:
|
||||||
// - BSD make, which doesn't support any escaping. This means that many special
|
// - BSD make, which doesn't support any escaping. This means that many special
|
||||||
// characters are not supported in file names.
|
// characters are not supported in file names.
|
||||||
// - GNU make, which supports escaping using a backslash but when it fails to
|
// - GNU make, which supports escaping using a backslash but when it fails to
|
||||||
// find a file it tries to fall back with the literal path name (to match BSD
|
// find a file it tries to fall back with the literal path name (to match BSD
|
||||||
// make).
|
// make).
|
||||||
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
|
||||||
// any weird characters.
|
// any weird characters.
|
||||||
|
//
|
||||||
// Clang supports two variants: a format that's a compromise between BSD and GNU
|
// Clang supports two variants: a format that's a compromise between BSD and GNU
|
||||||
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
|
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
|
||||||
// is at least somewhat sane. This last format isn't perfect either: it does not
|
// is at least somewhat sane. This last format isn't perfect either: it does not
|
||||||
@@ -266,7 +267,7 @@ func hashFile(path string) (string, error) {
|
|||||||
// allowed on Windows, but of course can be used on POSIX like systems. Still,
|
// allowed on Windows, but of course can be used on POSIX like systems. Still,
|
||||||
// it's the most sane of any of the formats so readDepFile will use that format.
|
// it's the most sane of any of the formats so readDepFile will use that format.
|
||||||
func readDepFile(filename string) ([]string, error) {
|
func readDepFile(filename string) ([]string, error) {
|
||||||
buf, err := ioutil.ReadFile(filename)
|
buf, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-4
@@ -1,4 +1,4 @@
|
|||||||
// +build byollvm
|
//go:build byollvm
|
||||||
|
|
||||||
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||||
//
|
//
|
||||||
@@ -101,6 +101,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
// Target Options
|
// Target Options
|
||||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||||
|
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
||||||
|
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
||||||
|
|
||||||
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
||||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||||
|
|
||||||
@@ -203,6 +206,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
.Default(0);
|
.Default(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto *A = Args.getLastArg(OPT_femit_dwarf_unwind_EQ)) {
|
||||||
|
Opts.EmitDwarfUnwind =
|
||||||
|
llvm::StringSwitch<EmitDwarfUnwindType>(A->getValue())
|
||||||
|
.Case("always", EmitDwarfUnwindType::Always)
|
||||||
|
.Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind)
|
||||||
|
.Case("default", EmitDwarfUnwindType::Default);
|
||||||
|
}
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +264,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
assert(MRI && "Unable to create target register info!");
|
assert(MRI && "Unable to create target register info!");
|
||||||
|
|
||||||
MCTargetOptions MCOptions;
|
MCTargetOptions MCOptions;
|
||||||
|
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||||
|
|
||||||
std::unique_ptr<MCAsmInfo> MAI(
|
std::unique_ptr<MCAsmInfo> MAI(
|
||||||
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
|
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
|
||||||
assert(MAI && "Unable to create target asm info!");
|
assert(MAI && "Unable to create target asm info!");
|
||||||
@@ -299,6 +312,8 @@ 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);
|
||||||
Ctx.setObjectFileInfo(MOFI.get());
|
Ctx.setObjectFileInfo(MOFI.get());
|
||||||
|
|
||||||
if (Opts.SaveTemporaryLabels)
|
if (Opts.SaveTemporaryLabels)
|
||||||
@@ -347,7 +362,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
std::unique_ptr<MCCodeEmitter> CE;
|
std::unique_ptr<MCCodeEmitter> CE;
|
||||||
if (Opts.ShowEncoding)
|
if (Opts.ShowEncoding)
|
||||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx));
|
||||||
std::unique_ptr<MCAsmBackend> MAB(
|
std::unique_ptr<MCAsmBackend> MAB(
|
||||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||||
|
|
||||||
@@ -367,7 +382,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<MCCodeEmitter> CE(
|
std::unique_ptr<MCCodeEmitter> CE(
|
||||||
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
TheTarget->createMCCodeEmitter(*MCII, Ctx));
|
||||||
std::unique_ptr<MCAsmBackend> MAB(
|
std::unique_ptr<MCAsmBackend> MAB(
|
||||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||||
assert(MAB && "Unable to create asm backend!");
|
assert(MAB && "Unable to create asm backend!");
|
||||||
@@ -389,7 +404,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
|
if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) {
|
||||||
MCSection *AsmLabel = Ctx.getMachOSection(
|
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||||
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||||
Str.get()->SwitchSection(AsmLabel);
|
Str.get()->switchSection(AsmLabel);
|
||||||
Str.get()->emitZeros(1);
|
Str.get()->emitZeros(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ struct AssemblerInvocation {
|
|||||||
unsigned IncrementalLinkerCompatible : 1;
|
unsigned IncrementalLinkerCompatible : 1;
|
||||||
unsigned EmbedBitcode : 1;
|
unsigned EmbedBitcode : 1;
|
||||||
|
|
||||||
|
/// Whether to emit DWARF unwind info.
|
||||||
|
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||||
|
|
||||||
/// The name of the relocation model to use.
|
/// The name of the relocation model to use.
|
||||||
std::string RelocationModel;
|
std::string RelocationModel;
|
||||||
|
|
||||||
@@ -92,6 +95,9 @@ struct AssemblerInvocation {
|
|||||||
/// otherwise.
|
/// otherwise.
|
||||||
std::string TargetABI;
|
std::string TargetABI;
|
||||||
|
|
||||||
|
/// Darwin target variant triple, the variant of the deployment target
|
||||||
|
/// for which the code is being compiled.
|
||||||
|
llvm::Optional<llvm::Triple> DarwinTargetVariantTriple;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -112,6 +118,7 @@ public:
|
|||||||
Dwarf64 = 0;
|
Dwarf64 = 0;
|
||||||
DwarfVersion = 0;
|
DwarfVersion = 0;
|
||||||
EmbedBitcode = 0;
|
EmbedBitcode = 0;
|
||||||
|
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build byollvm
|
//go:build byollvm
|
||||||
|
|
||||||
#include <clang/Basic/DiagnosticOptions.h>
|
#include <clang/Basic/DiagnosticOptions.h>
|
||||||
#include <clang/CodeGen/CodeGenAction.h>
|
#include <clang/CodeGen/CodeGenAction.h>
|
||||||
|
|||||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||||
}
|
}
|
||||||
if major != 1 || minor < 16 || minor > 19 {
|
if major != 1 || minor < 18 || minor > 20 {
|
||||||
return nil, fmt.Errorf("requires go version 1.16 through 1.19, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ func getElfSectionData(executable string, sectionName string) ([]byte, elf.FileH
|
|||||||
|
|
||||||
section := elfFile.Section(sectionName)
|
section := elfFile.Section(sectionName)
|
||||||
if section == nil {
|
if section == nil {
|
||||||
return nil, elf.FileHeader{}, nil
|
return nil, elf.FileHeader{}, fmt.Errorf("could not find %s section", sectionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := section.Data()
|
data, err := section.Data()
|
||||||
|
|||||||
+4
-2
@@ -1,6 +1,8 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -17,13 +19,13 @@ import (
|
|||||||
func getClangHeaderPath(TINYGOROOT string) string {
|
func getClangHeaderPath(TINYGOROOT string) string {
|
||||||
// Check whether we're running from the source directory.
|
// Check whether we're running from the source directory.
|
||||||
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether we're running from the installation directory.
|
// Check whether we're running from the installation directory.
|
||||||
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
|
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
|
||||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -13,7 +13,7 @@ import (
|
|||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -189,5 +189,5 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the image to the output file.
|
// Write the image to the output file.
|
||||||
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
return os.WriteFile(outfile, outf.Bytes(), 0666)
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-12
@@ -1,7 +1,8 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -28,7 +29,7 @@ type Library struct {
|
|||||||
sourceDir func() string
|
sourceDir func() string
|
||||||
|
|
||||||
// The source files, relative to sourceDir.
|
// The source files, relative to sourceDir.
|
||||||
librarySources func(target string) []string
|
librarySources func(target string) ([]string, error)
|
||||||
|
|
||||||
// The source code for the crt1.o file, relative to sourceDir.
|
// The source code for the crt1.o file, relative to sourceDir.
|
||||||
crt1Source string
|
crt1Source string
|
||||||
@@ -94,7 +95,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
target := config.Triple()
|
target := config.Triple()
|
||||||
if l.makeHeaders != nil {
|
if l.makeHeaders != nil {
|
||||||
if _, err = os.Stat(headerPath); err != nil {
|
if _, err = os.Stat(headerPath); err != nil {
|
||||||
temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*")
|
temporaryHeaderPath, err := os.MkdirTemp(outdir, "include.tmp*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -110,10 +111,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
err = os.Rename(temporaryHeaderPath, headerPath)
|
err = os.Rename(temporaryHeaderPath, headerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
case os.IsExist(err):
|
case errors.Is(err, fs.ErrExist):
|
||||||
// Another invocation of TinyGo also seems to have already created the headers.
|
// Another invocation of TinyGo also seems to have already created the headers.
|
||||||
|
|
||||||
case runtime.GOOS == "windows" && os.IsPermission(err):
|
case runtime.GOOS == "windows" && errors.Is(err, fs.ErrPermission):
|
||||||
// On Windows, a rename with a destination directory that already
|
// On Windows, a rename with a destination directory that already
|
||||||
// exists does not result in an IsExist error, but rather in an
|
// exists does not result in an IsExist error, but rather in an
|
||||||
// access denied error. To be sure, check for this case by checking
|
// access denied error. To be sure, check for this case by checking
|
||||||
@@ -141,7 +142,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", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||||
cpu := config.CPU()
|
cpu := config.CPU()
|
||||||
if cpu != "" {
|
if cpu != "" {
|
||||||
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
||||||
@@ -154,8 +155,15 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
args = append(args, "-mcpu="+cpu)
|
args = append(args, "-mcpu="+cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if config.ABI() != "" {
|
||||||
|
args = append(args, "-mabi="+config.ABI())
|
||||||
|
}
|
||||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
if strings.Split(target, "-")[2] == "linux" {
|
||||||
|
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||||
|
} else {
|
||||||
|
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "avr") {
|
if strings.HasPrefix(target, "avr") {
|
||||||
// AVR defaults to C float and double both being 32-bit. This deviates
|
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||||
@@ -165,10 +173,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
args = append(args, "-mdouble=64")
|
args = append(args, "-mdouble=64")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "riscv32-") {
|
if strings.HasPrefix(target, "riscv32-") {
|
||||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
args = append(args, "-march=rv32imac", "-fforce-enable-int128")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "riscv64-") {
|
if strings.HasPrefix(target, "riscv64-") {
|
||||||
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
args = append(args, "-march=rv64gc")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "xtensa") {
|
if strings.HasPrefix(target, "xtensa") {
|
||||||
// Hack to work around an issue in the Xtensa port:
|
// Hack to work around an issue in the Xtensa port:
|
||||||
@@ -189,7 +197,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
defer once.Do(unlock)
|
defer once.Do(unlock)
|
||||||
|
|
||||||
// Create an archive of all object files.
|
// Create an archive of all object files.
|
||||||
f, err := ioutil.TempFile(outdir, "libc.a.tmp*")
|
f, err := os.CreateTemp(outdir, "libc.a.tmp*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -214,7 +222,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
|
|
||||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||||
// archive job above, so must be run first.
|
// archive job above, so must be run first.
|
||||||
for _, path := range l.librarySources(target) {
|
paths, err := l.librarySources(target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
for _, path := range paths {
|
||||||
// Strip leading "../" parts off the path.
|
// Strip leading "../" parts off the path.
|
||||||
cleanpath := path
|
cleanpath := path
|
||||||
for strings.HasPrefix(cleanpath, "../") {
|
for strings.HasPrefix(cleanpath, "../") {
|
||||||
@@ -230,6 +242,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
var compileArgs []string
|
var compileArgs []string
|
||||||
compileArgs = append(compileArgs, args...)
|
compileArgs = append(compileArgs, args...)
|
||||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||||
|
if config.Options.PrintCommands != nil {
|
||||||
|
config.Options.PrintCommands("clang", compileArgs...)
|
||||||
|
}
|
||||||
err := runCCompiler(compileArgs...)
|
err := runCCompiler(compileArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", srcpath, err}
|
return &commandError{"failed to build", srcpath, err}
|
||||||
@@ -250,12 +265,15 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
run: func(*compileJob) error {
|
run: func(*compileJob) error {
|
||||||
var compileArgs []string
|
var compileArgs []string
|
||||||
compileArgs = append(compileArgs, args...)
|
compileArgs = append(compileArgs, args...)
|
||||||
tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*")
|
tmpfile, err := os.CreateTemp(outdir, "crt1.o.tmp*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tmpfile.Close()
|
tmpfile.Close()
|
||||||
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
|
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
|
||||||
|
if config.Options.PrintCommands != nil {
|
||||||
|
config.Options.PrintCommands("clang", compileArgs...)
|
||||||
|
}
|
||||||
err = runCCompiler(compileArgs...)
|
err = runCCompiler(compileArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", srcpath, err}
|
return &commandError{"failed to build", srcpath, err}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build byollvm
|
//go:build byollvm
|
||||||
|
|
||||||
// This file provides C wrappers for liblld.
|
// This file provides C wrappers for liblld.
|
||||||
|
|
||||||
|
|||||||
+18
-6
@@ -1,6 +1,7 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -31,9 +32,9 @@ var MinGW = Library{
|
|||||||
// No flags necessary because there are no files to compile.
|
// No flags necessary because there are no files to compile.
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) ([]string, error) {
|
||||||
// We only use the UCRT DLL file. No source files necessary.
|
// We only use the UCRT DLL file. No source files necessary.
|
||||||
return nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ var MinGW = Library{
|
|||||||
//
|
//
|
||||||
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
|
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
|
||||||
// compile these files.
|
// compile these files.
|
||||||
func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
||||||
var jobs []*compileJob
|
var jobs []*compileJob
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
||||||
@@ -52,7 +53,7 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
|||||||
for _, name := range []string{
|
for _, name := range []string{
|
||||||
"kernel32.def.in",
|
"kernel32.def.in",
|
||||||
"api-ms-win-crt-conio-l1-1-0.def",
|
"api-ms-win-crt-conio-l1-1-0.def",
|
||||||
"api-ms-win-crt-convert-l1-1-0.def",
|
"api-ms-win-crt-convert-l1-1-0.def.in",
|
||||||
"api-ms-win-crt-environment-l1-1-0.def",
|
"api-ms-win-crt-environment-l1-1-0.def",
|
||||||
"api-ms-win-crt-filesystem-l1-1-0.def",
|
"api-ms-win-crt-filesystem-l1-1-0.def",
|
||||||
"api-ms-win-crt-heap-l1-1-0.def",
|
"api-ms-win-crt-heap-l1-1-0.def",
|
||||||
@@ -74,16 +75,27 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
|
|||||||
result: outpath,
|
result: outpath,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
defpath := inpath
|
defpath := inpath
|
||||||
|
var archDef, emulation string
|
||||||
|
switch goarch {
|
||||||
|
case "amd64":
|
||||||
|
archDef = "-DDEF_X64"
|
||||||
|
emulation = "i386pep"
|
||||||
|
case "arm64":
|
||||||
|
archDef = "-DDEF_ARM64"
|
||||||
|
emulation = "arm64pe"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported architecture for mingw-w64: %s", goarch)
|
||||||
|
}
|
||||||
if strings.HasSuffix(inpath, ".in") {
|
if strings.HasSuffix(inpath, ".in") {
|
||||||
// .in files need to be preprocessed by a preprocessor (-E)
|
// .in files need to be preprocessed by a preprocessor (-E)
|
||||||
// first.
|
// first.
|
||||||
defpath = outpath + ".def"
|
defpath = outpath + ".def"
|
||||||
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", archDef, "-DDATA", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
|
return link("ld.lld", "-m", emulation, "-o", outpath, defpath)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
|
|||||||
+26
-9
@@ -3,14 +3,15 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Musl = Library{
|
var Musl = Library{
|
||||||
@@ -35,7 +36,7 @@ var Musl = Library{
|
|||||||
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||||
}
|
}
|
||||||
for _, infile := range infiles {
|
for _, infile := range infiles {
|
||||||
data, err := ioutil.ReadFile(infile)
|
data, err := os.ReadFile(infile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -63,7 +64,7 @@ var Musl = Library{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
data, err := os.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,7 @@ var Musl = Library{
|
|||||||
cflags: func(target, headerPath string) []string {
|
cflags: func(target, headerPath string) []string {
|
||||||
arch := compileopts.MuslArchitecture(target)
|
arch := compileopts.MuslArchitecture(target)
|
||||||
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
|
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
|
||||||
return []string{
|
cflags := []string{
|
||||||
"-std=c99", // same as in musl
|
"-std=c99", // same as in musl
|
||||||
"-D_XOPEN_SOURCE=700", // same as in musl
|
"-D_XOPEN_SOURCE=700", // same as in musl
|
||||||
// Musl triggers some warnings and we don't want to show any
|
// Musl triggers some warnings and we don't want to show any
|
||||||
@@ -90,6 +91,8 @@ var Musl = Library{
|
|||||||
"-Wno-shift-op-parentheses",
|
"-Wno-shift-op-parentheses",
|
||||||
"-Wno-ignored-attributes",
|
"-Wno-ignored-attributes",
|
||||||
"-Wno-string-plus-int",
|
"-Wno-string-plus-int",
|
||||||
|
"-Wno-ignored-pragmas",
|
||||||
|
"-Wno-tautological-constant-out-of-range-compare",
|
||||||
"-Qunused-arguments",
|
"-Qunused-arguments",
|
||||||
// Select include dirs. Don't include standard library includes
|
// Select include dirs. Don't include standard library includes
|
||||||
// (that would introduce host dependencies and other complications),
|
// (that would introduce host dependencies and other complications),
|
||||||
@@ -103,9 +106,15 @@ var Musl = Library{
|
|||||||
"-I" + muslDir + "/include",
|
"-I" + muslDir + "/include",
|
||||||
"-fno-stack-protector",
|
"-fno-stack-protector",
|
||||||
}
|
}
|
||||||
|
llvmMajor, _ := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||||
|
if llvmMajor >= 15 {
|
||||||
|
// This flag was added in Clang 15. It is not present in LLVM 14.
|
||||||
|
cflags = append(cflags, "-Wno-deprecated-non-prototype")
|
||||||
|
}
|
||||||
|
return cflags
|
||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) ([]string, error) {
|
||||||
arch := compileopts.MuslArchitecture(target)
|
arch := compileopts.MuslArchitecture(target)
|
||||||
globs := []string{
|
globs := []string{
|
||||||
"env/*.c",
|
"env/*.c",
|
||||||
@@ -117,16 +126,21 @@ var Musl = Library{
|
|||||||
"internal/vdso.c",
|
"internal/vdso.c",
|
||||||
"legacy/*.c",
|
"legacy/*.c",
|
||||||
"malloc/*.c",
|
"malloc/*.c",
|
||||||
|
"malloc/mallocng/*.c",
|
||||||
"mman/*.c",
|
"mman/*.c",
|
||||||
|
"math/*.c",
|
||||||
"signal/*.c",
|
"signal/*.c",
|
||||||
"stdio/*.c",
|
"stdio/*.c",
|
||||||
"string/*.c",
|
"string/*.c",
|
||||||
"thread/" + arch + "/*.s",
|
"thread/" + arch + "/*.s",
|
||||||
"thread/" + arch + "/*.c",
|
|
||||||
"thread/*.c",
|
"thread/*.c",
|
||||||
"time/*.c",
|
"time/*.c",
|
||||||
"unistd/*.c",
|
"unistd/*.c",
|
||||||
}
|
}
|
||||||
|
if arch == "arm" {
|
||||||
|
// These files need to be added to the start for some reason.
|
||||||
|
globs = append([]string{"thread/arm/*.c"}, globs...)
|
||||||
|
}
|
||||||
|
|
||||||
var sources []string
|
var sources []string
|
||||||
seenSources := map[string]struct{}{}
|
seenSources := map[string]struct{}{}
|
||||||
@@ -140,13 +154,16 @@ var Musl = Library{
|
|||||||
// > ErrBadPattern, when pattern is malformed.
|
// > ErrBadPattern, when pattern is malformed.
|
||||||
// So the only possible error is when the (statically defined)
|
// So the only possible error is when the (statically defined)
|
||||||
// pattern is wrong. In other words, a programming bug.
|
// pattern is wrong. In other words, a programming bug.
|
||||||
panic("could not glob source dirs: " + err.Error())
|
return nil, fmt.Errorf("musl: could not glob source dirs: %w", err)
|
||||||
|
}
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, fmt.Errorf("musl: did not find any files for pattern %#v", pattern)
|
||||||
}
|
}
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
relpath, err := filepath.Rel(basepath, match)
|
relpath, err := filepath.Rel(basepath, match)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Not sure if this is even possible.
|
// Not sure if this is even possible.
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
// Make sure architecture specific files override generic files.
|
// Make sure architecture specific files override generic files.
|
||||||
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
|
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
|
||||||
@@ -158,7 +175,7 @@ var Musl = Library{
|
|||||||
sources = append(sources, relpath)
|
sources = append(sources, relpath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sources
|
return sources, nil
|
||||||
},
|
},
|
||||||
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
|
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -18,7 +18,7 @@ func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
|
cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
|
||||||
cmd.Stdout = ioutil.Discard
|
cmd.Stdout = io.Discard
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
|
return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ func extractROM(path string) (uint64, []byte, error) {
|
|||||||
// Pad the difference
|
// Pad the difference
|
||||||
rom = append(rom, make([]byte, diff)...)
|
rom = append(rom, make([]byte, diff)...)
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(prog.Open())
|
data, err := io.ReadAll(prog.Open())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err}
|
return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err}
|
||||||
}
|
}
|
||||||
|
|||||||
+384
-207
@@ -19,227 +19,404 @@ var Picolibc = Library{
|
|||||||
return f.Close()
|
return f.Close()
|
||||||
},
|
},
|
||||||
cflags: func(target, headerPath string) []string {
|
cflags: func(target, headerPath string) []string {
|
||||||
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
newlibDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib")
|
||||||
return []string{
|
return []string{
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-Wall",
|
"-Wall",
|
||||||
"-std=gnu11",
|
"-std=gnu11",
|
||||||
"-D_COMPILING_NEWLIB",
|
"-D_COMPILING_NEWLIB",
|
||||||
"-DHAVE_ALIAS_ATTRIBUTE",
|
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||||
"-DTINY_STDIO",
|
"-DTINY_STDIO",
|
||||||
|
"-DPOSIX_IO",
|
||||||
|
"-D_IEEE_LIBM",
|
||||||
|
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
||||||
|
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||||
|
"-D_WANT_IO_C99_FORMATS",
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", picolibcDir + "/include",
|
"-isystem", newlibDir + "/libc/include",
|
||||||
"-I" + picolibcDir + "/tinystdio",
|
"-I" + newlibDir + "/libc/tinystdio",
|
||||||
|
"-I" + newlibDir + "/libm/common",
|
||||||
"-I" + headerPath,
|
"-I" + headerPath,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||||
librarySources: func(target string) []string {
|
librarySources: func(target string) ([]string, error) {
|
||||||
return picolibcSources
|
return picolibcSources, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var picolibcSources = []string{
|
var picolibcSources = []string{
|
||||||
"../../../picolibc-stdio.c",
|
"../../picolibc-stdio.c",
|
||||||
|
|
||||||
"tinystdio/asprintf.c",
|
// srcs_tinystdio
|
||||||
"tinystdio/atod_engine.c",
|
"libc/tinystdio/asprintf.c",
|
||||||
"tinystdio/atod_ryu.c",
|
"libc/tinystdio/bufio.c",
|
||||||
"tinystdio/atof_engine.c",
|
"libc/tinystdio/clearerr.c",
|
||||||
"tinystdio/atof_ryu.c",
|
"libc/tinystdio/ecvt_r.c",
|
||||||
//"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
|
"libc/tinystdio/ecvt.c",
|
||||||
"tinystdio/clearerr.c",
|
"libc/tinystdio/ecvtf_r.c",
|
||||||
"tinystdio/compare_exchange.c",
|
"libc/tinystdio/ecvtf.c",
|
||||||
"tinystdio/dtoa_data.c",
|
"libc/tinystdio/fcvt.c",
|
||||||
"tinystdio/dtoa_engine.c",
|
"libc/tinystdio/fcvt_r.c",
|
||||||
"tinystdio/dtoa_ryu.c",
|
"libc/tinystdio/fcvtf.c",
|
||||||
"tinystdio/ecvtbuf.c",
|
"libc/tinystdio/fcvtf_r.c",
|
||||||
"tinystdio/ecvt.c",
|
"libc/tinystdio/gcvt.c",
|
||||||
"tinystdio/ecvt_data.c",
|
"libc/tinystdio/gcvtf.c",
|
||||||
"tinystdio/ecvtfbuf.c",
|
"libc/tinystdio/fclose.c",
|
||||||
"tinystdio/ecvtf.c",
|
"libc/tinystdio/fdevopen.c",
|
||||||
"tinystdio/ecvtf_data.c",
|
"libc/tinystdio/feof.c",
|
||||||
"tinystdio/exchange.c",
|
"libc/tinystdio/ferror.c",
|
||||||
//"tinystdio/fclose.c", // posix-io
|
"libc/tinystdio/fflush.c",
|
||||||
"tinystdio/fcvtbuf.c",
|
"libc/tinystdio/fgetc.c",
|
||||||
"tinystdio/fcvt.c",
|
"libc/tinystdio/fgets.c",
|
||||||
"tinystdio/fcvtfbuf.c",
|
"libc/tinystdio/fileno.c",
|
||||||
"tinystdio/fcvtf.c",
|
"libc/tinystdio/filestrget.c",
|
||||||
"tinystdio/fdevopen.c",
|
"libc/tinystdio/filestrput.c",
|
||||||
//"tinystdio/fdopen.c", // posix-io
|
"libc/tinystdio/filestrputalloc.c",
|
||||||
"tinystdio/feof.c",
|
"libc/tinystdio/fmemopen.c",
|
||||||
"tinystdio/ferror.c",
|
"libc/tinystdio/fprintf.c",
|
||||||
"tinystdio/fflush.c",
|
"libc/tinystdio/fputc.c",
|
||||||
"tinystdio/fgetc.c",
|
"libc/tinystdio/fputs.c",
|
||||||
"tinystdio/fgets.c",
|
"libc/tinystdio/fread.c",
|
||||||
"tinystdio/fileno.c",
|
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
|
||||||
"tinystdio/filestrget.c",
|
"libc/tinystdio/fscanf.c",
|
||||||
"tinystdio/filestrputalloc.c",
|
"libc/tinystdio/fseek.c",
|
||||||
"tinystdio/filestrput.c",
|
"libc/tinystdio/fseeko.c",
|
||||||
//"tinystdio/fopen.c", // posix-io
|
"libc/tinystdio/ftell.c",
|
||||||
"tinystdio/fprintf.c",
|
"libc/tinystdio/ftello.c",
|
||||||
"tinystdio/fputc.c",
|
"libc/tinystdio/fwrite.c",
|
||||||
"tinystdio/fputs.c",
|
"libc/tinystdio/getchar.c",
|
||||||
"tinystdio/fread.c",
|
"libc/tinystdio/gets.c",
|
||||||
"tinystdio/fscanf.c",
|
"libc/tinystdio/matchcaseprefix.c",
|
||||||
"tinystdio/fseek.c",
|
"libc/tinystdio/mktemp.c",
|
||||||
"tinystdio/ftell.c",
|
"libc/tinystdio/perror.c",
|
||||||
"tinystdio/ftoa_data.c",
|
"libc/tinystdio/printf.c",
|
||||||
"tinystdio/ftoa_engine.c",
|
"libc/tinystdio/putchar.c",
|
||||||
"tinystdio/ftoa_ryu.c",
|
"libc/tinystdio/puts.c",
|
||||||
"tinystdio/fwrite.c",
|
"libc/tinystdio/rewind.c",
|
||||||
"tinystdio/gcvtbuf.c",
|
"libc/tinystdio/scanf.c",
|
||||||
"tinystdio/gcvt.c",
|
"libc/tinystdio/setbuf.c",
|
||||||
"tinystdio/gcvtfbuf.c",
|
"libc/tinystdio/setbuffer.c",
|
||||||
"tinystdio/gcvtf.c",
|
"libc/tinystdio/setlinebuf.c",
|
||||||
"tinystdio/getchar.c",
|
"libc/tinystdio/setvbuf.c",
|
||||||
"tinystdio/gets.c",
|
"libc/tinystdio/snprintf.c",
|
||||||
"tinystdio/matchcaseprefix.c",
|
"libc/tinystdio/sprintf.c",
|
||||||
"tinystdio/perror.c",
|
"libc/tinystdio/snprintfd.c",
|
||||||
//"tinystdio/posixiob.c", // posix-io
|
"libc/tinystdio/snprintff.c",
|
||||||
//"tinystdio/posixio.c", // posix-io
|
"libc/tinystdio/sprintff.c",
|
||||||
"tinystdio/printf.c",
|
"libc/tinystdio/sprintfd.c",
|
||||||
"tinystdio/putchar.c",
|
"libc/tinystdio/sscanf.c",
|
||||||
"tinystdio/puts.c",
|
"libc/tinystdio/strfromf.c",
|
||||||
"tinystdio/ryu_divpow2.c",
|
"libc/tinystdio/strfromd.c",
|
||||||
"tinystdio/ryu_log10.c",
|
"libc/tinystdio/strtof.c",
|
||||||
"tinystdio/ryu_log2pow5.c",
|
"libc/tinystdio/strtof_l.c",
|
||||||
"tinystdio/ryu_pow5bits.c",
|
"libc/tinystdio/strtod.c",
|
||||||
"tinystdio/ryu_table.c",
|
"libc/tinystdio/strtod_l.c",
|
||||||
"tinystdio/ryu_umul128.c",
|
"libc/tinystdio/ungetc.c",
|
||||||
"tinystdio/scanf.c",
|
"libc/tinystdio/vasprintf.c",
|
||||||
"tinystdio/setbuf.c",
|
"libc/tinystdio/vfiprintf.c",
|
||||||
"tinystdio/setvbuf.c",
|
"libc/tinystdio/vfprintf.c",
|
||||||
//"tinystdio/sflags.c", // posix-io
|
"libc/tinystdio/vfprintff.c",
|
||||||
"tinystdio/snprintf.c",
|
"libc/tinystdio/vfscanf.c",
|
||||||
"tinystdio/snprintfd.c",
|
"libc/tinystdio/vfiscanf.c",
|
||||||
"tinystdio/snprintff.c",
|
"libc/tinystdio/vfscanff.c",
|
||||||
"tinystdio/sprintf.c",
|
"libc/tinystdio/vprintf.c",
|
||||||
"tinystdio/sprintfd.c",
|
"libc/tinystdio/vscanf.c",
|
||||||
"tinystdio/sprintff.c",
|
"libc/tinystdio/vsscanf.c",
|
||||||
"tinystdio/sscanf.c",
|
"libc/tinystdio/vsnprintf.c",
|
||||||
"tinystdio/strfromd.c",
|
"libc/tinystdio/vsprintf.c",
|
||||||
"tinystdio/strfromf.c",
|
|
||||||
"tinystdio/strtod.c",
|
|
||||||
"tinystdio/strtod_l.c",
|
|
||||||
"tinystdio/strtof.c",
|
|
||||||
//"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
|
|
||||||
//"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
|
|
||||||
"tinystdio/ungetc.c",
|
|
||||||
"tinystdio/vasprintf.c",
|
|
||||||
"tinystdio/vfiprintf.c",
|
|
||||||
"tinystdio/vfiscanf.c",
|
|
||||||
"tinystdio/vfprintf.c",
|
|
||||||
"tinystdio/vfprintff.c",
|
|
||||||
"tinystdio/vfscanf.c",
|
|
||||||
"tinystdio/vfscanff.c",
|
|
||||||
"tinystdio/vprintf.c",
|
|
||||||
"tinystdio/vscanf.c",
|
|
||||||
"tinystdio/vsnprintf.c",
|
|
||||||
"tinystdio/vsprintf.c",
|
|
||||||
"tinystdio/vsscanf.c",
|
|
||||||
|
|
||||||
"string/bcmp.c",
|
"libc/string/bcmp.c",
|
||||||
"string/bcopy.c",
|
"libc/string/bcopy.c",
|
||||||
"string/bzero.c",
|
"libc/string/bzero.c",
|
||||||
"string/explicit_bzero.c",
|
"libc/string/explicit_bzero.c",
|
||||||
"string/ffsl.c",
|
"libc/string/ffsl.c",
|
||||||
"string/ffsll.c",
|
"libc/string/ffsll.c",
|
||||||
"string/fls.c",
|
"libc/string/fls.c",
|
||||||
"string/flsl.c",
|
"libc/string/flsl.c",
|
||||||
"string/flsll.c",
|
"libc/string/flsll.c",
|
||||||
"string/gnu_basename.c",
|
"libc/string/gnu_basename.c",
|
||||||
"string/index.c",
|
"libc/string/index.c",
|
||||||
"string/memccpy.c",
|
"libc/string/memccpy.c",
|
||||||
"string/memchr.c",
|
"libc/string/memchr.c",
|
||||||
"string/memcmp.c",
|
"libc/string/memcmp.c",
|
||||||
"string/memcpy.c",
|
"libc/string/memcpy.c",
|
||||||
"string/memmem.c",
|
"libc/string/memmem.c",
|
||||||
"string/memmove.c",
|
"libc/string/memmove.c",
|
||||||
"string/mempcpy.c",
|
"libc/string/mempcpy.c",
|
||||||
"string/memrchr.c",
|
"libc/string/memrchr.c",
|
||||||
"string/memset.c",
|
"libc/string/memset.c",
|
||||||
"string/rawmemchr.c",
|
"libc/string/rawmemchr.c",
|
||||||
"string/rindex.c",
|
"libc/string/rindex.c",
|
||||||
"string/stpcpy.c",
|
"libc/string/stpcpy.c",
|
||||||
"string/stpncpy.c",
|
"libc/string/stpncpy.c",
|
||||||
"string/strcasecmp.c",
|
"libc/string/strcasecmp.c",
|
||||||
"string/strcasecmp_l.c",
|
"libc/string/strcasecmp_l.c",
|
||||||
"string/strcasestr.c",
|
"libc/string/strcasestr.c",
|
||||||
"string/strcat.c",
|
"libc/string/strcat.c",
|
||||||
"string/strchr.c",
|
"libc/string/strchr.c",
|
||||||
"string/strchrnul.c",
|
"libc/string/strchrnul.c",
|
||||||
"string/strcmp.c",
|
"libc/string/strcmp.c",
|
||||||
"string/strcoll.c",
|
"libc/string/strcoll.c",
|
||||||
"string/strcoll_l.c",
|
"libc/string/strcoll_l.c",
|
||||||
"string/strcpy.c",
|
"libc/string/strcpy.c",
|
||||||
"string/strcspn.c",
|
"libc/string/strcspn.c",
|
||||||
"string/strdup.c",
|
"libc/string/strdup.c",
|
||||||
"string/strerror.c",
|
"libc/string/strerror.c",
|
||||||
"string/strerror_r.c",
|
"libc/string/strerror_r.c",
|
||||||
"string/strlcat.c",
|
"libc/string/strlcat.c",
|
||||||
"string/strlcpy.c",
|
"libc/string/strlcpy.c",
|
||||||
"string/strlen.c",
|
"libc/string/strlen.c",
|
||||||
"string/strlwr.c",
|
"libc/string/strlwr.c",
|
||||||
"string/strncasecmp.c",
|
"libc/string/strncasecmp.c",
|
||||||
"string/strncasecmp_l.c",
|
"libc/string/strncasecmp_l.c",
|
||||||
"string/strncat.c",
|
"libc/string/strncat.c",
|
||||||
"string/strncmp.c",
|
"libc/string/strncmp.c",
|
||||||
"string/strncpy.c",
|
"libc/string/strncpy.c",
|
||||||
"string/strndup.c",
|
"libc/string/strndup.c",
|
||||||
"string/strnlen.c",
|
"libc/string/strnlen.c",
|
||||||
"string/strnstr.c",
|
"libc/string/strnstr.c",
|
||||||
"string/strpbrk.c",
|
"libc/string/strpbrk.c",
|
||||||
"string/strrchr.c",
|
"libc/string/strrchr.c",
|
||||||
"string/strsep.c",
|
"libc/string/strsep.c",
|
||||||
"string/strsignal.c",
|
"libc/string/strsignal.c",
|
||||||
"string/strspn.c",
|
"libc/string/strspn.c",
|
||||||
"string/strstr.c",
|
"libc/string/strstr.c",
|
||||||
"string/strtok.c",
|
"libc/string/strtok.c",
|
||||||
"string/strtok_r.c",
|
"libc/string/strtok_r.c",
|
||||||
"string/strupr.c",
|
"libc/string/strupr.c",
|
||||||
"string/strverscmp.c",
|
"libc/string/strverscmp.c",
|
||||||
"string/strxfrm.c",
|
"libc/string/strxfrm.c",
|
||||||
"string/strxfrm_l.c",
|
"libc/string/strxfrm_l.c",
|
||||||
"string/swab.c",
|
"libc/string/swab.c",
|
||||||
"string/timingsafe_bcmp.c",
|
"libc/string/timingsafe_bcmp.c",
|
||||||
"string/timingsafe_memcmp.c",
|
"libc/string/timingsafe_memcmp.c",
|
||||||
"string/u_strerr.c",
|
"libc/string/u_strerr.c",
|
||||||
"string/wcpcpy.c",
|
"libc/string/wcpcpy.c",
|
||||||
"string/wcpncpy.c",
|
"libc/string/wcpncpy.c",
|
||||||
"string/wcscasecmp.c",
|
"libc/string/wcscasecmp.c",
|
||||||
"string/wcscasecmp_l.c",
|
"libc/string/wcscasecmp_l.c",
|
||||||
"string/wcscat.c",
|
"libc/string/wcscat.c",
|
||||||
"string/wcschr.c",
|
"libc/string/wcschr.c",
|
||||||
"string/wcscmp.c",
|
"libc/string/wcscmp.c",
|
||||||
"string/wcscoll.c",
|
"libc/string/wcscoll.c",
|
||||||
"string/wcscoll_l.c",
|
"libc/string/wcscoll_l.c",
|
||||||
"string/wcscpy.c",
|
"libc/string/wcscpy.c",
|
||||||
"string/wcscspn.c",
|
"libc/string/wcscspn.c",
|
||||||
"string/wcsdup.c",
|
"libc/string/wcsdup.c",
|
||||||
"string/wcslcat.c",
|
"libc/string/wcslcat.c",
|
||||||
"string/wcslcpy.c",
|
"libc/string/wcslcpy.c",
|
||||||
"string/wcslen.c",
|
"libc/string/wcslen.c",
|
||||||
"string/wcsncasecmp.c",
|
"libc/string/wcsncasecmp.c",
|
||||||
"string/wcsncasecmp_l.c",
|
"libc/string/wcsncasecmp_l.c",
|
||||||
"string/wcsncat.c",
|
"libc/string/wcsncat.c",
|
||||||
"string/wcsncmp.c",
|
"libc/string/wcsncmp.c",
|
||||||
"string/wcsncpy.c",
|
"libc/string/wcsncpy.c",
|
||||||
"string/wcsnlen.c",
|
"libc/string/wcsnlen.c",
|
||||||
"string/wcspbrk.c",
|
"libc/string/wcspbrk.c",
|
||||||
"string/wcsrchr.c",
|
"libc/string/wcsrchr.c",
|
||||||
"string/wcsspn.c",
|
"libc/string/wcsspn.c",
|
||||||
"string/wcsstr.c",
|
"libc/string/wcsstr.c",
|
||||||
"string/wcstok.c",
|
"libc/string/wcstok.c",
|
||||||
"string/wcswidth.c",
|
"libc/string/wcswidth.c",
|
||||||
"string/wcsxfrm.c",
|
"libc/string/wcsxfrm.c",
|
||||||
"string/wcsxfrm_l.c",
|
"libc/string/wcsxfrm_l.c",
|
||||||
"string/wcwidth.c",
|
"libc/string/wcwidth.c",
|
||||||
"string/wmemchr.c",
|
"libc/string/wmemchr.c",
|
||||||
"string/wmemcmp.c",
|
"libc/string/wmemcmp.c",
|
||||||
"string/wmemcpy.c",
|
"libc/string/wmemcpy.c",
|
||||||
"string/wmemmove.c",
|
"libc/string/wmemmove.c",
|
||||||
"string/wmempcpy.c",
|
"libc/string/wmempcpy.c",
|
||||||
"string/wmemset.c",
|
"libc/string/wmemset.c",
|
||||||
"string/xpg_strerror_r.c",
|
"libc/string/xpg_strerror_r.c",
|
||||||
|
|
||||||
|
"libm/common/sf_finite.c",
|
||||||
|
"libm/common/sf_copysign.c",
|
||||||
|
"libm/common/sf_modf.c",
|
||||||
|
"libm/common/sf_scalbn.c",
|
||||||
|
"libm/common/sf_cbrt.c",
|
||||||
|
"libm/common/sf_exp10.c",
|
||||||
|
"libm/common/sf_expm1.c",
|
||||||
|
"libm/common/sf_ilogb.c",
|
||||||
|
"libm/common/sf_infinity.c",
|
||||||
|
"libm/common/sf_isinf.c",
|
||||||
|
"libm/common/sf_isinff.c",
|
||||||
|
"libm/common/sf_isnan.c",
|
||||||
|
"libm/common/sf_isnanf.c",
|
||||||
|
"libm/common/sf_issignaling.c",
|
||||||
|
"libm/common/sf_log1p.c",
|
||||||
|
"libm/common/sf_nan.c",
|
||||||
|
"libm/common/sf_nextafter.c",
|
||||||
|
"libm/common/sf_pow10.c",
|
||||||
|
"libm/common/sf_rint.c",
|
||||||
|
"libm/common/sf_logb.c",
|
||||||
|
"libm/common/sf_fdim.c",
|
||||||
|
"libm/common/sf_fma.c",
|
||||||
|
"libm/common/sf_fmax.c",
|
||||||
|
"libm/common/sf_fmin.c",
|
||||||
|
"libm/common/sf_fpclassify.c",
|
||||||
|
"libm/common/sf_lrint.c",
|
||||||
|
"libm/common/sf_llrint.c",
|
||||||
|
"libm/common/sf_lround.c",
|
||||||
|
"libm/common/sf_llround.c",
|
||||||
|
"libm/common/sf_nearbyint.c",
|
||||||
|
"libm/common/sf_remquo.c",
|
||||||
|
"libm/common/sf_round.c",
|
||||||
|
"libm/common/sf_scalbln.c",
|
||||||
|
"libm/common/sf_trunc.c",
|
||||||
|
"libm/common/sf_exp.c",
|
||||||
|
"libm/common/sf_exp2.c",
|
||||||
|
"libm/common/sf_exp2_data.c",
|
||||||
|
"libm/common/sf_log.c",
|
||||||
|
"libm/common/sf_log_data.c",
|
||||||
|
"libm/common/sf_log2.c",
|
||||||
|
"libm/common/sf_log2_data.c",
|
||||||
|
"libm/common/sf_pow_log2_data.c",
|
||||||
|
"libm/common/sf_pow.c",
|
||||||
|
|
||||||
|
"libm/common/s_finite.c",
|
||||||
|
"libm/common/s_copysign.c",
|
||||||
|
"libm/common/s_modf.c",
|
||||||
|
"libm/common/s_scalbn.c",
|
||||||
|
"libm/common/s_cbrt.c",
|
||||||
|
"libm/common/s_exp10.c",
|
||||||
|
"libm/common/s_expm1.c",
|
||||||
|
"libm/common/s_ilogb.c",
|
||||||
|
"libm/common/s_infinity.c",
|
||||||
|
"libm/common/s_iseqsig.c",
|
||||||
|
"libm/common/s_isinf.c",
|
||||||
|
"libm/common/s_isinfd.c",
|
||||||
|
"libm/common/s_isnan.c",
|
||||||
|
"libm/common/s_isnand.c",
|
||||||
|
"libm/common/s_issignaling.c",
|
||||||
|
"libm/common/s_log1p.c",
|
||||||
|
"libm/common/s_nan.c",
|
||||||
|
"libm/common/s_nextafter.c",
|
||||||
|
"libm/common/s_pow10.c",
|
||||||
|
"libm/common/s_rint.c",
|
||||||
|
"libm/common/s_logb.c",
|
||||||
|
"libm/common/s_log2.c",
|
||||||
|
"libm/common/s_fdim.c",
|
||||||
|
"libm/common/s_fma.c",
|
||||||
|
"libm/common/s_fmax.c",
|
||||||
|
"libm/common/s_fmin.c",
|
||||||
|
"libm/common/s_fpclassify.c",
|
||||||
|
"libm/common/s_getpayload.c",
|
||||||
|
"libm/common/s_lrint.c",
|
||||||
|
"libm/common/s_llrint.c",
|
||||||
|
"libm/common/s_lround.c",
|
||||||
|
"libm/common/s_llround.c",
|
||||||
|
"libm/common/s_nearbyint.c",
|
||||||
|
"libm/common/s_remquo.c",
|
||||||
|
"libm/common/s_round.c",
|
||||||
|
"libm/common/s_scalbln.c",
|
||||||
|
"libm/common/s_signbit.c",
|
||||||
|
"libm/common/s_trunc.c",
|
||||||
|
"libm/common/exp.c",
|
||||||
|
"libm/common/exp2.c",
|
||||||
|
"libm/common/exp_data.c",
|
||||||
|
"libm/common/math_err_with_errno.c",
|
||||||
|
"libm/common/math_err_uflow.c",
|
||||||
|
"libm/common/math_err_oflow.c",
|
||||||
|
"libm/common/math_err_divzero.c",
|
||||||
|
"libm/common/math_err_invalid.c",
|
||||||
|
"libm/common/math_err_may_uflow.c",
|
||||||
|
"libm/common/math_err_check_uflow.c",
|
||||||
|
"libm/common/math_err_check_oflow.c",
|
||||||
|
"libm/common/math_inexact.c",
|
||||||
|
"libm/common/math_inexactf.c",
|
||||||
|
"libm/common/log.c",
|
||||||
|
"libm/common/log_data.c",
|
||||||
|
"libm/common/log2.c",
|
||||||
|
"libm/common/log2_data.c",
|
||||||
|
"libm/common/pow.c",
|
||||||
|
"libm/common/pow_log_data.c",
|
||||||
|
|
||||||
|
"libm/math/k_cos.c",
|
||||||
|
"libm/math/k_rem_pio2.c",
|
||||||
|
"libm/math/k_sin.c",
|
||||||
|
"libm/math/k_tan.c",
|
||||||
|
"libm/math/kf_cos.c",
|
||||||
|
"libm/math/kf_rem_pio2.c",
|
||||||
|
"libm/math/kf_sin.c",
|
||||||
|
"libm/math/kf_tan.c",
|
||||||
|
"libm/math/s_acos.c",
|
||||||
|
"libm/math/s_acosh.c",
|
||||||
|
"libm/math/s_asin.c",
|
||||||
|
"libm/math/s_asinh.c",
|
||||||
|
"libm/math/s_atan.c",
|
||||||
|
"libm/math/s_atan2.c",
|
||||||
|
"libm/math/s_atanh.c",
|
||||||
|
"libm/math/s_ceil.c",
|
||||||
|
"libm/math/s_cos.c",
|
||||||
|
"libm/math/s_cosh.c",
|
||||||
|
"libm/math/s_drem.c",
|
||||||
|
"libm/math/s_erf.c",
|
||||||
|
"libm/math/s_exp.c",
|
||||||
|
"libm/math/s_exp2.c",
|
||||||
|
"libm/math/s_fabs.c",
|
||||||
|
"libm/math/s_floor.c",
|
||||||
|
"libm/math/s_fmod.c",
|
||||||
|
"libm/math/s_frexp.c",
|
||||||
|
"libm/math/s_gamma.c",
|
||||||
|
"libm/math/s_hypot.c",
|
||||||
|
"libm/math/s_j0.c",
|
||||||
|
"libm/math/s_j1.c",
|
||||||
|
"libm/math/s_jn.c",
|
||||||
|
"libm/math/s_lgamma.c",
|
||||||
|
"libm/math/s_log.c",
|
||||||
|
"libm/math/s_log10.c",
|
||||||
|
"libm/math/s_pow.c",
|
||||||
|
"libm/math/s_rem_pio2.c",
|
||||||
|
"libm/math/s_remainder.c",
|
||||||
|
"libm/math/s_scalb.c",
|
||||||
|
"libm/math/s_signif.c",
|
||||||
|
"libm/math/s_sin.c",
|
||||||
|
"libm/math/s_sincos.c",
|
||||||
|
"libm/math/s_sinh.c",
|
||||||
|
"libm/math/s_sqrt.c",
|
||||||
|
"libm/math/s_tan.c",
|
||||||
|
"libm/math/s_tanh.c",
|
||||||
|
"libm/math/s_tgamma.c",
|
||||||
|
"libm/math/sf_acos.c",
|
||||||
|
"libm/math/sf_acosh.c",
|
||||||
|
"libm/math/sf_asin.c",
|
||||||
|
"libm/math/sf_asinh.c",
|
||||||
|
"libm/math/sf_atan.c",
|
||||||
|
"libm/math/sf_atan2.c",
|
||||||
|
"libm/math/sf_atanh.c",
|
||||||
|
"libm/math/sf_ceil.c",
|
||||||
|
"libm/math/sf_cos.c",
|
||||||
|
"libm/math/sf_cosh.c",
|
||||||
|
"libm/math/sf_drem.c",
|
||||||
|
"libm/math/sf_erf.c",
|
||||||
|
"libm/math/sf_exp.c",
|
||||||
|
"libm/math/sf_exp2.c",
|
||||||
|
"libm/math/sf_fabs.c",
|
||||||
|
"libm/math/sf_floor.c",
|
||||||
|
"libm/math/sf_fmod.c",
|
||||||
|
"libm/math/sf_frexp.c",
|
||||||
|
"libm/math/sf_gamma.c",
|
||||||
|
"libm/math/sf_hypot.c",
|
||||||
|
"libm/math/sf_j0.c",
|
||||||
|
"libm/math/sf_j1.c",
|
||||||
|
"libm/math/sf_jn.c",
|
||||||
|
"libm/math/sf_lgamma.c",
|
||||||
|
"libm/math/sf_log.c",
|
||||||
|
"libm/math/sf_log10.c",
|
||||||
|
"libm/math/sf_log2.c",
|
||||||
|
"libm/math/sf_pow.c",
|
||||||
|
"libm/math/sf_rem_pio2.c",
|
||||||
|
"libm/math/sf_remainder.c",
|
||||||
|
"libm/math/sf_scalb.c",
|
||||||
|
"libm/math/sf_signif.c",
|
||||||
|
"libm/math/sf_sin.c",
|
||||||
|
"libm/math/sf_sincos.c",
|
||||||
|
"libm/math/sf_sinh.c",
|
||||||
|
"libm/math/sf_sqrt.c",
|
||||||
|
"libm/math/sf_tan.c",
|
||||||
|
"libm/math/sf_tanh.c",
|
||||||
|
"libm/math/sf_tgamma.c",
|
||||||
|
"libm/math/sr_lgamma.c",
|
||||||
|
"libm/math/srf_lgamma.c",
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-8
@@ -118,10 +118,6 @@ var (
|
|||||||
// pack: data created when storing a constant in an interface for example
|
// pack: data created when storing a constant in an interface for example
|
||||||
// string: buffer behind strings
|
// string: buffer behind strings
|
||||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
||||||
|
|
||||||
// Reflect sidetables. Created by the reflect lowering pass.
|
|
||||||
// See src/reflect/sidetables.go.
|
|
||||||
reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// readProgramSizeFromDWARF reads the source location for each line of code and
|
// readProgramSizeFromDWARF reads the source location for each line of code and
|
||||||
@@ -375,7 +371,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
if section.Flags&elf.SHF_ALLOC == 0 {
|
if section.Flags&elf.SHF_ALLOC == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
|
if packageSymbolRegexp.MatchString(symbol.Name) {
|
||||||
addresses = append(addresses, addressLine{
|
addresses = append(addresses, addressLine{
|
||||||
Address: symbol.Value,
|
Address: symbol.Value,
|
||||||
Length: symbol.Size,
|
Length: symbol.Size,
|
||||||
@@ -836,9 +832,8 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
|
|||||||
} else if packageSymbolRegexp.MatchString(path) {
|
} else if packageSymbolRegexp.MatchString(path) {
|
||||||
// Parse symbol names like main$alloc or runtime$string.
|
// Parse symbol names like main$alloc or runtime$string.
|
||||||
packagePath = path[:strings.LastIndex(path, "$")]
|
packagePath = path[:strings.LastIndex(path, "$")]
|
||||||
} else if reflectDataRegexp.MatchString(path) {
|
} else if path == "<Go type>" {
|
||||||
// Parse symbol names like reflect.structTypesSidetable.
|
packagePath = "Go types"
|
||||||
packagePath = "Go reflect data"
|
|
||||||
} else if path == "<Go interface assert>" {
|
} else if path == "<Go interface assert>" {
|
||||||
// Interface type assert, generated by the interface lowering pass.
|
// Interface type assert, generated by the interface lowering pass.
|
||||||
packagePath = "Go interface assert"
|
packagePath = "Go interface assert"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build byollvm
|
//go:build byollvm
|
||||||
// +build byollvm
|
|
||||||
|
|
||||||
package builder
|
package builder
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ const hasBuiltinTools = true
|
|||||||
func RunTool(tool string, args ...string) error {
|
func RunTool(tool string, args ...string) error {
|
||||||
linker := "elf"
|
linker := "elf"
|
||||||
if tool == "ld.lld" && len(args) >= 2 {
|
if tool == "ld.lld" && len(args) >= 2 {
|
||||||
if args[0] == "-m" && args[1] == "i386pep" {
|
if args[0] == "-m" && (args[1] == "i386pep" || args[1] == "arm64pe") {
|
||||||
linker = "mingw"
|
linker = "mingw"
|
||||||
} else if args[0] == "-flavor" {
|
} else if args[0] == "-flavor" {
|
||||||
linker = args[1]
|
linker = args[1]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !byollvm
|
//go:build !byollvm
|
||||||
// +build !byollvm
|
|
||||||
|
|
||||||
package builder
|
package builder
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(outfile, output, 0644)
|
return os.WriteFile(outfile, output, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
||||||
|
|||||||
+83
-31
@@ -32,6 +32,7 @@ type cgoPackage struct {
|
|||||||
errors []error
|
errors []error
|
||||||
currentDir string // current working directory
|
currentDir string // current working directory
|
||||||
packageDir string // full path to the package to process
|
packageDir string // full path to the package to process
|
||||||
|
importPath string
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
tokenFiles map[string]*token.File
|
tokenFiles map[string]*token.File
|
||||||
definedGlobally map[string]ast.Node
|
definedGlobally map[string]ast.Node
|
||||||
@@ -39,12 +40,15 @@ type cgoPackage struct {
|
|||||||
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
|
||||||
|
cgoHeaders []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// cgoFile holds information only for a single Go file (with one or more
|
// cgoFile holds information only for a single Go file (with one or more
|
||||||
// `import "C"` statements).
|
// `import "C"` statements).
|
||||||
type cgoFile struct {
|
type cgoFile struct {
|
||||||
*cgoPackage
|
*cgoPackage
|
||||||
|
file *ast.File
|
||||||
|
index int
|
||||||
defined map[string]ast.Node
|
defined map[string]ast.Node
|
||||||
names map[string]clangCursor
|
names map[string]clangCursor
|
||||||
}
|
}
|
||||||
@@ -82,6 +86,8 @@ var cgoAliases = map[string]string{
|
|||||||
"C.uint32_t": "uint32",
|
"C.uint32_t": "uint32",
|
||||||
"C.uint64_t": "uint64",
|
"C.uint64_t": "uint64",
|
||||||
"C.uintptr_t": "uintptr",
|
"C.uintptr_t": "uintptr",
|
||||||
|
"C.float": "float32",
|
||||||
|
"C.double": "float64",
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtinAliases are handled specially because they only exist on the Go side
|
// builtinAliases are handled specially because they only exist on the Go side
|
||||||
@@ -158,9 +164,10 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||||
// hashes of the accessed C header files. If there is one or more error, it
|
// hashes of the accessed C header files. If there is one or more error, it
|
||||||
// returns these in the []error slice but still modifies the AST.
|
// returns these in the []error slice but still modifies the AST.
|
||||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
currentDir: dir,
|
currentDir: dir,
|
||||||
|
importPath: importPath,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
tokenFiles: map[string]*token.File{},
|
tokenFiles: map[string]*token.File{},
|
||||||
definedGlobally: map[string]ast.Node{},
|
definedGlobally: map[string]ast.Node{},
|
||||||
@@ -210,13 +217,13 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Patch some types, for example *C.char in C.CString.
|
// Patch some types, for example *C.char in C.CString.
|
||||||
cf := p.newCGoFile()
|
cf := p.newCGoFile(nil, -1) // dummy *cgoFile for the walker
|
||||||
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
|
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
|
||||||
return cf.walker(cursor, nil)
|
return cf.walker(cursor, nil)
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
// Find `import "C"` C fragments in the file.
|
// Find `import "C"` C fragments in the file.
|
||||||
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 string
|
||||||
for i := 0; i < len(f.Decls); i++ {
|
for i := 0; i < len(f.Decls); i++ {
|
||||||
@@ -275,7 +282,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
cgoHeader += fragment
|
cgoHeader += fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
cgoHeaders[i] = cgoHeader
|
p.cgoHeaders[i] = cgoHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define CFlags that will be used while parsing the package.
|
// Define CFlags that will be used while parsing the package.
|
||||||
@@ -289,7 +296,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve types such as C.int, C.longlong, etc from C.
|
// Retrieve types such as C.int, C.longlong, etc from C.
|
||||||
p.newCGoFile().readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
|
p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: token.NoPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
@@ -303,8 +310,14 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
|
|
||||||
// Process CGo imports for each file.
|
// Process CGo imports for each file.
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
cf := p.newCGoFile()
|
cf := p.newCGoFile(f, i)
|
||||||
cf.readNames(cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
// Float and double are aliased, meaning that C.float is the same thing
|
||||||
|
// as float32 in Go.
|
||||||
|
cf.names["float"] = clangCursor{}
|
||||||
|
cf.names["double"] = clangCursor{}
|
||||||
|
// Now read all the names (identifies) that C defines in the header
|
||||||
|
// snippet.
|
||||||
|
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
||||||
for _, name := range builtinAliases {
|
for _, name := range builtinAliases {
|
||||||
// Names such as C.int should not be obtained from C.
|
// Names such as C.int should not be obtained from C.
|
||||||
// This works around an issue in picolibc that has `#define int`
|
// This works around an issue in picolibc that has `#define int`
|
||||||
@@ -320,12 +333,14 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
// Print the newly generated in-memory AST, for debugging.
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
//ast.Print(fset, p.generated)
|
//ast.Print(fset, p.generated)
|
||||||
|
|
||||||
return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) newCGoFile() *cgoFile {
|
func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
|
||||||
return &cgoFile{
|
return &cgoFile{
|
||||||
cgoPackage: p,
|
cgoPackage: p,
|
||||||
|
file: file,
|
||||||
|
index: index,
|
||||||
defined: make(map[string]ast.Node),
|
defined: make(map[string]ast.Node),
|
||||||
names: make(map[string]clangCursor),
|
names: make(map[string]clangCursor),
|
||||||
}
|
}
|
||||||
@@ -493,15 +508,15 @@ func (p *cgoPackage) makeUnionField(typ *elaboratedTypeInfo) *ast.StructType {
|
|||||||
// createUnionAccessor creates a function that returns a typed pointer to a
|
// createUnionAccessor creates a function that returns a typed pointer to a
|
||||||
// union field for each field in a union. For example:
|
// union field for each field in a union. For example:
|
||||||
//
|
//
|
||||||
// func (union *C.union_1) unionfield_d() *float64 {
|
// func (union *C.union_1) unionfield_d() *float64 {
|
||||||
// return (*float64)(unsafe.Pointer(&union.$union))
|
// return (*float64)(unsafe.Pointer(&union.$union))
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Where C.union_1 is defined as:
|
// Where C.union_1 is defined as:
|
||||||
//
|
//
|
||||||
// type C.union_1 struct{
|
// type C.union_1 struct{
|
||||||
// $union uint64
|
// $union uint64
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The returned pointer can be used to get or set the field, or get the pointer
|
// The returned pointer can be used to get or set the field, or get the pointer
|
||||||
// to a subfield.
|
// to a subfield.
|
||||||
@@ -617,9 +632,9 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
|||||||
|
|
||||||
// createBitfieldGetter creates a bitfield getter function like the following:
|
// createBitfieldGetter creates a bitfield getter function like the following:
|
||||||
//
|
//
|
||||||
// func (s *C.struct_foo) bitfield_b() byte {
|
// func (s *C.struct_foo) bitfield_b() byte {
|
||||||
// return (s.__bitfield_1 >> 5) & 0x1
|
// return (s.__bitfield_1 >> 5) & 0x1
|
||||||
// }
|
// }
|
||||||
func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string) {
|
func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string) {
|
||||||
// The value to return from the getter.
|
// The value to return from the getter.
|
||||||
// Not complete: this is just an expression to get the complete field.
|
// Not complete: this is just an expression to get the complete field.
|
||||||
@@ -729,15 +744,15 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
|||||||
|
|
||||||
// createBitfieldSetter creates a bitfield setter function like the following:
|
// createBitfieldSetter creates a bitfield setter function like the following:
|
||||||
//
|
//
|
||||||
// func (s *C.struct_foo) set_bitfield_b(value byte) {
|
// func (s *C.struct_foo) set_bitfield_b(value byte) {
|
||||||
// s.__bitfield_1 = s.__bitfield_1 ^ 0x60 | ((value & 1) << 5)
|
// s.__bitfield_1 = s.__bitfield_1 ^ 0x60 | ((value & 1) << 5)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Or the following:
|
// Or the following:
|
||||||
//
|
//
|
||||||
// func (s *C.struct_foo) set_bitfield_c(value byte) {
|
// func (s *C.struct_foo) set_bitfield_c(value byte) {
|
||||||
// s.__bitfield_1 = s.__bitfield_1 & 0x3f | (value << 6)
|
// s.__bitfield_1 = s.__bitfield_1 & 0x3f | (value << 6)
|
||||||
// }
|
// }
|
||||||
func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string) {
|
func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string) {
|
||||||
// The full field with all bitfields.
|
// The full field with all bitfields.
|
||||||
var field ast.Expr = &ast.SelectorExpr{
|
var field ast.Expr = &ast.SelectorExpr{
|
||||||
@@ -941,6 +956,9 @@ func (p *cgoPackage) isEquivalentAST(a, b ast.Node) bool {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if node == nil || b == nil {
|
||||||
|
return node == b
|
||||||
|
}
|
||||||
if len(node.List) != len(b.List) {
|
if len(node.List) != len(b.List) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -1117,8 +1135,11 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st
|
|||||||
return alias
|
return alias
|
||||||
}
|
}
|
||||||
node := f.getASTDeclNode(name, found, iscall)
|
node := f.getASTDeclNode(name, found, iscall)
|
||||||
if _, ok := node.(*ast.FuncDecl); ok && !iscall {
|
if node, ok := node.(*ast.FuncDecl); ok {
|
||||||
return "C." + name + "$funcaddr"
|
if !iscall {
|
||||||
|
return node.Name.Name + "$funcaddr"
|
||||||
|
}
|
||||||
|
return node.Name.Name
|
||||||
}
|
}
|
||||||
return "C." + name
|
return "C." + name
|
||||||
}
|
}
|
||||||
@@ -1142,7 +1163,7 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
|
|||||||
// Original cgo reports an error like
|
// Original cgo reports an error like
|
||||||
// cgo: inconsistent definitions for C.myint
|
// cgo: inconsistent definitions for C.myint
|
||||||
// which is far less helpful.
|
// which is far less helpful.
|
||||||
f.addError(getPos(node), "defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
|
f.addError(getPos(node), name+" defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
|
||||||
}
|
}
|
||||||
f.defined[name] = node
|
f.defined[name] = node
|
||||||
return node
|
return node
|
||||||
@@ -1150,11 +1171,39 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
|
|||||||
|
|
||||||
// The declaration has no AST node. Create it now.
|
// The declaration has no AST node. Create it now.
|
||||||
f.defined[name] = nil
|
f.defined[name] = nil
|
||||||
node, elaboratedType := f.createASTNode(name, found)
|
node, extra := f.createASTNode(name, found)
|
||||||
f.defined[name] = node
|
f.defined[name] = node
|
||||||
f.definedGlobally[name] = node
|
|
||||||
switch node := node.(type) {
|
switch node := node.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
|
if strings.HasPrefix(node.Doc.List[0].Text, "//export _Cgo_static_") {
|
||||||
|
// Static function. Only accessible in the current Go file.
|
||||||
|
globalName := strings.TrimPrefix(node.Doc.List[0].Text, "//export ")
|
||||||
|
// Make an alias. Normally this is done using the alias function
|
||||||
|
// attribute, but MacOS for some reason doesn't support this (even
|
||||||
|
// though the linker has support for aliases in the form of N_INDR).
|
||||||
|
// Therefore, create an actual function for MacOS.
|
||||||
|
var params []string
|
||||||
|
for _, param := range node.Type.Params.List {
|
||||||
|
params = append(params, param.Names[0].Name)
|
||||||
|
}
|
||||||
|
callInst := fmt.Sprintf("%s(%s);", name, strings.Join(params, ", "))
|
||||||
|
if node.Type.Results != nil {
|
||||||
|
callInst = "return " + callInst
|
||||||
|
}
|
||||||
|
aliasDeclaration := fmt.Sprintf(`
|
||||||
|
#ifdef __APPLE__
|
||||||
|
%s {
|
||||||
|
%s
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
extern __typeof(%s) %s __attribute__((alias(%#v)));
|
||||||
|
#endif
|
||||||
|
`, extra.(string), callInst, name, globalName, name)
|
||||||
|
f.cgoHeaders[f.index] += "\n\n" + aliasDeclaration
|
||||||
|
} else {
|
||||||
|
// Regular (non-static) function.
|
||||||
|
f.definedGlobally[name] = node
|
||||||
|
}
|
||||||
f.generated.Decls = append(f.generated.Decls, node)
|
f.generated.Decls = append(f.generated.Decls, node)
|
||||||
// Also add a declaration like the following:
|
// Also add a declaration like the following:
|
||||||
// var C.foo$funcaddr unsafe.Pointer
|
// var C.foo$funcaddr unsafe.Pointer
|
||||||
@@ -1162,7 +1211,7 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
|
|||||||
Tok: token.VAR,
|
Tok: token.VAR,
|
||||||
Specs: []ast.Spec{
|
Specs: []ast.Spec{
|
||||||
&ast.ValueSpec{
|
&ast.ValueSpec{
|
||||||
Names: []*ast.Ident{{Name: "C." + name + "$funcaddr"}},
|
Names: []*ast.Ident{{Name: node.Name.Name + "$funcaddr"}},
|
||||||
Type: &ast.SelectorExpr{
|
Type: &ast.SelectorExpr{
|
||||||
X: &ast.Ident{Name: "unsafe"},
|
X: &ast.Ident{Name: "unsafe"},
|
||||||
Sel: &ast.Ident{Name: "Pointer"},
|
Sel: &ast.Ident{Name: "Pointer"},
|
||||||
@@ -1171,8 +1220,10 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
case *ast.GenDecl:
|
case *ast.GenDecl:
|
||||||
|
f.definedGlobally[name] = node
|
||||||
f.generated.Decls = append(f.generated.Decls, node)
|
f.generated.Decls = append(f.generated.Decls, node)
|
||||||
case *ast.TypeSpec:
|
case *ast.TypeSpec:
|
||||||
|
f.definedGlobally[name] = node
|
||||||
f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
|
f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
Specs: []ast.Spec{node},
|
Specs: []ast.Spec{node},
|
||||||
@@ -1186,7 +1237,8 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
|
|||||||
|
|
||||||
// If this is a struct or union it may need bitfields or union accessor
|
// If this is a struct or union it may need bitfields or union accessor
|
||||||
// methods.
|
// methods.
|
||||||
if elaboratedType != nil {
|
switch elaboratedType := extra.(type) {
|
||||||
|
case *elaboratedTypeInfo:
|
||||||
// Add struct bitfields.
|
// Add struct bitfields.
|
||||||
for _, bitfield := range elaboratedType.bitfields {
|
for _, bitfield := range elaboratedType.bitfields {
|
||||||
f.createBitfieldGetter(bitfield, "C."+name)
|
f.createBitfieldGetter(bitfield, "C."+name)
|
||||||
|
|||||||
+93
-23
@@ -5,13 +5,13 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/format"
|
"go/format"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -22,9 +22,15 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
|||||||
|
|
||||||
// normalizeResult normalizes Go source code that comes out of tests across
|
// normalizeResult normalizes Go source code that comes out of tests across
|
||||||
// platforms and Go versions.
|
// platforms and Go versions.
|
||||||
func normalizeResult(result string) string {
|
func normalizeResult(t *testing.T, result string) string {
|
||||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
result = strings.ReplaceAll(result, "\r\n", "\n")
|
||||||
return actual
|
|
||||||
|
// This changed to 'undefined:', in Go 1.20.
|
||||||
|
result = strings.ReplaceAll(result, ": undeclared name:", ": undefined:")
|
||||||
|
// Go 1.20 added a bit more detail
|
||||||
|
result = regexp.MustCompile(`(unknown field z in struct literal).*`).ReplaceAllString(result, "$1")
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCGo(t *testing.T) {
|
func TestCGo(t *testing.T) {
|
||||||
@@ -40,20 +46,6 @@ func TestCGo(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
name := name // avoid a race condition
|
name := name // avoid a race condition
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
// Skip tests that require specific Go version.
|
|
||||||
if name == "errors" {
|
|
||||||
ok := false
|
|
||||||
for _, version := range build.Default.ReleaseTags {
|
|
||||||
if version == "go1.16" {
|
|
||||||
ok = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
t.Skip("Results for errors test are only valid for Go 1.16+")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the AST in memory.
|
// Read the AST in memory.
|
||||||
path := filepath.Join("testdata", name+".go")
|
path := filepath.Join("testdata", name+".go")
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
@@ -63,7 +55,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the AST with CGo.
|
// Process the AST with CGo.
|
||||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags, "")
|
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "")
|
||||||
|
|
||||||
// Check the AST for type errors.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
@@ -103,11 +95,11 @@ func TestCGo(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not write out CGo AST: %v", err)
|
t.Errorf("could not write out CGo AST: %v", err)
|
||||||
}
|
}
|
||||||
actual := normalizeResult(buf.String())
|
actual := normalizeResult(t, buf.String())
|
||||||
|
|
||||||
// Read the file with the expected output, to compare against.
|
// Read the file with the expected output, to compare against.
|
||||||
outfile := filepath.Join("testdata", name+".out.go")
|
outfile := filepath.Join("testdata", name+".out.go")
|
||||||
expectedBytes, err := ioutil.ReadFile(outfile)
|
expectedBytes, err := os.ReadFile(outfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not read expected output: %v", err)
|
t.Fatalf("could not read expected output: %v", err)
|
||||||
}
|
}
|
||||||
@@ -118,7 +110,7 @@ func TestCGo(t *testing.T) {
|
|||||||
// It is not. Test failed.
|
// It is not. Test failed.
|
||||||
if *flagUpdate {
|
if *flagUpdate {
|
||||||
// Update the file with the expected data.
|
// Update the file with the expected data.
|
||||||
err := ioutil.WriteFile(outfile, []byte(actual), 0666)
|
err := os.WriteFile(outfile, []byte(actual), 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("could not write updated output file:", err)
|
t.Error("could not write updated output file:", err)
|
||||||
}
|
}
|
||||||
@@ -130,6 +122,84 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_cgoPackage_isEquivalentAST(t *testing.T) {
|
||||||
|
fieldA := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "a"}}
|
||||||
|
fieldB := &ast.Field{Type: &ast.BasicLit{Kind: token.STRING, Value: "b"}}
|
||||||
|
listOfFieldA := &ast.FieldList{List: []*ast.Field{fieldA}}
|
||||||
|
listOfFieldB := &ast.FieldList{List: []*ast.Field{fieldB}}
|
||||||
|
funcDeclA := &ast.FuncDecl{Name: &ast.Ident{Name: "a"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldA}}
|
||||||
|
funcDeclB := &ast.FuncDecl{Name: &ast.Ident{Name: "b"}, Type: &ast.FuncType{Params: &ast.FieldList{}, Results: listOfFieldB}}
|
||||||
|
funcDeclNoResults := &ast.FuncDecl{Name: &ast.Ident{Name: "C"}, Type: &ast.FuncType{Params: &ast.FieldList{}}}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
a, b ast.Node
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "both nil",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not same type",
|
||||||
|
a: fieldA,
|
||||||
|
b: &ast.FuncDecl{},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Field same",
|
||||||
|
a: fieldA,
|
||||||
|
b: fieldA,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Field different",
|
||||||
|
a: fieldA,
|
||||||
|
b: fieldB,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FuncDecl Type Results nil",
|
||||||
|
a: funcDeclNoResults,
|
||||||
|
b: funcDeclNoResults,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FuncDecl Type Results same",
|
||||||
|
a: funcDeclA,
|
||||||
|
b: funcDeclA,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FuncDecl Type Results different",
|
||||||
|
a: funcDeclA,
|
||||||
|
b: funcDeclB,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FuncDecl Type Results a nil",
|
||||||
|
a: funcDeclNoResults,
|
||||||
|
b: funcDeclB,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FuncDecl Type Results b nil",
|
||||||
|
a: funcDeclA,
|
||||||
|
b: funcDeclNoResults,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
p := &cgoPackage{}
|
||||||
|
if got := p.isEquivalentAST(tc.a, tc.b); tc.expected != got {
|
||||||
|
t.Errorf("expected %v, got %v", tc.expected, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// simpleImporter implements the types.Importer interface, but only allows
|
// simpleImporter implements the types.Importer interface, but only allows
|
||||||
// importing the unsafe package.
|
// importing the unsafe package.
|
||||||
type simpleImporter struct {
|
type simpleImporter struct {
|
||||||
|
|||||||
+25
-2
@@ -14,6 +14,9 @@ import (
|
|||||||
var (
|
var (
|
||||||
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
|
prefixParseFns map[token.Token]func(*tokenizer) (ast.Expr, *scanner.Error)
|
||||||
precedences = map[token.Token]int{
|
precedences = map[token.Token]int{
|
||||||
|
token.OR: precedenceOr,
|
||||||
|
token.XOR: precedenceXor,
|
||||||
|
token.AND: precedenceAnd,
|
||||||
token.ADD: precedenceAdd,
|
token.ADD: precedenceAdd,
|
||||||
token.SUB: precedenceAdd,
|
token.SUB: precedenceAdd,
|
||||||
token.MUL: precedenceMul,
|
token.MUL: precedenceMul,
|
||||||
@@ -24,6 +27,9 @@ var (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
precedenceLowest = iota + 1
|
precedenceLowest = iota + 1
|
||||||
|
precedenceOr
|
||||||
|
precedenceXor
|
||||||
|
precedenceAnd
|
||||||
precedenceAdd
|
precedenceAdd
|
||||||
precedenceMul
|
precedenceMul
|
||||||
precedencePrefix
|
precedencePrefix
|
||||||
@@ -76,7 +82,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
|
|||||||
|
|
||||||
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
|
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
|
||||||
switch t.peekToken {
|
switch t.peekToken {
|
||||||
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
case token.OR, token.XOR, token.AND, token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
|
||||||
t.Next()
|
t.Next()
|
||||||
leftExpr, err = parseBinaryExpr(t, leftExpr)
|
leftExpr, err = parseBinaryExpr(t, leftExpr)
|
||||||
}
|
}
|
||||||
@@ -199,7 +205,18 @@ func (t *tokenizer) Next() {
|
|||||||
// https://en.cppreference.com/w/cpp/string/byte/isspace
|
// https://en.cppreference.com/w/cpp/string/byte/isspace
|
||||||
t.peekPos++
|
t.peekPos++
|
||||||
t.buf = t.buf[1:]
|
t.buf = t.buf[1:]
|
||||||
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%':
|
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&"):
|
||||||
|
// Two-character tokens.
|
||||||
|
switch c {
|
||||||
|
case '&':
|
||||||
|
t.peekToken = token.LAND
|
||||||
|
case '|':
|
||||||
|
t.peekToken = token.LOR
|
||||||
|
}
|
||||||
|
t.peekValue = t.buf[:2]
|
||||||
|
t.buf = t.buf[2:]
|
||||||
|
return
|
||||||
|
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
|
||||||
// Single-character tokens.
|
// Single-character tokens.
|
||||||
// TODO: ++ (increment) and -- (decrement) operators.
|
// TODO: ++ (increment) and -- (decrement) operators.
|
||||||
switch c {
|
switch c {
|
||||||
@@ -217,6 +234,12 @@ func (t *tokenizer) Next() {
|
|||||||
t.peekToken = token.QUO
|
t.peekToken = token.QUO
|
||||||
case '%':
|
case '%':
|
||||||
t.peekToken = token.REM
|
t.peekToken = token.REM
|
||||||
|
case '&':
|
||||||
|
t.peekToken = token.AND
|
||||||
|
case '|':
|
||||||
|
t.peekToken = token.OR
|
||||||
|
case '^':
|
||||||
|
t.peekToken = token.XOR
|
||||||
}
|
}
|
||||||
t.peekValue = t.buf[:1]
|
t.peekValue = t.buf[:1]
|
||||||
t.buf = t.buf[1:]
|
t.buf = t.buf[1:]
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func TestParseConst(t *testing.T) {
|
|||||||
{`5*5`, `5 * 5`},
|
{`5*5`, `5 * 5`},
|
||||||
{`5/5`, `5 / 5`},
|
{`5/5`, `5 / 5`},
|
||||||
{`5%5`, `5 % 5`},
|
{`5%5`, `5 % 5`},
|
||||||
|
{`5&5`, `5 & 5`},
|
||||||
|
{`5|5`, `5 | 5`},
|
||||||
|
{`5^5`, `5 ^ 5`},
|
||||||
|
{`5||5`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet
|
||||||
{`(5/5)`, `(5 / 5)`},
|
{`(5/5)`, `(5 / 5)`},
|
||||||
{`1 - 2`, `1 - 2`},
|
{`1 - 2`, `1 - 2`},
|
||||||
{`1 - 2 + 3`, `1 - 2 + 3`},
|
{`1 - 2 + 3`, `1 - 2 + 3`},
|
||||||
|
|||||||
+57
-19
@@ -4,7 +4,9 @@ package cgo
|
|||||||
// modification. It does not touch the AST itself.
|
// modification. It does not touch the AST itself.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
@@ -43,6 +45,8 @@ typedef struct {
|
|||||||
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
|
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
|
||||||
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
|
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
|
||||||
CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
|
CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
|
||||||
|
CXString tinygo_clang_getCursorPrettyPrinted(GoCXCursor c, CXPrintingPolicy Policy);
|
||||||
|
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(GoCXCursor c);
|
||||||
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
|
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
|
||||||
CXType tinygo_clang_getCursorType(GoCXCursor c);
|
CXType tinygo_clang_getCursorType(GoCXCursor c);
|
||||||
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
|
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
|
||||||
@@ -50,6 +54,7 @@ CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
|
|||||||
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
||||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||||
|
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(GoCXCursor c);
|
||||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||||
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
@@ -189,7 +194,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
|
|
||||||
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||||
// it.
|
// it.
|
||||||
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
|
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
pos := f.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
@@ -200,19 +205,43 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
|
|||||||
Kind: ast.Fun,
|
Kind: ast.Fun,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
|
exportName := name
|
||||||
|
localName := name
|
||||||
|
var stringSignature string
|
||||||
|
if C.tinygo_clang_Cursor_getStorageClass(c) == C.CX_SC_Static {
|
||||||
|
// A static function is assigned a globally unique symbol name based
|
||||||
|
// on the file path (like _Cgo_static_2d09198adbf58f4f4655_foo) and
|
||||||
|
// has a different Go name in the form of C.foo!symbols.go instead
|
||||||
|
// of just C.foo.
|
||||||
|
path := f.importPath + "/" + filepath.Base(f.fset.File(f.file.Pos()).Name())
|
||||||
|
staticIDBuf := sha256.Sum256([]byte(path))
|
||||||
|
staticID := hex.EncodeToString(staticIDBuf[:10])
|
||||||
|
exportName = "_Cgo_static_" + staticID + "_" + name
|
||||||
|
localName = name + "!" + filepath.Base(path)
|
||||||
|
|
||||||
|
// Create a signature. This is necessary for MacOS to forward the
|
||||||
|
// call, because MacOS doesn't support aliases like ELF and PE do.
|
||||||
|
// (There is N_INDR but __attribute__((alias("..."))) doesn't work).
|
||||||
|
policy := C.tinygo_clang_getCursorPrintingPolicy(c)
|
||||||
|
defer C.clang_PrintingPolicy_dispose(policy)
|
||||||
|
C.clang_PrintingPolicy_setProperty(policy, C.CXPrintingPolicy_TerseOutput, 1)
|
||||||
|
stringSignature = getString(C.tinygo_clang_getCursorPrettyPrinted(c, policy))
|
||||||
|
stringSignature = strings.Replace(stringSignature, " "+name+"(", " "+exportName+"(", 1)
|
||||||
|
stringSignature = strings.TrimPrefix(stringSignature, "static ")
|
||||||
|
}
|
||||||
args := make([]*ast.Field, numArgs)
|
args := make([]*ast.Field, numArgs)
|
||||||
decl := &ast.FuncDecl{
|
decl := &ast.FuncDecl{
|
||||||
Doc: &ast.CommentGroup{
|
Doc: &ast.CommentGroup{
|
||||||
List: []*ast.Comment{
|
List: []*ast.Comment{
|
||||||
{
|
{
|
||||||
Slash: pos - 1,
|
Slash: pos - 1,
|
||||||
Text: "//export " + name,
|
Text: "//export " + exportName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C." + name,
|
Name: "C." + localName,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
@@ -263,7 +292,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
obj.Decl = decl
|
obj.Decl = decl
|
||||||
return decl, nil
|
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)
|
||||||
typeName := "C." + name
|
typeName := "C." + name
|
||||||
@@ -504,6 +533,26 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the precise location in the source code. Used for uniquely identifying
|
||||||
|
// source locations.
|
||||||
|
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} {
|
||||||
|
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
|
var file C.CXFile
|
||||||
|
var line C.unsigned
|
||||||
|
var column C.unsigned
|
||||||
|
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||||
|
location := token.Position{
|
||||||
|
Filename: getString(C.clang_getFileName(file)),
|
||||||
|
Line: int(line),
|
||||||
|
Column: int(column),
|
||||||
|
}
|
||||||
|
if location.Filename == "" || location.Line == 0 {
|
||||||
|
// Not sure when this would happen, but protect from it anyway.
|
||||||
|
f.addError(pos, "could not find file/line information")
|
||||||
|
}
|
||||||
|
return location
|
||||||
|
}
|
||||||
|
|
||||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||||
@@ -759,20 +808,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// Anonymous record, probably inside a typedef.
|
// Anonymous record, probably inside a typedef.
|
||||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
location := f.getUniqueLocationID(pos, cursor)
|
||||||
var file C.CXFile
|
|
||||||
var line C.unsigned
|
|
||||||
var column C.unsigned
|
|
||||||
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
|
||||||
location := token.Position{
|
|
||||||
Filename: getString(C.clang_getFileName(file)),
|
|
||||||
Line: int(line),
|
|
||||||
Column: int(column),
|
|
||||||
}
|
|
||||||
if location.Filename == "" || location.Line == 0 {
|
|
||||||
// Not sure when this would happen, but protect from it anyway.
|
|
||||||
f.addError(pos, "could not find file/line information")
|
|
||||||
}
|
|
||||||
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||||
} else {
|
} else {
|
||||||
name = cgoRecordPrefix + name
|
name = cgoRecordPrefix + name
|
||||||
@@ -785,7 +821,9 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
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))
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
|
// Anonymous enum, probably inside a typedef.
|
||||||
|
location := f.getUniqueLocationID(pos, cursor)
|
||||||
|
name = f.getUnnamedDeclName("_Ctype_enum___", location)
|
||||||
} else {
|
} else {
|
||||||
name = "enum_" + name
|
name = "enum_" + name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build !byollvm && llvm13
|
|
||||||
// +build !byollvm,llvm13
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-13/include
|
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@13/include
|
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@13/include
|
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm13/include
|
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-13/lib -lclang
|
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@13/lib -lclang -lffi
|
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@13/lib -lclang -lffi
|
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm13/lib -lclang
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !byollvm && !llvm13
|
//go:build !byollvm && llvm14
|
||||||
// +build !byollvm,!llvm13
|
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !byollvm && !llvm14
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/lib/llvm-15/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@15/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang -lffi
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang -lffi
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
@@ -17,6 +17,14 @@ CXString tinygo_clang_getCursorSpelling(CXCursor c) {
|
|||||||
return clang_getCursorSpelling(c);
|
return clang_getCursorSpelling(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXString tinygo_clang_getCursorPrettyPrinted(CXCursor c, CXPrintingPolicy policy) {
|
||||||
|
return clang_getCursorPrettyPrinted(c, policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(CXCursor c) {
|
||||||
|
return clang_getCursorPrintingPolicy(c);
|
||||||
|
}
|
||||||
|
|
||||||
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
|
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
|
||||||
return clang_getCursorKind(c);
|
return clang_getCursorKind(c);
|
||||||
}
|
}
|
||||||
@@ -45,6 +53,10 @@ CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
|
|||||||
return clang_Cursor_getArgument(c, i);
|
return clang_Cursor_getArgument(c, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(CXCursor c) {
|
||||||
|
return clang_Cursor_getStorageClass(c);
|
||||||
|
}
|
||||||
|
|
||||||
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
||||||
return clang_getCursorLocation(c);
|
return clang_getCursorLocation(c);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -8,9 +8,9 @@
|
|||||||
// Type checking errors after CGo processing:
|
// Type checking errors after CGo processing:
|
||||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||||
// testdata/errors.go:105: unknown field z in struct literal
|
// testdata/errors.go:105: unknown field z in struct literal
|
||||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
// testdata/errors.go:108: undefined: C.SOME_CONST_1
|
||||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||||
// testdata/errors.go:112: undeclared name: C.SOME_CONST_4
|
// testdata/errors.go:112: undefined: C.SOME_CONST_4
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
@@ -5,6 +5,7 @@ package main
|
|||||||
int foo(int a, int b);
|
int foo(int a, int b);
|
||||||
void variadic0();
|
void variadic0();
|
||||||
void variadic2(int x, int y, ...);
|
void variadic2(int x, int y, ...);
|
||||||
|
static void staticfunc(int x);
|
||||||
|
|
||||||
// Global variable signatures.
|
// Global variable signatures.
|
||||||
extern int someValue;
|
extern int someValue;
|
||||||
@@ -16,6 +17,7 @@ func accessFunctions() {
|
|||||||
C.foo(3, 4)
|
C.foo(3, 4)
|
||||||
C.variadic0()
|
C.variadic0()
|
||||||
C.variadic2(3, 5)
|
C.variadic2(3, 5)
|
||||||
|
C.staticfunc(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func accessGlobals() {
|
func accessGlobals() {
|
||||||
|
|||||||
Vendored
+5
@@ -55,5 +55,10 @@ func C.variadic2(x C.int, y C.int)
|
|||||||
|
|
||||||
var C.variadic2$funcaddr unsafe.Pointer
|
var C.variadic2$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
|
//export _Cgo_static_173c95a79b6df1980521_staticfunc
|
||||||
|
func C.staticfunc!symbols.go(x C.int)
|
||||||
|
|
||||||
|
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//go:extern someValue
|
//go:extern someValue
|
||||||
var C.someValue C.int
|
var C.someValue C.int
|
||||||
|
|||||||
Vendored
+4
@@ -112,6 +112,10 @@ import "C"
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// aliases
|
||||||
|
_ C.float
|
||||||
|
_ C.double
|
||||||
|
|
||||||
// Simple typedefs.
|
// Simple typedefs.
|
||||||
_ C.myint
|
_ C.myint
|
||||||
|
|
||||||
|
|||||||
+71
-32
@@ -47,6 +47,12 @@ func (c *Config) Features() string {
|
|||||||
return c.Target.Features + "," + c.Options.LLVMFeatures
|
return c.Target.Features + "," + c.Options.LLVMFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ABI returns the -mabi= flag for this target (like -mabi=lp64). A zero-length
|
||||||
|
// string is returned if the target doesn't specify an ABI.
|
||||||
|
func (c *Config) ABI() string {
|
||||||
|
return c.Target.ABI
|
||||||
|
}
|
||||||
|
|
||||||
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
||||||
// for example, bare-metal targets will usually pretend to be linux to get the
|
// for example, bare-metal targets will usually pretend to be linux to get the
|
||||||
// standard library to compile.
|
// standard library to compile.
|
||||||
@@ -69,13 +75,12 @@ func (c *Config) GOARM() string {
|
|||||||
|
|
||||||
// BuildTags returns the complete list of build tags used during this build.
|
// BuildTags returns the complete list of build tags used during this build.
|
||||||
func (c *Config) BuildTags() []string {
|
func (c *Config) BuildTags() []string {
|
||||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
targetTags := filterTags(c.Target.BuildTags, c.Options.Tags)
|
||||||
|
tags := append(targetTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
}
|
}
|
||||||
if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 {
|
tags = append(tags, c.Options.Tags...)
|
||||||
tags = append(tags, extraTags...)
|
|
||||||
}
|
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +91,7 @@ func (c *Config) CgoEnabled() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GC returns the garbage collection strategy in use on this platform. Valid
|
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||||
// values are "none", "leaking", and "conservative".
|
// values are "none", "leaking", "conservative" and "precise".
|
||||||
func (c *Config) GC() string {
|
func (c *Config) GC() string {
|
||||||
if c.Options.GC != "" {
|
if c.Options.GC != "" {
|
||||||
return c.Options.GC
|
return c.Options.GC
|
||||||
@@ -101,7 +106,7 @@ func (c *Config) GC() string {
|
|||||||
// that can be traced by the garbage collector.
|
// that can be traced by the garbage collector.
|
||||||
func (c *Config) NeedsStackObjects() bool {
|
func (c *Config) NeedsStackObjects() bool {
|
||||||
switch c.GC() {
|
switch c.GC() {
|
||||||
case "conservative":
|
case "conservative", "custom", "precise":
|
||||||
for _, tag := range c.BuildTags() {
|
for _, tag := range c.BuildTags() {
|
||||||
if tag == "tinygo.wasm" {
|
if tag == "tinygo.wasm" {
|
||||||
return true
|
return true
|
||||||
@@ -177,22 +182,20 @@ func (c *Config) AutomaticStackSize() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseThinLTO returns whether ThinLTO should be used for the given target. Some
|
// StackSize returns the default stack size to be used for goroutines, if the
|
||||||
// targets (such as wasm) are not yet supported.
|
// stack size could not be determined automatically at compile time.
|
||||||
// We should try and remove as many exceptions as possible in the future, so
|
func (c *Config) StackSize() uint64 {
|
||||||
// that this optimization can be applied in more places.
|
if c.Options.StackSize != 0 {
|
||||||
|
return c.Options.StackSize
|
||||||
|
}
|
||||||
|
return c.Target.DefaultStackSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseThinLTO returns whether ThinLTO should be used for the given target.
|
||||||
func (c *Config) UseThinLTO() bool {
|
func (c *Config) UseThinLTO() bool {
|
||||||
parts := strings.Split(c.Triple(), "-")
|
// All architectures support ThinLTO now. However, this code is kept for the
|
||||||
if parts[0] == "wasm32" {
|
// time being in case there are regressions. The non-ThinLTO code support
|
||||||
// wasm-ld doesn't seem to support ThinLTO yet.
|
// should be removed when it is proven to work reliably.
|
||||||
return false
|
|
||||||
}
|
|
||||||
if parts[0] == "avr" || parts[0] == "xtensa" {
|
|
||||||
// These use external (GNU) linkers which might perhaps support ThinLTO
|
|
||||||
// through a plugin, but it's too much hassle to set up.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Other architectures support ThinLTO.
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +226,9 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
|||||||
if c.CPU() != "" {
|
if c.CPU() != "" {
|
||||||
archname += "-" + c.CPU()
|
archname += "-" + c.CPU()
|
||||||
}
|
}
|
||||||
|
if c.ABI() != "" {
|
||||||
|
archname += "-" + c.ABI()
|
||||||
|
}
|
||||||
|
|
||||||
// Try to load a precompiled library.
|
// Try to load a precompiled library.
|
||||||
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
|
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
|
||||||
@@ -313,7 +319,7 @@ func (c *Config) CFlags() []string {
|
|||||||
panic("unknown libc: " + c.Target.Libc)
|
panic("unknown libc: " + c.Target.Libc)
|
||||||
}
|
}
|
||||||
// Always emit debug information. It is optionally stripped at link time.
|
// Always emit debug information. It is optionally stripped at link time.
|
||||||
cflags = append(cflags, "-g")
|
cflags = append(cflags, "-gdwarf-4")
|
||||||
// Use the same optimization level as TinyGo.
|
// 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.
|
||||||
@@ -331,6 +337,10 @@ func (c *Config) CFlags() []string {
|
|||||||
cflags = append(cflags, "-mcpu="+c.Target.CPU)
|
cflags = append(cflags, "-mcpu="+c.Target.CPU)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Set the -mabi flag, if needed.
|
||||||
|
if c.ABI() != "" {
|
||||||
|
cflags = append(cflags, "-mabi="+c.ABI())
|
||||||
|
}
|
||||||
return cflags
|
return cflags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,8 +380,8 @@ func (c *Config) VerifyIR() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debug returns whether debug (DWARF) information should be retained by the
|
// Debug returns whether debug (DWARF) information should be retained by the
|
||||||
// linker. By default, debug information is retained but it can be removed with
|
// linker. By default, debug information is retained, but it can be removed
|
||||||
// the -no-debug flag.
|
// with the -no-debug flag.
|
||||||
func (c *Config) Debug() bool {
|
func (c *Config) Debug() bool {
|
||||||
return c.Options.Debug
|
return c.Options.Debug
|
||||||
}
|
}
|
||||||
@@ -442,13 +452,13 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
|||||||
if openocdInterface == "" {
|
if openocdInterface == "" {
|
||||||
return nil, errors.New("OpenOCD programmer not set")
|
return nil, errors.New("OpenOCD programmer not set")
|
||||||
}
|
}
|
||||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(openocdInterface) {
|
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(openocdInterface) {
|
||||||
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface)
|
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface)
|
||||||
}
|
}
|
||||||
if c.Target.OpenOCDTarget == "" {
|
if c.Target.OpenOCDTarget == "" {
|
||||||
return nil, errors.New("OpenOCD chip not set")
|
return nil, errors.New("OpenOCD chip not set")
|
||||||
}
|
}
|
||||||
if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(c.Target.OpenOCDTarget) {
|
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(c.Target.OpenOCDTarget) {
|
||||||
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget)
|
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget)
|
||||||
}
|
}
|
||||||
if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" {
|
if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" {
|
||||||
@@ -459,7 +469,14 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
|||||||
args = append(args, "-c", cmd)
|
args = append(args, "-c", cmd)
|
||||||
}
|
}
|
||||||
if c.Target.OpenOCDTransport != "" {
|
if c.Target.OpenOCDTransport != "" {
|
||||||
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
|
transport := c.Target.OpenOCDTransport
|
||||||
|
if transport == "swd" {
|
||||||
|
switch openocdInterface {
|
||||||
|
case "stlink-dap":
|
||||||
|
transport = "dapdirect_swd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = append(args, "-c", "transport select "+transport)
|
||||||
}
|
}
|
||||||
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
|
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
|
||||||
return args, nil
|
return args, nil
|
||||||
@@ -484,12 +501,8 @@ func (c *Config) RelocationModel() string {
|
|||||||
return "static"
|
return "static"
|
||||||
}
|
}
|
||||||
|
|
||||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file, and
|
// WasmAbi returns the WASM ABI which is specified in the target JSON file.
|
||||||
// the value is overridden by `-wasm-abi` flag if it is provided
|
|
||||||
func (c *Config) WasmAbi() string {
|
func (c *Config) WasmAbi() string {
|
||||||
if c.Options.WasmAbi != "" {
|
|
||||||
return c.Options.WasmAbi
|
|
||||||
}
|
|
||||||
return c.Target.WasmAbi
|
return c.Target.WasmAbi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,6 +539,8 @@ func (c *Config) Emulator(format, binary string) ([]string, error) {
|
|||||||
var emulator []string
|
var emulator []string
|
||||||
for _, s := range parts {
|
for _, s := range parts {
|
||||||
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
|
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
|
||||||
|
// Allow replacement of what's usually /tmp except notably Windows.
|
||||||
|
s = strings.ReplaceAll(s, "{tmpDir}", os.TempDir())
|
||||||
s = strings.ReplaceAll(s, "{"+format+"}", binary)
|
s = strings.ReplaceAll(s, "{"+format+"}", binary)
|
||||||
emulator = append(emulator, s)
|
emulator = append(emulator, s)
|
||||||
}
|
}
|
||||||
@@ -536,3 +551,27 @@ type TestConfig struct {
|
|||||||
CompileTestBinary bool
|
CompileTestBinary bool
|
||||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filterTags removes predefined build tags for a target if a conflicting option
|
||||||
|
// is provided by the user.
|
||||||
|
func filterTags(targetTags []string, userTags []string) []string {
|
||||||
|
var filtered []string
|
||||||
|
for _, t := range targetTags {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(t, "runtime_memhash_"):
|
||||||
|
overridden := false
|
||||||
|
for _, ut := range userTags {
|
||||||
|
if strings.HasPrefix(ut, "runtime_memhash_") {
|
||||||
|
overridden = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !overridden {
|
||||||
|
filtered = append(filtered, t)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
filtered = append(filtered, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package compileopts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildTags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
targetTags []string
|
||||||
|
userTags []string
|
||||||
|
result []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
targetTags: []string{},
|
||||||
|
userTags: []string{},
|
||||||
|
result: []string{
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetTags: []string{"bear"},
|
||||||
|
userTags: []string{},
|
||||||
|
result: []string{
|
||||||
|
"bear",
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetTags: []string{},
|
||||||
|
userTags: []string{"cat"},
|
||||||
|
result: []string{
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
"cat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetTags: []string{"bear"},
|
||||||
|
userTags: []string{"cat"},
|
||||||
|
result: []string{
|
||||||
|
"bear",
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
"cat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetTags: []string{"bear", "runtime_memhash_leveldb"},
|
||||||
|
userTags: []string{"cat"},
|
||||||
|
result: []string{
|
||||||
|
"bear",
|
||||||
|
"runtime_memhash_leveldb",
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
"cat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetTags: []string{"bear", "runtime_memhash_leveldb"},
|
||||||
|
userTags: []string{"cat", "runtime_memhash_leveldb"},
|
||||||
|
result: []string{
|
||||||
|
"bear",
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
"cat",
|
||||||
|
"runtime_memhash_leveldb",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetTags: []string{"bear", "runtime_memhash_leveldb"},
|
||||||
|
userTags: []string{"cat", "runtime_memhash_sip"},
|
||||||
|
result: []string{
|
||||||
|
"bear",
|
||||||
|
"tinygo",
|
||||||
|
"math_big_pure_go",
|
||||||
|
"gc.conservative",
|
||||||
|
"scheduler.none",
|
||||||
|
"serial.none",
|
||||||
|
"cat",
|
||||||
|
"runtime_memhash_sip",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tt := tc
|
||||||
|
t.Run(fmt.Sprintf("%s+%s", strings.Join(tt.targetTags, ","), strings.Join(tt.userTags, ",")), func(t *testing.T) {
|
||||||
|
c := &Config{
|
||||||
|
Target: &TargetSpec{
|
||||||
|
BuildTags: tt.targetTags,
|
||||||
|
},
|
||||||
|
Options: &Options{
|
||||||
|
Tags: tt.userTags,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res := c.BuildTags()
|
||||||
|
|
||||||
|
if len(res) != len(tt.result) {
|
||||||
|
t.Errorf("expected %d tags, got %d", len(tt.result), len(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tag := range tt.result {
|
||||||
|
if tag != res[i] {
|
||||||
|
t.Errorf("tag %d: expected %s, got %s", i, tt.result[i], tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
validGCOptions = []string{"none", "leaking", "conservative"}
|
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||||
validSerialOptions = []string{"none", "uart", "usb"}
|
validSerialOptions = []string{"none", "uart", "usb"}
|
||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||||
@@ -27,8 +28,10 @@ type Options struct {
|
|||||||
GC string
|
GC string
|
||||||
PanicStrategy string
|
PanicStrategy string
|
||||||
Scheduler string
|
Scheduler string
|
||||||
|
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
|
||||||
PrintIR bool
|
PrintIR bool
|
||||||
DumpSSA bool
|
DumpSSA bool
|
||||||
VerifyIR bool
|
VerifyIR bool
|
||||||
@@ -38,8 +41,7 @@ type Options struct {
|
|||||||
PrintSizes string
|
PrintSizes string
|
||||||
PrintAllocs *regexp.Regexp // regexp string
|
PrintAllocs *regexp.Regexp // regexp string
|
||||||
PrintStacks bool
|
PrintStacks bool
|
||||||
Tags string
|
Tags []string
|
||||||
WasmAbi string
|
|
||||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||||
TestConfig TestConfig
|
TestConfig TestConfig
|
||||||
Programmer string
|
Programmer string
|
||||||
@@ -47,6 +49,9 @@ type Options struct {
|
|||||||
LLVMFeatures string
|
LLVMFeatures string
|
||||||
Directory string
|
Directory string
|
||||||
PrintJSON bool
|
PrintJSON bool
|
||||||
|
Monitor bool
|
||||||
|
BaudRate int
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
func TestVerifyOptions(t *testing.T) {
|
func TestVerifyOptions(t *testing.T) {
|
||||||
|
|
||||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative`)
|
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
|
||||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
||||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
||||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||||
@@ -48,6 +48,12 @@ func TestVerifyOptions(t *testing.T) {
|
|||||||
GC: "conservative",
|
GC: "conservative",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "GCOptionCustom",
|
||||||
|
opts: compileopts.Options{
|
||||||
|
GC: "custom",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "InvalidSchedulerOption",
|
name: "InvalidSchedulerOption",
|
||||||
opts: compileopts.Options{
|
opts: compileopts.Options{
|
||||||
|
|||||||
+49
-20
@@ -26,6 +26,7 @@ type TargetSpec struct {
|
|||||||
Inherits []string `json:"inherits"`
|
Inherits []string `json:"inherits"`
|
||||||
Triple string `json:"llvm-target"`
|
Triple string `json:"llvm-target"`
|
||||||
CPU string `json:"cpu"`
|
CPU string `json:"cpu"`
|
||||||
|
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
|
||||||
Features string `json:"features"`
|
Features string `json:"features"`
|
||||||
GOOS string `json:"goos"`
|
GOOS string `json:"goos"`
|
||||||
GOARCH string `json:"goarch"`
|
GOARCH string `json:"goarch"`
|
||||||
@@ -47,7 +48,7 @@ type TargetSpec struct {
|
|||||||
FlashCommand string `json:"flash-command"`
|
FlashCommand string `json:"flash-command"`
|
||||||
GDB []string `json:"gdb"`
|
GDB []string `json:"gdb"`
|
||||||
PortReset string `json:"flash-1200-bps-reset"`
|
PortReset string `json:"flash-1200-bps-reset"`
|
||||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid"
|
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid"
|
||||||
FlashMethod string `json:"flash-method"`
|
FlashMethod string `json:"flash-method"`
|
||||||
FlashVolume string `json:"msd-volume-name"`
|
FlashVolume string `json:"msd-volume-name"`
|
||||||
FlashFilename string `json:"msd-firmware-name"`
|
FlashFilename string `json:"msd-firmware-name"`
|
||||||
@@ -65,7 +66,7 @@ type TargetSpec struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
||||||
func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
|
||||||
specType := reflect.TypeOf(spec).Elem()
|
specType := reflect.TypeOf(spec).Elem()
|
||||||
specValue := reflect.ValueOf(spec).Elem()
|
specValue := reflect.ValueOf(spec).Elem()
|
||||||
childValue := reflect.ValueOf(child).Elem()
|
childValue := reflect.ValueOf(child).Elem()
|
||||||
@@ -88,12 +89,22 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
|||||||
if !src.IsNil() {
|
if !src.IsNil() {
|
||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
}
|
}
|
||||||
case reflect.Slice: // for slices, append the field
|
case reflect.Slice: // for slices, append the field and check for duplicates
|
||||||
dst.Set(reflect.AppendSlice(dst, src))
|
dst.Set(reflect.AppendSlice(dst, src))
|
||||||
|
for i := 0; i < dst.Len(); i++ {
|
||||||
|
v := dst.Index(i).String()
|
||||||
|
for j := i + 1; j < dst.Len(); j++ {
|
||||||
|
w := dst.Index(j).String()
|
||||||
|
if v == w {
|
||||||
|
return fmt.Errorf("duplicate value '%s' in field %s", v, field.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown field type : " + kind.String())
|
return fmt.Errorf("unknown field type: %s", kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// load reads a target specification from the JSON in the given io.Reader. It
|
// load reads a target specification from the JSON in the given io.Reader. It
|
||||||
@@ -108,10 +119,10 @@ func (spec *TargetSpec) load(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loadFromGivenStr loads the TargetSpec from the given string that could be:
|
// loadFromGivenStr loads the TargetSpec from the given string that could be:
|
||||||
// - targets/ directory inside the compiler sources
|
// - targets/ directory inside the compiler sources
|
||||||
// - a relative or absolute path to custom (project specific) target specification .json file;
|
// - a relative or absolute path to custom (project specific) target specification .json file;
|
||||||
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
|
// the Inherits[] could contain the files from target folder (ex. stm32f4disco)
|
||||||
// as well as path to custom files (ex. myAwesomeProject.json)
|
// as well as path to custom files (ex. myAwesomeProject.json)
|
||||||
func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
func (spec *TargetSpec) loadFromGivenStr(str string) error {
|
||||||
path := ""
|
path := ""
|
||||||
if strings.HasSuffix(str, ".json") {
|
if strings.HasSuffix(str, ".json") {
|
||||||
@@ -141,11 +152,17 @@ func (spec *TargetSpec) resolveInherits() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newSpec.overrideProperties(subtarget)
|
err = newSpec.overrideProperties(subtarget)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When all properties are loaded, make sure they are properly inherited.
|
// When all properties are loaded, make sure they are properly inherited.
|
||||||
newSpec.overrideProperties(spec)
|
err := newSpec.overrideProperties(spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
*spec = *newSpec
|
*spec = *newSpec
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -178,6 +195,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
default:
|
default:
|
||||||
llvmarch = options.GOARCH
|
llvmarch = options.GOARCH
|
||||||
}
|
}
|
||||||
|
llvmvendor := "unknown"
|
||||||
llvmos := options.GOOS
|
llvmos := options.GOOS
|
||||||
if llvmos == "darwin" {
|
if llvmos == "darwin" {
|
||||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||||
@@ -187,17 +205,18 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
// Looks like Apple prefers to call this architecture ARM64
|
// Looks like Apple prefers to call this architecture ARM64
|
||||||
// instead of AArch64.
|
// instead of AArch64.
|
||||||
llvmarch = "arm64"
|
llvmarch = "arm64"
|
||||||
|
llvmos = "macosx11.0.0"
|
||||||
}
|
}
|
||||||
|
llvmvendor = "apple"
|
||||||
}
|
}
|
||||||
// Target triples (which actually have four components, but are called
|
// Target triples (which actually have four components, but are called
|
||||||
// triples for historical reasons) have the form:
|
// triples for historical reasons) have the form:
|
||||||
// arch-vendor-os-environment
|
// arch-vendor-os-environment
|
||||||
target := llvmarch + "-unknown-" + llvmos
|
target := llvmarch + "-" + llvmvendor + "-" + llvmos
|
||||||
if options.GOARCH == "arm" {
|
|
||||||
target += "-gnueabihf"
|
|
||||||
}
|
|
||||||
if options.GOOS == "windows" {
|
if options.GOOS == "windows" {
|
||||||
target += "-gnu"
|
target += "-gnu"
|
||||||
|
} else if options.GOARCH == "arm" {
|
||||||
|
target += "-gnueabihf"
|
||||||
}
|
}
|
||||||
return defaultTarget(options.GOOS, options.GOARCH, target)
|
return defaultTarget(options.GOOS, options.GOARCH, target)
|
||||||
}
|
}
|
||||||
@@ -213,7 +232,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
// it includes all parents as specified in the "inherits" key.
|
// it includes all parents as specified in the "inherits" key.
|
||||||
err = spec.resolveInherits()
|
err = spec.resolveInherits()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("%s : %w", options.Target, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if spec.Scheduler == "asyncify" {
|
if spec.Scheduler == "asyncify" {
|
||||||
@@ -231,6 +250,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
GOOS: goos,
|
GOOS: goos,
|
||||||
GOARCH: goarch,
|
GOARCH: goarch,
|
||||||
BuildTags: []string{goos, goarch},
|
BuildTags: []string{goos, goarch},
|
||||||
|
GC: "precise",
|
||||||
Scheduler: "tasks",
|
Scheduler: "tasks",
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
DefaultStackSize: 1024 * 64, // 64kB
|
DefaultStackSize: 1024 * 64, // 64kB
|
||||||
@@ -284,10 +304,19 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
// normally present in Go (without explicitly opting in).
|
// normally present in Go (without explicitly opting in).
|
||||||
// For more discussion:
|
// For more discussion:
|
||||||
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
||||||
|
switch goarch {
|
||||||
|
case "amd64":
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"-m", "i386pep",
|
||||||
|
"--image-base", "0x400000",
|
||||||
|
)
|
||||||
|
case "arm64":
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"-m", "arm64pe",
|
||||||
|
)
|
||||||
|
}
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-m", "i386pep",
|
|
||||||
"-Bdynamic",
|
"-Bdynamic",
|
||||||
"--image-base", "0x400000",
|
|
||||||
"--gc-sections",
|
"--gc-sections",
|
||||||
"--no-insert-timestamp",
|
"--no-insert-timestamp",
|
||||||
"--no-dynamicbase",
|
"--no-dynamicbase",
|
||||||
@@ -297,9 +326,9 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
if goarch != "wasm" {
|
if goarch != "wasm" {
|
||||||
suffix := ""
|
suffix := ""
|
||||||
if goos == "windows" {
|
if goos == "windows" && goarch == "amd64" {
|
||||||
// Windows uses a different calling convention from other operating
|
// Windows uses a different calling convention on amd64 from other
|
||||||
// systems so we need separate assembly files.
|
// operating systems so we need separate assembly files.
|
||||||
suffix = "_windows"
|
suffix = "_windows"
|
||||||
}
|
}
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package compileopts
|
package compileopts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -17,7 +18,7 @@ func TestLoadTarget(t *testing.T) {
|
|||||||
t.Error("LoadTarget should have failed with non existing target")
|
t.Error("LoadTarget should have failed with non existing target")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !os.IsNotExist(err) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
t.Error("LoadTarget failed for wrong reason:", err)
|
t.Error("LoadTarget failed for wrong reason:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-44
@@ -16,6 +16,8 @@ import "tinygo.org/x/go-llvm"
|
|||||||
|
|
||||||
var stdlibAliases = map[string]string{
|
var stdlibAliases = map[string]string{
|
||||||
// crypto packages
|
// crypto packages
|
||||||
|
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
|
||||||
|
"crypto/ed25519/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
|
||||||
"crypto/md5.block": "crypto/md5.blockGeneric",
|
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||||
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||||
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||||
@@ -23,53 +25,10 @@ var stdlibAliases = map[string]string{
|
|||||||
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||||
|
|
||||||
// math package
|
// math package
|
||||||
"math.Asin": "math.asin",
|
|
||||||
"math.Asinh": "math.asinh",
|
|
||||||
"math.Acos": "math.acos",
|
|
||||||
"math.Acosh": "math.acosh",
|
|
||||||
"math.Atan": "math.atan",
|
|
||||||
"math.Atanh": "math.atanh",
|
|
||||||
"math.Atan2": "math.atan2",
|
|
||||||
"math.Cbrt": "math.cbrt",
|
|
||||||
"math.Ceil": "math.ceil",
|
|
||||||
"math.archCeil": "math.ceil",
|
|
||||||
"math.Cos": "math.cos",
|
|
||||||
"math.Cosh": "math.cosh",
|
|
||||||
"math.Erf": "math.erf",
|
|
||||||
"math.Erfc": "math.erfc",
|
|
||||||
"math.Exp": "math.exp",
|
|
||||||
"math.archExp": "math.exp",
|
|
||||||
"math.Expm1": "math.expm1",
|
|
||||||
"math.Exp2": "math.exp2",
|
|
||||||
"math.archExp2": "math.exp2",
|
|
||||||
"math.Floor": "math.floor",
|
|
||||||
"math.archFloor": "math.floor",
|
|
||||||
"math.Frexp": "math.frexp",
|
|
||||||
"math.Hypot": "math.hypot",
|
|
||||||
"math.archHypot": "math.hypot",
|
"math.archHypot": "math.hypot",
|
||||||
"math.Ldexp": "math.ldexp",
|
|
||||||
"math.Log": "math.log",
|
|
||||||
"math.archLog": "math.log",
|
|
||||||
"math.Log1p": "math.log1p",
|
|
||||||
"math.Log10": "math.log10",
|
|
||||||
"math.Log2": "math.log2",
|
|
||||||
"math.Max": "math.max",
|
|
||||||
"math.archMax": "math.max",
|
"math.archMax": "math.max",
|
||||||
"math.Min": "math.min",
|
|
||||||
"math.archMin": "math.min",
|
"math.archMin": "math.min",
|
||||||
"math.Mod": "math.mod",
|
|
||||||
"math.Modf": "math.modf",
|
|
||||||
"math.archModf": "math.modf",
|
"math.archModf": "math.modf",
|
||||||
"math.Pow": "math.pow",
|
|
||||||
"math.Remainder": "math.remainder",
|
|
||||||
"math.Sin": "math.sin",
|
|
||||||
"math.Sinh": "math.sinh",
|
|
||||||
"math.Sqrt": "math.sqrt",
|
|
||||||
"math.archSqrt": "math.sqrt",
|
|
||||||
"math.Tan": "math.tan",
|
|
||||||
"math.Tanh": "math.tanh",
|
|
||||||
"math.Trunc": "math.trunc",
|
|
||||||
"math.archTrunc": "math.trunc",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createAlias implements the function (in the builder) as a call to the alias
|
// createAlias implements the function (in the builder) as a call to the alias
|
||||||
@@ -93,7 +52,7 @@ func (b *builder) createAlias(alias llvm.Value) {
|
|||||||
b.CreateUnreachable()
|
b.CreateUnreachable()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
result := b.CreateCall(alias.GlobalValueType(), alias, b.llvmFn.Params(), "")
|
||||||
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||||
b.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+8
-7
@@ -89,10 +89,11 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
|
|||||||
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||||
}
|
}
|
||||||
|
|
||||||
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice
|
||||||
// function must panic if the ptr/len parameters are invalid.
|
// and unsafe.String. This function must panic if the ptr/len parameters are
|
||||||
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
// invalid.
|
||||||
// From the documentation of unsafe.Slice:
|
func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, elementType llvm.Type, lenType *types.Basic) {
|
||||||
|
// From the documentation of unsafe.Slice and unsafe.String:
|
||||||
// > At run time, if len is negative, or if ptr is nil and len is not
|
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||||
// > zero, a run-time panic occurs.
|
// > zero, a run-time panic occurs.
|
||||||
// However, in practice, it is also necessary to check that the length is
|
// However, in practice, it is also necessary to check that the length is
|
||||||
@@ -105,7 +106,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
|
|||||||
|
|
||||||
// Determine the maximum slice size, and therefore the maximum value of the
|
// Determine the maximum slice size, and therefore the maximum value of the
|
||||||
// len parameter.
|
// len parameter.
|
||||||
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
maxSize := b.maxSliceSize(elementType)
|
||||||
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||||
|
|
||||||
// Do the check. By using unsigned greater than for the length check, signed
|
// Do the check. By using unsigned greater than for the length check, signed
|
||||||
@@ -117,7 +118,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
|
|||||||
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||||
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||||
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||||
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
b.createRuntimeAssert(assert, name, "unsafeSlicePanic")
|
||||||
}
|
}
|
||||||
|
|
||||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||||
@@ -145,7 +146,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
|||||||
}
|
}
|
||||||
// Make the maxBufSize actually the maximum allowed value (in number of
|
// Make the maxBufSize actually the maximum allowed value (in number of
|
||||||
// elements in the channel buffer).
|
// elements in the channel buffer).
|
||||||
maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false))
|
maxBufSize = b.CreateUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false), "")
|
||||||
|
|
||||||
// Make sure maxBufSize has the same type as bufSize.
|
// Make sure maxBufSize has the same type as bufSize.
|
||||||
if maxBufSize.Type() != bufSize.Type() {
|
if maxBufSize.Type() != bufSize.Type() {
|
||||||
|
|||||||
+3
-3
@@ -25,7 +25,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
|
|||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false))
|
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false))
|
||||||
}
|
}
|
||||||
oldVal := b.createCall(fn, []llvm.Value{ptr, val}, "")
|
oldVal := b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val}, "")
|
||||||
// Return the new value, not the original value returned.
|
// Return the new value, not the original value returned.
|
||||||
return b.CreateAdd(oldVal, val, "")
|
return b.CreateAdd(oldVal, val, "")
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
|
|||||||
return swapped
|
return swapped
|
||||||
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
||||||
ptr := b.getValue(b.fn.Params[0])
|
ptr := b.getValue(b.fn.Params[0])
|
||||||
val := b.CreateLoad(ptr, "")
|
val := b.CreateLoad(b.getLLVMType(b.fn.Signature.Results().At(0).Type()), ptr, "")
|
||||||
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||||
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||||
return val
|
return val
|
||||||
@@ -78,7 +78,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
|
|||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
||||||
}
|
}
|
||||||
b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
|
b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
|
||||||
return llvm.Value{}
|
return llvm.Value{}
|
||||||
}
|
}
|
||||||
store := b.CreateStore(val, ptr)
|
store := b.CreateStore(val, ptr)
|
||||||
|
|||||||
+31
-41
@@ -20,7 +20,7 @@ const maxFieldsPerParam = 3
|
|||||||
type paramInfo struct {
|
type paramInfo struct {
|
||||||
llvmType llvm.Type
|
llvmType llvm.Type
|
||||||
name string // name, possibly with suffixes for e.g. struct fields
|
name string // name, possibly with suffixes for e.g. struct fields
|
||||||
flags paramFlags
|
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||||
@@ -37,15 +37,15 @@ const (
|
|||||||
// createRuntimeInvoke instead.
|
// createRuntimeInvoke instead.
|
||||||
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||||
llvmFn := b.getFunction(fn)
|
fnType, llvmFn := b.getFunction(fn)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||||
}
|
}
|
||||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||||
if isInvoke {
|
if isInvoke {
|
||||||
return b.createInvoke(llvmFn, args, name)
|
return b.createInvoke(fnType, llvmFn, args, name)
|
||||||
}
|
}
|
||||||
return b.createCall(llvmFn, args, name)
|
return b.createCall(fnType, llvmFn, args, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRuntimeCall creates a new call to runtime.<fnName> with the given
|
// createRuntimeCall creates a new call to runtime.<fnName> with the given
|
||||||
@@ -65,22 +65,22 @@ func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name str
|
|||||||
|
|
||||||
// createCall creates a call to the given function with the arguments possibly
|
// createCall creates a call to the given function with the arguments possibly
|
||||||
// expanded.
|
// expanded.
|
||||||
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||||
expanded := make([]llvm.Value, 0, len(args))
|
expanded := make([]llvm.Value, 0, len(args))
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
fragments := b.expandFormalParam(arg)
|
fragments := b.expandFormalParam(arg)
|
||||||
expanded = append(expanded, fragments...)
|
expanded = append(expanded, fragments...)
|
||||||
}
|
}
|
||||||
return b.CreateCall(fn, expanded, name)
|
return b.CreateCall(fnType, fn, expanded, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInvoke is like createCall but continues execution at the landing pad if
|
// createInvoke is like createCall but continues execution at the landing pad if
|
||||||
// the call resulted in a panic.
|
// the call resulted in a panic.
|
||||||
func (b *builder) createInvoke(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||||
if b.hasDeferFrame() {
|
if b.hasDeferFrame() {
|
||||||
b.createInvokeCheckpoint()
|
b.createInvokeCheckpoint()
|
||||||
}
|
}
|
||||||
return b.createCall(fn, args, name)
|
return b.createCall(fnType, fn, args, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -96,13 +96,7 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
|
|||||||
// failed to expand this parameter: too many fields
|
// failed to expand this parameter: too many fields
|
||||||
}
|
}
|
||||||
// TODO: split small arrays
|
// TODO: split small arrays
|
||||||
return []paramInfo{
|
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||||
{
|
|
||||||
llvmType: t,
|
|
||||||
name: name,
|
|
||||||
flags: getTypeFlags(goType),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// expandFormalParamOffsets returns a list of offsets from the start of an
|
// expandFormalParamOffsets returns a list of offsets from the start of an
|
||||||
@@ -152,7 +146,6 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
|||||||
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
// Try to flatten a struct type to a list of types. Returns a 1-element slice
|
||||||
// with the passed in type if this is not possible.
|
// with the passed in type if this is not possible.
|
||||||
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
typeFlags := getTypeFlags(goType)
|
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
var paramInfos []paramInfo
|
var paramInfos []paramInfo
|
||||||
@@ -183,40 +176,37 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
for i := range subInfos {
|
|
||||||
subInfos[i].flags |= typeFlags
|
|
||||||
}
|
|
||||||
paramInfos = append(paramInfos, subInfos...)
|
paramInfos = append(paramInfos, subInfos...)
|
||||||
}
|
}
|
||||||
return paramInfos
|
return paramInfos
|
||||||
default:
|
default:
|
||||||
return []paramInfo{
|
return []paramInfo{c.getParamInfo(t, name, goType)}
|
||||||
{
|
|
||||||
llvmType: t,
|
|
||||||
name: name,
|
|
||||||
flags: typeFlags,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTypeFlags returns the type flags for a given type. It will not recurse
|
// getParamInfo collects information about a parameter. For example, if this
|
||||||
// into sub-types (such as in structs).
|
// parameter is pointer-like, it will also store the element type for the
|
||||||
func getTypeFlags(t types.Type) paramFlags {
|
// dereferenceable_or_null attribute.
|
||||||
if t == nil {
|
func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Type) paramInfo {
|
||||||
return 0
|
info := paramInfo{
|
||||||
|
llvmType: t,
|
||||||
|
name: name,
|
||||||
}
|
}
|
||||||
switch t.Underlying().(type) {
|
if goType != nil {
|
||||||
case *types.Pointer:
|
switch underlying := goType.Underlying().(type) {
|
||||||
// Pointers in Go must either point to an object or be nil.
|
case *types.Pointer:
|
||||||
return paramIsDeferenceableOrNull
|
// Pointers in Go must either point to an object or be nil.
|
||||||
case *types.Chan, *types.Map:
|
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMType(underlying.Elem()))
|
||||||
// Channels and maps are implemented as pointers pointing to some
|
case *types.Chan:
|
||||||
// object, and follow the same rules as *types.Pointer.
|
// Channels are implemented simply as a *runtime.channel.
|
||||||
return paramIsDeferenceableOrNull
|
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("channel"))
|
||||||
default:
|
case *types.Map:
|
||||||
return 0
|
// Maps are similar to channels: they are implemented as a
|
||||||
|
// *runtime.hashmap.
|
||||||
|
info.elemSize = c.targetData.TypeAllocSize(c.getLLVMRuntimeType("hashmap"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractSubfield extracts a field from a struct, or returns null if this is
|
// extractSubfield extracts a field from a struct, or returns null if this is
|
||||||
|
|||||||
+10
-10
@@ -43,7 +43,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
@@ -75,7 +75,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
@@ -84,7 +84,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
if isZeroSize {
|
if isZeroSize {
|
||||||
received = llvm.ConstNull(valueType)
|
received = llvm.ConstNull(valueType)
|
||||||
} else {
|
} else {
|
||||||
received = b.CreateLoad(valueAlloca, "chan.received")
|
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
@@ -173,7 +173,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
||||||
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||||
recvbuf = b.CreateGEP(recvbufAlloca, []llvm.Value{
|
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
}, "select.recvbuf")
|
}, "select.recvbuf")
|
||||||
@@ -184,13 +184,13 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
||||||
for i, state := range selectStates {
|
for i, state := range selectStates {
|
||||||
// Set each slice element to the appropriate channel.
|
// Set each slice element to the appropriate channel.
|
||||||
gep := b.CreateGEP(statesAlloca, []llvm.Value{
|
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
}, "")
|
}, "")
|
||||||
b.CreateStore(state, gep)
|
b.CreateStore(state, gep)
|
||||||
}
|
}
|
||||||
statesPtr := b.CreateGEP(statesAlloca, []llvm.Value{
|
statesPtr := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
}, "select.states")
|
}, "select.states")
|
||||||
@@ -204,7 +204,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
||||||
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
||||||
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||||
chBlockPtr := b.CreateGEP(chBlockAlloca, []llvm.Value{
|
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
}, "select.block")
|
}, "select.block")
|
||||||
@@ -264,8 +264,8 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
|||||||
// receive can proceed at a time) so we'll get that alloca, bitcast
|
// 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 := llvm.PointerType(b.getLLVMType(expr.Type()), 0)
|
typ := b.getLLVMType(expr.Type())
|
||||||
ptr := b.CreateBitCast(recvbuf, typ, "")
|
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "")
|
||||||
return b.CreateLoad(ptr, "")
|
return b.CreateLoad(typ, ptr, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+216
-132
@@ -22,10 +22,6 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var typeParamUnderlyingType = func(t types.Type) types.Type {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
llvm.InitializeAllTargetMCs()
|
llvm.InitializeAllTargetMCs()
|
||||||
@@ -45,6 +41,7 @@ type Config struct {
|
|||||||
Triple string
|
Triple string
|
||||||
CPU string
|
CPU string
|
||||||
Features string
|
Features string
|
||||||
|
ABI string
|
||||||
GOOS string
|
GOOS string
|
||||||
GOARCH string
|
GOARCH string
|
||||||
CodeModel string
|
CodeModel string
|
||||||
@@ -67,6 +64,7 @@ type compilerContext struct {
|
|||||||
DumpSSA bool
|
DumpSSA bool
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
|
builder llvm.Builder // only used for constant operations
|
||||||
dibuilder *llvm.DIBuilder
|
dibuilder *llvm.DIBuilder
|
||||||
cu llvm.Metadata
|
cu llvm.Metadata
|
||||||
difiles map[string]llvm.Metadata
|
difiles map[string]llvm.Metadata
|
||||||
@@ -78,6 +76,7 @@ type compilerContext struct {
|
|||||||
i8ptrType llvm.Type // for convenience
|
i8ptrType llvm.Type // for convenience
|
||||||
rawVoidFuncType llvm.Type // for convenience
|
rawVoidFuncType llvm.Type // for convenience
|
||||||
funcPtrAddrSpace int
|
funcPtrAddrSpace int
|
||||||
|
hasTypedPointers bool // for LLVM 14 backwards compatibility
|
||||||
uintptrType llvm.Type
|
uintptrType llvm.Type
|
||||||
program *ssa.Program
|
program *ssa.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
@@ -102,6 +101,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.ctx = llvm.NewContext()
|
c.ctx = llvm.NewContext()
|
||||||
|
c.builder = c.ctx.NewBuilder()
|
||||||
c.mod = c.ctx.NewModule(moduleName)
|
c.mod = c.ctx.NewModule(moduleName)
|
||||||
c.mod.SetTarget(config.Triple)
|
c.mod.SetTarget(config.Triple)
|
||||||
c.mod.SetDataLayout(c.targetData.String())
|
c.mod.SetDataLayout(c.targetData.String())
|
||||||
@@ -124,17 +124,25 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
|||||||
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
|
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
|
||||||
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
|
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
|
||||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||||
|
c.hasTypedPointers = c.i8ptrType != llvm.PointerType(c.ctx.Int16Type(), 0) // with opaque pointers, all pointers are the same type (LLVM 15+)
|
||||||
c.rawVoidFuncType = dummyFunc.Type()
|
c.rawVoidFuncType = dummyFunc.Type()
|
||||||
dummyFunc.EraseFromParentAsFunction()
|
dummyFunc.EraseFromParentAsFunction()
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispose everything related to the context, _except_ for the IR module (and
|
||||||
|
// the associated context).
|
||||||
|
func (c *compilerContext) dispose() {
|
||||||
|
c.builder.Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
// builder contains all information relevant to build a single function.
|
// builder contains all information relevant to build a single function.
|
||||||
type builder struct {
|
type builder struct {
|
||||||
*compilerContext
|
*compilerContext
|
||||||
llvm.Builder
|
llvm.Builder
|
||||||
fn *ssa.Function
|
fn *ssa.Function
|
||||||
|
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
|
||||||
@@ -144,6 +152,7 @@ type builder struct {
|
|||||||
phis []phiNode
|
phis []phiNode
|
||||||
deferPtr llvm.Value
|
deferPtr llvm.Value
|
||||||
deferFrame llvm.Value
|
deferFrame llvm.Value
|
||||||
|
stackChainAlloca llvm.Value
|
||||||
landingpad llvm.BasicBlock
|
landingpad llvm.BasicBlock
|
||||||
difunc llvm.Metadata
|
difunc llvm.Metadata
|
||||||
dilocals map[*types.Var]llvm.Metadata
|
dilocals map[*types.Var]llvm.Metadata
|
||||||
@@ -159,11 +168,13 @@ type builder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
||||||
|
fnType, fn := c.getFunction(f)
|
||||||
return &builder{
|
return &builder{
|
||||||
compilerContext: c,
|
compilerContext: c,
|
||||||
Builder: irbuilder,
|
Builder: irbuilder,
|
||||||
fn: f,
|
fn: f,
|
||||||
llvmFn: c.getFunction(f),
|
llvmFnType: fnType,
|
||||||
|
llvmFn: fn,
|
||||||
info: c.getFunctionInfo(f),
|
info: c.getFunctionInfo(f),
|
||||||
locals: make(map[ssa.Value]llvm.Value),
|
locals: make(map[ssa.Value]llvm.Value),
|
||||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||||
@@ -257,6 +268,7 @@ 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.
|
||||||
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()
|
||||||
c.packageDir = pkg.OriginalDir()
|
c.packageDir = pkg.OriginalDir()
|
||||||
c.embedGlobals = pkg.EmbedGlobals
|
c.embedGlobals = pkg.EmbedGlobals
|
||||||
c.pkg = pkg.Pkg
|
c.pkg = pkg.Pkg
|
||||||
@@ -313,15 +325,30 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
c.dibuilder.Destroy()
|
c.dibuilder.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the "target-abi" flag, which is necessary on RISC-V otherwise it will
|
||||||
|
// pick one that doesn't match the -mabi Clang flag.
|
||||||
|
if c.ABI != "" {
|
||||||
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||||
|
c.ctx.MDString("target-abi"),
|
||||||
|
c.ctx.MDString(c.ABI),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return c.mod, c.diagnostics
|
return c.mod, c.diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) getRuntimeType(name string) types.Type {
|
||||||
|
return c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
|
||||||
|
}
|
||||||
|
|
||||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||||
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
||||||
// getLLVMType(getRuntimeType(name)).
|
// getLLVMType(getRuntimeType(name)).
|
||||||
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
||||||
typ := c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
|
return c.getLLVMType(c.getRuntimeType(name))
|
||||||
return c.getLLVMType(typ)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
|
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
|
||||||
@@ -345,7 +372,6 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
|||||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||||
// getLLVMType instead.
|
// getLLVMType instead.
|
||||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||||
goType = typeParamUnderlyingType(goType)
|
|
||||||
switch typ := goType.(type) {
|
switch typ := goType.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
@@ -402,14 +428,20 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
}
|
}
|
||||||
return c.getLLVMType(typ.Underlying())
|
return c.getLLVMType(typ.Underlying())
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
ptrTo := c.getLLVMType(typ.Elem())
|
if c.hasTypedPointers {
|
||||||
return llvm.PointerType(ptrTo, 0)
|
ptrTo := c.getLLVMType(typ.Elem())
|
||||||
|
return llvm.PointerType(ptrTo, 0)
|
||||||
|
}
|
||||||
|
return c.i8ptrType // all pointers are the same
|
||||||
case *types.Signature: // function value
|
case *types.Signature: // function value
|
||||||
return c.getFuncType(typ)
|
return c.getFuncType(typ)
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
ptrType := c.i8ptrType
|
||||||
|
if c.hasTypedPointers {
|
||||||
|
ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0)
|
||||||
|
}
|
||||||
members := []llvm.Type{
|
members := []llvm.Type{
|
||||||
llvm.PointerType(elemType, 0),
|
ptrType,
|
||||||
c.uintptrType, // len
|
c.uintptrType, // len
|
||||||
c.uintptrType, // cap
|
c.uintptrType, // cap
|
||||||
}
|
}
|
||||||
@@ -420,6 +452,8 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
members[i] = c.getLLVMType(typ.Field(i).Type())
|
members[i] = c.getLLVMType(typ.Field(i).Type())
|
||||||
}
|
}
|
||||||
return c.ctx.StructType(members, false)
|
return c.ctx.StructType(members, false)
|
||||||
|
case *types.TypeParam:
|
||||||
|
return c.getLLVMType(typ.Underlying())
|
||||||
case *types.Tuple:
|
case *types.Tuple:
|
||||||
members := make([]llvm.Type, typ.Len())
|
members := make([]llvm.Type, typ.Len())
|
||||||
for i := 0; i < typ.Len(); i++ {
|
for i := 0; i < typ.Len(); i++ {
|
||||||
@@ -455,7 +489,6 @@ func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
|
|||||||
// createDIType creates a new DWARF type. Don't call this function directly,
|
// createDIType creates a new DWARF type. Don't call this function directly,
|
||||||
// call getDIType instead.
|
// call getDIType instead.
|
||||||
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
||||||
typ = typeParamUnderlyingType(typ)
|
|
||||||
llvmType := c.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
@@ -619,6 +652,8 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
|||||||
})
|
})
|
||||||
temporaryMDNode.ReplaceAllUsesWith(md)
|
temporaryMDNode.ReplaceAllUsesWith(md)
|
||||||
return md
|
return md
|
||||||
|
case *types.TypeParam:
|
||||||
|
return c.getDIType(typ.Underlying())
|
||||||
default:
|
default:
|
||||||
panic("unknown type while generating DWARF debug type: " + typ.String())
|
panic("unknown type while generating DWARF debug type: " + typ.String())
|
||||||
}
|
}
|
||||||
@@ -688,7 +723,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
|
|||||||
Name: param.Name(),
|
Name: param.Name(),
|
||||||
File: b.getDIFile(pos.Filename),
|
File: b.getDIFile(pos.Filename),
|
||||||
Line: pos.Line,
|
Line: pos.Line,
|
||||||
Type: b.getDIType(variable.Type()),
|
Type: b.getDIType(param.Type()),
|
||||||
AlwaysPreserve: true,
|
AlwaysPreserve: true,
|
||||||
ArgNo: i + 1,
|
ArgNo: i + 1,
|
||||||
})
|
})
|
||||||
@@ -713,7 +748,8 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
|
|||||||
// DISubprogram metadata node.
|
// DISubprogram metadata node.
|
||||||
func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
|
func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
|
||||||
pos := c.program.Fset.Position(f.Syntax().Pos())
|
pos := c.program.Fset.Position(f.Syntax().Pos())
|
||||||
return c.attachDebugInfoRaw(f, c.getFunction(f), "", pos.Filename, pos.Line)
|
_, fn := c.getFunction(f)
|
||||||
|
return c.attachDebugInfoRaw(f, fn, "", pos.Filename, pos.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
// attachDebugInfo adds debug info to a function declaration. It returns the
|
// attachDebugInfo adds debug info to a function declaration. It returns the
|
||||||
@@ -786,12 +822,18 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
member := pkg.Members[name]
|
member := pkg.Members[name]
|
||||||
switch member := member.(type) {
|
switch member := member.(type) {
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
if member.Synthetic == "generic function" {
|
if member.TypeParams() != nil {
|
||||||
// Do not try to build generic (non-instantiated) functions.
|
// Do not try to build generic (non-instantiated) functions.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 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 {
|
||||||
|
// The body of this function (if there is one) is ignored and
|
||||||
|
// replaced with a LLVM intrinsic call.
|
||||||
|
b.defineMathOp()
|
||||||
|
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()
|
||||||
@@ -845,7 +887,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
if files, ok := c.embedGlobals[member.Name()]; ok {
|
if files, ok := c.embedGlobals[member.Name()]; ok {
|
||||||
c.createEmbedGlobal(member, global, files)
|
c.createEmbedGlobal(member, global, files)
|
||||||
} else if !info.extern {
|
} else if !info.extern {
|
||||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
if info.section != "" {
|
if info.section != "" {
|
||||||
global.SetSection(info.section)
|
global.SetSection(info.section)
|
||||||
@@ -910,7 +952,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
bufferGlobal.SetInitializer(bufferValue)
|
bufferGlobal.SetInitializer(bufferValue)
|
||||||
bufferGlobal.SetLinkage(llvm.InternalLinkage)
|
bufferGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
bufferGlobal.SetAlignment(1)
|
bufferGlobal.SetAlignment(1)
|
||||||
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
|
slicePtr := llvm.ConstInBoundsGEP(bufferValue.Type(), bufferGlobal, []llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
})
|
})
|
||||||
@@ -964,10 +1006,10 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
for _, file := range allFiles {
|
for _, file := range allFiles {
|
||||||
fileStruct := llvm.ConstNull(embedFileStructType)
|
fileStruct := llvm.ConstNull(embedFileStructType)
|
||||||
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
|
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
|
||||||
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
|
fileStruct = c.builder.CreateInsertValue(fileStruct, name, 0, "") // "name" field
|
||||||
if file.Hash != "" {
|
if file.Hash != "" {
|
||||||
data := c.getEmbedFileString(file)
|
data := c.getEmbedFileString(file)
|
||||||
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
|
fileStruct = c.builder.CreateInsertValue(fileStruct, data, 1, "") // "data" field
|
||||||
}
|
}
|
||||||
fileStructs = append(fileStructs, fileStruct)
|
fileStructs = append(fileStructs, fileStruct)
|
||||||
}
|
}
|
||||||
@@ -982,7 +1024,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
// Create the slice object itself.
|
// Create the slice object itself.
|
||||||
// Because embed.FS refers to it as *[]embed.file instead of a plain
|
// Because embed.FS refers to it as *[]embed.file instead of a plain
|
||||||
// []embed.file, we have to store this as a global.
|
// []embed.file, we have to store this as a global.
|
||||||
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
|
slicePtr := llvm.ConstInBoundsGEP(sliceDataInitializer.Type(), sliceDataGlobal, []llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
})
|
})
|
||||||
@@ -998,7 +1040,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
// Define the embed.FS struct. It has only one field: the files (as a
|
// Define the embed.FS struct. It has only one field: the files (as a
|
||||||
// *[]embed.file).
|
// *[]embed.file).
|
||||||
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||||
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
|
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
|
||||||
global.SetInitializer(globalInitializer)
|
global.SetInitializer(globalInitializer)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||||
@@ -1010,11 +1052,11 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
|
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
|
||||||
dataGlobalName := "embed/file_" + file.Hash
|
dataGlobalName := "embed/file_" + file.Hash
|
||||||
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
|
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
|
||||||
|
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
|
||||||
if dataGlobal.IsNil() {
|
if dataGlobal.IsNil() {
|
||||||
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
|
|
||||||
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
|
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
|
||||||
}
|
}
|
||||||
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
|
strPtr := llvm.ConstInBoundsGEP(dataGlobalType, dataGlobal, []llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
})
|
})
|
||||||
@@ -1026,7 +1068,7 @@ func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value
|
|||||||
// parameters, create basic blocks, and set up debug information.
|
// parameters, create basic blocks, and set up debug information.
|
||||||
// This is separated out from createFunction() so that it is also usable to
|
// This is separated out from createFunction() so that it is also usable to
|
||||||
// define compiler intrinsics like the atomic operations in sync/atomic.
|
// define compiler intrinsics like the atomic operations in sync/atomic.
|
||||||
func (b *builder) createFunctionStart() {
|
func (b *builder) createFunctionStart(intrinsic bool) {
|
||||||
if b.DumpSSA {
|
if b.DumpSSA {
|
||||||
fmt.Printf("\nfunc %s:\n", b.fn)
|
fmt.Printf("\nfunc %s:\n", b.fn)
|
||||||
}
|
}
|
||||||
@@ -1039,9 +1081,17 @@ func (b *builder) createFunctionStart() {
|
|||||||
b.addError(b.fn.Pos(), errValue)
|
b.addError(b.fn.Pos(), errValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b.addStandardDefinedAttributes(b.llvmFn)
|
b.addStandardDefinedAttributes(b.llvmFn)
|
||||||
if !b.info.exported {
|
if !b.info.exported {
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
// Do not set visibility for local linkage (internal or private).
|
||||||
|
// Otherwise a "local linkage requires default visibility"
|
||||||
|
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
|
||||||
|
// is thrown.
|
||||||
|
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
|
||||||
|
b.llvmFn.Linkage() != llvm.PrivateLinkage {
|
||||||
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
if b.info.section != "" {
|
if b.info.section != "" {
|
||||||
@@ -1052,6 +1102,11 @@ func (b *builder) createFunctionStart() {
|
|||||||
// otherwise the function is not exported.
|
// otherwise the function is not exported.
|
||||||
functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
|
functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
|
||||||
b.llvmFn.AddFunctionAttr(functionAttr)
|
b.llvmFn.AddFunctionAttr(functionAttr)
|
||||||
|
// Unlike most targets, exported functions are actually visible in
|
||||||
|
// WebAssembly (even if it's not called from within the WebAssembly
|
||||||
|
// module). But LTO generally optimizes such functions away. Therefore,
|
||||||
|
// exported functions must be explicitly marked as used.
|
||||||
|
llvmutil.AppendToGlobal(b.mod, "llvm.used", b.llvmFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some functions have a pragma controlling the inlining level.
|
// Some functions have a pragma controlling the inlining level.
|
||||||
@@ -1091,20 +1146,20 @@ func (b *builder) createFunctionStart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pre-create all basic blocks in the function.
|
// Pre-create all basic blocks in the function.
|
||||||
for _, block := range b.fn.DomPreorder() {
|
|
||||||
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
|
||||||
b.blockEntries[block] = llvmBlock
|
|
||||||
b.blockExits[block] = llvmBlock
|
|
||||||
}
|
|
||||||
var entryBlock llvm.BasicBlock
|
var entryBlock llvm.BasicBlock
|
||||||
if len(b.fn.Blocks) != 0 {
|
if intrinsic {
|
||||||
// Normal functions have an entry block.
|
|
||||||
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
|
||||||
} else {
|
|
||||||
// This function isn't defined in Go SSA. It is probably a compiler
|
// This function isn't defined in Go SSA. It is probably a compiler
|
||||||
// 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 {
|
||||||
|
for _, block := range b.fn.DomPreorder() {
|
||||||
|
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
||||||
|
b.blockEntries[block] = llvmBlock
|
||||||
|
b.blockExits[block] = llvmBlock
|
||||||
|
}
|
||||||
|
// Normal functions have an entry block.
|
||||||
|
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
||||||
}
|
}
|
||||||
b.SetInsertPointAtEnd(entryBlock)
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
|
|
||||||
@@ -1180,13 +1235,20 @@ func (b *builder) createFunctionStart() {
|
|||||||
// them.
|
// them.
|
||||||
b.deferInitFunc()
|
b.deferInitFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.NeedsStackObjects {
|
||||||
|
// Create a dummy alloca that will be used in runtime.trackPointer.
|
||||||
|
// It is necessary to pass a dummy alloca to runtime.trackPointer
|
||||||
|
// because runtime.trackPointer is replaced by an alloca store.
|
||||||
|
b.stackChainAlloca = b.CreateAlloca(b.ctx.Int8Type(), "stackalloc")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createFunction builds the LLVM IR implementation for this function. The
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
// function must not yet be defined, otherwise this function will create a
|
// function must not yet be defined, otherwise this function will create a
|
||||||
// diagnostic.
|
// diagnostic.
|
||||||
func (b *builder) createFunction() {
|
func (b *builder) createFunction() {
|
||||||
b.createFunctionStart()
|
b.createFunctionStart(false)
|
||||||
|
|
||||||
// Fill blocks with instructions.
|
// Fill blocks with instructions.
|
||||||
for _, block := range b.fn.DomPreorder() {
|
for _, block := range b.fn.DomPreorder() {
|
||||||
@@ -1265,6 +1327,7 @@ func (b *builder) createFunction() {
|
|||||||
// Create anonymous functions (closures etc.).
|
// Create anonymous functions (closures etc.).
|
||||||
for _, sub := range b.fn.AnonFuncs {
|
for _, sub := range b.fn.AnonFuncs {
|
||||||
b := newBuilder(b.compilerContext, b.Builder, sub)
|
b := newBuilder(b.compilerContext, b.Builder, sub)
|
||||||
|
b.llvmFn.SetLinkage(llvm.InternalLinkage)
|
||||||
b.createFunction()
|
b.createFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1379,7 +1442,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
b.CreateRet(b.getValue(instr.Results[0]))
|
b.CreateRet(b.getValue(instr.Results[0]))
|
||||||
} else {
|
} else {
|
||||||
// Multiple return values. Put them all in a struct.
|
// Multiple return values. Put them all in a struct.
|
||||||
retVal := llvm.ConstNull(b.llvmFn.Type().ElementType().ReturnType())
|
retVal := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
|
||||||
for i, result := range instr.Results {
|
for i, result := range instr.Results {
|
||||||
val := b.getValue(result)
|
val := b.getValue(result)
|
||||||
retVal = b.CreateInsertValue(retVal, val, i, "")
|
retVal = b.CreateInsertValue(retVal, val, i, "")
|
||||||
@@ -1418,7 +1481,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
|
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
|
||||||
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
|
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
|
||||||
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
||||||
elemType := srcBuf.Type().ElementType()
|
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{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
||||||
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
||||||
@@ -1471,7 +1534,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
||||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
||||||
elemType := dstBuf.Type().ElementType()
|
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||||
dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr")
|
dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr")
|
||||||
srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr")
|
srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr")
|
||||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
@@ -1584,7 +1647,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
// Note: the pointer is always of type *i8.
|
// Note: the pointer is always of type *i8.
|
||||||
ptr := argValues[0]
|
ptr := argValues[0]
|
||||||
len := argValues[1]
|
len := argValues[1]
|
||||||
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
return b.CreateGEP(b.ctx.Int8Type(), ptr, []llvm.Value{len}, ""), nil
|
||||||
case "Alignof": // unsafe.Alignof
|
case "Alignof": // unsafe.Alignof
|
||||||
align := b.targetData.ABITypeAlignment(argValues[0].Type())
|
align := b.targetData.ABITypeAlignment(argValues[0].Type())
|
||||||
return llvm.ConstInt(b.uintptrType, uint64(align), false), nil
|
return llvm.ConstInt(b.uintptrType, uint64(align), false), nil
|
||||||
@@ -1599,19 +1662,20 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
case "Sizeof": // unsafe.Sizeof
|
case "Sizeof": // unsafe.Sizeof
|
||||||
size := b.targetData.TypeAllocSize(argValues[0].Type())
|
size := b.targetData.TypeAllocSize(argValues[0].Type())
|
||||||
return llvm.ConstInt(b.uintptrType, size, false), nil
|
return llvm.ConstInt(b.uintptrType, size, false), nil
|
||||||
case "Slice": // unsafe.Slice
|
case "Slice", "String": // unsafe.Slice, unsafe.String
|
||||||
// This creates a slice from a pointer and a length.
|
// This creates a slice or string from a pointer and a length.
|
||||||
// Note that the exception mentioned in the documentation (if the
|
// Note that the exception mentioned in the documentation (if the
|
||||||
// pointer and length are nil, the slice is also nil) is trivially
|
// pointer and length are nil, the slice is also nil) is trivially
|
||||||
// already the case.
|
// already the case.
|
||||||
ptr := argValues[0]
|
ptr := argValues[0]
|
||||||
len := argValues[1]
|
len := argValues[1]
|
||||||
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
|
var elementType llvm.Type
|
||||||
ptr.Type(),
|
if callName == "Slice" {
|
||||||
b.uintptrType,
|
elementType = b.getLLVMType(argTypes[0].Underlying().(*types.Pointer).Elem())
|
||||||
b.uintptrType,
|
} else {
|
||||||
}, false))
|
elementType = b.ctx.Int8Type()
|
||||||
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
|
}
|
||||||
|
b.createUnsafeSliceStringCheck("unsafe."+callName, ptr, len, elementType, argTypes[1].Underlying().(*types.Basic))
|
||||||
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||||
// Too small, zero-extend len.
|
// Too small, zero-extend len.
|
||||||
len = b.CreateZExt(len, b.uintptrType, "")
|
len = b.CreateZExt(len, b.uintptrType, "")
|
||||||
@@ -1619,10 +1683,24 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
// Too big, truncate len.
|
// Too big, truncate len.
|
||||||
len = b.CreateTrunc(len, b.uintptrType, "")
|
len = b.CreateTrunc(len, b.uintptrType, "")
|
||||||
}
|
}
|
||||||
slice = b.CreateInsertValue(slice, ptr, 0, "")
|
if callName == "Slice" {
|
||||||
slice = b.CreateInsertValue(slice, len, 1, "")
|
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
|
||||||
slice = b.CreateInsertValue(slice, len, 2, "")
|
ptr.Type(),
|
||||||
return slice, nil
|
b.uintptrType,
|
||||||
|
b.uintptrType,
|
||||||
|
}, false))
|
||||||
|
slice = b.CreateInsertValue(slice, ptr, 0, "")
|
||||||
|
slice = b.CreateInsertValue(slice, len, 1, "")
|
||||||
|
slice = b.CreateInsertValue(slice, len, 2, "")
|
||||||
|
return slice, nil
|
||||||
|
} else {
|
||||||
|
str := llvm.Undef(b.getLLVMRuntimeType("_string"))
|
||||||
|
str = b.CreateInsertValue(str, argValues[0], 0, "")
|
||||||
|
str = b.CreateInsertValue(str, len, 1, "")
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
case "SliceData", "StringData": // unsafe.SliceData, unsafe.StringData
|
||||||
|
return b.CreateExtractValue(argValues[0], 0, "slice.data"), nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
||||||
}
|
}
|
||||||
@@ -1641,17 +1719,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
|
|
||||||
// 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
|
||||||
exported := false
|
exported := false
|
||||||
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 := fn.RelString(nil)
|
||||||
switch {
|
switch {
|
||||||
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
|
||||||
result, ok := b.createMathOp(instr)
|
|
||||||
if ok {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
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)
|
||||||
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||||
@@ -1676,7 +1750,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return b.createInterruptGlobal(instr)
|
return b.createInterruptGlobal(instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
callee = b.getFunction(fn)
|
calleeType, callee = b.getFunction(fn)
|
||||||
info := b.getFunctionInfo(fn)
|
info := b.getFunctionInfo(fn)
|
||||||
if callee.IsNil() {
|
if callee.IsNil() {
|
||||||
return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+info.linkName)
|
return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+info.linkName)
|
||||||
@@ -1690,8 +1764,8 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
// Eventually we might be able to eliminate this special case
|
// Eventually we might be able to eliminate this special case
|
||||||
// entirely. For details, see:
|
// entirely. For details, see:
|
||||||
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
|
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
|
||||||
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
|
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
|
||||||
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
|
callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace))
|
||||||
}
|
}
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// A call on a func value, but the callee is trivial to find. For
|
// A call on a func value, but the callee is trivial to find. For
|
||||||
@@ -1718,13 +1792,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
params = append([]llvm.Value{value}, params...)
|
params = append([]llvm.Value{value}, params...)
|
||||||
params = append(params, typecode)
|
params = append(params, typecode)
|
||||||
callee = b.getInvokeFunction(instr)
|
callee = b.getInvokeFunction(instr)
|
||||||
|
calleeType = callee.GlobalValueType()
|
||||||
context = llvm.Undef(b.i8ptrType)
|
context = llvm.Undef(b.i8ptrType)
|
||||||
} else {
|
} else {
|
||||||
// Function pointer.
|
// Function pointer.
|
||||||
value := b.getValue(instr.Value)
|
value := b.getValue(instr.Value)
|
||||||
// This is a func value, which cannot be called directly. We have to
|
// This is a func value, which cannot be called directly. We have to
|
||||||
// extract the function pointer and context first from the func value.
|
// extract the function pointer and context first from the func value.
|
||||||
callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
calleeType, callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
||||||
b.createNilCheck(instr.Value, callee, "fpcall")
|
b.createNilCheck(instr.Value, callee, "fpcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1734,7 +1809,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
params = append(params, context)
|
params = append(params, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.createInvoke(callee, params, ""), nil
|
return b.createInvoke(calleeType, callee, params, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getValue returns the LLVM value of a constant, function value, global, or
|
// getValue returns the LLVM value of a constant, function value, global, or
|
||||||
@@ -1748,7 +1823,8 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
|||||||
b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String())
|
b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String())
|
||||||
return llvm.Undef(b.getLLVMType(expr.Type()))
|
return llvm.Undef(b.getLLVMType(expr.Type()))
|
||||||
}
|
}
|
||||||
return b.createFuncValue(b.getFunction(expr), llvm.Undef(b.i8ptrType), expr.Signature)
|
_, fn := b.getFunction(expr)
|
||||||
|
return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature)
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
value := b.getGlobal(expr)
|
value := b.getGlobal(expr)
|
||||||
if value.IsNil() {
|
if value.IsNil() {
|
||||||
@@ -1896,46 +1972,77 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false),
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false),
|
||||||
}
|
}
|
||||||
return b.CreateInBoundsGEP(val, indices, ""), nil
|
elementType := b.getLLVMType(expr.X.Type().Underlying().(*types.Pointer).Elem())
|
||||||
|
return b.CreateInBoundsGEP(elementType, val, indices, ""), nil
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
panic("function is not an expression")
|
panic("function is not an expression")
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
panic("global is not an expression")
|
panic("global is not an expression")
|
||||||
case *ssa.Index:
|
case *ssa.Index:
|
||||||
array := b.getValue(expr.X)
|
collection := b.getValue(expr.X)
|
||||||
index := b.getValue(expr.Index)
|
index := b.getValue(expr.Index)
|
||||||
|
|
||||||
// Extend index to at least uintptr size, because getelementptr assumes
|
switch xType := expr.X.Type().Underlying().(type) {
|
||||||
// index is a signed integer.
|
case *types.Basic: // extract byte from string
|
||||||
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
// Value type must be a string, which is a basic type.
|
||||||
|
if xType.Info()&types.IsString == 0 {
|
||||||
|
panic("lookup on non-string?")
|
||||||
|
}
|
||||||
|
|
||||||
// Check bounds.
|
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
|
||||||
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
|
// correctly extend that type for two reasons:
|
||||||
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
// 1. The lookup bounds check expects an index of at least uintptr
|
||||||
b.createLookupBoundsCheck(arrayLenLLVM, index)
|
// size.
|
||||||
|
// 2. getelementptr has signed operands, and therefore s[uint8(x)]
|
||||||
|
// can be lowered as s[int8(x)]. That would be a bug.
|
||||||
|
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
||||||
|
|
||||||
// Can't load directly from array (as index is non-constant), so have to
|
// Bounds check.
|
||||||
// do it using an alloca+gep+load.
|
length := b.CreateExtractValue(collection, 1, "len")
|
||||||
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(array.Type(), "index.alloca")
|
b.createLookupBoundsCheck(length, index)
|
||||||
b.CreateStore(array, alloca)
|
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
// Lookup byte
|
||||||
ptr := b.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep")
|
buf := b.CreateExtractValue(collection, 0, "")
|
||||||
result := b.CreateLoad(ptr, "index.load")
|
bufElemType := b.ctx.Int8Type()
|
||||||
b.emitLifetimeEnd(allocaPtr, allocaSize)
|
bufPtr := b.CreateInBoundsGEP(bufElemType, buf, []llvm.Value{index}, "")
|
||||||
return result, nil
|
return b.CreateLoad(bufElemType, bufPtr, ""), nil
|
||||||
|
case *types.Array: // extract element from array
|
||||||
|
// Extend index to at least uintptr size, because getelementptr
|
||||||
|
// assumes index is a signed integer.
|
||||||
|
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
||||||
|
|
||||||
|
// Check bounds.
|
||||||
|
arrayLen := llvm.ConstInt(b.uintptrType, uint64(xType.Len()), false)
|
||||||
|
b.createLookupBoundsCheck(arrayLen, index)
|
||||||
|
|
||||||
|
// Can't load directly from array (as index is non-constant), so
|
||||||
|
// have to do it using an alloca+gep+load.
|
||||||
|
arrayType := collection.Type()
|
||||||
|
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
|
||||||
|
b.CreateStore(collection, alloca)
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
|
||||||
|
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
|
||||||
|
b.emitLifetimeEnd(allocaPtr, allocaSize)
|
||||||
|
return result, nil
|
||||||
|
default:
|
||||||
|
panic("unknown *ssa.Index type")
|
||||||
|
}
|
||||||
case *ssa.IndexAddr:
|
case *ssa.IndexAddr:
|
||||||
val := b.getValue(expr.X)
|
val := b.getValue(expr.X)
|
||||||
index := b.getValue(expr.Index)
|
index := b.getValue(expr.Index)
|
||||||
|
|
||||||
// Get buffer pointer and length
|
// Get buffer pointer and length
|
||||||
var bufptr, buflen llvm.Value
|
var bufptr, buflen llvm.Value
|
||||||
|
var bufType llvm.Type
|
||||||
switch ptrTyp := expr.X.Type().Underlying().(type) {
|
switch ptrTyp := expr.X.Type().Underlying().(type) {
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
|
typ := ptrTyp.Elem().Underlying()
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
bufptr = val
|
bufptr = val
|
||||||
buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false)
|
buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false)
|
||||||
|
bufType = b.getLLVMType(typ)
|
||||||
// Check for nil pointer before calculating the address, from
|
// Check for nil pointer before calculating the address, from
|
||||||
// the spec:
|
// the spec:
|
||||||
// > For an operand x of type T, the address operation &x
|
// > For an operand x of type T, the address operation &x
|
||||||
@@ -1949,6 +2056,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr")
|
bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr")
|
||||||
buflen = b.CreateExtractValue(val, 1, "indexaddr.len")
|
buflen = b.CreateExtractValue(val, 1, "indexaddr.len")
|
||||||
|
bufType = b.getLLVMType(ptrTyp.Elem())
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
|
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
|
||||||
}
|
}
|
||||||
@@ -1966,47 +2074,20 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
return b.CreateInBoundsGEP(bufptr, indices, ""), nil
|
return b.CreateInBoundsGEP(bufType, bufptr, indices, ""), nil
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
return b.CreateInBoundsGEP(bufptr, []llvm.Value{index}, ""), nil
|
return b.CreateInBoundsGEP(bufType, bufptr, []llvm.Value{index}, ""), nil
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
case *ssa.Lookup:
|
case *ssa.Lookup: // map lookup
|
||||||
value := b.getValue(expr.X)
|
value := b.getValue(expr.X)
|
||||||
index := b.getValue(expr.Index)
|
index := b.getValue(expr.Index)
|
||||||
switch xType := expr.X.Type().Underlying().(type) {
|
valueType := expr.Type()
|
||||||
case *types.Basic:
|
if expr.CommaOk {
|
||||||
// Value type must be a string, which is a basic type.
|
valueType = valueType.(*types.Tuple).At(0).Type()
|
||||||
if xType.Info()&types.IsString == 0 {
|
|
||||||
panic("lookup on non-string?")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
|
|
||||||
// correctly extend that type for two reasons:
|
|
||||||
// 1. The lookup bounds check expects an index of at least uintptr
|
|
||||||
// size.
|
|
||||||
// 2. getelementptr has signed operands, and therefore s[uint8(x)]
|
|
||||||
// can be lowered as s[int8(x)]. That would be a bug.
|
|
||||||
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
|
|
||||||
|
|
||||||
// Bounds check.
|
|
||||||
length := b.CreateExtractValue(value, 1, "len")
|
|
||||||
b.createLookupBoundsCheck(length, index)
|
|
||||||
|
|
||||||
// Lookup byte
|
|
||||||
buf := b.CreateExtractValue(value, 0, "")
|
|
||||||
bufPtr := b.CreateInBoundsGEP(buf, []llvm.Value{index}, "")
|
|
||||||
return b.CreateLoad(bufPtr, ""), nil
|
|
||||||
case *types.Map:
|
|
||||||
valueType := expr.Type()
|
|
||||||
if expr.CommaOk {
|
|
||||||
valueType = valueType.(*types.Tuple).At(0).Type()
|
|
||||||
}
|
|
||||||
return b.createMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos())
|
|
||||||
default:
|
|
||||||
panic("unknown lookup type: " + expr.String())
|
|
||||||
}
|
}
|
||||||
|
return b.createMapLookup(expr.X.Type().Underlying().(*types.Map).Key(), valueType, value, 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:
|
||||||
@@ -2130,7 +2211,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
switch typ := expr.X.Type().Underlying().(type) {
|
switch typ := expr.X.Type().Underlying().(type) {
|
||||||
case *types.Pointer: // pointer to array
|
case *types.Pointer: // pointer to array
|
||||||
// slice an array
|
// slice an array
|
||||||
length := typ.Elem().Underlying().(*types.Array).Len()
|
arrayType := typ.Elem().Underlying().(*types.Array)
|
||||||
|
length := arrayType.Len()
|
||||||
llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false)
|
llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false)
|
||||||
if high.IsNil() {
|
if high.IsNil() {
|
||||||
high = llvmLen
|
high = llvmLen
|
||||||
@@ -2159,7 +2241,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sliceLen := b.CreateSub(high, low, "slice.len")
|
sliceLen := b.CreateSub(high, low, "slice.len")
|
||||||
slicePtr := b.CreateInBoundsGEP(value, indices, "slice.ptr")
|
slicePtr := b.CreateInBoundsGEP(b.getLLVMType(arrayType), value, indices, "slice.ptr")
|
||||||
sliceCap := b.CreateSub(max, low, "slice.cap")
|
sliceCap := b.CreateSub(max, low, "slice.cap")
|
||||||
|
|
||||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
slice := b.ctx.ConstStruct([]llvm.Value{
|
||||||
@@ -2198,7 +2280,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
max = b.CreateTrunc(max, b.uintptrType, "")
|
max = b.CreateTrunc(max, b.uintptrType, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
ptrElemType := b.getLLVMType(typ.Elem())
|
||||||
|
newPtr := b.CreateInBoundsGEP(ptrElemType, oldPtr, []llvm.Value{low}, "")
|
||||||
newLen := b.CreateSub(high, low, "")
|
newLen := b.CreateSub(high, low, "")
|
||||||
newCap := b.CreateSub(max, low, "")
|
newCap := b.CreateSub(max, low, "")
|
||||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
slice := b.ctx.ConstStruct([]llvm.Value{
|
||||||
@@ -2238,7 +2321,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
high = b.CreateTrunc(high, b.uintptrType, "")
|
high = b.CreateTrunc(high, b.uintptrType, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
newPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), oldPtr, []llvm.Value{low}, "")
|
||||||
newLen := b.CreateSub(high, low, "")
|
newLen := b.CreateSub(high, low, "")
|
||||||
str := llvm.Undef(b.getLLVMRuntimeType("_string"))
|
str := llvm.Undef(b.getLLVMRuntimeType("_string"))
|
||||||
str = b.CreateInsertValue(str, newPtr, 0, "")
|
str = b.CreateInsertValue(str, newPtr, 0, "")
|
||||||
@@ -2701,14 +2784,15 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
var strPtr llvm.Value
|
var strPtr llvm.Value
|
||||||
if str != "" {
|
if str != "" {
|
||||||
objname := c.pkg.Path() + "$string"
|
objname := c.pkg.Path() + "$string"
|
||||||
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
globalType := llvm.ArrayType(c.ctx.Int8Type(), len(str))
|
||||||
|
global := llvm.AddGlobal(c.mod, globalType, objname)
|
||||||
global.SetInitializer(c.ctx.ConstString(str, false))
|
global.SetInitializer(c.ctx.ConstString(str, false))
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
global.SetAlignment(1)
|
global.SetAlignment(1)
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
|
||||||
} else {
|
} else {
|
||||||
strPtr = llvm.ConstNull(c.i8ptrType)
|
strPtr = llvm.ConstNull(c.i8ptrType)
|
||||||
}
|
}
|
||||||
@@ -2733,15 +2817,15 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||||
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||||
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx
|
return cplx
|
||||||
} else if typ.Kind() == types.Complex128 {
|
} else if typ.Kind() == types.Complex128 {
|
||||||
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||||
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||||
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx
|
return cplx
|
||||||
} else {
|
} else {
|
||||||
panic("unknown constant of basic type: " + expr.String())
|
panic("unknown constant of basic type: " + expr.String())
|
||||||
@@ -2828,7 +2912,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
|||||||
// create a GEP that is not in bounds. However, we're
|
// create a GEP that is not in bounds. However, we're
|
||||||
// talking about unsafe code here so the programmer has to
|
// talking about unsafe code here so the programmer has to
|
||||||
// be careful anyway.
|
// be careful anyway.
|
||||||
return b.CreateInBoundsGEP(origptr, []llvm.Value{index}, ""), nil
|
return b.CreateInBoundsGEP(b.ctx.Int8Type(), origptr, []llvm.Value{index}, ""), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3061,10 +3145,10 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
||||||
}
|
}
|
||||||
case token.MUL: // *x, dereference pointer
|
case token.MUL: // *x, dereference pointer
|
||||||
unop.X.Type().Underlying().(*types.Pointer).Elem()
|
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Pointer).Elem())
|
||||||
if b.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
if b.targetData.TypeAllocSize(valueType) == 0 {
|
||||||
// zero-length data
|
// zero-length data
|
||||||
return llvm.ConstNull(x.Type().ElementType()), nil
|
return llvm.ConstNull(valueType), nil
|
||||||
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
|
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
|
||||||
// CGo function pointer. The cgo part has rewritten CGo function
|
// CGo function pointer. The cgo part has rewritten CGo function
|
||||||
// pointers as stub global variables of the form:
|
// pointers as stub global variables of the form:
|
||||||
@@ -3072,14 +3156,14 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
// Instead of a load from the global, create a bitcast of the
|
// Instead of a load from the global, create a bitcast of the
|
||||||
// function pointer itself.
|
// function pointer itself.
|
||||||
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
||||||
fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
|
_, fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||||
}
|
}
|
||||||
return b.CreateBitCast(fn, b.i8ptrType, ""), nil
|
return b.CreateBitCast(fn, b.i8ptrType, ""), nil
|
||||||
} else {
|
} else {
|
||||||
b.createNilCheck(unop.X, x, "deref")
|
b.createNilCheck(unop.X, x, "deref")
|
||||||
load := b.CreateLoad(x, "")
|
load := b.CreateLoad(valueType, x, "")
|
||||||
return load, nil
|
return load, nil
|
||||||
}
|
}
|
||||||
case token.XOR: // ^x, toggle all bits in integer
|
case token.XOR: // ^x, toggle all bits in integer
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
//go:build go1.18
|
|
||||||
// +build go1.18
|
|
||||||
|
|
||||||
package compiler
|
|
||||||
|
|
||||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
|
||||||
// support.
|
|
||||||
|
|
||||||
import "go/types"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
typeParamUnderlyingType = func(t types.Type) types.Type {
|
|
||||||
if t, ok := t.(*types.TypeParam); ok {
|
|
||||||
return t.Underlying()
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ package compiler
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -28,12 +28,6 @@ type testCase struct {
|
|||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Determine LLVM version.
|
|
||||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -54,19 +48,10 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"goroutine.go", "wasm", "asyncify"},
|
{"goroutine.go", "wasm", "asyncify"},
|
||||||
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||||
{"channel.go", "", ""},
|
{"channel.go", "", ""},
|
||||||
{"intrinsics.go", "cortex-m-qemu", ""},
|
|
||||||
{"intrinsics.go", "wasm", ""},
|
|
||||||
{"gc.go", "", ""},
|
{"gc.go", "", ""},
|
||||||
}
|
}
|
||||||
if llvmMajor >= 12 {
|
if goMinor >= 20 {
|
||||||
tests = append(tests, testCase{"intrinsics.go", "cortex-m-qemu", ""})
|
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||||
tests = append(tests, testCase{"intrinsics.go", "wasm", ""})
|
|
||||||
}
|
|
||||||
if goMinor >= 17 {
|
|
||||||
tests = append(tests, testCase{"go1.17.go", "", ""})
|
|
||||||
}
|
|
||||||
if goMinor >= 18 {
|
|
||||||
tests = append(tests, testCase{"generics.go", "", ""})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -98,13 +83,14 @@ func TestCompiler(t *testing.T) {
|
|||||||
compilerConfig := &Config{
|
compilerConfig := &Config{
|
||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
Features: config.Features(),
|
Features: config.Features(),
|
||||||
|
ABI: config.ABI(),
|
||||||
GOOS: config.GOOS(),
|
GOOS: config.GOOS(),
|
||||||
GOARCH: config.GOARCH(),
|
GOARCH: config.GOARCH(),
|
||||||
CodeModel: config.CodeModel(),
|
CodeModel: config.CodeModel(),
|
||||||
RelocationModel: config.RelocationModel(),
|
RelocationModel: config.RelocationModel(),
|
||||||
Scheduler: config.Scheduler(),
|
Scheduler: config.Scheduler(),
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.Target.DefaultStackSize,
|
DefaultStackSize: config.StackSize(),
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
}
|
}
|
||||||
machine, err := NewTargetMachine(compilerConfig)
|
machine, err := NewTargetMachine(compilerConfig)
|
||||||
@@ -162,14 +148,14 @@ 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 := ioutil.WriteFile(outPath, []byte(mod.String()), 0666)
|
err := os.WriteFile(outPath, []byte(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)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, err := ioutil.ReadFile(outPath)
|
expected, err := os.ReadFile(outPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to read golden file:", err)
|
t.Fatal("failed to read golden file:", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-24
@@ -162,9 +162,9 @@ mov x0, #0
|
|||||||
1:
|
1:
|
||||||
`
|
`
|
||||||
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
||||||
if b.GOOS != "darwin" {
|
if b.GOOS != "darwin" && b.GOOS != "windows" {
|
||||||
// These registers cause the following warning when compiling for
|
// These registers cause the following warning when compiling for
|
||||||
// MacOS:
|
// MacOS and Windows:
|
||||||
// warning: inline asm clobber list contains reserved registers:
|
// warning: inline asm clobber list contains reserved registers:
|
||||||
// X18, FP
|
// X18, FP
|
||||||
// Reserved registers on the clobber list may not be preserved
|
// Reserved registers on the clobber list may not be preserved
|
||||||
@@ -201,7 +201,7 @@ li a0, 0
|
|||||||
}
|
}
|
||||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||||
result := b.CreateCall(asm, []llvm.Value{b.deferFrame}, "setjmp")
|
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||||
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||||
continueBB := b.insertBasicBlock("")
|
continueBB := b.insertBasicBlock("")
|
||||||
@@ -249,7 +249,8 @@ func isInLoop(start *ssa.BasicBlock) bool {
|
|||||||
func (b *builder) createDefer(instr *ssa.Defer) {
|
func (b *builder) createDefer(instr *ssa.Defer) {
|
||||||
// The pointer to the previous defer struct, which we will replace to
|
// The pointer to the previous defer struct, which we will replace to
|
||||||
// make a linked list.
|
// make a linked list.
|
||||||
next := b.CreateLoad(b.deferPtr, "defer.next")
|
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||||
|
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
|
||||||
|
|
||||||
var values []llvm.Value
|
var values []llvm.Value
|
||||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
||||||
@@ -270,7 +271,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
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 = []llvm.Value{callback, next, typecode, receiverValue}
|
||||||
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
|
||||||
for _, arg := range instr.Call.Args {
|
for _, arg := range instr.Call.Args {
|
||||||
val := b.getValue(arg)
|
val := b.getValue(arg)
|
||||||
values = append(values, val)
|
values = append(values, val)
|
||||||
@@ -406,6 +407,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
|
|
||||||
// createRunDefers emits code to run all deferred functions.
|
// createRunDefers emits code to run all deferred functions.
|
||||||
func (b *builder) createRunDefers() {
|
func (b *builder) createRunDefers() {
|
||||||
|
deferType := b.getLLVMRuntimeType("_defer")
|
||||||
|
deferPtrType := llvm.PointerType(deferType, 0)
|
||||||
|
|
||||||
// Add a loop like the following:
|
// Add a loop like the following:
|
||||||
// for stack != nil {
|
// for stack != nil {
|
||||||
// _stack := stack
|
// _stack := stack
|
||||||
@@ -431,7 +435,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// Create loop head:
|
// Create loop head:
|
||||||
// for stack != nil {
|
// for stack != nil {
|
||||||
b.SetInsertPointAtEnd(loophead)
|
b.SetInsertPointAtEnd(loophead)
|
||||||
deferData := b.CreateLoad(b.deferPtr, "")
|
deferData := b.CreateLoad(deferPtrType, b.deferPtr, "")
|
||||||
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
|
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
|
||||||
b.CreateCondBr(stackIsNil, end, loop)
|
b.CreateCondBr(stackIsNil, end, loop)
|
||||||
|
|
||||||
@@ -440,17 +444,17 @@ func (b *builder) createRunDefers() {
|
|||||||
// stack = stack.next
|
// stack = stack.next
|
||||||
// switch stack.callback {
|
// switch stack.callback {
|
||||||
b.SetInsertPointAtEnd(loop)
|
b.SetInsertPointAtEnd(loop)
|
||||||
nextStackGEP := b.CreateInBoundsGEP(deferData, []llvm.Value{
|
nextStackGEP := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
|
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
|
||||||
}, "stack.next.gep")
|
}, "stack.next.gep")
|
||||||
nextStack := b.CreateLoad(nextStackGEP, "stack.next")
|
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next")
|
||||||
b.CreateStore(nextStack, b.deferPtr)
|
b.CreateStore(nextStack, b.deferPtr)
|
||||||
gep := b.CreateInBoundsGEP(deferData, []llvm.Value{
|
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field
|
||||||
}, "callback.gep")
|
}, "callback.gep")
|
||||||
callback := b.CreateLoad(gep, "callback")
|
callback := b.CreateLoad(b.uintptrType, gep, "callback")
|
||||||
sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
|
sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
|
||||||
|
|
||||||
for i, callback := range b.allDeferFuncs {
|
for i, callback := range b.allDeferFuncs {
|
||||||
@@ -472,7 +476,7 @@ func (b *builder) createRunDefers() {
|
|||||||
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||||
} else {
|
} else {
|
||||||
//Expect typecode
|
//Expect typecode
|
||||||
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, arg := range callback.Args {
|
for _, arg := range callback.Args {
|
||||||
@@ -486,12 +490,13 @@ func (b *builder) createRunDefers() {
|
|||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fnPtr llvm.Value
|
var fnPtr llvm.Value
|
||||||
|
var fnType llvm.Type
|
||||||
|
|
||||||
if !callback.IsInvoke() {
|
if !callback.IsInvoke() {
|
||||||
// Isolate the func value.
|
// Isolate the func value.
|
||||||
@@ -499,8 +504,8 @@ func (b *builder) createRunDefers() {
|
|||||||
forwardParams = forwardParams[1:]
|
forwardParams = forwardParams[1:]
|
||||||
|
|
||||||
//Get function pointer and context
|
//Get function pointer and context
|
||||||
fp, context := b.decodeFuncValue(funcValue, callback.Signature())
|
var context llvm.Value
|
||||||
fnPtr = fp
|
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature())
|
||||||
|
|
||||||
//Pass context
|
//Pass context
|
||||||
forwardParams = append(forwardParams, context)
|
forwardParams = append(forwardParams, context)
|
||||||
@@ -509,6 +514,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// parameters.
|
// parameters.
|
||||||
forwardParams = append(forwardParams[1:], forwardParams[0])
|
forwardParams = append(forwardParams[1:], forwardParams[0])
|
||||||
fnPtr = b.getInvokeFunction(callback)
|
fnPtr = b.getInvokeFunction(callback)
|
||||||
|
fnType = fnPtr.GlobalValueType()
|
||||||
|
|
||||||
// Add the context parameter. An interface call cannot also be a
|
// Add the context parameter. An interface call cannot also be a
|
||||||
// closure but we have to supply the parameter anyway for platforms
|
// closure but we have to supply the parameter anyway for platforms
|
||||||
@@ -516,7 +522,7 @@ func (b *builder) createRunDefers() {
|
|||||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.createCall(fnPtr, forwardParams, "")
|
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||||
|
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
// Direct call.
|
// Direct call.
|
||||||
@@ -533,8 +539,8 @@ func (b *builder) createRunDefers() {
|
|||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := range getParams(callback.Signature) {
|
for i := range getParams(callback.Signature) {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +553,8 @@ func (b *builder) createRunDefers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call real function.
|
// Call real function.
|
||||||
b.createInvoke(b.getFunction(callback), forwardParams, "")
|
fnType, fn := b.getFunction(callback)
|
||||||
|
b.createInvoke(fnType, fn, forwardParams, "")
|
||||||
|
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
@@ -565,13 +572,14 @@ func (b *builder) createRunDefers() {
|
|||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call deferred function.
|
// Call deferred function.
|
||||||
b.createCall(b.getFunction(fn), forwardParams, "")
|
fnType, llvmFn := b.getFunction(fn)
|
||||||
|
b.createCall(fnType, llvmFn, forwardParams, "")
|
||||||
case *ssa.Builtin:
|
case *ssa.Builtin:
|
||||||
db := b.deferBuiltinFuncs[callback]
|
db := b.deferBuiltinFuncs[callback]
|
||||||
|
|
||||||
@@ -591,8 +599,8 @@ func (b *builder) createRunDefers() {
|
|||||||
var argValues []llvm.Value
|
var argValues []llvm.Value
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||||
argValues = append(argValues, forwardParam)
|
argValues = append(argValues, forwardParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-10
@@ -55,15 +55,17 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
|||||||
|
|
||||||
// decodeFuncValue extracts the context and the function pointer from this func
|
// decodeFuncValue extracts the context and the function pointer from this func
|
||||||
// value. This may be an expensive operation.
|
// value. This may be an expensive operation.
|
||||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) {
|
||||||
context = b.CreateExtractValue(funcValue, 0, "")
|
context = b.CreateExtractValue(funcValue, 0, "")
|
||||||
bitcast := b.CreateExtractValue(funcValue, 1, "")
|
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||||
if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
|
if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast {
|
||||||
funcPtr = bitcast.Operand(0)
|
funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers)
|
||||||
return
|
}
|
||||||
|
if sig != nil {
|
||||||
|
funcType = b.getRawFuncType(sig)
|
||||||
|
llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace)
|
||||||
|
funcPtr = b.CreateBitCast(funcPtr, llvmSig, "")
|
||||||
}
|
}
|
||||||
llvmSig := b.getRawFuncType(sig)
|
|
||||||
funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +74,7 @@ func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
|||||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
// getRawFuncType returns a LLVM function type for a given signature.
|
||||||
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||||
// Get the return type.
|
// Get the return type.
|
||||||
var returnType llvm.Type
|
var returnType llvm.Type
|
||||||
@@ -117,7 +119,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
|||||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||||
|
|
||||||
// Make a func type out of the signature.
|
// Make a func type out of the signature.
|
||||||
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
|
return llvm.FunctionType(returnType, paramTypes, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseMakeClosure makes a function value (with context) from the given
|
// parseMakeClosure makes a function value (with context) from the given
|
||||||
@@ -141,5 +143,6 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
|||||||
context := b.emitPointerPack(boundVars)
|
context := b.emitPointerPack(boundVars)
|
||||||
|
|
||||||
// Create the closure.
|
// Create the closure.
|
||||||
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
|
_, fn := b.getFunction(f)
|
||||||
|
return b.createFuncValue(fn, context, f.Signature), nil
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@ func (b *builder) trackPointer(value llvm.Value) {
|
|||||||
if value.Type() != b.i8ptrType {
|
if value.Type() != b.i8ptrType {
|
||||||
value = b.CreateBitCast(value, b.i8ptrType, "")
|
value = b.CreateBitCast(value, b.i8ptrType, "")
|
||||||
}
|
}
|
||||||
b.createRuntimeCall("trackPointer", []llvm.Value{value}, "")
|
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// typeHasPointers returns whether this type is a pointer or contains pointers.
|
// typeHasPointers returns whether this type is a pointer or contains pointers.
|
||||||
|
|||||||
+39
-34
@@ -7,7 +7,6 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
@@ -22,6 +21,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
|
|
||||||
var prefix string
|
var prefix string
|
||||||
var funcPtr llvm.Value
|
var funcPtr llvm.Value
|
||||||
|
var funcPtrType llvm.Type
|
||||||
hasContext := false
|
hasContext := 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
|
||||||
@@ -42,7 +42,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
params = append(params, context) // context parameter
|
params = append(params, context) // context parameter
|
||||||
hasContext = true
|
hasContext = true
|
||||||
}
|
}
|
||||||
funcPtr = b.getFunction(callee)
|
funcPtrType, funcPtr = b.getFunction(callee)
|
||||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||||
// We cheat. None of the builtins do any long or blocking operation, so
|
// We cheat. None of the builtins do any long or blocking operation, so
|
||||||
// we might as well run these builtins right away without the program
|
// we might as well run these builtins right away without the program
|
||||||
@@ -80,6 +80,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
||||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||||
|
funcPtrType = funcPtr.GlobalValueType()
|
||||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||||
params = append(params, itfTypeCode) // end with typecode
|
params = append(params, itfTypeCode) // end with typecode
|
||||||
} else {
|
} else {
|
||||||
@@ -89,7 +90,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
// * The function context, for closures.
|
// * The function context, for closures.
|
||||||
// * The function pointer (for tasks).
|
// * The function pointer (for tasks).
|
||||||
var context llvm.Value
|
var context llvm.Value
|
||||||
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||||
params = append(params, context, funcPtr)
|
params = append(params, context, funcPtr)
|
||||||
hasContext = true
|
hasContext = true
|
||||||
prefix = b.fn.RelString(nil)
|
prefix = b.fn.RelString(nil)
|
||||||
@@ -97,14 +98,14 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
|
|
||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
var stackSize llvm.Value
|
var stackSize llvm.Value
|
||||||
callee := b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
|
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos())
|
||||||
if b.AutomaticStackSize {
|
if b.AutomaticStackSize {
|
||||||
// The stack size is not known until after linking. Call a dummy
|
// The stack size is not known until after linking. Call a dummy
|
||||||
// function that will be replaced with a load from a special ELF
|
// function that will be replaced with a load from a special ELF
|
||||||
// section that contains the stack size (and is modified after
|
// section that contains the stack size (and is modified after
|
||||||
// linking).
|
// linking).
|
||||||
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||||
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
|
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||||
} else {
|
} else {
|
||||||
// The stack size is fixed at compile time. By emitting it here as a
|
// The stack size is fixed at compile time. By emitting it here as a
|
||||||
// constant, it can be optimized.
|
// constant, it can be optimized.
|
||||||
@@ -113,23 +114,23 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
}
|
}
|
||||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||||
}
|
}
|
||||||
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||||
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "")
|
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// createGoroutineStartWrapper creates a wrapper for the task-based
|
// createGoroutineStartWrapper creates a wrapper for the task-based
|
||||||
// implementation of goroutines. For example, to call a function like this:
|
// implementation of goroutines. For example, to call a function like this:
|
||||||
//
|
//
|
||||||
// func add(x, y int) int { ... }
|
// func add(x, y int) int { ... }
|
||||||
//
|
//
|
||||||
// It creates a wrapper like this:
|
// It creates a wrapper like this:
|
||||||
//
|
//
|
||||||
// func add$gowrapper(ptr *unsafe.Pointer) {
|
// func add$gowrapper(ptr *unsafe.Pointer) {
|
||||||
// args := (*struct{
|
// args := (*struct{
|
||||||
// x, y int
|
// x, y int
|
||||||
// })(ptr)
|
// })(ptr)
|
||||||
// add(args.x, args.y)
|
// add(args.x, args.y)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// This is useful because the task-based goroutine start implementation only
|
// This is useful because the task-based goroutine start implementation only
|
||||||
// allows a single (pointer) argument to the newly started goroutine. Also, it
|
// allows a single (pointer) argument to the newly started goroutine. Also, it
|
||||||
@@ -140,15 +141,19 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
// to last parameter of the function) is used for this wrapper. If hasContext is
|
// to last parameter of the function) is used for this wrapper. If hasContext is
|
||||||
// false, the parameter bundle is assumed to have no context parameter and undef
|
// false, the parameter bundle is assumed to have no context parameter and undef
|
||||||
// is passed instead.
|
// is passed instead.
|
||||||
func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
|
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
|
||||||
var wrapper llvm.Value
|
var wrapper llvm.Value
|
||||||
|
|
||||||
builder := c.ctx.NewBuilder()
|
b := &builder{
|
||||||
defer builder.Dispose()
|
compilerContext: c,
|
||||||
|
Builder: c.ctx.NewBuilder(),
|
||||||
|
}
|
||||||
|
defer b.Dispose()
|
||||||
|
|
||||||
var deadlock llvm.Value
|
var deadlock llvm.Value
|
||||||
|
var deadlockType llvm.Type
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
deadlockType, deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fn.IsAFunction().IsNil() {
|
if !fn.IsAFunction().IsNil() {
|
||||||
@@ -167,7 +172,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
b.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
pos := c.program.Fset.Position(pos)
|
pos := c.program.Fset.Position(pos)
|
||||||
@@ -188,24 +193,24 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
Optimized: true,
|
Optimized: true,
|
||||||
})
|
})
|
||||||
wrapper.SetSubprogram(difunc)
|
wrapper.SetSubprogram(difunc)
|
||||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the list of params for the call.
|
// Create the list of params for the call.
|
||||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
paramTypes := fnType.ParamTypes()
|
||||||
if !hasContext {
|
if !hasContext {
|
||||||
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
|
||||||
}
|
}
|
||||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||||
if !hasContext {
|
if !hasContext {
|
||||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the call.
|
// Create the call.
|
||||||
builder.CreateCall(fn, params, "")
|
b.CreateCall(fnType, fn, params, "")
|
||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
builder.CreateCall(deadlock, []llvm.Value{
|
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||||
llvm.Undef(c.i8ptrType),
|
llvm.Undef(c.i8ptrType),
|
||||||
}, "")
|
}, "")
|
||||||
}
|
}
|
||||||
@@ -236,7 +241,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
b.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
pos := c.program.Fset.Position(pos)
|
pos := c.program.Fset.Position(pos)
|
||||||
@@ -257,23 +262,23 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
Optimized: true,
|
Optimized: true,
|
||||||
})
|
})
|
||||||
wrapper.SetSubprogram(difunc)
|
wrapper.SetSubprogram(difunc)
|
||||||
builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the list of parameters, with the extra parameters at the end.
|
// Get the list of parameters, with the extra parameters at the end.
|
||||||
paramTypes := fn.Type().ElementType().ParamTypes()
|
paramTypes := fnType.ParamTypes()
|
||||||
paramTypes = append(paramTypes, fn.Type()) // the last element is the function pointer
|
paramTypes = append(paramTypes, fn.Type()) // the last element is the function pointer
|
||||||
params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
|
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||||
|
|
||||||
// Get the function pointer.
|
// Get the function pointer.
|
||||||
fnPtr := params[len(params)-1]
|
fnPtr := params[len(params)-1]
|
||||||
params = params[:len(params)-1]
|
params = params[:len(params)-1]
|
||||||
|
|
||||||
// Create the call.
|
// Create the call.
|
||||||
builder.CreateCall(fnPtr, params, "")
|
b.CreateCall(fnType, fnPtr, params, "")
|
||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
builder.CreateCall(deadlock, []llvm.Value{
|
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||||
llvm.Undef(c.i8ptrType),
|
llvm.Undef(c.i8ptrType),
|
||||||
}, "")
|
}, "")
|
||||||
}
|
}
|
||||||
@@ -281,13 +286,13 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
// The goroutine was terminated via deadlock.
|
// The goroutine was terminated via deadlock.
|
||||||
builder.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
|
||||||
// because goroutines never return a value we can simply return void.
|
// because goroutines never return a value we can simply return void.
|
||||||
builder.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a ptrtoint of the wrapper, not the function itself.
|
// Return a ptrtoint of the wrapper, not the function itself.
|
||||||
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
|
return b.CreatePtrToInt(wrapper, c.uintptrType, "")
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-30
@@ -17,7 +17,7 @@ import (
|
|||||||
// operands or return values. It is useful for trivial instructions, like wfi in
|
// operands or return values. It is useful for trivial instructions, like wfi in
|
||||||
// ARM or sleep in AVR.
|
// ARM or sleep in AVR.
|
||||||
//
|
//
|
||||||
// func Asm(asm string)
|
// func Asm(asm string)
|
||||||
//
|
//
|
||||||
// The provided assembly must be a constant.
|
// The provided assembly must be a constant.
|
||||||
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||||
@@ -25,23 +25,23 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
|||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
|
||||||
asm := constant.StringVal(args[0].(*ssa.Const).Value)
|
asm := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, "", true, false, 0, false)
|
||||||
return b.CreateCall(target, nil, ""), nil
|
return b.CreateCall(fnType, target, nil, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin, which allows assembly to be called in a flexible
|
// This is a compiler builtin, which allows assembly to be called in a flexible
|
||||||
// way.
|
// way.
|
||||||
//
|
//
|
||||||
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
// func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||||
//
|
//
|
||||||
// The asm parameter must be a constant string. The regs parameter must be
|
// The asm parameter must be a constant string. The regs parameter must be
|
||||||
// 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": &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)
|
||||||
registers := map[string]llvm.Value{}
|
registers := map[string]llvm.Value{}
|
||||||
@@ -120,7 +120,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(outputType, argTypes, false)
|
fnType := llvm.FunctionType(outputType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0, false)
|
||||||
result := b.CreateCall(target, args, "")
|
result := b.CreateCall(fnType, target, args, "")
|
||||||
if hasOutput {
|
if hasOutput {
|
||||||
return result, nil
|
return result, nil
|
||||||
} else {
|
} else {
|
||||||
@@ -132,11 +132,11 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||||
// be one of:
|
// be one of:
|
||||||
//
|
//
|
||||||
// func SVCall0(num uintptr) uintptr
|
// func SVCall0(num uintptr) uintptr
|
||||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||||
//
|
//
|
||||||
// The num parameter must be a constant. All other parameters may be any scalar
|
// The num parameter must be a constant. All other parameters may be any scalar
|
||||||
// value supported by LLVM inline assembly.
|
// value supported by LLVM inline assembly.
|
||||||
@@ -163,17 +163,17 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
|||||||
constraints += ",~{r1},~{r2},~{r3}"
|
constraints += ",~{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, true, false, 0, false)
|
||||||
return b.CreateCall(target, llvmArgs, ""), nil
|
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||||
// be one of:
|
// be one of:
|
||||||
//
|
//
|
||||||
// func SVCall0(num uintptr) uintptr
|
// func SVCall0(num uintptr) uintptr
|
||||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||||
//
|
//
|
||||||
// The num parameter must be a constant. All other parameters may be any scalar
|
// The num parameter must be a constant. All other parameters may be any scalar
|
||||||
// value supported by LLVM inline assembly.
|
// value supported by LLVM inline assembly.
|
||||||
@@ -201,15 +201,15 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
|
|||||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
constraints += ",~{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, true, false, 0, false)
|
||||||
return b.CreateCall(target, llvmArgs, ""), nil
|
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
||||||
//
|
//
|
||||||
// func (csr CSR) Get() uintptr
|
// func (csr CSR) Get() uintptr
|
||||||
// func (csr CSR) Set(uintptr)
|
// func (csr CSR) Set(uintptr)
|
||||||
// func (csr CSR) SetBits(uintptr) uintptr
|
// func (csr CSR) SetBits(uintptr) uintptr
|
||||||
// func (csr CSR) ClearBits(uintptr) uintptr
|
// func (csr CSR) ClearBits(uintptr) uintptr
|
||||||
//
|
//
|
||||||
// The csr parameter (method receiver) must be a constant. Other parameter can
|
// The csr parameter (method receiver) must be a constant. Other parameter can
|
||||||
// be any value.
|
// be any value.
|
||||||
@@ -226,24 +226,24 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
fnType := llvm.FunctionType(b.uintptrType, nil, false)
|
fnType := llvm.FunctionType(b.uintptrType, nil, false)
|
||||||
asm := fmt.Sprintf("csrr $0, %d", csr)
|
asm := fmt.Sprintf("csrr $0, %d", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0, false)
|
||||||
return b.CreateCall(target, nil, ""), nil
|
return b.CreateCall(fnType, target, nil, ""), nil
|
||||||
case "Set":
|
case "Set":
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
|
||||||
asm := fmt.Sprintf("csrw %d, $0", csr)
|
asm := fmt.Sprintf("csrw %d, $0", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0, false)
|
||||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||||
case "SetBits":
|
case "SetBits":
|
||||||
// Note: it may be possible to optimize this to csrrsi in many cases.
|
// Note: it may be possible to optimize this to csrrsi in many cases.
|
||||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||||
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
||||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||||
case "ClearBits":
|
case "ClearBits":
|
||||||
// Note: it may be possible to optimize this to csrrci in many cases.
|
// Note: it may be possible to optimize this to csrrci in many cases.
|
||||||
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||||
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0, false)
|
||||||
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
return b.CreateCall(fnType, target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||||
}
|
}
|
||||||
|
|||||||
+300
-175
@@ -6,6 +6,7 @@ package compiler
|
|||||||
// interface-lowering.go for more details.
|
// interface-lowering.go for more details.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -15,6 +16,49 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Type kinds for basic types.
|
||||||
|
// They must match the constants for the Kind type in src/reflect/type.go.
|
||||||
|
var basicTypes = [...]uint8{
|
||||||
|
types.Bool: 1,
|
||||||
|
types.Int: 2,
|
||||||
|
types.Int8: 3,
|
||||||
|
types.Int16: 4,
|
||||||
|
types.Int32: 5,
|
||||||
|
types.Int64: 6,
|
||||||
|
types.Uint: 7,
|
||||||
|
types.Uint8: 8,
|
||||||
|
types.Uint16: 9,
|
||||||
|
types.Uint32: 10,
|
||||||
|
types.Uint64: 11,
|
||||||
|
types.Uintptr: 12,
|
||||||
|
types.Float32: 13,
|
||||||
|
types.Float64: 14,
|
||||||
|
types.Complex64: 15,
|
||||||
|
types.Complex128: 16,
|
||||||
|
types.String: 17,
|
||||||
|
types.UnsafePointer: 18,
|
||||||
|
}
|
||||||
|
|
||||||
|
// These must also match the constants for the Kind type in src/reflect/type.go.
|
||||||
|
const (
|
||||||
|
typeKindChan = 19
|
||||||
|
typeKindInterface = 20
|
||||||
|
typeKindPointer = 21
|
||||||
|
typeKindSlice = 22
|
||||||
|
typeKindArray = 23
|
||||||
|
typeKindSignature = 24
|
||||||
|
typeKindMap = 25
|
||||||
|
typeKindStruct = 26
|
||||||
|
)
|
||||||
|
|
||||||
|
// Flags stored in the first byte of the struct field byte array. Must be kept
|
||||||
|
// up to date with src/reflect/type.go.
|
||||||
|
const (
|
||||||
|
structFieldFlagAnonymous = 1 << iota
|
||||||
|
structFieldFlagHasTag
|
||||||
|
structFieldFlagIsExported
|
||||||
|
)
|
||||||
|
|
||||||
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
|
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
|
||||||
// It tries to put the type in the interface value, but if that's not possible,
|
// It tries to put the type in the interface value, but if that's not possible,
|
||||||
// it will do an allocation of the right size and put that in the interface
|
// it will do an allocation of the right size and put that in the interface
|
||||||
@@ -23,10 +67,9 @@ import (
|
|||||||
// An interface value is a {typecode, value} tuple named runtime._interface.
|
// An interface value is a {typecode, value} tuple named runtime._interface.
|
||||||
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
||||||
itfValue := b.emitPointerPack([]llvm.Value{val})
|
itfValue := b.emitPointerPack([]llvm.Value{val})
|
||||||
itfTypeCodeGlobal := b.getTypeCode(typ)
|
itfType := b.getTypeCode(typ)
|
||||||
itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
|
|
||||||
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
||||||
itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
|
itf = b.CreateInsertValue(itf, itfType, 0, "")
|
||||||
itf = b.CreateInsertValue(itf, itfValue, 1, "")
|
itf = b.CreateInsertValue(itf, itfValue, 1, "")
|
||||||
return itf
|
return itf
|
||||||
}
|
}
|
||||||
@@ -41,118 +84,236 @@ func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getTypeCode returns a reference to a type code.
|
// getTypeCode returns a reference to a type code.
|
||||||
// It returns a pointer to an external global which should be replaced with the
|
// A type code is a pointer to a constant global that describes the type.
|
||||||
// real type in the interface lowering pass.
|
// This function returns a pointer to the 'kind' field (which might not be the
|
||||||
|
// first field in the struct).
|
||||||
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
|
ms := c.program.MethodSets.MethodSet(typ)
|
||||||
|
hasMethodSet := ms.Len() != 0
|
||||||
|
if _, ok := typ.Underlying().(*types.Interface); ok {
|
||||||
|
hasMethodSet = false
|
||||||
|
}
|
||||||
globalName := "reflect/types.type:" + getTypeCodeName(typ)
|
globalName := "reflect/types.type:" + getTypeCodeName(typ)
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if global.IsNil() {
|
if global.IsNil() {
|
||||||
// Create a new typecode global.
|
var typeFields []llvm.Value
|
||||||
global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
|
// Define the type fields. These must match the structs in
|
||||||
// Some type classes contain more information for underlying types or
|
// src/reflect/type.go (ptrType, arrayType, etc). See the comment at the
|
||||||
// element types. Store it directly in the typecode global to make
|
// top of src/reflect/type.go for more information on the layout of these structs.
|
||||||
// reflect lowering simpler.
|
typeFieldTypes := []*types.Var{
|
||||||
var references llvm.Value
|
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
|
||||||
var length int64
|
}
|
||||||
var methodSet llvm.Value
|
|
||||||
var ptrTo llvm.Value
|
|
||||||
var typeAssert llvm.Value
|
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
)
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
references = c.getTypeCode(typ.Underlying())
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
case *types.Chan:
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
references = c.getTypeCode(typ.Elem())
|
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
|
||||||
|
)
|
||||||
|
case *types.Chan, *types.Slice:
|
||||||
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||||
|
)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
references = c.getTypeCode(typ.Elem())
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
case *types.Slice:
|
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||||
references = c.getTypeCode(typ.Elem())
|
)
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
references = c.getTypeCode(typ.Elem())
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
length = typ.Len()
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||||
|
)
|
||||||
|
case *types.Map:
|
||||||
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
)
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
// Take a pointer to the typecodeID of the first field (if it exists).
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
structGlobal := c.makeStructTypeFields(typ)
|
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
|
||||||
references = llvm.ConstBitCast(structGlobal, global.Type())
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
|
||||||
|
)
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
methodSetGlobal := c.getInterfaceMethodSet(typ)
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
references = llvm.ConstBitCast(methodSetGlobal, global.Type())
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
)
|
||||||
|
// TODO: methods
|
||||||
|
case *types.Signature:
|
||||||
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
|
)
|
||||||
|
// TODO: signature params and return values
|
||||||
}
|
}
|
||||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
if hasMethodSet {
|
||||||
methodSet = c.getTypeMethodSet(typ)
|
// This method set is appended at the start of the struct. It is
|
||||||
} else {
|
// removed in the interface lowering pass.
|
||||||
typeAssert = c.getInterfaceImplementsFunc(typ)
|
// TODO: don't remove these and instead do what upstream Go is doing
|
||||||
typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
|
// instead. See: https://research.swtch.com/interfaces. This can
|
||||||
|
// likely be optimized in LLVM using
|
||||||
|
// https://llvm.org/docs/TypeMetadata.html.
|
||||||
|
typeFieldTypes = append([]*types.Var{
|
||||||
|
types.NewVar(token.NoPos, nil, "methodSet", types.Typ[types.UnsafePointer]),
|
||||||
|
}, typeFieldTypes...)
|
||||||
}
|
}
|
||||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
globalType := types.NewStruct(typeFieldTypes, nil)
|
||||||
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
global = llvm.AddGlobal(c.mod, c.getLLVMType(globalType), globalName)
|
||||||
|
metabyte := getTypeKind(typ)
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||||
|
case *types.Named:
|
||||||
|
typeFields = []llvm.Value{
|
||||||
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
c.getTypeCode(typ.Underlying()), // underlying
|
||||||
|
}
|
||||||
|
metabyte |= 1 << 5 // "named" flag
|
||||||
|
case *types.Chan:
|
||||||
|
typeFields = []llvm.Value{
|
||||||
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
c.getTypeCode(typ.Elem()), // elementType
|
||||||
|
}
|
||||||
|
case *types.Slice:
|
||||||
|
typeFields = []llvm.Value{
|
||||||
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
c.getTypeCode(typ.Elem()), // elementType
|
||||||
|
}
|
||||||
|
case *types.Pointer:
|
||||||
|
typeFields = []llvm.Value{c.getTypeCode(typ.Elem())}
|
||||||
|
case *types.Array:
|
||||||
|
typeFields = []llvm.Value{
|
||||||
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
c.getTypeCode(typ.Elem()), // elementType
|
||||||
|
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
|
||||||
|
}
|
||||||
|
case *types.Map:
|
||||||
|
typeFields = []llvm.Value{
|
||||||
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
}
|
||||||
|
case *types.Struct:
|
||||||
|
typeFields = []llvm.Value{
|
||||||
|
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
|
||||||
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
}
|
||||||
|
structFieldType := c.getLLVMRuntimeType("structField")
|
||||||
|
var fields []llvm.Value
|
||||||
|
for i := 0; i < typ.NumFields(); i++ {
|
||||||
|
field := typ.Field(i)
|
||||||
|
var flags uint8
|
||||||
|
if field.Anonymous() {
|
||||||
|
flags |= structFieldFlagAnonymous
|
||||||
|
}
|
||||||
|
if typ.Tag(i) != "" {
|
||||||
|
flags |= structFieldFlagHasTag
|
||||||
|
}
|
||||||
|
if token.IsExported(field.Name()) {
|
||||||
|
flags |= structFieldFlagIsExported
|
||||||
|
}
|
||||||
|
data := string(flags) + field.Name() + "\x00"
|
||||||
|
if typ.Tag(i) != "" {
|
||||||
|
if len(typ.Tag(i)) > 0xff {
|
||||||
|
c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i))))
|
||||||
|
}
|
||||||
|
data += string([]byte{byte(len(typ.Tag(i)))}) + typ.Tag(i)
|
||||||
|
}
|
||||||
|
dataInitializer := c.ctx.ConstString(data, false)
|
||||||
|
dataGlobal := llvm.AddGlobal(c.mod, dataInitializer.Type(), globalName+"."+field.Name())
|
||||||
|
dataGlobal.SetInitializer(dataInitializer)
|
||||||
|
dataGlobal.SetAlignment(1)
|
||||||
|
dataGlobal.SetUnnamedAddr(true)
|
||||||
|
dataGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
dataGlobal.SetGlobalConstant(true)
|
||||||
|
fieldType := c.getTypeCode(field.Type())
|
||||||
|
fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{
|
||||||
|
fieldType,
|
||||||
|
llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
|
||||||
|
case *types.Interface:
|
||||||
|
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||||
|
// TODO: methods
|
||||||
|
case *types.Signature:
|
||||||
|
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||||
|
// TODO: params, return values, etc
|
||||||
}
|
}
|
||||||
globalValue := llvm.ConstNull(global.Type().ElementType())
|
// Prepend metadata byte.
|
||||||
if !references.IsNil() {
|
typeFields = append([]llvm.Value{
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false),
|
||||||
}
|
}, typeFields...)
|
||||||
if length != 0 {
|
if hasMethodSet {
|
||||||
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
typeFields = append([]llvm.Value{
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType),
|
||||||
}
|
}, typeFields...)
|
||||||
if !methodSet.IsNil() {
|
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
|
|
||||||
}
|
|
||||||
if !ptrTo.IsNil() {
|
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
|
||||||
}
|
|
||||||
if !typeAssert.IsNil() {
|
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
|
|
||||||
}
|
}
|
||||||
|
alignment := c.targetData.TypeAllocSize(c.i8ptrType)
|
||||||
|
globalValue := c.ctx.ConstStruct(typeFields, false)
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
}
|
global.SetAlignment(int(alignment))
|
||||||
return global
|
if c.Debug {
|
||||||
}
|
file := c.getDIFile("<Go type>")
|
||||||
|
diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
|
||||||
// makeStructTypeFields creates a new global that stores all type information
|
Name: "type " + typ.String(),
|
||||||
// related to this struct type, and returns the resulting global. This global is
|
File: file,
|
||||||
// actually an array of all the fields in the structs.
|
Line: 1,
|
||||||
func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
Type: c.getDIType(globalType),
|
||||||
// The global is an array of runtime.structField structs.
|
LocalToUnit: false,
|
||||||
runtimeStructField := c.getLLVMRuntimeType("structField")
|
Expr: c.dibuilder.CreateExpression(nil),
|
||||||
structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields())
|
AlignInBits: uint32(alignment * 8),
|
||||||
structGlobal := llvm.AddGlobal(c.mod, structGlobalType, "reflect/types.structFields")
|
|
||||||
structGlobalValue := llvm.ConstNull(structGlobalType)
|
|
||||||
for i := 0; i < typ.NumFields(); i++ {
|
|
||||||
fieldGlobalValue := llvm.ConstNull(runtimeStructField)
|
|
||||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
|
|
||||||
fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
|
|
||||||
fieldName.SetLinkage(llvm.PrivateLinkage)
|
|
||||||
fieldName.SetUnnamedAddr(true)
|
|
||||||
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
})
|
|
||||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
|
|
||||||
if typ.Tag(i) != "" {
|
|
||||||
fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
|
|
||||||
fieldTag.SetLinkage(llvm.PrivateLinkage)
|
|
||||||
fieldTag.SetUnnamedAddr(true)
|
|
||||||
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
})
|
})
|
||||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
|
global.AddMetadata(0, diglobal)
|
||||||
}
|
}
|
||||||
if typ.Field(i).Embedded() {
|
|
||||||
fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
|
|
||||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldEmbedded, []uint32{3})
|
|
||||||
}
|
|
||||||
structGlobalValue = llvm.ConstInsertValue(structGlobalValue, fieldGlobalValue, []uint32{uint32(i)})
|
|
||||||
}
|
}
|
||||||
structGlobal.SetInitializer(structGlobalValue)
|
offset := uint64(0)
|
||||||
structGlobal.SetUnnamedAddr(true)
|
if hasMethodSet {
|
||||||
structGlobal.SetLinkage(llvm.PrivateLinkage)
|
// The pointer to the method set is always the first element of the
|
||||||
return structGlobal
|
// global (if there is a method set). However, the pointer we return
|
||||||
|
// should point to the 'kind' field not the method set.
|
||||||
|
offset = 1
|
||||||
|
}
|
||||||
|
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), offset, false),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var basicTypes = [...]string{
|
// getTypeKind returns the type kind for the given type, as defined by
|
||||||
|
// reflect.Kind.
|
||||||
|
func getTypeKind(t types.Type) uint8 {
|
||||||
|
switch t := t.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return basicTypes[t.Kind()]
|
||||||
|
case *types.Chan:
|
||||||
|
return typeKindChan
|
||||||
|
case *types.Interface:
|
||||||
|
return typeKindInterface
|
||||||
|
case *types.Pointer:
|
||||||
|
return typeKindPointer
|
||||||
|
case *types.Slice:
|
||||||
|
return typeKindSlice
|
||||||
|
case *types.Array:
|
||||||
|
return typeKindArray
|
||||||
|
case *types.Signature:
|
||||||
|
return typeKindSignature
|
||||||
|
case *types.Map:
|
||||||
|
return typeKindMap
|
||||||
|
case *types.Struct:
|
||||||
|
return typeKindStruct
|
||||||
|
default:
|
||||||
|
panic("unknown type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicTypeNames = [...]string{
|
||||||
types.Bool: "bool",
|
types.Bool: "bool",
|
||||||
types.Int: "int",
|
types.Int: "int",
|
||||||
types.Int8: "int8",
|
types.Int8: "int8",
|
||||||
@@ -183,7 +344,7 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
case *types.Array:
|
case *types.Array:
|
||||||
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return "basic:" + basicTypes[t.Kind()]
|
return "basic:" + basicTypeNames[t.Kind()]
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
return "chan:" + getTypeCodeName(t.Elem())
|
return "chan:" + getTypeCodeName(t.Elem())
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
@@ -235,75 +396,40 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||||
// method set should be unreferenced after the interface lowering pass.
|
// method set should be unreferenced after the interface lowering pass.
|
||||||
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||||
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
globalName := typ.String() + "$methodset"
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if !global.IsNil() {
|
if global.IsNil() {
|
||||||
// the method set already exists
|
ms := c.program.MethodSets.MethodSet(typ)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
|
||||||
}
|
|
||||||
|
|
||||||
ms := c.program.MethodSets.MethodSet(typ)
|
// Create method set.
|
||||||
if ms.Len() == 0 {
|
var signatures, wrappers []llvm.Value
|
||||||
// no methods, so can leave that one out
|
for i := 0; i < ms.Len(); i++ {
|
||||||
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
|
method := ms.At(i)
|
||||||
}
|
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||||
|
signatures = append(signatures, signatureGlobal)
|
||||||
methods := make([]llvm.Value, ms.Len())
|
fn := c.program.MethodValue(method)
|
||||||
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
|
llvmFnType, llvmFn := c.getFunction(fn)
|
||||||
for i := 0; i < ms.Len(); i++ {
|
if llvmFn.IsNil() {
|
||||||
method := ms.At(i)
|
// compiler error, so panic
|
||||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
|
||||||
fn := c.program.MethodValue(method)
|
}
|
||||||
llvmFn := c.getFunction(fn)
|
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
|
||||||
if llvmFn.IsNil() {
|
wrappers = append(wrappers, wrapper)
|
||||||
// compiler error, so panic
|
|
||||||
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
|
|
||||||
}
|
}
|
||||||
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn)
|
|
||||||
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
|
||||||
signatureGlobal,
|
|
||||||
llvm.ConstPtrToInt(wrapper, c.uintptrType),
|
|
||||||
})
|
|
||||||
methods[i] = methodInfo
|
|
||||||
}
|
|
||||||
arrayType := llvm.ArrayType(interfaceMethodInfoType, len(methods))
|
|
||||||
value := llvm.ConstArray(interfaceMethodInfoType, methods)
|
|
||||||
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
|
||||||
global.SetInitializer(value)
|
|
||||||
global.SetGlobalConstant(true)
|
|
||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInterfaceMethodSet returns a global variable with the method set of the
|
// Construct global value.
|
||||||
// given named interface type. This method set is used by the interface lowering
|
globalValue := c.ctx.ConstStruct([]llvm.Value{
|
||||||
// pass.
|
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
|
||||||
func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
llvm.ConstArray(c.i8ptrType, signatures),
|
||||||
name := typ.String()
|
c.ctx.ConstStruct(wrappers, false),
|
||||||
if _, ok := typ.(*types.Named); !ok {
|
}, false)
|
||||||
// Anonymous interface.
|
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
|
||||||
name = "reflect/types.interface:" + name
|
global.SetInitializer(globalValue)
|
||||||
|
global.SetGlobalConstant(true)
|
||||||
|
global.SetUnnamedAddr(true)
|
||||||
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
}
|
}
|
||||||
global := c.mod.NamedGlobal(name + "$interface")
|
return global
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
|
||||||
if !global.IsNil() {
|
|
||||||
// method set already exist, return it
|
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Every method is a *i8 reference indicating the signature of this method.
|
|
||||||
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
|
||||||
for i := range methods {
|
|
||||||
method := typ.Underlying().(*types.Interface).Method(i)
|
|
||||||
methods[i] = c.getMethodSignature(method)
|
|
||||||
}
|
|
||||||
|
|
||||||
value := llvm.ConstArray(c.i8ptrType, methods)
|
|
||||||
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
|
||||||
global.SetInitializer(value)
|
|
||||||
global.SetGlobalConstant(true)
|
|
||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMethodSignatureName returns a unique name (that can be used as the name of
|
// getMethodSignatureName returns a unique name (that can be used as the name of
|
||||||
@@ -361,7 +487,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
// implements each method of the interface. See:
|
// implements each method of the interface. See:
|
||||||
// https://research.swtch.com/interfaces
|
// https://research.swtch.com/interfaces
|
||||||
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||||
commaOk = b.CreateCall(fn, []llvm.Value{actualTypeNum}, "")
|
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
@@ -443,7 +569,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
|
|||||||
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
|
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
|
||||||
llvmFn := c.mod.NamedFunction(fnName)
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
|
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false)
|
||||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
c.addStandardDeclaredAttributes(llvmFn)
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||||
@@ -464,8 +590,8 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
|||||||
for i := 0; i < sig.Params().Len(); i++ {
|
for i := 0; i < sig.Params().Len(); i++ {
|
||||||
paramTuple = append(paramTuple, sig.Params().At(i))
|
paramTuple = append(paramTuple, sig.Params().At(i))
|
||||||
}
|
}
|
||||||
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
|
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
|
||||||
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)).ElementType()
|
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
|
||||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
c.addStandardDeclaredAttributes(llvmFn)
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
||||||
@@ -480,7 +606,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
|||||||
// value, dereferences or unpacks it if necessary, and calls the real method.
|
// value, dereferences or unpacks it if necessary, and calls the real method.
|
||||||
// If the method to wrap has a pointer receiver, no wrapping is necessary and
|
// If the method to wrap has a pointer receiver, no wrapping is necessary and
|
||||||
// the function is returned directly.
|
// the function is returned directly.
|
||||||
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llvm.Value) llvm.Value {
|
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType llvm.Type, llvmFn llvm.Value) llvm.Value {
|
||||||
wrapperName := llvmFn.Name() + "$invoke"
|
wrapperName := llvmFn.Name() + "$invoke"
|
||||||
wrapper := c.mod.NamedFunction(wrapperName)
|
wrapper := c.mod.NamedFunction(wrapperName)
|
||||||
if !wrapper.IsNil() {
|
if !wrapper.IsNil() {
|
||||||
@@ -505,9 +631,8 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create wrapper function
|
// create wrapper function
|
||||||
fnType := llvmFn.Type().ElementType()
|
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||||
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
|
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
|
||||||
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
|
|
||||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||||
c.addStandardAttributes(wrapper)
|
c.addStandardAttributes(wrapper)
|
||||||
|
|
||||||
@@ -534,11 +659,11 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
|
|
||||||
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
|
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
|
||||||
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
|
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
|
||||||
if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFnType, llvmFn, params, "")
|
||||||
b.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
} else {
|
} else {
|
||||||
ret := b.CreateCall(llvmFn, params, "ret")
|
ret := b.CreateCall(llvmFnType, llvmFn, params, "ret")
|
||||||
b.CreateRet(ret)
|
b.CreateRet(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,8 +675,8 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
// internally to match interfaces and to call the correct method on an
|
// internally to match interfaces and to call the correct method on an
|
||||||
// interface. Examples:
|
// interface. Examples:
|
||||||
//
|
//
|
||||||
// String() string
|
// String() string
|
||||||
// Read([]byte) (int, error)
|
// Read([]byte) (int, error)
|
||||||
func methodSignature(method *types.Func) string {
|
func methodSignature(method *types.Func) string {
|
||||||
return method.Name() + signature(method.Type().(*types.Signature))
|
return method.Name() + signature(method.Type().(*types.Signature))
|
||||||
}
|
}
|
||||||
@@ -559,8 +684,8 @@ func methodSignature(method *types.Func) string {
|
|||||||
// Make a readable version of a function (pointer) signature.
|
// Make a readable version of a function (pointer) signature.
|
||||||
// Examples:
|
// Examples:
|
||||||
//
|
//
|
||||||
// () string
|
// () string
|
||||||
// (string, int) (int, error)
|
// (string, int) (int, error)
|
||||||
func signature(sig *types.Signature) string {
|
func signature(sig *types.Signature) string {
|
||||||
s := ""
|
s := ""
|
||||||
if sig.Params().Len() == 0 {
|
if sig.Params().Len() == 0 {
|
||||||
@@ -602,7 +727,7 @@ func typestring(t types.Type) string {
|
|||||||
case *types.Array:
|
case *types.Array:
|
||||||
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return basicTypes[t.Kind()]
|
return basicTypeNames[t.Kind()]
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
switch t.Dir() {
|
switch t.Dir() {
|
||||||
case types.SendRecv:
|
case types.SendRecv:
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
// Fall back to a generic error.
|
// Fall back to a generic error.
|
||||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
||||||
}
|
}
|
||||||
funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
|
_, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
|
||||||
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
|
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
|
||||||
|
|
||||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||||
@@ -49,9 +49,11 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
|
initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
|
||||||
initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
|
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
|
||||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
|
initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{
|
||||||
|
llvm.ConstInt(b.intType, uint64(id.Int64()), true),
|
||||||
|
}), 2, "")
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
|
|
||||||
// Add debug info to the interrupt global.
|
// Add debug info to the interrupt global.
|
||||||
@@ -85,7 +87,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||||
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
|
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
|
||||||
}
|
}
|
||||||
b.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
b.CreateCall(useFn.GlobalValueType(), useFn, []llvm.Value{interrupt}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
return interrupt, nil
|
return interrupt, nil
|
||||||
|
|||||||
+66
-46
@@ -7,7 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,12 +24,14 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
b.createMemoryCopyImpl()
|
b.createMemoryCopyImpl()
|
||||||
case name == "runtime.memzero":
|
case name == "runtime.memzero":
|
||||||
b.createMemoryZeroImpl()
|
b.createMemoryZeroImpl()
|
||||||
|
case name == "runtime.KeepAlive":
|
||||||
|
b.createKeepAliveImpl()
|
||||||
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"):
|
||||||
b.createVolatileStore()
|
b.createVolatileStore()
|
||||||
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
|
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
|
||||||
b.createFunctionStart()
|
b.createFunctionStart(true)
|
||||||
returnValue := b.createAtomicOp(b.fn.Name())
|
returnValue := b.createAtomicOp(b.fn.Name())
|
||||||
if !returnValue.IsNil() {
|
if !returnValue.IsNil() {
|
||||||
b.CreateRet(returnValue)
|
b.CreateRet(returnValue)
|
||||||
@@ -44,8 +46,11 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
// specially by optimization passes possibly resulting in better generated code,
|
// specially by optimization passes possibly resulting in better generated code,
|
||||||
// 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()
|
b.createFunctionStart(true)
|
||||||
fnName := "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
|
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||||
|
fnName = "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
|
}
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||||
@@ -56,7 +61,7 @@ func (b *builder) createMemoryCopyImpl() {
|
|||||||
params = append(params, b.getValue(param))
|
params = append(params, b.getValue(param))
|
||||||
}
|
}
|
||||||
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||||
b.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +69,11 @@ func (b *builder) createMemoryCopyImpl() {
|
|||||||
// memory, declaring the function if needed. These calls will be lowered to
|
// memory, declaring the function if needed. These calls will be lowered to
|
||||||
// regular libc memset calls if they aren't optimized out in a different way.
|
// regular libc memset calls if they aren't optimized out in a different way.
|
||||||
func (b *builder) createMemoryZeroImpl() {
|
func (b *builder) createMemoryZeroImpl() {
|
||||||
b.createFunctionStart()
|
b.createFunctionStart(true)
|
||||||
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
|
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
||||||
|
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
|
}
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
||||||
@@ -77,60 +85,72 @@ func (b *builder) createMemoryZeroImpl() {
|
|||||||
b.getValue(b.fn.Params[1]),
|
b.getValue(b.fn.Params[1]),
|
||||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||||
}
|
}
|
||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
|
||||||
|
b.CreateRetVoid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
||||||
|
// using inline assembly.
|
||||||
|
func (b *builder) createKeepAliveImpl() {
|
||||||
|
b.createFunctionStart(true)
|
||||||
|
|
||||||
|
// Get the underlying value of the interface value.
|
||||||
|
interfaceValue := b.getValue(b.fn.Params[0])
|
||||||
|
pointerValue := b.CreateExtractValue(interfaceValue, 1, "")
|
||||||
|
|
||||||
|
// Create an equivalent of the following C code, which is basically just a
|
||||||
|
// nop but ensures the pointerValue is kept alive:
|
||||||
|
//
|
||||||
|
// __asm__ __volatile__("" : : "r"(pointerValue))
|
||||||
|
//
|
||||||
|
// It should be portable to basically everything as the "r" register type
|
||||||
|
// exists basically everywhere.
|
||||||
|
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false)
|
||||||
|
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
|
||||||
|
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
||||||
|
|
||||||
b.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
var mathToLLVMMapping = map[string]string{
|
var mathToLLVMMapping = map[string]string{
|
||||||
"math.Sqrt": "llvm.sqrt.f64",
|
|
||||||
"math.Floor": "llvm.floor.f64",
|
|
||||||
"math.Ceil": "llvm.ceil.f64",
|
"math.Ceil": "llvm.ceil.f64",
|
||||||
|
"math.Exp": "llvm.exp.f64",
|
||||||
|
"math.Exp2": "llvm.exp2.f64",
|
||||||
|
"math.Floor": "llvm.floor.f64",
|
||||||
|
"math.Log": "llvm.log.f64",
|
||||||
|
"math.Sqrt": "llvm.sqrt.f64",
|
||||||
"math.Trunc": "llvm.trunc.f64",
|
"math.Trunc": "llvm.trunc.f64",
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
|
||||||
// possible. It returns the call result if possible, and a boolean whether it
|
// instead of the regular Go implementation. This allows LLVM to reason about
|
||||||
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
// the math operation and (depending on the architecture) allows it to lower the
|
||||||
// intrinsic.
|
// operation to very fast floating point instructions. If this is not possible,
|
||||||
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
// LLVM will emit a call to a libm function that implements the same operation.
|
||||||
// Check whether this intrinsic is supported on the given GOARCH.
|
//
|
||||||
// If it is unsupported, this can have two reasons:
|
// One example of an optimization that LLVM can do is to convert
|
||||||
//
|
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||||
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||||
// the result doesn't pass the tests of the math package.
|
// more expensive than 32-bit ones.
|
||||||
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
func (b *builder) defineMathOp() {
|
||||||
// libm function call, but the libm function call also fails the math
|
b.createFunctionStart(true)
|
||||||
// package tests.
|
llvmName := mathToLLVMMapping[b.fn.RelString(nil)]
|
||||||
//
|
if llvmName == "" {
|
||||||
// Whatever the implementation, it must pass the tests in the math package
|
panic("unreachable: unknown math operation") // sanity check
|
||||||
// so unfortunately only the below intrinsic+architecture combinations are
|
|
||||||
// supported.
|
|
||||||
name := call.StaticCallee().RelString(nil)
|
|
||||||
switch name {
|
|
||||||
case "math.Ceil", "math.Floor", "math.Trunc":
|
|
||||||
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
|
||||||
return llvm.Value{}, false
|
|
||||||
}
|
|
||||||
case "math.Sqrt":
|
|
||||||
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
|
||||||
return llvm.Value{}, false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return llvm.Value{}, false // only the above functions are supported.
|
|
||||||
}
|
}
|
||||||
|
llvmFn := b.mod.NamedFunction(llvmName)
|
||||||
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
|
||||||
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
|
// At the moment, all supported intrinsics have the form "double
|
||||||
// foo(double %x)" so we can hardcode the signature here.
|
// foo(double %x)" so we can hardcode the signature here.
|
||||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||||
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType)
|
||||||
}
|
}
|
||||||
// Create a call to the intrinsic.
|
// Create a call to the intrinsic.
|
||||||
args := make([]llvm.Value, len(call.Args))
|
args := make([]llvm.Value, len(b.fn.Params))
|
||||||
for i, arg := range call.Args {
|
for i, param := range b.fn.Params {
|
||||||
args[i] = b.getValue(arg)
|
args[i] = b.getValue(param)
|
||||||
}
|
}
|
||||||
return b.CreateCall(llvmFn, args, ""), true
|
result := b.CreateCall(llvmFn.GlobalValueType(), llvmFn, args, "")
|
||||||
|
b.CreateRet(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,10 +70,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
|||||||
return fmt.Errorf("failed to verify element type of array type %s: %s", t.String(), err.Error())
|
return fmt.Errorf("failed to verify element type of array type %s: %s", t.String(), err.Error())
|
||||||
}
|
}
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
// check underlying type
|
// Pointers can't be checked in an opaque pointer world.
|
||||||
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
|
||||||
return fmt.Errorf("failed to verify underlying type of pointer type %s: %s", t.String(), err.Error())
|
|
||||||
}
|
|
||||||
case llvm.VectorTypeKind:
|
case llvm.VectorTypeKind:
|
||||||
// check element type
|
// check element type
|
||||||
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
if err := c.checkType(t.ElementType(), checked, specials); err != nil {
|
||||||
|
|||||||
+165
-7
@@ -51,29 +51,175 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
|||||||
// emitPointerPack packs the list of values into a single pointer value using
|
// emitPointerPack packs the list of values into a single pointer value using
|
||||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||||
// pointer value directly. It returns the pointer with the packed data.
|
// pointer value directly. It returns the pointer with the packed data.
|
||||||
|
// If the values are all constants, they are be stored in a constant global and
|
||||||
|
// deduplicated.
|
||||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||||
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.pkg.Path(), b.NeedsStackObjects, values)
|
valueTypes := make([]llvm.Type, len(values))
|
||||||
|
for i, value := range values {
|
||||||
|
valueTypes[i] = value.Type()
|
||||||
|
}
|
||||||
|
packedType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
|
// Allocate memory for the packed data.
|
||||||
|
size := b.targetData.TypeAllocSize(packedType)
|
||||||
|
if size == 0 {
|
||||||
|
return llvm.ConstPointerNull(b.i8ptrType)
|
||||||
|
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
|
return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr")
|
||||||
|
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
|
||||||
|
// Packed data fits in a pointer, so store it directly inside the
|
||||||
|
// pointer.
|
||||||
|
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
|
// Try to keep this cast in SSA form.
|
||||||
|
return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||||
|
// it in a *i8 alloca first and load the *i8 value from there. This is
|
||||||
|
// effectively a bitcast.
|
||||||
|
packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "")
|
||||||
|
|
||||||
|
if size < b.targetData.TypeAllocSize(b.i8ptrType) {
|
||||||
|
// The alloca is bigger than the value that will be stored in it.
|
||||||
|
// To avoid having some bits undefined, zero the alloca first.
|
||||||
|
// Hopefully this will get optimized away.
|
||||||
|
b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all values in the alloca.
|
||||||
|
packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
|
||||||
|
for i, value := range values {
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "")
|
||||||
|
b.CreateStore(value, gep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load value (the *i8) from the alloca.
|
||||||
|
result := b.CreateLoad(b.i8ptrType, packedAlloc, "")
|
||||||
|
|
||||||
|
// End the lifetime of the alloca, to help the optimizer.
|
||||||
|
packedPtr := b.CreateBitCast(packedAlloc, b.i8ptrType, "")
|
||||||
|
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||||
|
b.emitLifetimeEnd(packedPtr, packedSize)
|
||||||
|
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
// Check if the values are all constants.
|
||||||
|
constant := true
|
||||||
|
for _, v := range values {
|
||||||
|
if !v.IsConstant() {
|
||||||
|
constant = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constant {
|
||||||
|
// The data is known at compile time, so store it in a constant global.
|
||||||
|
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
|
||||||
|
global := llvm.AddGlobal(b.mod, packedType, b.pkg.Path()+"$pack")
|
||||||
|
global.SetInitializer(b.ctx.ConstStruct(values, false))
|
||||||
|
global.SetGlobalConstant(true)
|
||||||
|
global.SetUnnamedAddr(true)
|
||||||
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
|
return llvm.ConstBitCast(global, b.i8ptrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
|
alloc := b.mod.NamedFunction("runtime.alloc")
|
||||||
|
packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
||||||
|
sizeValue,
|
||||||
|
llvm.ConstNull(b.i8ptrType),
|
||||||
|
llvm.Undef(b.i8ptrType), // unused context parameter
|
||||||
|
}, "")
|
||||||
|
if b.NeedsStackObjects {
|
||||||
|
b.trackPointer(packedHeapAlloc)
|
||||||
|
}
|
||||||
|
packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||||
|
|
||||||
|
// Store all values in the heap pointer.
|
||||||
|
for i, value := range values {
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||||
|
b.CreateStore(value, gep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the original heap allocation pointer, which already is an *i8.
|
||||||
|
return packedHeapAlloc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||||
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||||
return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
|
packedType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
|
// Get a correctly-typed pointer to the packed data.
|
||||||
|
var packedAlloc, packedRawAlloc llvm.Value
|
||||||
|
size := b.targetData.TypeAllocSize(packedType)
|
||||||
|
if size == 0 {
|
||||||
|
// No data to unpack.
|
||||||
|
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||||
|
// A single pointer is always stored directly.
|
||||||
|
return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
||||||
|
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
|
||||||
|
// Packed data stored directly in pointer.
|
||||||
|
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||||
|
// Keep this cast in SSA form.
|
||||||
|
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||||
|
}
|
||||||
|
// Fallback: load it using an alloca.
|
||||||
|
packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc")
|
||||||
|
packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value")
|
||||||
|
b.CreateStore(packedRawValue, packedRawAlloc)
|
||||||
|
packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
||||||
|
} else {
|
||||||
|
// Packed data stored on the heap. Bitcast the passed-in pointer to the
|
||||||
|
// correct pointer type.
|
||||||
|
packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
|
||||||
|
}
|
||||||
|
// Load each value from the packed data.
|
||||||
|
values := make([]llvm.Value, len(valueTypes))
|
||||||
|
for i, valueType := range valueTypes {
|
||||||
|
if b.targetData.TypeAllocSize(valueType) == 0 {
|
||||||
|
// This value has length zero, so there's nothing to load.
|
||||||
|
values[i] = llvm.ConstNull(valueType)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||||
|
values[i] = b.CreateLoad(valueType, gep, "")
|
||||||
|
}
|
||||||
|
if !packedRawAlloc.IsNil() {
|
||||||
|
allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "")
|
||||||
|
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
|
||||||
|
b.emitLifetimeEnd(allocPtr, allocSize)
|
||||||
|
}
|
||||||
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||||
// contents, and returns the global.
|
// contents, and returns the global and initializer type.
|
||||||
// Note that it is left with the default linkage etc., you should set
|
// Note that it is left with the default linkage etc., you should set
|
||||||
// linkage/constant/etc properties yourself.
|
// linkage/constant/etc properties yourself.
|
||||||
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value {
|
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) (llvm.Type, llvm.Value) {
|
||||||
globalType := llvm.ArrayType(elementType, len(buf))
|
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 := 0; i < len(buf); i++ {
|
||||||
ch := uint64(buf[i])
|
ch := uint64(buf[i])
|
||||||
value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
|
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||||
}
|
}
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
return global
|
return globalType, global
|
||||||
}
|
}
|
||||||
|
|
||||||
// createObjectLayout returns a LLVM value (of type i8*) that describes where
|
// createObjectLayout returns a LLVM value (of type i8*) that describes where
|
||||||
@@ -84,6 +230,8 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
// which words contain a pointer (indicated by setting the given bit to 1). For
|
// which words contain a pointer (indicated by setting the given bit to 1). For
|
||||||
// arrays, only the element is stored. This works because the GC knows the
|
// arrays, only the element is stored. This works because the GC knows the
|
||||||
// object size and can therefore know how this value is repeated in the object.
|
// object size and can therefore know how this value is repeated in the object.
|
||||||
|
//
|
||||||
|
// For details on what's in this value, see src/runtime/gc_precise.go.
|
||||||
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||||
// 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 {
|
||||||
@@ -166,6 +314,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
// Create the global initializer.
|
// Create the global initializer.
|
||||||
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
||||||
bitmap.FillBytes(bitmapBytes)
|
bitmap.FillBytes(bitmapBytes)
|
||||||
|
reverseBytes(bitmapBytes) // big-endian to little-endian
|
||||||
var bitmapByteValues []llvm.Value
|
var bitmapByteValues []llvm.Value
|
||||||
for _, b := range bitmapBytes {
|
for _, b := range bitmapBytes {
|
||||||
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
||||||
@@ -312,5 +461,14 @@ func (b *builder) readStackPointer() llvm.Value {
|
|||||||
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
|
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
|
||||||
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
||||||
}
|
}
|
||||||
return b.CreateCall(stacksave, nil, "")
|
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse a slice of bytes. From the wiki:
|
||||||
|
// https://github.com/golang/go/wiki/SliceTricks#reversing
|
||||||
|
func reverseBytes(buf []byte) {
|
||||||
|
for i := len(buf)/2 - 1; i >= 0; i-- {
|
||||||
|
opp := len(buf) - 1 - i
|
||||||
|
buf[i], buf[opp] = buf[opp], buf[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-15
@@ -7,7 +7,22 @@
|
|||||||
// places would be a big risk if only one of them is updated.
|
// places would be a big risk if only one of them is updated.
|
||||||
package llvmutil
|
package llvmutil
|
||||||
|
|
||||||
import "tinygo.org/x/go-llvm"
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Major returns the LLVM major version.
|
||||||
|
func Major() int {
|
||||||
|
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||||
|
if err != nil {
|
||||||
|
// sanity check, should be unreachable
|
||||||
|
panic("could not parse LLVM version: " + err.Error())
|
||||||
|
}
|
||||||
|
return llvmMajor
|
||||||
|
}
|
||||||
|
|
||||||
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
|
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
|
||||||
// the IR builder is located elsewhere. It assumes that the insert point is
|
// the IR builder is located elsewhere. It assumes that the insert point is
|
||||||
@@ -39,7 +54,8 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
|||||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||||
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||||
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
fnType, fn := getLifetimeStartFunc(mod)
|
||||||
|
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +70,15 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
|||||||
builder.SetInsertPointBefore(inst)
|
builder.SetInsertPointBefore(inst)
|
||||||
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||||
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||||
builder.CreateCall(getLifetimeStartFunc(mod), []llvm.Value{size, bitcast}, "")
|
fnType, fn := getLifetimeStartFunc(mod)
|
||||||
|
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
||||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||||
builder.SetInsertPointBefore(next)
|
builder.SetInsertPointBefore(next)
|
||||||
} else {
|
} else {
|
||||||
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||||
}
|
}
|
||||||
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, bitcast}, "")
|
fnType, fn = getLifetimeEndFunc(mod)
|
||||||
|
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
||||||
return alloca
|
return alloca
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,33 +86,42 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
|||||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||||
// 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) {
|
||||||
builder.CreateCall(getLifetimeEndFunc(mod), []llvm.Value{size, ptr}, "")
|
fnType, fn := getLifetimeEndFunc(mod)
|
||||||
|
builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||||
// first if it doesn't exist yet.
|
// first if it doesn't exist yet.
|
||||||
func getLifetimeStartFunc(mod llvm.Module) llvm.Value {
|
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||||
fn := mod.NamedFunction("llvm.lifetime.start.p0i8")
|
fnName := "llvm.lifetime.start.p0"
|
||||||
|
if Major() < 15 { // compatibility with LLVM 14
|
||||||
|
fnName = "llvm.lifetime.start.p0i8"
|
||||||
|
}
|
||||||
|
fn := mod.NamedFunction(fnName)
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
|
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||||
fn = llvm.AddFunction(mod, "llvm.lifetime.start.p0i8", fnType)
|
|
||||||
}
|
}
|
||||||
return fn
|
return fnType, fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||||
// first if it doesn't exist yet.
|
// first if it doesn't exist yet.
|
||||||
func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||||
fn := mod.NamedFunction("llvm.lifetime.end.p0i8")
|
fnName := "llvm.lifetime.end.p0"
|
||||||
|
if Major() < 15 {
|
||||||
|
fnName = "llvm.lifetime.end.p0i8"
|
||||||
|
}
|
||||||
|
fn := mod.NamedFunction(fnName)
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
|
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||||
fn = llvm.AddFunction(mod, "llvm.lifetime.end.p0i8", fnType)
|
|
||||||
}
|
}
|
||||||
return fn
|
return fnType, fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||||
@@ -168,3 +195,32 @@ func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llv
|
|||||||
|
|
||||||
return newBlock
|
return newBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append the given values to a global array like llvm.used. The global might
|
||||||
|
// not exist yet. The values can be any pointer type, they will be cast to i8*.
|
||||||
|
func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
|
||||||
|
// Read the existing values in the llvm.used array (if it exists).
|
||||||
|
var usedValues []llvm.Value
|
||||||
|
if used := mod.NamedGlobal(globalName); !used.IsNil() {
|
||||||
|
builder := mod.Context().NewBuilder()
|
||||||
|
defer builder.Dispose()
|
||||||
|
usedInitializer := used.Initializer()
|
||||||
|
num := usedInitializer.Type().ArrayLength()
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
usedValues = append(usedValues, builder.CreateExtractValue(usedInitializer, i, ""))
|
||||||
|
}
|
||||||
|
used.EraseFromParentAsGlobal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new values.
|
||||||
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
|
for _, value := range values {
|
||||||
|
usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new array (with the old and new values).
|
||||||
|
usedInitializer := llvm.ConstArray(i8ptrType, usedValues)
|
||||||
|
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
|
||||||
|
used.SetInitializer(usedInitializer)
|
||||||
|
used.SetLinkage(llvm.AppendingLinkage)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,182 +0,0 @@
|
|||||||
package llvmutil
|
|
||||||
|
|
||||||
// This file contains utility functions to pack and unpack sets of values. It
|
|
||||||
// can take in a list of values and tries to store it efficiently in the pointer
|
|
||||||
// itself if possible and legal.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EmitPointerPack packs the list of values into a single pointer value using
|
|
||||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
|
||||||
// pointer value directly. It returns the pointer with the packed data.
|
|
||||||
// If the values are all constants, they are be stored in a constant global and deduplicated.
|
|
||||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
|
||||||
ctx := mod.Context()
|
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
|
||||||
defer targetData.Dispose()
|
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
|
||||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
|
||||||
|
|
||||||
valueTypes := make([]llvm.Type, len(values))
|
|
||||||
for i, value := range values {
|
|
||||||
valueTypes[i] = value.Type()
|
|
||||||
}
|
|
||||||
packedType := ctx.StructType(valueTypes, false)
|
|
||||||
|
|
||||||
// Allocate memory for the packed data.
|
|
||||||
size := targetData.TypeAllocSize(packedType)
|
|
||||||
if size == 0 {
|
|
||||||
return llvm.ConstPointerNull(i8ptrType)
|
|
||||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
|
||||||
return builder.CreateBitCast(values[0], i8ptrType, "pack.ptr")
|
|
||||||
} else if size <= targetData.TypeAllocSize(i8ptrType) {
|
|
||||||
// Packed data fits in a pointer, so store it directly inside the
|
|
||||||
// pointer.
|
|
||||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
|
||||||
// Try to keep this cast in SSA form.
|
|
||||||
return builder.CreateIntToPtr(values[0], i8ptrType, "pack.int")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
|
||||||
// it in a *i8 alloca first and load the *i8 value from there. This is
|
|
||||||
// effectively a bitcast.
|
|
||||||
packedAlloc, _, _ := CreateTemporaryAlloca(builder, mod, i8ptrType, "")
|
|
||||||
|
|
||||||
if size < targetData.TypeAllocSize(i8ptrType) {
|
|
||||||
// The alloca is bigger than the value that will be stored in it.
|
|
||||||
// To avoid having some bits undefined, zero the alloca first.
|
|
||||||
// Hopefully this will get optimized away.
|
|
||||||
builder.CreateStore(llvm.ConstNull(i8ptrType), packedAlloc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store all values in the alloca.
|
|
||||||
packedAllocCast := builder.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
|
|
||||||
for i, value := range values {
|
|
||||||
indices := []llvm.Value{
|
|
||||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
|
||||||
}
|
|
||||||
gep := builder.CreateInBoundsGEP(packedAllocCast, indices, "")
|
|
||||||
builder.CreateStore(value, gep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load value (the *i8) from the alloca.
|
|
||||||
result := builder.CreateLoad(packedAlloc, "")
|
|
||||||
|
|
||||||
// End the lifetime of the alloca, to help the optimizer.
|
|
||||||
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
|
|
||||||
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
|
|
||||||
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
|
|
||||||
|
|
||||||
return result
|
|
||||||
} else {
|
|
||||||
// Check if the values are all constants.
|
|
||||||
constant := true
|
|
||||||
for _, v := range values {
|
|
||||||
if !v.IsConstant() {
|
|
||||||
constant = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if constant {
|
|
||||||
// The data is known at compile time, so store it in a constant global.
|
|
||||||
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
|
|
||||||
global := llvm.AddGlobal(mod, packedType, prefix+"$pack")
|
|
||||||
global.SetInitializer(ctx.ConstStruct(values, false))
|
|
||||||
global.SetGlobalConstant(true)
|
|
||||||
global.SetUnnamedAddr(true)
|
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
|
||||||
return llvm.ConstBitCast(global, i8ptrType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
|
||||||
sizeValue := llvm.ConstInt(uintptrType, size, false)
|
|
||||||
alloc := mod.NamedFunction("runtime.alloc")
|
|
||||||
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
|
|
||||||
sizeValue,
|
|
||||||
llvm.ConstNull(i8ptrType),
|
|
||||||
llvm.Undef(i8ptrType), // unused context parameter
|
|
||||||
}, "")
|
|
||||||
if needsStackObjects {
|
|
||||||
trackPointer := mod.NamedFunction("runtime.trackPointer")
|
|
||||||
builder.CreateCall(trackPointer, []llvm.Value{
|
|
||||||
packedHeapAlloc,
|
|
||||||
llvm.Undef(i8ptrType), // unused context parameter
|
|
||||||
}, "")
|
|
||||||
}
|
|
||||||
packedAlloc := builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
|
||||||
|
|
||||||
// Store all values in the heap pointer.
|
|
||||||
for i, value := range values {
|
|
||||||
indices := []llvm.Value{
|
|
||||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
|
||||||
}
|
|
||||||
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
|
||||||
builder.CreateStore(value, gep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the original heap allocation pointer, which already is an *i8.
|
|
||||||
return packedHeapAlloc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitPointerUnpack extracts a list of values packed using EmitPointerPack.
|
|
||||||
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
|
||||||
ctx := mod.Context()
|
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
|
||||||
defer targetData.Dispose()
|
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
|
||||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
|
||||||
|
|
||||||
packedType := ctx.StructType(valueTypes, false)
|
|
||||||
|
|
||||||
// Get a correctly-typed pointer to the packed data.
|
|
||||||
var packedAlloc, packedRawAlloc llvm.Value
|
|
||||||
size := targetData.TypeAllocSize(packedType)
|
|
||||||
if size == 0 {
|
|
||||||
// No data to unpack.
|
|
||||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
|
||||||
// A single pointer is always stored directly.
|
|
||||||
return []llvm.Value{builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
|
||||||
} else if size <= targetData.TypeAllocSize(i8ptrType) {
|
|
||||||
// Packed data stored directly in pointer.
|
|
||||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
|
||||||
// Keep this cast in SSA form.
|
|
||||||
return []llvm.Value{builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
|
||||||
}
|
|
||||||
// Fallback: load it using an alloca.
|
|
||||||
packedRawAlloc, _, _ = CreateTemporaryAlloca(builder, mod, llvm.PointerType(i8ptrType, 0), "unpack.raw.alloc")
|
|
||||||
packedRawValue := builder.CreateBitCast(ptr, llvm.PointerType(i8ptrType, 0), "unpack.raw.value")
|
|
||||||
builder.CreateStore(packedRawValue, packedRawAlloc)
|
|
||||||
packedAlloc = builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
|
||||||
} else {
|
|
||||||
// Packed data stored on the heap. Bitcast the passed-in pointer to the
|
|
||||||
// correct pointer type.
|
|
||||||
packedAlloc = builder.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
|
|
||||||
}
|
|
||||||
// Load each value from the packed data.
|
|
||||||
values := make([]llvm.Value, len(valueTypes))
|
|
||||||
for i, valueType := range valueTypes {
|
|
||||||
if targetData.TypeAllocSize(valueType) == 0 {
|
|
||||||
// This value has length zero, so there's nothing to load.
|
|
||||||
values[i] = llvm.ConstNull(valueType)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
indices := []llvm.Value{
|
|
||||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
|
||||||
}
|
|
||||||
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
|
||||||
values[i] = builder.CreateLoad(gep, "")
|
|
||||||
}
|
|
||||||
if !packedRawAlloc.IsNil() {
|
|
||||||
allocPtr := builder.CreateBitCast(packedRawAlloc, i8ptrType, "")
|
|
||||||
allocSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(uintptrType), false)
|
|
||||||
EmitLifetimeEnd(builder, mod, allocPtr, allocSize)
|
|
||||||
}
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
+5
-5
@@ -41,8 +41,8 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), 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)
|
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||||
if expr.Reserve != nil {
|
if expr.Reserve != nil {
|
||||||
@@ -106,7 +106,7 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
|||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
// Load the resulting value from the hashmap. The value is set to the zero
|
||||||
// value if the key doesn't exist in the hashmap.
|
// value if the key doesn't exist in the hashmap.
|
||||||
mapValue := b.CreateLoad(mapValueAlloca, "")
|
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||||
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
||||||
|
|
||||||
if commaOk {
|
if commaOk {
|
||||||
@@ -217,8 +217,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
||||||
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
||||||
mapKey := b.CreateLoad(mapKeyAlloca, "")
|
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
||||||
mapValue := b.CreateLoad(mapValueAlloca, "")
|
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||||
|
|
||||||
if isKeyStoredAsInterface {
|
if isKeyStoredAsInterface {
|
||||||
// The key is stored as an interface but it isn't of interface type.
|
// The key is stored as an interface but it isn't of interface type.
|
||||||
|
|||||||
+1
-1
@@ -152,7 +152,7 @@ func (s *stdSizes) Sizeof(T types.Type) int64 {
|
|||||||
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
|
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
return s.PtrSize * 2
|
return s.PtrSize * 2
|
||||||
case *types.Pointer:
|
case *types.Pointer, *types.Chan, *types.Map:
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
// Func values in TinyGo are two words in size.
|
// Func values in TinyGo are two words in size.
|
||||||
|
|||||||
+41
-26
@@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -53,11 +54,11 @@ const (
|
|||||||
|
|
||||||
// getFunction returns the LLVM function for the given *ssa.Function, creating
|
// getFunction returns the LLVM function for the given *ssa.Function, creating
|
||||||
// it if needed. It can later be filled with compilerContext.createFunction().
|
// it if needed. It can later be filled with compilerContext.createFunction().
|
||||||
func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) {
|
||||||
info := c.getFunctionInfo(fn)
|
info := c.getFunctionInfo(fn)
|
||||||
llvmFn := c.mod.NamedFunction(info.linkName)
|
llvmFn := c.mod.NamedFunction(info.linkName)
|
||||||
if !llvmFn.IsNil() {
|
if !llvmFn.IsNil() {
|
||||||
return llvmFn
|
return llvmFn.GlobalValueType(), llvmFn
|
||||||
}
|
}
|
||||||
|
|
||||||
var retType llvm.Type
|
var retType llvm.Type
|
||||||
@@ -83,7 +84,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
// 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
|
||||||
// closures and bound methods, but should be optimized away when not used.
|
// closures and bound methods, but should be optimized away when not used.
|
||||||
if !info.exported {
|
if !info.exported {
|
||||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
|
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
@@ -112,17 +113,8 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
|
|
||||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||||
for i, info := range paramInfos {
|
for i, info := range paramInfos {
|
||||||
if info.flags¶mIsDeferenceableOrNull == 0 {
|
if info.elemSize != 0 {
|
||||||
continue
|
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize)
|
||||||
}
|
|
||||||
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
|
|
||||||
el := info.llvmType.ElementType()
|
|
||||||
size := c.targetData.TypeAllocSize(el)
|
|
||||||
if size == 0 {
|
|
||||||
// dereferenceable_or_null(0) appears to be illegal in LLVM.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, size)
|
|
||||||
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,23 +150,38 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
// 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("nocapture"), 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":
|
||||||
|
if strings.Split(c.Triple, "-")[0] == "avr" {
|
||||||
|
// These functions are compiler-rt/libgcc functions that are
|
||||||
|
// currently implemented in Go. Assembly versions should appear in
|
||||||
|
// LLVM 16 hopefully. Until then, they need to be made available to
|
||||||
|
// the linker and the best way to do that is llvm.compiler.used.
|
||||||
|
// I considered adding a pragma for this, but the LLVM language
|
||||||
|
// reference explicitly says that this feature should not be exposed
|
||||||
|
// to source languages:
|
||||||
|
// > This is a rare construct that should only be used in rare
|
||||||
|
// > circumstances, and should not be exposed to source languages.
|
||||||
|
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// External/exported functions may not retain pointer values.
|
// External/exported functions may not retain pointer values.
|
||||||
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
||||||
if info.exported {
|
if info.exported {
|
||||||
// Set the wasm-import-module attribute if the function's module is set.
|
if c.archFamily() == "wasm32" {
|
||||||
if info.module != "" {
|
|
||||||
|
|
||||||
// We need to add the wasm-import-module and the wasm-import-name
|
// We need to add the wasm-import-module and the wasm-import-name
|
||||||
wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module)
|
// attributes.
|
||||||
llvmFn.AddFunctionAttr(wasmImportModuleAttr)
|
module := info.module
|
||||||
|
if module == "" {
|
||||||
// Add the Wasm Import Name, if we are a named wasm import
|
module = "env"
|
||||||
if info.importName != "" {
|
|
||||||
wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName)
|
|
||||||
llvmFn.AddFunctionAttr(wasmImportNameAttr)
|
|
||||||
}
|
}
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
|
||||||
|
|
||||||
|
name := info.importName
|
||||||
|
if name == "" {
|
||||||
|
name = info.linkName
|
||||||
|
}
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
|
||||||
}
|
}
|
||||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||||
@@ -200,7 +207,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
llvmFn.SetUnnamedAddr(true)
|
llvmFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return llvmFn
|
return fnType, llvmFn
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFunctionInfo returns information about a function that is not directly
|
// getFunctionInfo returns information about a function that is not directly
|
||||||
@@ -360,7 +367,15 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
|
|||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
|
||||||
if strings.Split(c.Triple, "-")[0] == "x86_64" {
|
if strings.Split(c.Triple, "-")[0] == "x86_64" {
|
||||||
// Required by the ABI.
|
// Required by the ABI.
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
|
if llvmutil.Major() < 15 {
|
||||||
|
// Needed for LLVM 14 support.
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
|
||||||
|
} else {
|
||||||
|
// The uwtable has two possible values: sync (1) or async (2). We
|
||||||
|
// use sync because we currently don't use async unwind tables.
|
||||||
|
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -44,7 +44,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
constraints += ",~{rcx},~{r11}"
|
constraints += ",~{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, true, false, llvm.InlineAsmDialectIntel, false)
|
||||||
return b.CreateCall(target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||||
// Sources:
|
// Sources:
|
||||||
// syscall(2) man page
|
// syscall(2) man page
|
||||||
@@ -70,7 +70,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
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, true, false, llvm.InlineAsmDialectIntel, false)
|
||||||
return b.CreateCall(target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||||
// 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.
|
||||||
@@ -102,7 +102,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
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, true, false, 0, false)
|
||||||
return b.CreateCall(target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
@@ -134,7 +134,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
constraints += ",~{x16},~{x17}" // scratch registers
|
constraints += ",~{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, true, false, 0, false)
|
||||||
return b.CreateCall(target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||||
}
|
}
|
||||||
@@ -205,9 +205,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// Note that SetLastError/GetLastError could be replaced with direct
|
// Note that SetLastError/GetLastError could be replaced with direct
|
||||||
// access to the thread control block, which is probably smaller and
|
// access to the thread control block, which is probably smaller and
|
||||||
// faster. The Go runtime does this in assembly.
|
// faster. The Go runtime does this in assembly.
|
||||||
b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||||
syscallResult := b.CreateCall(fnPtr, params, "")
|
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
|
||||||
errResult := b.CreateCall(getLastError, nil, "err")
|
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||||
if b.uintptrType != b.ctx.Int32Type() {
|
if b.uintptrType != b.ctx.Int32Type() {
|
||||||
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+14
-4
@@ -75,14 +75,24 @@ func complexMul(x, y complex64) complex64 {
|
|||||||
// A type 'kv' also exists in function foo. Test that these two types don't
|
// A type 'kv' also exists in function foo. Test that these two types don't
|
||||||
// conflict with each other.
|
// conflict with each other.
|
||||||
type kv struct {
|
type kv struct {
|
||||||
v float32
|
v float32
|
||||||
|
x, y, z int
|
||||||
}
|
}
|
||||||
|
|
||||||
func foo(a *kv) {
|
var kvGlobal kv
|
||||||
|
|
||||||
|
func foo() {
|
||||||
// Define a new 'kv' type.
|
// Define a new 'kv' type.
|
||||||
type kv struct {
|
type kv struct {
|
||||||
v byte
|
v byte
|
||||||
|
x, y, z int
|
||||||
}
|
}
|
||||||
// Use this type.
|
// Use this type.
|
||||||
func(b *kv) {}(nil)
|
func(b kv) {}(kv{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type T1 []T1
|
||||||
|
type T2 [2]*T2
|
||||||
|
|
||||||
|
var a T1
|
||||||
|
var b T2
|
||||||
|
|||||||
Vendored
+43
-30
@@ -3,35 +3,39 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%main.kv = type { float }
|
%main.kv = type { float, i32, i32, i32 }
|
||||||
%main.kv.0 = type { i8 }
|
%main.kv.0 = type { i8, i32, i32, i32 }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
@main.kvGlobal = hidden global %main.kv zeroinitializer, align 4
|
||||||
|
@main.a = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
|
@main.b = hidden global [2 x ptr] zeroinitializer, align 4
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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 i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -45,14 +49,14 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.divideByZeroPanic(i8*) #0
|
declare void @runtime.divideByZeroPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -62,12 +66,12 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -81,12 +85,12 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -96,66 +100,66 @@ divbyzero.next: ; preds = %entry
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
divbyzero.throw: ; preds = %entry
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #2
|
call void @runtime.divideByZeroPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -165,7 +169,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, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -175,7 +179,7 @@ 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, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -189,15 +193,24 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #1 {
|
define hidden void @main.foo(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"main.foo$1"(%main.kv.0* null, i8* undef)
|
%complit = alloca %main.kv.0, align 8
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
store %main.kv.0 zeroinitializer, ptr %complit, align 8
|
||||||
|
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #1 {
|
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
%b1 = alloca %main.kv.0, align 8
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
store %main.kv.0 zeroinitializer, ptr %b1, align 8
|
||||||
|
call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
store %main.kv.0 %b, ptr %b1, align 8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+48
-63
@@ -3,110 +3,95 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
%runtime.channelBlockedList = type { ptr, ptr, ptr, { ptr, i32, i32 } }
|
||||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
%runtime.chanSelectState = type { ptr, ptr }
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
|
||||||
%"internal/task.gcData" = type { i8* }
|
|
||||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
|
||||||
%"internal/task.stackState" = type { i32, i32 }
|
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
store i32 3, ptr %chan.value, align 4
|
||||||
store i32 3, i32* %chan.value, align 4
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
|
||||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
||||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2
|
||||||
|
|
||||||
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
|
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
||||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
|
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca {}, align 8
|
%complit = alloca {}, align 8
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%0 = bitcast {}* %complit to i8*
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #3
|
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||||
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3
|
||||||
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context) unnamed_addr #1 {
|
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %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, i32* %select.send.value, align 4
|
store i32 1, ptr %select.send.value, align 4
|
||||||
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||||
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
store ptr %ch1, ptr %select.states.alloca, align 8
|
||||||
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
|
%select.states.alloca.repack1 = getelementptr inbounds %runtime.chanSelectState, ptr %select.states.alloca, i32 0, i32 1
|
||||||
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
|
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
||||||
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
|
%0 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1
|
||||||
%0 = bitcast i8** %.repack1 to i32**
|
store ptr %ch2, ptr %0, align 8
|
||||||
store i32* %select.send.value, i32** %0, align 4
|
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1
|
||||||
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
|
store ptr null, ptr %.repack3, align 4
|
||||||
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
|
%select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #3
|
||||||
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||||
store i8* null, i8** %.repack4, align 4
|
|
||||||
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
|
|
||||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef) #3
|
|
||||||
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
|
||||||
%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
|
||||||
@@ -122,9 +107,9 @@ select.body: ; preds = %select.next
|
|||||||
br label %select.done
|
br label %select.done
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*) #0
|
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #0
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { argmemonly nofree nosync nounwind willreturn }
|
attributes #2 = { argmemonly nocallback nofree nosync nounwind willreturn }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
+127
-138
@@ -3,103 +3,99 @@ 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._defer = type { i32, %runtime._defer* }
|
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
|
||||||
%runtime.deferFrame = type { i8*, i8*, [0 x i8*], %runtime.deferFrame*, i1, %runtime._interface }
|
%runtime._interface = type { ptr, ptr }
|
||||||
%runtime._interface = type { i32, i8* }
|
%runtime._defer = type { i32, ptr }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #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
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.external(i8*) #0
|
declare void @main.external(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferSimple(i8* %context) unnamed_addr #1 {
|
define hidden void @main.deferSimple(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca = alloca { i32, %runtime._defer* }, align 4
|
%defer.alloca = alloca { i32, ptr }, align 4
|
||||||
%deferPtr = alloca %runtime._defer*, align 4
|
%deferPtr = alloca ptr, align 4
|
||||||
store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
|
store ptr null, ptr %deferPtr, align 4
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
%0 = call i8* @llvm.stacksave()
|
%0 = call ptr @llvm.stacksave()
|
||||||
call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3
|
||||||
%defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
store i32 0, i32* %defer.alloca.repack, align 4
|
%defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
|
||||||
%defer.alloca.repack16 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
|
store ptr null, ptr %defer.alloca.repack15, align 4
|
||||||
store %runtime._defer* null, %runtime._defer** %defer.alloca.repack16, align 4
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
%1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
|
||||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||||
br i1 %setjmp.result, label %2, label %lpad
|
br i1 %setjmp.result, label %1, label %lpad
|
||||||
|
|
||||||
2: ; preds = %entry
|
1: ; preds = %entry
|
||||||
call void @main.external(i8* undef) #3
|
call void @main.external(ptr undef) #3
|
||||||
br label %rundefers.loophead
|
br label %rundefers.loophead
|
||||||
|
|
||||||
rundefers.loophead: ; preds = %4, %2
|
rundefers.loophead: ; preds = %3, %1
|
||||||
%3 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
%2 = load ptr, ptr %deferPtr, align 4
|
||||||
%stackIsNil = icmp eq %runtime._defer* %3, null
|
%stackIsNil = icmp eq ptr %2, null
|
||||||
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 %runtime._defer, %runtime._defer* %3, i32 0, i32 1
|
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
|
||||||
%stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
%callback.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %3, i32 0, i32 0
|
%callback = load i32, ptr %2, align 4
|
||||||
%callback = load i32, i32* %callback.gep, align 4
|
|
||||||
switch i32 %callback, label %rundefers.default [
|
switch i32 %callback, label %rundefers.default [
|
||||||
i32 0, label %rundefers.callback0
|
i32 0, label %rundefers.callback0
|
||||||
]
|
]
|
||||||
|
|
||||||
rundefers.callback0: ; preds = %rundefers.loop
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
%setjmp1 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
%setjmp1 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
%setjmp.result2 = icmp eq i32 %setjmp1, 0
|
%setjmp.result2 = icmp eq i32 %setjmp1, 0
|
||||||
br i1 %setjmp.result2, label %4, label %lpad
|
br i1 %setjmp.result2, label %3, label %lpad
|
||||||
|
|
||||||
4: ; preds = %rundefers.callback0
|
3: ; preds = %rundefers.callback0
|
||||||
call void @"main.deferSimple$1"(i8* undef)
|
call void @"main.deferSimple$1"(ptr undef)
|
||||||
br label %rundefers.loophead
|
br label %rundefers.loophead
|
||||||
|
|
||||||
rundefers.default: ; preds = %rundefers.loop
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
rundefers.end: ; preds = %rundefers.loophead
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
recover: ; preds = %rundefers.end3
|
recover: ; preds = %rundefers.end3
|
||||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
|
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
|
||||||
br label %rundefers.loophead6
|
br label %rundefers.loophead6
|
||||||
|
|
||||||
rundefers.loophead6: ; preds = %6, %lpad
|
rundefers.loophead6: ; preds = %5, %lpad
|
||||||
%5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
%4 = load ptr, ptr %deferPtr, align 4
|
||||||
%stackIsNil7 = icmp eq %runtime._defer* %5, null
|
%stackIsNil7 = icmp eq ptr %4, null
|
||||||
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 %runtime._defer, %runtime._defer* %5, i32 0, i32 1
|
%stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %4, i32 0, i32 1
|
||||||
%stack.next9 = load %runtime._defer*, %runtime._defer** %stack.next.gep8, align 4
|
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
|
||||||
store %runtime._defer* %stack.next9, %runtime._defer** %deferPtr, align 4
|
store ptr %stack.next9, ptr %deferPtr, align 4
|
||||||
%callback.gep10 = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 0
|
%callback11 = load i32, ptr %4, align 4
|
||||||
%callback11 = load i32, i32* %callback.gep10, align 4
|
|
||||||
switch i32 %callback11, label %rundefers.default4 [
|
switch i32 %callback11, label %rundefers.default4 [
|
||||||
i32 0, label %rundefers.callback012
|
i32 0, label %rundefers.callback012
|
||||||
]
|
]
|
||||||
|
|
||||||
rundefers.callback012: ; preds = %rundefers.loop5
|
rundefers.callback012: ; preds = %rundefers.loop5
|
||||||
%setjmp14 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
%setjmp13 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
%setjmp.result15 = icmp eq i32 %setjmp14, 0
|
%setjmp.result14 = icmp eq i32 %setjmp13, 0
|
||||||
br i1 %setjmp.result15, label %6, label %lpad
|
br i1 %setjmp.result14, label %5, label %lpad
|
||||||
|
|
||||||
6: ; preds = %rundefers.callback012
|
5: ; preds = %rundefers.callback012
|
||||||
call void @"main.deferSimple$1"(i8* undef)
|
call void @"main.deferSimple$1"(ptr undef)
|
||||||
br label %rundefers.loophead6
|
br label %rundefers.loophead6
|
||||||
|
|
||||||
rundefers.default4: ; preds = %rundefers.loop5
|
rundefers.default4: ; preds = %rundefers.loop5
|
||||||
@@ -109,158 +105,151 @@ rundefers.end3: ; preds = %rundefers.loophead6
|
|||||||
br label %recover
|
br label %recover
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||||
declare i8* @llvm.stacksave() #2
|
declare ptr @llvm.stacksave() #2
|
||||||
|
|
||||||
declare void @runtime.setupDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*, i8*) #0
|
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.deferSimple$1"(i8* %context) unnamed_addr #1 {
|
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printint32(i32 3, i8* undef) #3
|
call void @runtime.printint32(i32 3, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.destroyDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*) #0
|
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #0
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*) #0
|
declare void @runtime.printint32(i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferMultiple(i8* %context) unnamed_addr #1 {
|
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%defer.alloca2 = alloca { i32, %runtime._defer* }, align 4
|
%defer.alloca2 = alloca { i32, ptr }, align 4
|
||||||
%defer.alloca = alloca { i32, %runtime._defer* }, align 4
|
%defer.alloca = alloca { i32, ptr }, align 4
|
||||||
%deferPtr = alloca %runtime._defer*, align 4
|
%deferPtr = alloca ptr, align 4
|
||||||
store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
|
store ptr null, ptr %deferPtr, align 4
|
||||||
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
%0 = call i8* @llvm.stacksave()
|
%0 = call ptr @llvm.stacksave()
|
||||||
call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #3
|
||||||
%defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
store i32 0, i32* %defer.alloca.repack, align 4
|
%defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
|
||||||
%defer.alloca.repack26 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
|
store ptr null, ptr %defer.alloca.repack22, align 4
|
||||||
store %runtime._defer* null, %runtime._defer** %defer.alloca.repack26, align 4
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
%1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
store i32 1, ptr %defer.alloca2, align 4
|
||||||
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, align 4
|
%defer.alloca2.repack23 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1
|
||||||
%defer.alloca2.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 0
|
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
|
||||||
store i32 1, i32* %defer.alloca2.repack, align 4
|
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
||||||
%defer.alloca2.repack27 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 1
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
%2 = bitcast %runtime._defer** %defer.alloca2.repack27 to { i32, %runtime._defer* }**
|
|
||||||
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %2, align 4
|
|
||||||
%3 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
|
||||||
store { i32, %runtime._defer* }* %defer.alloca2, { i32, %runtime._defer* }** %3, 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
|
||||||
%setjmp.result = icmp eq i32 %setjmp, 0
|
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||||
br i1 %setjmp.result, label %4, label %lpad
|
br i1 %setjmp.result, label %1, label %lpad
|
||||||
|
|
||||||
4: ; preds = %entry
|
1: ; preds = %entry
|
||||||
call void @main.external(i8* undef) #3
|
call void @main.external(ptr undef) #3
|
||||||
br label %rundefers.loophead
|
br label %rundefers.loophead
|
||||||
|
|
||||||
rundefers.loophead: ; preds = %7, %6, %4
|
rundefers.loophead: ; preds = %4, %3, %1
|
||||||
%5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
%2 = load ptr, ptr %deferPtr, align 4
|
||||||
%stackIsNil = icmp eq %runtime._defer* %5, null
|
%stackIsNil = icmp eq ptr %2, null
|
||||||
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 %runtime._defer, %runtime._defer* %5, i32 0, i32 1
|
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
|
||||||
%stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
%callback.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 0
|
%callback = load i32, ptr %2, align 4
|
||||||
%callback = load i32, i32* %callback.gep, align 4
|
|
||||||
switch i32 %callback, label %rundefers.default [
|
switch i32 %callback, label %rundefers.default [
|
||||||
i32 0, label %rundefers.callback0
|
i32 0, label %rundefers.callback0
|
||||||
i32 1, label %rundefers.callback1
|
i32 1, label %rundefers.callback1
|
||||||
]
|
]
|
||||||
|
|
||||||
rundefers.callback0: ; preds = %rundefers.loop
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
%setjmp4 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
%setjmp3 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
%setjmp.result5 = icmp eq i32 %setjmp4, 0
|
%setjmp.result4 = icmp eq i32 %setjmp3, 0
|
||||||
br i1 %setjmp.result5, label %6, label %lpad
|
br i1 %setjmp.result4, label %3, label %lpad
|
||||||
|
|
||||||
6: ; preds = %rundefers.callback0
|
3: ; preds = %rundefers.callback0
|
||||||
call void @"main.deferMultiple$1"(i8* undef)
|
call void @"main.deferMultiple$1"(ptr undef)
|
||||||
br label %rundefers.loophead
|
br label %rundefers.loophead
|
||||||
|
|
||||||
rundefers.callback1: ; preds = %rundefers.loop
|
rundefers.callback1: ; preds = %rundefers.loop
|
||||||
%setjmp7 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
%setjmp5 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
%setjmp.result8 = icmp eq i32 %setjmp7, 0
|
%setjmp.result6 = icmp eq i32 %setjmp5, 0
|
||||||
br i1 %setjmp.result8, label %7, label %lpad
|
br i1 %setjmp.result6, label %4, label %lpad
|
||||||
|
|
||||||
7: ; preds = %rundefers.callback1
|
4: ; preds = %rundefers.callback1
|
||||||
call void @"main.deferMultiple$2"(i8* undef)
|
call void @"main.deferMultiple$2"(ptr undef)
|
||||||
br label %rundefers.loophead
|
br label %rundefers.loophead
|
||||||
|
|
||||||
rundefers.default: ; preds = %rundefers.loop
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
rundefers.end: ; preds = %rundefers.loophead
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
recover: ; preds = %rundefers.end9
|
recover: ; preds = %rundefers.end7
|
||||||
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
lpad: ; preds = %rundefers.callback122, %rundefers.callback018, %rundefers.callback1, %rundefers.callback0, %entry
|
lpad: ; preds = %rundefers.callback119, %rundefers.callback016, %rundefers.callback1, %rundefers.callback0, %entry
|
||||||
br label %rundefers.loophead12
|
br label %rundefers.loophead10
|
||||||
|
|
||||||
rundefers.loophead12: ; preds = %10, %9, %lpad
|
rundefers.loophead10: ; preds = %7, %6, %lpad
|
||||||
%8 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
%5 = load ptr, ptr %deferPtr, align 4
|
||||||
%stackIsNil13 = icmp eq %runtime._defer* %8, null
|
%stackIsNil11 = icmp eq ptr %5, null
|
||||||
br i1 %stackIsNil13, label %rundefers.end9, label %rundefers.loop11
|
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
|
||||||
|
|
||||||
rundefers.loop11: ; preds = %rundefers.loophead12
|
rundefers.loop9: ; preds = %rundefers.loophead10
|
||||||
%stack.next.gep14 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 1
|
%stack.next.gep12 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1
|
||||||
%stack.next15 = load %runtime._defer*, %runtime._defer** %stack.next.gep14, align 4
|
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
|
||||||
store %runtime._defer* %stack.next15, %runtime._defer** %deferPtr, align 4
|
store ptr %stack.next13, ptr %deferPtr, align 4
|
||||||
%callback.gep16 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 0
|
%callback15 = load i32, ptr %5, align 4
|
||||||
%callback17 = load i32, i32* %callback.gep16, align 4
|
switch i32 %callback15, label %rundefers.default8 [
|
||||||
switch i32 %callback17, label %rundefers.default10 [
|
i32 0, label %rundefers.callback016
|
||||||
i32 0, label %rundefers.callback018
|
i32 1, label %rundefers.callback119
|
||||||
i32 1, label %rundefers.callback122
|
|
||||||
]
|
]
|
||||||
|
|
||||||
rundefers.callback018: ; preds = %rundefers.loop11
|
rundefers.callback016: ; preds = %rundefers.loop9
|
||||||
%setjmp20 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
%setjmp17 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result18 = icmp eq i32 %setjmp17, 0
|
||||||
|
br i1 %setjmp.result18, label %6, label %lpad
|
||||||
|
|
||||||
|
6: ; preds = %rundefers.callback016
|
||||||
|
call void @"main.deferMultiple$1"(ptr undef)
|
||||||
|
br label %rundefers.loophead10
|
||||||
|
|
||||||
|
rundefers.callback119: ; preds = %rundefers.loop9
|
||||||
|
%setjmp20 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #4
|
||||||
%setjmp.result21 = icmp eq i32 %setjmp20, 0
|
%setjmp.result21 = icmp eq i32 %setjmp20, 0
|
||||||
br i1 %setjmp.result21, label %9, label %lpad
|
br i1 %setjmp.result21, label %7, label %lpad
|
||||||
|
|
||||||
9: ; preds = %rundefers.callback018
|
7: ; preds = %rundefers.callback119
|
||||||
call void @"main.deferMultiple$1"(i8* undef)
|
call void @"main.deferMultiple$2"(ptr undef)
|
||||||
br label %rundefers.loophead12
|
br label %rundefers.loophead10
|
||||||
|
|
||||||
rundefers.callback122: ; preds = %rundefers.loop11
|
rundefers.default8: ; preds = %rundefers.loop9
|
||||||
%setjmp24 = 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}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
|
||||||
%setjmp.result25 = icmp eq i32 %setjmp24, 0
|
|
||||||
br i1 %setjmp.result25, label %10, label %lpad
|
|
||||||
|
|
||||||
10: ; preds = %rundefers.callback122
|
|
||||||
call void @"main.deferMultiple$2"(i8* undef)
|
|
||||||
br label %rundefers.loophead12
|
|
||||||
|
|
||||||
rundefers.default10: ; preds = %rundefers.loop11
|
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
rundefers.end9: ; preds = %rundefers.loophead12
|
rundefers.end7: ; preds = %rundefers.loophead10
|
||||||
br label %recover
|
br label %recover
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.deferMultiple$1"(i8* %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printint32(i32 3, i8* undef) #3
|
call void @runtime.printint32(i32 3, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.deferMultiple$2"(i8* %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.printint32(i32 5, i8* undef) #3
|
call void @runtime.printint32(i32 5, ptr undef) #3
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { nofree nosync nounwind willreturn }
|
attributes #2 = { nocallback nofree nosync nounwind willreturn }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
attributes #4 = { nounwind returns_twice }
|
attributes #4 = { nounwind returns_twice }
|
||||||
|
|||||||
Vendored
+11
-11
@@ -3,18 +3,18 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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 i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -26,25 +26,25 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.maxu32f(i8* %context) unnamed_addr #1 {
|
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(i8* %context) unnamed_addr #1 {
|
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(i8* %context) unnamed_addr #1 {
|
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, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -54,7 +54,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -67,7 +67,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #1 {
|
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
|
||||||
@@ -79,7 +79,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #1 {
|
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
|
||||||
|
|||||||
Vendored
+11
-12
@@ -3,43 +3,42 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #1 {
|
define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq void ()* %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
|
||||||
%1 = bitcast void ()* %callback.funcptr to void (i32, i8*)*
|
call void %callback.funcptr(i32 3, ptr %callback.context) #2
|
||||||
call void %1(i32 3, i8* %callback.context) #2
|
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
fpcall.throw: ; preds = %entry
|
fpcall.throw: ; preds = %entry
|
||||||
call void @runtime.nilPanic(i8* undef) #2
|
call void @runtime.nilPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.nilPanic(i8*) #0
|
declare void @runtime.nilPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.bar(i8* %context) unnamed_addr #1 {
|
define hidden void @main.bar(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @main.foo(i8* undef, void ()* bitcast (void (i32, i8*)* @main.someFunc to void ()*), i8* 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, i8* %context) unnamed_addr #1 {
|
define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+94
-92
@@ -3,133 +3,135 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
%runtime._interface = type { ptr, ptr }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
|
||||||
%runtime._interface = type { i32, i8* }
|
|
||||||
|
|
||||||
@main.scalar1 = hidden global i8* null, align 4
|
@main.scalar1 = hidden global ptr null, align 4
|
||||||
@main.scalar2 = hidden global i32* null, align 4
|
@main.scalar2 = hidden global ptr null, align 4
|
||||||
@main.scalar3 = hidden global i64* null, align 4
|
@main.scalar3 = hidden global ptr null, align 4
|
||||||
@main.scalar4 = hidden global float* null, align 4
|
@main.scalar4 = hidden global ptr null, align 4
|
||||||
@main.array1 = hidden global [3 x i8]* null, align 4
|
@main.array1 = hidden global ptr null, align 4
|
||||||
@main.array2 = hidden global [71 x i8]* null, align 4
|
@main.array2 = hidden global ptr null, align 4
|
||||||
@main.array3 = hidden global [3 x i8*]* null, align 4
|
@main.array3 = hidden global ptr null, align 4
|
||||||
@main.struct1 = hidden global {}* null, align 4
|
@main.struct1 = hidden global ptr null, align 4
|
||||||
@main.struct2 = hidden global { i32, i32 }* null, align 4
|
@main.struct2 = hidden global ptr null, align 4
|
||||||
@main.struct3 = hidden global { i8*, [60 x i32], i8* }* null, align 4
|
@main.struct3 = hidden global ptr null, align 4
|
||||||
@main.struct4 = hidden global { i8*, [61 x i32] }* null, align 4
|
@main.struct4 = hidden global ptr null, align 4
|
||||||
@main.slice1 = hidden global { i8*, i32, i32 } zeroinitializer, align 8
|
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
|
||||||
@main.slice2 = hidden global { i32**, i32, i32 } zeroinitializer, align 8
|
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
|
||||||
@main.slice3 = hidden global { { i8*, i32, i32 }*, i32, i32 } zeroinitializer, align 8
|
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
|
||||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c" \00\00\00\00\00\00\01" }
|
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\00\00\00\00\00\00\00\01" }
|
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 }
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 16, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:complex128", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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.newScalar(i8* %context) unnamed_addr #1 {
|
define hidden void @main.newScalar(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
%new = call ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new, i8** @main.scalar1, align 4
|
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
store ptr %new, ptr @main.scalar1, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
%new1 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
store ptr %new1, ptr @main.scalar2, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
%new2 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
store ptr %new2, ptr @main.scalar3, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
|
%new3 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
store ptr %new3, ptr @main.scalar4, align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newArray(i8* %context) unnamed_addr #1 {
|
define hidden void @main.newArray(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
%new = call ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
store ptr %new, ptr @main.array1, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
%new1 = call ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
|
store ptr %new1, ptr @main.array2, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
%new2 = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2
|
||||||
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
store ptr %new2, ptr @main.array3, align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newStruct(i8* %context) unnamed_addr #1 {
|
define hidden void @main.newStruct(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
%new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
store ptr %new, ptr @main.struct1, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
%new1 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #2
|
store ptr %new1, ptr @main.struct2, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
%new2 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #2
|
||||||
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #2
|
store ptr %new2, ptr @main.struct3, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
|
%new3 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #2
|
||||||
store i8* %new3, i8** bitcast ({ i8*, [61 x i32] }** @main.struct4 to i8**), align 4
|
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
store ptr %new3, ptr @main.struct4, align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = bitcast i8* %new to { i8*, void ()* }*
|
%new = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret { i8*, void ()* }* %0
|
ret ptr %new
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.makeSlice(i8* %context) unnamed_addr #1 {
|
define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
|
%makeslice = call ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
|
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 1), align 4
|
store ptr %makeslice, ptr @main.slice1, align 8
|
||||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8
|
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4
|
||||||
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
|
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 8
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #2
|
%makeslice1 = call ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2
|
||||||
store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
|
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 1), align 4
|
store ptr %makeslice1, ptr @main.slice2, align 8
|
||||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8
|
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4
|
||||||
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #2
|
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 8
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #2
|
%makeslice3 = call ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #2
|
||||||
store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
|
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 1), align 4
|
store ptr %makeslice3, ptr @main.slice3, align 8
|
||||||
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8
|
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
|
||||||
|
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #1 {
|
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #2
|
||||||
%.repack = bitcast i8* %0 to double*
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
store double %v.r, double* %.repack, align 8
|
store double %v.r, ptr %0, align 8
|
||||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
|
||||||
%1 = bitcast i8* %.repack1 to double*
|
store double %v.i, ptr %.repack1, align 8
|
||||||
store double %v.i, double* %1, align 8
|
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
|
||||||
%2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret %runtime._interface %2
|
ret %runtime._interface %1
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
|||||||
Vendored
-41
@@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Test changes to the language introduced in Go 1.17.
|
|
||||||
// For details, see: https://tip.golang.org/doc/go1.17#language
|
|
||||||
// These tests should be merged into the regular slice tests once Go 1.17 is the
|
|
||||||
// minimun Go version for TinyGo.
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
|
|
||||||
return unsafe.Add(p, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
|
|
||||||
return unsafe.Add(p, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceToArray(s []int) *[4]int {
|
|
||||||
return (*[4]int)(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceToArrayConst() *[4]int {
|
|
||||||
s := make([]int, 6)
|
|
||||||
return (*[4]int)(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceInt(ptr *int, len int) []int {
|
|
||||||
return unsafe.Slice(ptr, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceUint16(ptr *byte, len uint16) []byte {
|
|
||||||
return unsafe.Slice(ptr, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceUint64(ptr *int, len uint64) []int {
|
|
||||||
return unsafe.Slice(ptr, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceInt64(ptr *int, len int64) []int {
|
|
||||||
return unsafe.Slice(ptr, len)
|
|
||||||
}
|
|
||||||
Vendored
-161
@@ -1,161 +0,0 @@
|
|||||||
; ModuleID = 'go1.17.go'
|
|
||||||
source_filename = "go1.17.go"
|
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
|
||||||
target triple = "wasm32-unknown-wasi"
|
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = getelementptr i8, i8* %p, i32 %len
|
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
|
||||||
ret i8* %0
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = trunc i64 %len to i32
|
|
||||||
%1 = getelementptr i8, i8* %p, i32 %0
|
|
||||||
call void @runtime.trackPointer(i8* %1, i8* undef) #2
|
|
||||||
ret i8* %1
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = icmp ult i32 %s.len, 4
|
|
||||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
|
||||||
|
|
||||||
slicetoarray.next: ; preds = %entry
|
|
||||||
%1 = bitcast i32* %s.data to [4 x i32]*
|
|
||||||
ret [4 x i32]* %1
|
|
||||||
|
|
||||||
slicetoarray.throw: ; preds = %entry
|
|
||||||
call void @runtime.sliceToArrayPointerPanic(i8* undef) #2
|
|
||||||
unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
declare void @runtime.sliceToArrayPointerPanic(i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
|
|
||||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
|
||||||
|
|
||||||
slicetoarray.next: ; preds = %entry
|
|
||||||
%0 = bitcast i8* %makeslice to [4 x i32]*
|
|
||||||
ret [4 x i32]* %0
|
|
||||||
|
|
||||||
slicetoarray.throw: ; preds = %entry
|
|
||||||
unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = icmp ugt i32 %len, 1073741823
|
|
||||||
%1 = icmp eq i32* %ptr, null
|
|
||||||
%2 = icmp ne i32 %len, 0
|
|
||||||
%3 = and i1 %1, %2
|
|
||||||
%4 = or i1 %3, %0
|
|
||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
|
||||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
|
||||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
|
||||||
%8 = bitcast i32* %ptr to i8*
|
|
||||||
call void @runtime.trackPointer(i8* %8, i8* undef) #2
|
|
||||||
ret { i32*, i32, i32 } %7
|
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
|
||||||
unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
declare void @runtime.unsafeSlicePanic(i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = icmp eq i8* %ptr, null
|
|
||||||
%1 = icmp ne i16 %len, 0
|
|
||||||
%2 = and i1 %0, %1
|
|
||||||
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
|
||||||
%3 = zext i16 %len to i32
|
|
||||||
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
|
||||||
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
|
||||||
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
|
||||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
|
||||||
ret { i8*, i32, i32 } %6
|
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
|
||||||
unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = icmp ugt i64 %len, 1073741823
|
|
||||||
%1 = icmp eq i32* %ptr, null
|
|
||||||
%2 = icmp ne i64 %len, 0
|
|
||||||
%3 = and i1 %1, %2
|
|
||||||
%4 = or i1 %3, %0
|
|
||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
|
||||||
%5 = trunc i64 %len to i32
|
|
||||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
|
||||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
|
||||||
%9 = bitcast i32* %ptr to i8*
|
|
||||||
call void @runtime.trackPointer(i8* %9, i8* undef) #2
|
|
||||||
ret { i32*, i32, i32 } %8
|
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
|
||||||
unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = icmp ugt i64 %len, 1073741823
|
|
||||||
%1 = icmp eq i32* %ptr, null
|
|
||||||
%2 = icmp ne i64 %len, 0
|
|
||||||
%3 = and i1 %1, %2
|
|
||||||
%4 = or i1 %3, %0
|
|
||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
|
||||||
%5 = trunc i64 %len to i32
|
|
||||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
|
||||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
|
||||||
%9 = bitcast i32* %ptr to i8*
|
|
||||||
call void @runtime.trackPointer(i8* %9, i8* undef) #2
|
|
||||||
ret { i32*, i32, i32 } %8
|
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
|
||||||
unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #2 = { nounwind }
|
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func unsafeSliceData(s []int) *int {
|
||||||
|
return unsafe.SliceData(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsafeString(ptr *byte, len int16) string {
|
||||||
|
return unsafe.String(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsafeStringData(s string) *byte {
|
||||||
|
return unsafe.StringData(s)
|
||||||
|
}
|
||||||
Vendored
+61
@@ -0,0 +1,61 @@
|
|||||||
|
; ModuleID = '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 triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%runtime._string = type { ptr, i32 }
|
||||||
|
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %s.data
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = icmp slt i16 %len, 0
|
||||||
|
%1 = icmp eq ptr %ptr, null
|
||||||
|
%2 = icmp ne i16 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.String.throw, label %unsafe.String.next
|
||||||
|
|
||||||
|
unsafe.String.next: ; preds = %entry
|
||||||
|
%5 = zext i16 %len to i32
|
||||||
|
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
||||||
|
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
||||||
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret %runtime._string %7
|
||||||
|
|
||||||
|
unsafe.String.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %s.data
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
+82
-107
@@ -3,201 +3,176 @@ source_filename = "goroutine.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.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
%runtime._string = type { ptr, i32 }
|
||||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
|
||||||
%"internal/task.gcData" = type {}
|
|
||||||
%"internal/task.state" = type { i32, i32* }
|
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
|
||||||
|
|
||||||
@"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
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #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.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #8
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #8
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, i8*) #0
|
declare void @main.regularFunction(i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint i8* %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*) #0
|
declare i32 @"internal/task.getGoroutineStackSize"(i32, ptr) #0
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
|
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #8
|
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) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
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"(i8* %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint i8* %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
|
%n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8
|
||||||
%0 = bitcast i8* %n to i32*
|
store i32 3, ptr %n, align 4
|
||||||
store i32 3, i32* %0, align 4
|
%0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8
|
||||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
|
store i32 5, ptr %0, align 4
|
||||||
%2 = bitcast i8* %1 to i32*
|
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
store i32 5, i32* %2, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #8
|
||||||
%4 = bitcast i8* %3 to i8**
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8
|
||||||
store i8* %n, i8** %4, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8
|
call void @runtime.printint32(i32 %2, ptr undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef) #8
|
|
||||||
%5 = load i32, i32* %0, align 4
|
|
||||||
call void @runtime.printint32(i32 %5, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.ptr = bitcast i8* %context to i32*
|
store i32 7, ptr %context, align 4
|
||||||
store i32 7, i32* %unpack.ptr, align 4
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = load i32, i32* %1, align 4
|
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
%5 = load i8*, i8** %4, align 4
|
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*) #0
|
declare void @runtime.printint32(i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
|
%0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8
|
||||||
%1 = bitcast i8* %0 to i32*
|
store i32 5, ptr %0, align 4
|
||||||
store i32 5, i32* %1, align 4
|
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
store ptr %fn.context, ptr %1, align 4
|
||||||
%3 = bitcast i8* %2 to i8**
|
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
||||||
store i8* %fn.context, i8** %3, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #8
|
||||||
%5 = bitcast i8* %4 to void ()**
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #8
|
||||||
store void ()* %fn.funcptr, void ()** %5, align 4
|
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef) #8
|
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #5 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = load i32, i32* %1, align 4
|
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
||||||
%5 = load i8*, i8** %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
call void %5(i32 %1, ptr %3) #8
|
||||||
%7 = bitcast i8* %6 to void (i32, i8*)**
|
|
||||||
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
|
||||||
call void %8(i32 %2, i8* %5) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
|
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(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %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 #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8
|
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
|
call void @runtime.chanClose(ptr %ch, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
|
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
|
||||||
%1 = bitcast i8* %0 to i8**
|
store ptr %itf.value, ptr %0, align 4
|
||||||
store i8* %itf.value, i8** %1, align 4
|
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
%.repack = bitcast i8* %2 to i8**
|
%.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
|
||||||
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
|
store i32 4, ptr %.repack1, align 4
|
||||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
|
||||||
%3 = bitcast i8* %.repack1 to i32*
|
store ptr %itf.typecode, ptr %2, align 4
|
||||||
store i32 4, i32* %3, align 4
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8
|
||||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
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) #8
|
||||||
%5 = bitcast i8* %4 to i32*
|
|
||||||
store i32 %itf.typecode, i32* %5, align 4
|
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* undef) #8
|
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i8**
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = load i8*, i8** %1, align 4
|
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
|
||||||
%5 = load i8*, i8** %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
%6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
|
||||||
%7 = bitcast i8* %6 to i32*
|
%7 = load ptr, ptr %6, align 4
|
||||||
%8 = load i32, i32* %7, align 4
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8
|
||||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
|
||||||
%10 = bitcast i8* %9 to i32*
|
|
||||||
%11 = load i32, i32* %10, align 4
|
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+92
-115
@@ -3,210 +3,187 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
%runtime._string = type { ptr, i32 }
|
||||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
|
||||||
%"internal/task.gcData" = type { i8* }
|
|
||||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
|
||||||
%"internal/task.stackState" = type { i32, i32 }
|
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
|
||||||
|
|
||||||
@"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
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, i8*) #0
|
declare void @main.regularFunction(i32, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.deadlock(i8*) #0
|
declare void @runtime.deadlock(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 {
|
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint i8* %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
|
call void @main.regularFunction(i32 %unpack.int, ptr undef) #8
|
||||||
call void @runtime.deadlock(i8* undef) #8
|
call void @runtime.deadlock(ptr undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
|
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
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"(i8* %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint i8* %0 to i32
|
%unpack.int = ptrtoint ptr %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
|
||||||
call void @runtime.deadlock(i8* undef) #8
|
call void @runtime.deadlock(ptr undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = bitcast i8* %n to i32*
|
%n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8
|
||||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
|
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8
|
||||||
store i32 3, i32* %0, align 4
|
store i32 3, ptr %n, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
|
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8
|
||||||
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #8
|
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #8
|
||||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
|
%0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #8
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
|
||||||
%2 = bitcast i8* %1 to i32*
|
store i32 5, ptr %0, align 4
|
||||||
store i32 5, i32* %2, align 4
|
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
store ptr %n, ptr %1, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8
|
||||||
store i8* %n, i8** %4, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #8
|
call void @runtime.printint32(i32 %2, ptr undef) #8
|
||||||
%5 = load i32, i32* %0, align 4
|
|
||||||
call void @runtime.printint32(i32 %5, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.ptr = bitcast i8* %context to i32*
|
store i32 7, ptr %context, align 4
|
||||||
store i32 7, i32* %unpack.ptr, align 4
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = load i32, i32* %1, align 4
|
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
%5 = load i8*, i8** %4, align 4
|
call void @runtime.deadlock(ptr undef) #8
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
|
|
||||||
call void @runtime.deadlock(i8* undef) #8
|
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*) #0
|
declare void @runtime.printint32(i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
|
%0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8
|
||||||
%1 = bitcast i8* %0 to i32*
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
|
||||||
store i32 5, i32* %1, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = bitcast i8* %2 to i8**
|
store ptr %fn.context, ptr %1, align 4
|
||||||
store i8* %fn.context, i8** %3, align 4
|
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
||||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
%5 = bitcast i8* %4 to void ()**
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 16384, ptr undef) #8
|
||||||
store void ()* %fn.funcptr, void ()** %5, align 4
|
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #5 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = load i32, i32* %1, align 4
|
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
||||||
%5 = load i8*, i8** %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
call void %5(i32 %1, ptr %3) #8
|
||||||
%7 = bitcast i8* %6 to void (i32, i8*)**
|
call void @runtime.deadlock(ptr undef) #8
|
||||||
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
|
||||||
call void %8(i32 %2, i8* %5) #8
|
|
||||||
call void @runtime.deadlock(i8* undef) #8
|
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
|
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(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %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 #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8
|
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
|
call void @runtime.chanClose(ptr %ch, ptr undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
|
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
|
||||||
%1 = bitcast i8* %0 to i8**
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
|
||||||
store i8* %itf.value, i8** %1, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
|
||||||
%.repack = bitcast i8* %2 to i8**
|
store ptr @"main$string", ptr %1, align 4
|
||||||
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
|
%.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
|
||||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
store i32 4, ptr %.repack1, align 4
|
||||||
%3 = bitcast i8* %.repack1 to i32*
|
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
|
||||||
store i32 4, i32* %3, align 4
|
store ptr %itf.typecode, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8
|
||||||
%5 = bitcast i8* %4 to i32*
|
|
||||||
store i32 %itf.typecode, i32* %5, align 4
|
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 16384, i8* undef) #8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i8**
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = load i8*, i8** %1, align 4
|
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
|
||||||
%5 = load i8*, i8** %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
%6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
|
||||||
%7 = bitcast i8* %6 to i32*
|
%7 = load ptr, ptr %6, align 4
|
||||||
%8 = load i32, i32* %7, align 4
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8
|
||||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
call void @runtime.deadlock(ptr undef) #8
|
||||||
%10 = bitcast i8* %9 to i32*
|
|
||||||
%11 = load i32, i32* %10, align 4
|
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #8
|
|
||||||
call void @runtime.deadlock(i8* undef) #8
|
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+52
-49
@@ -3,71 +3,69 @@ 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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
%runtime._interface = type { ptr, ptr }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime._string = type { ptr, i32 }
|
||||||
%runtime._interface = type { i32, i8* }
|
|
||||||
%runtime._string = type { i8*, i32 }
|
|
||||||
|
|
||||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int", i32 0 }
|
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 2, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:int" }, align 4
|
||||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:named:error" }, align 4
|
||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error", i32 ptrtoint (i1 (i32)* @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
|
@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 ptrtoint (i1 (i32)* @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
|
||||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", i32 ptrtoint (i1 (i32)* @"interface:{String:func:{}{basic:string}}.$typeassert" to i32) }
|
|
||||||
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
|
|
||||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
|
|
||||||
@"reflect/types.typeid:basic:int" = external constant i8
|
@"reflect/types.typeid:basic:int" = external constant i8
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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 %runtime._interface @main.simpleType(i8* %context) unnamed_addr #1 {
|
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
%stackalloc = alloca i8, align 1
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
|
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) #6
|
||||||
|
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #1 {
|
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
%stackalloc = alloca i8, align 1
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
|
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) #6
|
||||||
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #1 {
|
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
%stackalloc = alloca i8, align 1
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
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) #6
|
||||||
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #1 {
|
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
%stackalloc = alloca i8, align 1
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
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) #6
|
||||||
|
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #6
|
%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 +75,12 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*) #0
|
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
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"(i32 %itf.typecode) #6
|
%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,10 +90,12 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
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"(i32 %itf.typecode) #6
|
%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
|
||||||
@@ -105,25 +105,28 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
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"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #6
|
%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"(i8*, i32, i32, i8*) #4
|
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(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #6
|
%stackalloc = alloca i8, align 1
|
||||||
|
%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(i8* %1, i8* undef) #6
|
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"(i8*, i32, i8*) #5
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
|||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
; ModuleID = 'intrinsics.go'
|
|
||||||
source_filename = "intrinsics.go"
|
|
||||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
|
||||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = call double @math.Sqrt(double %x, i8* undef) #2
|
|
||||||
ret double %0
|
|
||||||
}
|
|
||||||
|
|
||||||
declare double @math.Sqrt(double, i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = call double @math.Trunc(double %x, i8* undef) #2
|
|
||||||
ret double %0
|
|
||||||
}
|
|
||||||
|
|
||||||
declare double @math.Trunc(double, i8*) #0
|
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
|
||||||
attributes #2 = { nounwind }
|
|
||||||
Vendored
-38
@@ -1,38 +0,0 @@
|
|||||||
; ModuleID = 'intrinsics.go'
|
|
||||||
source_filename = "intrinsics.go"
|
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
|
||||||
target triple = "wasm32-unknown-wasi"
|
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = call double @llvm.sqrt.f64(double %x)
|
|
||||||
ret double %0
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
|
||||||
declare double @llvm.sqrt.f64(double) #2
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
%0 = call double @llvm.trunc.f64(double %x)
|
|
||||||
ret double %0
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
|
||||||
declare double @llvm.trunc.f64(double) #2
|
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #2 = { nofree nosync nounwind readnone speculatable willreturn }
|
|
||||||
Vendored
-14
@@ -1,14 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Test how intrinsics are lowered: either as regular calls to the math
|
|
||||||
// functions or as LLVM builtins (such as llvm.sqrt.f64).
|
|
||||||
|
|
||||||
import "math"
|
|
||||||
|
|
||||||
func mySqrt(x float64) float64 {
|
|
||||||
return math.Sqrt(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func myTrunc(x float64) float64 {
|
|
||||||
return math.Trunc(x)
|
|
||||||
}
|
|
||||||
Vendored
+38
-36
@@ -3,76 +3,78 @@ source_filename = "pointer.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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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 [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #1 {
|
define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret [0 x i32] zeroinitializer
|
ret [0 x i32] zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i8* %x to i32*
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* %x, i8* undef) #2
|
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret i32* %0
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %x to i8*
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret i8* %0
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* %x, i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
ret i8* %x
|
call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.pointerUnsafeGEPFixedOffset(ptr dereferenceable_or_null(1) %ptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
%0 = getelementptr inbounds i8, ptr %ptr, i32 10
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret i8* %0
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.pointerUnsafeGEPByteOffset(ptr dereferenceable_or_null(1) %ptr, i32 %offset, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
%0 = getelementptr inbounds i8, ptr %ptr, i32 %offset
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret i8* %0
|
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
|
define hidden ptr @main.pointerUnsafeGEPIntOffset(ptr dereferenceable_or_null(4) %ptr, i32 %offset, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %ptr to i8*
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%1 = getelementptr i32, i32* %ptr, i32 %offset
|
%0 = shl i32 %offset, 2
|
||||||
%2 = bitcast i32* %1 to i8*
|
%1 = getelementptr inbounds i8, ptr %ptr, i32 %0
|
||||||
call void @runtime.trackPointer(i8* %2, i8* undef) #2
|
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%3 = bitcast i32* %1 to i8*
|
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* %3, i8* undef) #2
|
ret ptr %1
|
||||||
ret i32* %1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
|||||||
Vendored
+10
@@ -3,44 +3,53 @@ package main
|
|||||||
import _ "unsafe"
|
import _ "unsafe"
|
||||||
|
|
||||||
// Creates an external global with name extern_global.
|
// Creates an external global with name extern_global.
|
||||||
|
//
|
||||||
//go:extern extern_global
|
//go:extern extern_global
|
||||||
var externGlobal [0]byte
|
var externGlobal [0]byte
|
||||||
|
|
||||||
// Creates a
|
// Creates a
|
||||||
|
//
|
||||||
//go:align 32
|
//go:align 32
|
||||||
var alignedGlobal [4]uint32
|
var alignedGlobal [4]uint32
|
||||||
|
|
||||||
// Test conflicting pragmas (the last one counts).
|
// Test conflicting pragmas (the last one counts).
|
||||||
|
//
|
||||||
//go:align 64
|
//go:align 64
|
||||||
//go:align 16
|
//go:align 16
|
||||||
var alignedGlobal16 [4]uint32
|
var alignedGlobal16 [4]uint32
|
||||||
|
|
||||||
// Test exported functions.
|
// Test exported functions.
|
||||||
|
//
|
||||||
//export extern_func
|
//export extern_func
|
||||||
func externFunc() {
|
func externFunc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define a function in a different package using go:linkname.
|
// Define a function in a different package using go:linkname.
|
||||||
|
//
|
||||||
//go:linkname withLinkageName1 somepkg.someFunction1
|
//go:linkname withLinkageName1 somepkg.someFunction1
|
||||||
func withLinkageName1() {
|
func withLinkageName1() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import a function from a different package using go:linkname.
|
// Import a function from a different package using go:linkname.
|
||||||
|
//
|
||||||
//go:linkname withLinkageName2 somepkg.someFunction2
|
//go:linkname withLinkageName2 somepkg.someFunction2
|
||||||
func withLinkageName2()
|
func withLinkageName2()
|
||||||
|
|
||||||
// Function has an 'inline hint', similar to the inline keyword in C.
|
// Function has an 'inline hint', similar to the inline keyword in C.
|
||||||
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func inlineFunc() {
|
func inlineFunc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function should never be inlined, equivalent to GCC
|
// Function should never be inlined, equivalent to GCC
|
||||||
// __attribute__((noinline)).
|
// __attribute__((noinline)).
|
||||||
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func noinlineFunc() {
|
func noinlineFunc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function should have the specified section.
|
// This function should have the specified section.
|
||||||
|
//
|
||||||
//go:section .special_function_section
|
//go:section .special_function_section
|
||||||
func functionInSection() {
|
func functionInSection() {
|
||||||
}
|
}
|
||||||
@@ -51,6 +60,7 @@ func exportedFunctionInSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function should not: it's only a declaration and not a definition.
|
// This function should not: it's only a declaration and not a definition.
|
||||||
|
//
|
||||||
//go:section .special_function_section
|
//go:section .special_function_section
|
||||||
func undefinedFunctionNotInSection()
|
func undefinedFunctionNotInSection()
|
||||||
|
|
||||||
|
|||||||
Vendored
+12
-11
@@ -6,16 +6,17 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@extern_global = external global [0 x i8], align 1
|
@extern_global = external global [0 x i8], align 1
|
||||||
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||||
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||||
|
@llvm.used = appending global [2 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection]
|
||||||
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
||||||
@undefinedGlobalNotInSection = external global i32, align 4
|
@undefinedGlobalNotInSection = external global i32, align 4
|
||||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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
|
||||||
}
|
}
|
||||||
@@ -27,27 +28,27 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #1 {
|
define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @somepkg.someFunction2(i8*) #0
|
declare void @somepkg.someFunction2(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: inlinehint nounwind
|
; Function Attrs: inlinehint nounwind
|
||||||
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #3 {
|
define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #4 {
|
define hidden void @main.noinlineFunc(ptr %context) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.functionInSection(i8* %context) unnamed_addr #1 section ".special_function_section" {
|
define hidden void @main.functionInSection(ptr %context) unnamed_addr #1 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
@@ -58,11 +59,11 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.undefinedFunctionNotInSection(i8*) #0
|
declare void @main.undefinedFunctionNotInSection(ptr) #0
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" }
|
||||||
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" }
|
||||||
|
|||||||
Vendored
+35
@@ -1,5 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
func sliceLen(ints []int) int {
|
func sliceLen(ints []int) int {
|
||||||
return len(ints)
|
return len(ints)
|
||||||
}
|
}
|
||||||
@@ -41,3 +43,36 @@ func makeArraySlice(len int) [][3]byte {
|
|||||||
func makeInt32Slice(len int) []int32 {
|
func makeInt32Slice(len int) []int32 {
|
||||||
return make([]int32, len)
|
return make([]int32, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
|
||||||
|
return unsafe.Add(p, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
|
||||||
|
return unsafe.Add(p, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceToArray(s []int) *[4]int {
|
||||||
|
return (*[4]int)(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceToArrayConst() *[4]int {
|
||||||
|
s := make([]int, 6)
|
||||||
|
return (*[4]int)(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceInt(ptr *int, len int) []int {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceUint16(ptr *byte, len uint16) []byte {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceUint64(ptr *int, len uint64) []int {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceInt64(ptr *int, len int64) []int {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+225
-87
@@ -3,183 +3,321 @@ source_filename = "slice.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-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #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 i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.len
|
ret i32 %ints.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.cap
|
ret i32 %ints.cap
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #1 {
|
define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %ints.len
|
%.not = icmp ult i32 %index, %ints.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
lookup.next: ; preds = %entry
|
lookup.next: ; preds = %entry
|
||||||
%0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
|
%0 = getelementptr inbounds i32, ptr %ints.data, i32 %index
|
||||||
%1 = load i32, i32* %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
lookup.throw: ; preds = %entry
|
||||||
call void @runtime.lookupPanic(i8* undef) #2
|
call void @runtime.lookupPanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.lookupPanic(i8*) #0
|
declare void @runtime.lookupPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%stackalloc = alloca i8, align 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #2
|
%varargs = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
%0 = bitcast i8* %varargs to i32*
|
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
store i32 1, i32* %0, align 4
|
store i32 1, ptr %varargs, align 4
|
||||||
%1 = getelementptr inbounds i8, i8* %varargs, i32 4
|
%0 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 1
|
||||||
%2 = bitcast i8* %1 to i32*
|
store i32 2, ptr %0, align 4
|
||||||
store i32 2, i32* %2, align 4
|
%1 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 2
|
||||||
%3 = getelementptr inbounds i8, i8* %varargs, i32 8
|
store i32 3, ptr %1, align 4
|
||||||
%4 = bitcast i8* %3 to i32*
|
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #2
|
||||||
store i32 3, i32* %4, align 4
|
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
||||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
||||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, i8* undef) #2
|
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
||||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
%2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
||||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
%3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1
|
||||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
%4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2
|
||||||
%append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
|
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
ret { ptr, i32, i32 } %4
|
||||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %append.newLen, 1
|
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %append.newCap, 2
|
|
||||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
|
|
||||||
ret { i32*, i32, i32 } %7
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*) #0
|
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context) unnamed_addr #1 {
|
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
%stackalloc = alloca i8, align 1
|
||||||
%append.srcPtr1 = bitcast i32* %added.data to i8*
|
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr undef) #2
|
||||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef) #2
|
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
||||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
||||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
||||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
||||||
%append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1
|
||||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2
|
||||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
|
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
|
ret { ptr, i32, i32 } %2
|
||||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
|
|
||||||
ret { i32*, i32, i32 } %2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
define hidden i32 @main.sliceCopy(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.dstPtr = bitcast i32* %dst.data to i8*
|
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 4, ptr undef) #2
|
||||||
%copy.srcPtr = bitcast i32* %src.data to i8*
|
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef) #2
|
|
||||||
ret i32 %copy.n
|
ret i32 %copy.n
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #1 {
|
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp slt i32 %len, 0
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%makeslice.buf = call ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
ret { i8*, i32, i32 } %2
|
ret { ptr, i32, i32 } %2
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(i8* undef) #2
|
call void @runtime.slicePanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.slicePanic(i8*) #0
|
declare void @runtime.slicePanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #1 {
|
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp slt i32 %len, 0
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = shl i32 %len, 1
|
%makeslice.cap = shl i32 %len, 1
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
ret { ptr, i32, i32 } %2
|
||||||
ret { i16*, i32, i32 } %2
|
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(i8* undef) #2
|
call void @runtime.slicePanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #1 {
|
define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = mul i32 %len, 3
|
%makeslice.cap = mul i32 %len, 3
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
ret { ptr, i32, i32 } %2
|
||||||
ret { [3 x i8]*, i32, i32 } %2
|
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(i8* undef) #2
|
call void @runtime.slicePanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #1 {
|
define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = shl i32 %len, 2
|
%makeslice.cap = shl i32 %len, 2
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
|
||||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
|
||||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
ret { ptr, i32, i32 } %2
|
||||||
ret { i32*, i32, i32 } %2
|
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
slice.throw: ; preds = %entry
|
||||||
call void @runtime.slicePanic(i8* undef) #2
|
call void @runtime.slicePanic(ptr undef) #2
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = getelementptr i8, ptr %p, i32 %len
|
||||||
|
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = trunc i64 %len to i32
|
||||||
|
%1 = getelementptr i8, ptr %p, i32 %0
|
||||||
|
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret ptr %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = icmp ult i32 %s.len, 4
|
||||||
|
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
|
slicetoarray.next: ; preds = %entry
|
||||||
|
ret ptr %s.data
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
call void @runtime.sliceToArrayPointerPanic(ptr undef) #2
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.sliceToArrayPointerPanic(ptr) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%makeslice = call ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2
|
||||||
|
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
|
slicetoarray.next: ; preds = %entry
|
||||||
|
ret ptr %makeslice
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = icmp ugt i32 %len, 1073741823
|
||||||
|
%1 = icmp eq ptr %ptr, null
|
||||||
|
%2 = icmp ne i32 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
|
%6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1
|
||||||
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2
|
||||||
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret { ptr, i32, i32 } %7
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.unsafeSlicePanic(ptr) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = icmp eq ptr %ptr, null
|
||||||
|
%1 = icmp ne i16 %len, 0
|
||||||
|
%2 = and i1 %0, %1
|
||||||
|
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%3 = zext i16 %len to i32
|
||||||
|
%4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
|
%5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1
|
||||||
|
%6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2
|
||||||
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret { ptr, i32, i32 } %6
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
|
%1 = icmp eq ptr %ptr, null
|
||||||
|
%2 = icmp ne i64 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%5 = trunc i64 %len to i32
|
||||||
|
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||||
|
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
||||||
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret { ptr, i32, i32 } %8
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
|
%1 = icmp eq ptr %ptr, null
|
||||||
|
%2 = icmp ne i64 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%5 = trunc i64 %len to i32
|
||||||
|
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||||
|
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
||||||
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2
|
||||||
|
ret { ptr, i32, i32 } %8
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(ptr undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user