mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 490f000f8a | |||
| 7caf0732fa | |||
| edcece33ca | |||
| 30bbdd5aeb | |||
| 03383760d6 | |||
| fce403b7a0 | |||
| fb33f3813d | |||
| 670fcf59d8 | |||
| cceb655874 | |||
| 13891d428f | |||
| 6c9bb96bca | |||
| 403d93560b | |||
| b344d65781 | |||
| c638f03b3c | |||
| 79bdd3f79a | |||
| 39ff13fd1a | |||
| de6f831983 | |||
| fb6571e405 | |||
| 29206cf0a4 | |||
| c2165f74d8 | |||
| 02ef64f012 | |||
| 5792f3a1cf | |||
| f63c389f1a | |||
| 7c24925aa7 | |||
| d7b7583e83 | |||
| 1869efe954 | |||
| 54dd75f7b3 | |||
| 27cbb53538 | |||
| 0704794def | |||
| f24a93c51d | |||
| c454568688 | |||
| d46bf2e5e0 | |||
| 9e1b4de999 | |||
| a4afc3b4b0 | |||
| 90076f9401 | |||
| afd49e7cdd | |||
| 4199be9780 | |||
| 86f1e6aec4 | |||
| f99c600ad8 | |||
| 15d3f5f609 | |||
| c8719f8d14 | |||
| e848f47ad4 | |||
| 3fe7ab19f6 | |||
| b5d61760f7 | |||
| 14bb90c3c0 | |||
| 1645f45c1a | |||
| 38b9c55ae6 | |||
| 497c74e4a9 | |||
| ae864bdf0c | |||
| e50885a6f2 | |||
| 478dd3a28d | |||
| 112b369636 | |||
| 43efe94041 | |||
| b5b2600b7b | |||
| 3f89fa0bee | |||
| a5d905f19b | |||
| d1f1f267a3 | |||
| d21ffc63b9 | |||
| 90f4b0a266 | |||
| a88530b785 | |||
| 18aaed63b9 | |||
| a413d5dfe9 | |||
| 4d5ec6c57b | |||
| 98f84a497d | |||
| 00c73d62ad | |||
| dbfaaf7c13 | |||
| 878b62bbe8 | |||
| 5d8f25a622 | |||
| c7413837aa | |||
| b1ec8eb2e0 | |||
| 98fbf7ff25 | |||
| af00e218a8 | |||
| b31d241388 | |||
| a6246e60f3 | |||
| 0a80da46b1 | |||
| 36f1517e8d | |||
| 04040453b4 | |||
| 2b453db4da | |||
| 29d3e10dc9 | |||
| 06c60b4ecf | |||
| 5c0a337c4f | |||
| 04e8f44370 | |||
| e02727679f | |||
| 138add2b96 | |||
| bf9dab36f7 | |||
| 6234bf9a88 | |||
| 32899d1cc3 | |||
| 0dfb336563 | |||
| d9ad500cf7 | |||
| 49dd2ce393 | |||
| 7df8e8db42 | |||
| 25011c5078 | |||
| 54867f901a | |||
| 1573826005 | |||
| ecd8c2d902 | |||
| a116fd0dc6 | |||
| a590d791bd | |||
| cb147b9475 | |||
| c830f878c6 | |||
| c7c874a487 | |||
| c528a168ee | |||
| 88b9c27dbf | |||
| 37ee4bea40 | |||
| ca4f251050 | |||
| 1e92e5f6c6 | |||
| 3eb9dca695 | |||
| 53794cf339 | |||
| da6c14481f | |||
| fb5516604d | |||
| e9f1ed701a | |||
| 6315db21f7 | |||
| 910cc457ad | |||
| 485a9284e7 | |||
| 602d3d7c78 | |||
| 409688e67a | |||
| d348db4a0d | |||
| 95ab7cb8d1 | |||
| 32de906f6d | |||
| eaab05fc43 | |||
| 4d1945b467 | |||
| fd9422d218 | |||
| bbbe7d43ce | |||
| b3f1dacbb9 | |||
| 5fa1e7163a | |||
| 4b1f92600f | |||
| ca39bc9f35 | |||
| f985f2c376 | |||
| f0936ffccb | |||
| 97d48e5c02 | |||
| a7c53cce06 | |||
| 2d224ae049 | |||
| 6c6fea5387 | |||
| 98bd947817 | |||
| 255f35671d | |||
| 8e88e560a1 | |||
| d45497691f | |||
| ad73986070 | |||
| 931f87f96a | |||
| 192a32f8d9 | |||
| 972f4254eb | |||
| 7d83e2ee5c | |||
| 0f2f73be53 | |||
| a2cc5715ba | |||
| 59d53182bb | |||
| 25c7bfd404 | |||
| 04f520040e | |||
| f57e9622fd | |||
| c25a7cc747 | |||
| 5e5ce98d42 | |||
| a3c4421f39 | |||
| 58565b42cc | |||
| ca7c849da3 | |||
| 6c1301688b | |||
| d05103668f | |||
| 4f7b23c2b7 | |||
| cfae2d4f9a | |||
| 55789fd2c2 | |||
| 478c592b13 | |||
| ab47cea055 | |||
| 044fc1f7a0 | |||
| f029a039ea | |||
| 98e70c9b19 | |||
| 7434e5a2c7 | |||
| 65c1978965 | |||
| e834d78871 | |||
| 03481789b0 | |||
| 73cf187552 | |||
| 025f2fe7e4 | |||
| e9ddbdf4a8 | |||
| 43d5eee1e6 | |||
| 0ff290cecc | |||
| eb705f0d4f | |||
| 733da1226d | |||
| 84e61c183a | |||
| 1c2463f0b0 | |||
| df57c029a7 |
+16
-35
@@ -17,18 +17,15 @@ commands:
|
|||||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install \
|
sudo apt-get install --no-install-recommends \
|
||||||
llvm-<<parameters.llvm>>-dev \
|
llvm-<<parameters.llvm>>-dev \
|
||||||
clang-<<parameters.llvm>> \
|
clang-<<parameters.llvm>> \
|
||||||
libclang-<<parameters.llvm>>-dev \
|
libclang-<<parameters.llvm>>-dev \
|
||||||
lld-<<parameters.llvm>> \
|
lld-<<parameters.llvm>> \
|
||||||
gcc-arm-linux-gnueabihf \
|
|
||||||
gcc-aarch64-linux-gnu \
|
|
||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
gcc-avr \
|
gcc-avr \
|
||||||
avr-libc
|
avr-libc
|
||||||
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-8-dev
|
|
||||||
install-node:
|
install-node:
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
@@ -117,7 +114,10 @@ commands:
|
|||||||
key: wasi-libc-sysroot-systemclang-v3
|
key: wasi-libc-sysroot-systemclang-v3
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
|
- run:
|
||||||
|
name: "Test TinyGo"
|
||||||
|
command: go test -v -timeout=20m -tags=llvm<<parameters.llvm>> ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
no_output_timeout: 20m
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest XTENSA=0
|
- run: make smoketest XTENSA=0
|
||||||
- run: make tinygo-test
|
- run: make tinygo-test
|
||||||
@@ -136,16 +136,12 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install \
|
sudo apt-get install --no-install-recommends \
|
||||||
gcc-arm-linux-gnueabihf \
|
libgnutls30 libssl1.0.2 \
|
||||||
libc6-dev-armel-cross \
|
|
||||||
gcc-aarch64-linux-gnu \
|
|
||||||
libc6-dev-arm64-cross \
|
|
||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
gcc-avr \
|
gcc-avr \
|
||||||
avr-libc
|
avr-libc
|
||||||
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev
|
|
||||||
- install-node
|
- install-node
|
||||||
- install-wasmtime
|
- install-wasmtime
|
||||||
- install-xtensa-toolchain:
|
- install-xtensa-toolchain:
|
||||||
@@ -184,6 +180,7 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make ASSERT=1 test
|
command: make ASSERT=1 test
|
||||||
|
no_output_timeout: 20m
|
||||||
environment:
|
environment:
|
||||||
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
||||||
# necessary to keep memory consumption down and avoid OOM (for a
|
# necessary to keep memory consumption down and avoid OOM (for a
|
||||||
@@ -204,16 +201,12 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install \
|
sudo apt-get install --no-install-recommends \
|
||||||
gcc-arm-linux-gnueabihf \
|
libgnutls30 libssl1.0.2 \
|
||||||
libc6-dev-armel-cross \
|
|
||||||
gcc-aarch64-linux-gnu \
|
|
||||||
libc6-dev-arm64-cross \
|
|
||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
gcc-avr \
|
gcc-avr \
|
||||||
avr-libc
|
avr-libc
|
||||||
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev
|
|
||||||
- install-node
|
- install-node
|
||||||
- install-wasmtime
|
- install-wasmtime
|
||||||
- install-xtensa-toolchain:
|
- install-xtensa-toolchain:
|
||||||
@@ -251,6 +244,7 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
|
no_output_timeout: 20m
|
||||||
- run:
|
- run:
|
||||||
name: "Install fpm"
|
name: "Install fpm"
|
||||||
command: |
|
command: |
|
||||||
@@ -286,8 +280,8 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Install dependencies"
|
name: "Install dependencies"
|
||||||
command: |
|
command: |
|
||||||
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
|
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
||||||
sudo tar -C /usr/local -xzf go1.16.darwin-amd64.tar.gz
|
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
|
||||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||||
- install-xtensa-toolchain:
|
- install-xtensa-toolchain:
|
||||||
@@ -343,6 +337,7 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
|
no_output_timeout: 20m
|
||||||
- run:
|
- run:
|
||||||
name: "Build TinyGo release"
|
name: "Build TinyGo release"
|
||||||
command: |
|
command: |
|
||||||
@@ -365,18 +360,6 @@ commands:
|
|||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm10-go113:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.13-buster
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "10"
|
|
||||||
test-llvm10-go114:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.14-buster
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "10"
|
|
||||||
test-llvm11-go115:
|
test-llvm11-go115:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.15-buster
|
- image: circleci/golang:1.15-buster
|
||||||
@@ -391,12 +374,12 @@ jobs:
|
|||||||
llvm: "11"
|
llvm: "11"
|
||||||
assert-test-linux:
|
assert-test-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.14-stretch
|
- image: circleci/golang:1.17-stretch
|
||||||
steps:
|
steps:
|
||||||
- assert-test-linux
|
- assert-test-linux
|
||||||
build-linux:
|
build-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.14-stretch
|
- image: circleci/golang:1.17-stretch
|
||||||
steps:
|
steps:
|
||||||
- build-linux
|
- build-linux
|
||||||
build-macos:
|
build-macos:
|
||||||
@@ -410,8 +393,6 @@ jobs:
|
|||||||
workflows:
|
workflows:
|
||||||
test-all:
|
test-all:
|
||||||
jobs:
|
jobs:
|
||||||
- test-llvm10-go113
|
|
||||||
- test-llvm10-go114
|
|
||||||
- test-llvm11-go115
|
- test-llvm11-go115
|
||||||
- test-llvm11-go116
|
- test-llvm11-go116
|
||||||
- build-linux
|
- build-linux
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
build/
|
||||||
|
llvm-*/
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: CI for tinygo-dev docker container
|
name: CI for tinygo-dev docker container
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ dev ]
|
branches: [ dev, fix-docker-llvm-build ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push_to_registry:
|
push_to_registry:
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
name: Windows
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-2016
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.17'
|
||||||
|
- name: Install QEMU
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
choco install qemu --version=2020.06.12
|
||||||
|
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Cache LLVM source
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-source
|
||||||
|
with:
|
||||||
|
key: llvm-source-11-windows-v1
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Download LLVM source
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
run: make llvm-source
|
||||||
|
- name: Cache LLVM build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-build
|
||||||
|
with:
|
||||||
|
key: llvm-build-11-windows-v2
|
||||||
|
path: llvm-build
|
||||||
|
- name: Build LLVM
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# fetch LLVM source
|
||||||
|
rm -rf llvm-project
|
||||||
|
make llvm-source
|
||||||
|
# install dependencies
|
||||||
|
choco install ninja
|
||||||
|
# build!
|
||||||
|
make llvm-build
|
||||||
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
|
- name: Cache wasi-libc sysroot
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-wasi-libc
|
||||||
|
with:
|
||||||
|
key: wasi-libc-sysroot-v1
|
||||||
|
path: lib/wasi-libc/sysroot
|
||||||
|
- name: Build wasi-libc
|
||||||
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
|
run: make wasi-libc
|
||||||
|
- name: Test TinyGo
|
||||||
|
shell: bash
|
||||||
|
run: make test
|
||||||
|
- name: Build TinyGo release tarball
|
||||||
|
run: make build/release -j4
|
||||||
|
- name: Make release artifact
|
||||||
|
shell: bash
|
||||||
|
working-directory: build/release
|
||||||
|
run: 7z -tzip a release.zip tinygo
|
||||||
|
- name: Publish release artifact
|
||||||
|
# Note: this release artifact is double-zipped, see:
|
||||||
|
# https://github.com/actions/upload-artifact/issues/39
|
||||||
|
# We can essentially pick one of these:
|
||||||
|
# - have a dobule-zipped artifact when downloaded from the UI
|
||||||
|
# - have a very slow artifact upload
|
||||||
|
# We're doing the former here, to keep artifact uploads fast.
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: release-double-zipped
|
||||||
|
path: build/release/release.zip
|
||||||
|
- name: Smoke tests
|
||||||
|
shell: bash
|
||||||
|
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
||||||
@@ -23,3 +23,6 @@
|
|||||||
[submodule "lib/stm32-svd"]
|
[submodule "lib/stm32-svd"]
|
||||||
path = lib/stm32-svd
|
path = lib/stm32-svd
|
||||||
url = https://github.com/tinygo-org/stm32-svd
|
url = https://github.com/tinygo-org/stm32-svd
|
||||||
|
[submodule "lib/musl"]
|
||||||
|
path = lib/musl
|
||||||
|
url = git://git.musl-libc.org/musl
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ different guide:
|
|||||||
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.13+)
|
* Go (1.15+)
|
||||||
* Standard build tools (gcc/clang)
|
* Standard build tools (gcc/clang)
|
||||||
* git
|
* git
|
||||||
* CMake
|
* CMake
|
||||||
|
|||||||
@@ -1,3 +1,73 @@
|
|||||||
|
0.20.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **command line**
|
||||||
|
- add support for Go 1.17
|
||||||
|
- improve Go version detection
|
||||||
|
- add support for the Black Magic Probe (BMP)
|
||||||
|
- add a flag for creating cpu profiles
|
||||||
|
* **compiler**
|
||||||
|
- `builder:` list libraries at the end of the linker command
|
||||||
|
- `builder:` strip debug information at link time instead of at compile time
|
||||||
|
- `builder:` add missing error check for `ioutil.TempFile()`
|
||||||
|
- `builder:` simplify running of jobs
|
||||||
|
- `compiler:` move LLVM math builtin support into the compiler
|
||||||
|
- `compiler:` move math aliases from the runtime to the compiler
|
||||||
|
- `compiler:` add aliases for many hashing packages
|
||||||
|
- `compiler:` add `*ssa.MakeSlice` bounds tests
|
||||||
|
- `compiler:` fix max possible slice
|
||||||
|
- `compiler:` add support for new language features of Go 1.17
|
||||||
|
- `compiler:` fix equally named structs in different scopes
|
||||||
|
- `compiler:` avoid zero-sized alloca in channel operations
|
||||||
|
- `interp:` don't ignore array indices for untyped objects
|
||||||
|
- `interp:` keep reverted package initializers in order
|
||||||
|
- `interp:` fix bug in compiler-time/run-time package initializers
|
||||||
|
- `loader:` fix panic in CGo files with syntax errors
|
||||||
|
- `transform:` improve GC stack slot pass to work around a bug
|
||||||
|
* **standard library**
|
||||||
|
- `crypto/rand`: switch to `arc4random_buf`
|
||||||
|
- `math:` fix `math.Max` and `math.Min`
|
||||||
|
- `math/big`: fix undefined symbols error
|
||||||
|
- `net:` add MAC address implementation
|
||||||
|
- `os:` implement `os.Executable`
|
||||||
|
- `os:` add `SEEK_SET`, `SEEK_CUR`, and `SEEK_END`
|
||||||
|
- `reflect:` add StructField.IsExported method
|
||||||
|
- `runtime:` reset heapptr to heapStart after preinit()
|
||||||
|
- `runtime:` add `subsections_via_symbols` to assembly files on darwin
|
||||||
|
- `testing:` add subset implementation of Benchmark
|
||||||
|
- `testing:` test testing package using `tinygo test`
|
||||||
|
- `testing:` add support for the `-test.v` flag
|
||||||
|
* **targets**
|
||||||
|
- `386:` bump minimum requirement to the Pentium 4
|
||||||
|
- `arm:` switch to Thumb instruction set on ARM
|
||||||
|
- `atsamd:` fix copy-paste error for atsamd21/51 calibTrim block
|
||||||
|
- `baremetal`,`wasm`: support command line params and environment variables
|
||||||
|
- `cortexm:` fix stack overflow because of unaligned stacks
|
||||||
|
- `esp32c3:` add support for the ESP32-C3 from Espressif
|
||||||
|
- `nrf52840:` fix ram size
|
||||||
|
- `nxpmk66f18:` fix a suspicious bitwise operation
|
||||||
|
- `rp2040:` add SPI support
|
||||||
|
- `rp2040:` add I2C support
|
||||||
|
- `rp2040:` add PWM implementation
|
||||||
|
- `rp2040:` add openocd configuration
|
||||||
|
- `stm32:` add support for PortMask* functions for WS2812 support
|
||||||
|
- `unix:` fix time base for time.Now()
|
||||||
|
- `unix:` check for mmap error and act accordingly
|
||||||
|
- `wasm:` override dlmalloc heap implementation from wasi-libc
|
||||||
|
- `wasm:` align heap to 16 bytes
|
||||||
|
- `wasm:` add support for the crypto/rand package
|
||||||
|
* **boards**
|
||||||
|
- add `DefaultUART` to adafruit boards
|
||||||
|
- `arduino-mkrwifi1010:` add board definition for Arduino MKR WiFi 1010
|
||||||
|
- `arduino-mkrwifi1010:` fix pin definition of `NINA_RESETN`
|
||||||
|
- `feather-nrf52:` fix pin definition of uart
|
||||||
|
- `feather-rp2040:` add pin name definition
|
||||||
|
- `gameboy-advance:` fix ROM header
|
||||||
|
- `mdbt50qrx-uf2:` add Raytac MDBT50Q-RX Dongle with TinyUF2
|
||||||
|
- `nano-rp2040:` define `NINA_SPI` and fix wifinina pins
|
||||||
|
- `teensy40:` enable hardware UART reconfiguration, fix receive watermark interrupt
|
||||||
|
|
||||||
|
|
||||||
0.19.0
|
0.19.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
# TinyGo base stage installs the most recent Go 1.16.x, LLVM 11 and the TinyGo compiler itself.
|
# TinyGo base stage installs the most recent Go 1.17.x, LLVM 11 and the TinyGo compiler itself.
|
||||||
FROM golang:1.16 AS tinygo-base
|
FROM golang:1.17 AS tinygo-base
|
||||||
|
|
||||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
|
echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-11 main" >> /etc/apt/sources.list && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ RUN cd /tinygo/ && \
|
|||||||
git submodule sync && \
|
git submodule sync && \
|
||||||
git submodule update --init --recursive --force
|
git submodule update --init --recursive --force
|
||||||
|
|
||||||
COPY ./lib/picolibc-include/* /tinygo/lib/picolibc-include/
|
COPY ./lib/picolibc-stdio.c /tinygo/lib/picolibc-stdio.c
|
||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
go install /tinygo/
|
go install /tinygo/
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
|||||||
|
|
||||||
# Try to autodetect LLVM build tools.
|
# Try to autodetect LLVM build tools.
|
||||||
detect = $(shell command -v $(1) 2> /dev/null && echo $(1))
|
detect = $(shell command -v $(1) 2> /dev/null && echo $(1))
|
||||||
CLANG ?= $(word 1,$(abspath $(call detect,llvm-build/bin/clang))$(call detect,clang-11)$(call detect,clang-10)$(call detect,clang))
|
CLANG ?= $(word 1,$(abspath $(call detect,llvm-build/bin/clang))$(call detect,clang-11)$(call detect,clang))
|
||||||
LLVM_AR ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-ar))$(call detect,llvm-ar-11)$(call detect,llvm-ar-10)$(call detect,llvm-ar))
|
LLVM_AR ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-ar))$(call detect,llvm-ar-11)$(call detect,llvm-ar))
|
||||||
LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detect,llvm-nm-11)$(call detect,llvm-nm-10)$(call detect,llvm-nm))
|
LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detect,llvm-nm-11)$(call detect,llvm-nm))
|
||||||
|
|
||||||
# Go binary and GOROOT to select
|
# Go binary and GOROOT to select
|
||||||
GO ?= go
|
GO ?= go
|
||||||
@@ -100,7 +100,7 @@ endif
|
|||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
||||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||||
fmt:
|
fmt:
|
||||||
@gofmt -l -w $(FMT_PATHS)
|
@gofmt -l -w $(FMT_PATHS)
|
||||||
fmt-check:
|
fmt-check:
|
||||||
@@ -124,6 +124,7 @@ build/gen-device-svd: ./tools/gen-device-svd/*.go
|
|||||||
|
|
||||||
gen-device-esp: build/gen-device-svd
|
gen-device-esp: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
||||||
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif -interrupts=software lib/cmsis-svd/data/Espressif/ src/device/esp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/esp
|
GO111MODULE=off $(GO) fmt ./src/device/esp
|
||||||
|
|
||||||
gen-device-nrf: build/gen-device-svd
|
gen-device-nrf: build/gen-device-svd
|
||||||
@@ -174,7 +175,7 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
|||||||
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_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG" WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
||||||
|
|
||||||
|
|
||||||
# Build the Go compiler.
|
# Build the Go compiler.
|
||||||
@@ -183,23 +184,37 @@ tinygo:
|
|||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||||
|
|
||||||
test: wasi-libc
|
test: wasi-libc
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
|
||||||
TEST_PACKAGES = \
|
TEST_PACKAGES = \
|
||||||
|
compress/bzip2 \
|
||||||
container/heap \
|
container/heap \
|
||||||
container/list \
|
container/list \
|
||||||
container/ring \
|
container/ring \
|
||||||
crypto/des \
|
crypto/des \
|
||||||
|
crypto/dsa \
|
||||||
|
crypto/md5 \
|
||||||
|
crypto/rc4 \
|
||||||
|
crypto/sha1 \
|
||||||
|
crypto/sha256 \
|
||||||
|
crypto/sha512 \
|
||||||
encoding \
|
encoding \
|
||||||
encoding/ascii85 \
|
encoding/ascii85 \
|
||||||
encoding/base32 \
|
encoding/base32 \
|
||||||
encoding/hex \
|
encoding/hex \
|
||||||
|
hash \
|
||||||
hash/adler32 \
|
hash/adler32 \
|
||||||
hash/fnv \
|
hash/fnv \
|
||||||
hash/crc64 \
|
hash/crc64 \
|
||||||
|
html \
|
||||||
|
index/suffixarray \
|
||||||
math \
|
math \
|
||||||
math/cmplx \
|
math/cmplx \
|
||||||
|
testing \
|
||||||
|
testing/iotest \
|
||||||
text/scanner \
|
text/scanner \
|
||||||
|
text/scanner \
|
||||||
|
unicode \
|
||||||
unicode/utf8 \
|
unicode/utf8 \
|
||||||
|
|
||||||
# Test known-working standard library packages.
|
# Test known-working standard library packages.
|
||||||
@@ -243,17 +258,17 @@ smoketest:
|
|||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
# test simulated boards on play.tinygo.org
|
# test simulated boards on play.tinygo.org
|
||||||
$(TINYGO) build -o test.wasm -tags=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=hifive1b examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=reelboard examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=reelboard examples/blinky1
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=pca10040 examples/blinky2
|
$(TINYGO) build -size short -o test.wasm -tags=pca10040 examples/blinky2
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
$(TINYGO) build -size short -o test.wasm -tags=pca10056 examples/blinky2
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=circuitplay_express examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
# test all targets/boards
|
# test all targets/boards
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||||
@@ -358,6 +373,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=arduino-mkrwifi1010 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||||
@@ -424,15 +441,19 @@ ifneq ($(XTENSA), 0)
|
|||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
|
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/serial
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
endif
|
endif
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=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
|
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
||||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
# test various compiler flags
|
# test various compiler flags
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -444,6 +465,10 @@ endif
|
|||||||
@$(MD5SUM) test.nro
|
@$(MD5SUM) test.nro
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
|
ifneq ($(OS),Windows_NT)
|
||||||
|
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
wasmtest:
|
wasmtest:
|
||||||
@@ -454,12 +479,16 @@ build/release: tinygo gen-device wasi-libc
|
|||||||
@mkdir -p build/release/tinygo/lib/clang/include
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||||
|
@mkdir -p build/release/tinygo/lib/musl/arch
|
||||||
|
@mkdir -p build/release/tinygo/lib/musl/crt
|
||||||
|
@mkdir -p build/release/tinygo/lib/musl/src
|
||||||
@mkdir -p build/release/tinygo/lib/nrfx
|
@mkdir -p build/release/tinygo/lib/nrfx
|
||||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
|
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
@mkdir -p build/release/tinygo/lib/wasi-libc
|
@mkdir -p build/release/tinygo/lib/wasi-libc
|
||||||
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv6m-unknown-unknown-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv7m-unknown-unknown-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv7em-unknown-unknown-eabi
|
||||||
@echo copying source files
|
@echo copying source files
|
||||||
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
|
||||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||||
@@ -468,22 +497,44 @@ build/release: tinygo gen-device wasi-libc
|
|||||||
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
||||||
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
||||||
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
||||||
|
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
|
||||||
|
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||||
|
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
|
||||||
|
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
|
||||||
|
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
|
||||||
|
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||||
|
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||||
|
@cp -rp lib/musl/include build/release/tinygo/lib/musl
|
||||||
|
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/internal 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/signal build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
||||||
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@cp -rp lib/picolibc/newlib/libc/locale build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/locale 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/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-include build/release/tinygo/lib
|
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
|
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||||
@cp -rp src build/release/tinygo/src
|
@cp -rp src build/release/tinygo/src
|
||||||
@cp -rp targets build/release/tinygo/targets
|
@cp -rp targets build/release/tinygo/targets
|
||||||
./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a compiler-rt
|
./build/tinygo build-library -target=armv6m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv6m-unknown-unknown-eabi/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/compiler-rt.a compiler-rt
|
./build/tinygo build-library -target=armv7m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7m-unknown-unknown-eabi/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/compiler-rt.a compiler-rt
|
./build/tinygo build-library -target=armv7em-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7em-unknown-unknown-eabi/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/picolibc.a picolibc
|
./build/tinygo build-library -target=armv6m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv6m-unknown-unknown-eabi/picolibc picolibc
|
||||||
./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/picolibc.a picolibc
|
./build/tinygo build-library -target=armv7m-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7m-unknown-unknown-eabi/picolibc picolibc
|
||||||
./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/picolibc.a picolibc
|
./build/tinygo build-library -target=armv7em-unknown-unknown-eabi -o build/release/tinygo/pkg/armv7em-unknown-unknown-eabi/picolibc picolibc
|
||||||
|
|
||||||
release: build/release
|
release: build/release
|
||||||
tar -czf build/release.tar.gz -C build/release tinygo
|
tar -czf build/release.tar.gz -C build/release tinygo
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ 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 68 microcontroller boards are currently supported:
|
The following 71 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)
|
||||||
@@ -69,6 +69,7 @@ The following 68 microcontroller boards are currently supported:
|
|||||||
* [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)
|
||||||
|
* [Arduino MKR WiFi 1010](https://store.arduino.cc/usa/mkr-wifi-1010)
|
||||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||||
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||||
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||||
@@ -83,6 +84,7 @@ The following 68 microcontroller boards are currently supported:
|
|||||||
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
|
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
|
||||||
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
|
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
|
||||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||||
|
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
|
||||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||||
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
|
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
|
||||||
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
|
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
|
||||||
@@ -101,6 +103,7 @@ The following 68 microcontroller boards are currently supported:
|
|||||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||||
|
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.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)
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
# Avoid lengthy LLVM rebuilds on each newly pushed branch. Pull requests will
|
|
||||||
# be built anyway.
|
|
||||||
trigger:
|
|
||||||
- release
|
|
||||||
- dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: Build
|
|
||||||
timeoutInMinutes: 240 # 4h
|
|
||||||
pool:
|
|
||||||
vmImage: 'VS2017-Win2016'
|
|
||||||
steps:
|
|
||||||
- task: GoTool@0
|
|
||||||
inputs:
|
|
||||||
version: '1.16'
|
|
||||||
- checkout: self
|
|
||||||
fetchDepth: 1
|
|
||||||
- task: Cache@2
|
|
||||||
displayName: Cache LLVM source
|
|
||||||
inputs:
|
|
||||||
key: llvm-source-11-windows-v1
|
|
||||||
path: llvm-project
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Download LLVM source
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: make llvm-source
|
|
||||||
- task: CacheBeta@0
|
|
||||||
displayName: Cache LLVM build
|
|
||||||
inputs:
|
|
||||||
key: llvm-build-11-windows-v5
|
|
||||||
path: llvm-build
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Build LLVM
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: |
|
|
||||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
|
||||||
then
|
|
||||||
# install dependencies
|
|
||||||
choco install ninja
|
|
||||||
# hack ninja to use fewer jobs
|
|
||||||
echo -e 'C:\\ProgramData\\Chocolatey\\bin\\ninja -j4 %*' > /usr/bin/ninja.bat
|
|
||||||
# build!
|
|
||||||
make llvm-build
|
|
||||||
fi
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Install QEMU
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: choco install qemu --version=2020.06.12
|
|
||||||
- task: CacheBeta@0
|
|
||||||
displayName: Cache wasi-libc sysroot
|
|
||||||
inputs:
|
|
||||||
key: wasi-libc-sysroot-v4
|
|
||||||
path: lib/wasi-libc/sysroot
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Build wasi-libc
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: PATH=/usr/bin:$PATH make wasi-libc
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Test TinyGo
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: |
|
|
||||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
|
||||||
unset GOROOT
|
|
||||||
make test
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Build TinyGo release tarball
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: |
|
|
||||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
|
||||||
unset GOROOT
|
|
||||||
make build/release -j4
|
|
||||||
- publish: $(System.DefaultWorkingDirectory)/build/release/tinygo
|
|
||||||
displayName: Publish zip as artifact
|
|
||||||
artifact: tinygo
|
|
||||||
- task: Bash@3
|
|
||||||
displayName: Smoke tests
|
|
||||||
inputs:
|
|
||||||
targetType: inline
|
|
||||||
script: |
|
|
||||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
|
||||||
unset GOROOT
|
|
||||||
make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
|
||||||
+25
-25
@@ -5,6 +5,7 @@ import (
|
|||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -17,17 +18,12 @@ import (
|
|||||||
// 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(archivePath string, objs []string) error {
|
func makeArchive(arfile *os.File, objs []string) error {
|
||||||
// Open the archive file.
|
// Open the archive file.
|
||||||
arfile, err := os.Create(archivePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer arfile.Close()
|
|
||||||
arwriter := ar.NewWriter(arfile)
|
arwriter := ar.NewWriter(arfile)
|
||||||
err = arwriter.WriteGlobalHeader()
|
err := arwriter.WriteGlobalHeader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &os.PathError{"write ar header", archivePath, err}
|
return &os.PathError{Op: "write ar header", Path: arfile.Name(), Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open all object files and read the symbols for the symbol table.
|
// Open all object files and read the symbols for the symbol table.
|
||||||
@@ -35,21 +31,17 @@ func makeArchive(archivePath string, objs []string) error {
|
|||||||
name string // symbol name
|
name string // symbol name
|
||||||
fileIndex int // index into objfiles
|
fileIndex int // index into objfiles
|
||||||
}{}
|
}{}
|
||||||
objfiles := make([]struct {
|
archiveOffsets := make([]int32, len(objs))
|
||||||
file *os.File
|
|
||||||
archiveOffset int32
|
|
||||||
}, len(objs))
|
|
||||||
for i, objpath := range objs {
|
for i, objpath := range objs {
|
||||||
objfile, err := os.Open(objpath)
|
objfile, err := os.Open(objpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
objfiles[i].file = objfile
|
|
||||||
|
|
||||||
// Read the symbols and add them to the symbol table.
|
// Read the symbols and add them to the symbol table.
|
||||||
dbg, err := elf.NewFile(objfile)
|
dbg, err := elf.NewFile(objfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to open file %s: %w", objpath, err)
|
||||||
}
|
}
|
||||||
symbols, err := dbg.Symbols()
|
symbols, err := dbg.Symbols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -61,9 +53,8 @@ func makeArchive(archivePath string, objs []string) error {
|
|||||||
// Don't include local symbols (STB_LOCAL).
|
// Don't include local symbols (STB_LOCAL).
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC {
|
if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC && elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT {
|
||||||
// Not a function.
|
// Not a function.
|
||||||
// TODO: perhaps globals variables should also be included?
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Include in archive.
|
// Include in archive.
|
||||||
@@ -72,6 +63,10 @@ func makeArchive(archivePath string, objs []string) error {
|
|||||||
fileIndex int
|
fileIndex int
|
||||||
}{symbol.Name, i})
|
}{symbol.Name, i})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close file, to avoid issues with too many open files (especially on
|
||||||
|
// MacOS X).
|
||||||
|
objfile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the symbol table buffer.
|
// Create the symbol table buffer.
|
||||||
@@ -125,7 +120,12 @@ func makeArchive(archivePath string, objs []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add all object files to the archive.
|
// Add all object files to the archive.
|
||||||
for i, objfile := range objfiles {
|
for i, objpath := range objs {
|
||||||
|
objfile, err := os.Open(objpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Store the start index, for when we'll update the symbol table with
|
// Store the start index, for when we'll update the symbol table with
|
||||||
// the correct file start indices.
|
// the correct file start indices.
|
||||||
offset, err := arfile.Seek(0, os.SEEK_CUR)
|
offset, err := arfile.Seek(0, os.SEEK_CUR)
|
||||||
@@ -133,17 +133,17 @@ func makeArchive(archivePath string, objs []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if int64(int32(offset)) != offset {
|
if int64(int32(offset)) != offset {
|
||||||
return errors.New("large archives (4GB+) not supported: " + archivePath)
|
return errors.New("large archives (4GB+) not supported: " + arfile.Name())
|
||||||
}
|
}
|
||||||
objfiles[i].archiveOffset = int32(offset)
|
archiveOffsets[i] = int32(offset)
|
||||||
|
|
||||||
// Write the file header.
|
// Write the file header.
|
||||||
st, err := objfile.file.Stat()
|
st, err := objfile.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = arwriter.WriteHeader(&ar.Header{
|
err = arwriter.WriteHeader(&ar.Header{
|
||||||
Name: filepath.Base(objfile.file.Name()),
|
Name: filepath.Base(objfile.Name()),
|
||||||
ModTime: time.Unix(0, 0),
|
ModTime: time.Unix(0, 0),
|
||||||
Uid: 0,
|
Uid: 0,
|
||||||
Gid: 0,
|
Gid: 0,
|
||||||
@@ -155,22 +155,22 @@ func makeArchive(archivePath string, objs []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy the file contents into the archive.
|
// Copy the file contents into the archive.
|
||||||
n, err := io.Copy(arwriter, objfile.file)
|
n, err := io.Copy(arwriter, objfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if n != st.Size() {
|
if n != st.Size() {
|
||||||
return errors.New("file modified during ar creation: " + archivePath)
|
return errors.New("file modified during ar creation: " + arfile.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
// File is not needed anymore.
|
// File is not needed anymore.
|
||||||
objfile.file.Close()
|
objfile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create symbol indices.
|
// Create symbol indices.
|
||||||
indicesBuf := &bytes.Buffer{}
|
indicesBuf := &bytes.Buffer{}
|
||||||
for _, sym := range symbolTable {
|
for _, sym := range symbolTable {
|
||||||
err = binary.Write(indicesBuf, binary.BigEndian, objfiles[sym.fileIndex].archiveOffset)
|
err = binary.Write(indicesBuf, binary.BigEndian, archiveOffsets[sym.fileIndex])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+137
-57
@@ -21,6 +21,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/compiler"
|
"github.com/tinygo-org/tinygo/compiler"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
@@ -60,6 +61,7 @@ type BuildResult struct {
|
|||||||
// implementation of an imported package changes.
|
// implementation of an imported package changes.
|
||||||
type packageAction struct {
|
type packageAction struct {
|
||||||
ImportPath string
|
ImportPath string
|
||||||
|
CGoVersion int // cgo.Version
|
||||||
CompilerVersion int // compiler.Version
|
CompilerVersion int // compiler.Version
|
||||||
InterpVersion int // interp.Version
|
InterpVersion int // interp.Version
|
||||||
LLVMVersion string
|
LLVMVersion string
|
||||||
@@ -86,6 +88,38 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
// Check for a libc dependency.
|
||||||
|
// As a side effect, this also creates the headers for the given libc, if
|
||||||
|
// the libc needs them.
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
var libcDependencies []*compileJob
|
||||||
|
switch config.Target.Libc {
|
||||||
|
case "musl":
|
||||||
|
job, err := Musl.load(config, dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
|
||||||
|
libcDependencies = append(libcDependencies, job)
|
||||||
|
case "picolibc":
|
||||||
|
libcJob, err := Picolibc.load(config, dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
|
case "wasi-libc":
|
||||||
|
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||||
|
}
|
||||||
|
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
||||||
|
case "":
|
||||||
|
// no library specified, so nothing to do
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||||
|
}
|
||||||
|
|
||||||
|
optLevel, sizeLevel, _ := config.OptLevels()
|
||||||
compilerConfig := &compiler.Config{
|
compilerConfig := &compiler.Config{
|
||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
CPU: config.CPU(),
|
CPU: config.CPU(),
|
||||||
@@ -94,13 +128,14 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
GOARCH: config.GOARCH(),
|
GOARCH: config.GOARCH(),
|
||||||
CodeModel: config.CodeModel(),
|
CodeModel: config.CodeModel(),
|
||||||
RelocationModel: config.RelocationModel(),
|
RelocationModel: config.RelocationModel(),
|
||||||
|
SizeLevel: sizeLevel,
|
||||||
|
|
||||||
Scheduler: config.Scheduler(),
|
Scheduler: config.Scheduler(),
|
||||||
FuncImplementation: config.FuncImplementation(),
|
FuncImplementation: config.FuncImplementation(),
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.Target.DefaultStackSize,
|
DefaultStackSize: config.Target.DefaultStackSize,
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: config.Debug(),
|
Debug: true,
|
||||||
LLVMFeatures: config.LLVMFeatures(),
|
LLVMFeatures: config.LLVMFeatures(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +159,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The slice of jobs that orchestrates most of the build.
|
|
||||||
// This is somewhat like an in-memory Makefile with each job being a
|
|
||||||
// Makefile target.
|
|
||||||
var jobs []*compileJob
|
|
||||||
|
|
||||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// program so it's pretty fast and doesn't need to be parallelized.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
@@ -138,7 +168,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
var packageJobs []*compileJob
|
var packageJobs []*compileJob
|
||||||
packageBitcodePaths := make(map[string]string)
|
packageBitcodePaths := make(map[string]string)
|
||||||
packageActionIDs := make(map[string]string)
|
packageActionIDs := make(map[string]string)
|
||||||
optLevel, sizeLevel, _ := config.OptLevels()
|
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg // necessary to avoid a race condition
|
pkg := pkg // necessary to avoid a race condition
|
||||||
|
|
||||||
@@ -152,6 +181,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// the parameters for the build.
|
// the parameters for the build.
|
||||||
actionID := packageAction{
|
actionID := packageAction{
|
||||||
ImportPath: pkg.ImportPath,
|
ImportPath: pkg.ImportPath,
|
||||||
|
CGoVersion: cgo.Version,
|
||||||
CompilerVersion: compiler.Version,
|
CompilerVersion: compiler.Version,
|
||||||
InterpVersion: interp.Version,
|
InterpVersion: interp.Version,
|
||||||
LLVMVersion: llvm.Version,
|
LLVMVersion: llvm.Version,
|
||||||
@@ -212,6 +242,50 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load bitcode of CGo headers and join the modules together.
|
||||||
|
// This may seem vulnerable to cache problems, but this is not
|
||||||
|
// the case: the Go code that was just compiled already tracks
|
||||||
|
// all C files that are read and hashes them.
|
||||||
|
// These headers could be compiled in parallel but the benefit
|
||||||
|
// is so small that it's probably not worth parallelizing.
|
||||||
|
// Packages are compiled independently anyway.
|
||||||
|
for _, cgoHeader := range pkg.CGoHeaders {
|
||||||
|
// Store the header text in a temporary file.
|
||||||
|
f, err := ioutil.TempFile(dir, "cgosnippet-*.c")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.Write([]byte(cgoHeader))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
// Compile the code (if there is any) to bitcode.
|
||||||
|
flags := append([]string{"-c", "-emit-llvm", "-o", f.Name() + ".bc", f.Name()}, pkg.CFlags...)
|
||||||
|
if config.Options.PrintCommands != nil {
|
||||||
|
config.Options.PrintCommands("clang", flags...)
|
||||||
|
}
|
||||||
|
err = runCCompiler(flags...)
|
||||||
|
if err != nil {
|
||||||
|
return &commandError{"failed to build CGo header", "", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and link the bitcode.
|
||||||
|
// This makes it possible to optimize the functions defined
|
||||||
|
// in the header together with the Go code. In particular,
|
||||||
|
// this allows inlining. It also ensures there is only one
|
||||||
|
// file per package to cache.
|
||||||
|
headerMod, err := mod.Context().ParseBitcodeFile(f.Name() + ".bc")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||||
|
}
|
||||||
|
err = llvm.LinkModules(mod, headerMod)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to link module: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Erase all globals that are part of the undefinedGlobals list.
|
// Erase all globals that are part of the undefinedGlobals list.
|
||||||
// This list comes from the -ldflags="-X pkg.foo=val" option.
|
// This list comes from the -ldflags="-X pkg.foo=val" option.
|
||||||
// Instead of setting the value directly in the AST (which would
|
// Instead of setting the value directly in the AST (which would
|
||||||
@@ -246,16 +320,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return errors.New("verification error after interpreting " + pkgInit.Name())
|
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
if sizeLevel >= 2 {
|
|
||||||
// Set the "optsize" attribute to make slightly smaller
|
|
||||||
// binaries at the cost of some performance.
|
|
||||||
kind := llvm.AttributeKindID("optsize")
|
|
||||||
attr := mod.Context().CreateEnumAttribute(kind, 0)
|
|
||||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
|
||||||
fn.AddFunctionAttr(attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run function passes for each function in the module.
|
// Run function passes for each function in the module.
|
||||||
// These passes are intended to be run on each function right
|
// These passes are intended to be run on each function right
|
||||||
// after they're created to reduce IR size (and maybe also for
|
// after they're created to reduce IR size (and maybe also for
|
||||||
@@ -311,7 +375,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return os.Rename(f.Name(), bitcodePath)
|
return os.Rename(f.Name(), bitcodePath)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
|
||||||
packageJobs = append(packageJobs, job)
|
packageJobs = append(packageJobs, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +405,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||||
llvmInitFn.SetUnnamedAddr(true)
|
llvmInitFn.SetUnnamedAddr(true)
|
||||||
|
transform.AddStandardAttributes(llvmInitFn, config)
|
||||||
llvmInitFn.Param(0).SetName("context")
|
llvmInitFn.Param(0).SetName("context")
|
||||||
llvmInitFn.Param(1).SetName("parentHandle")
|
llvmInitFn.Param(1).SetName("parentHandle")
|
||||||
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
|
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
|
||||||
@@ -402,14 +466,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, programJob)
|
|
||||||
|
|
||||||
// Check whether we only need to create an object file.
|
// Check whether we only need to create an object file.
|
||||||
// If so, we don't need to link anything and will be finished quickly.
|
// If so, we don't need to link anything and will be finished quickly.
|
||||||
outext := filepath.Ext(outpath)
|
outext := filepath.Ext(outpath)
|
||||||
if outext == ".o" || outext == ".bc" || outext == ".ll" {
|
if outext == ".o" || outext == ".bc" || outext == ".ll" {
|
||||||
// Run jobs to produce the LLVM module.
|
// Run jobs to produce the LLVM module.
|
||||||
err := runJobs(jobs)
|
err := runJobs(programJob, config.Options.Parallelism)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -450,7 +513,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, outputObjectFileJob)
|
|
||||||
|
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
@@ -461,19 +523,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// 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, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
|
job, err := CompilerRT.load(config, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job.dependencies...)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add jobs to compile extra files. These files are in C or assembly and
|
// Add jobs to compile extra files. These files are in C or assembly and
|
||||||
// contain things like the interrupt vector table and low level operations
|
// contain things like the interrupt vector table and low level operations
|
||||||
// such as stack switching.
|
// such as stack switching.
|
||||||
root := goenv.Get("TINYGOROOT")
|
|
||||||
for _, path := range config.ExtraFiles() {
|
for _, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -484,7 +543,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +561,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,34 +571,48 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add libc dependency if needed.
|
// Add libc dependencies, if they exist.
|
||||||
switch config.Target.Libc {
|
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
||||||
case "picolibc":
|
|
||||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
// Strip debug information with -no-debug.
|
||||||
if err != nil {
|
if !config.Debug() {
|
||||||
return err
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// The library needs to be compiled (cache miss).
|
if config.Target.Linker == "wasm-ld" {
|
||||||
jobs = append(jobs, job.dependencies...)
|
// Don't just strip debug information, also compress relocations
|
||||||
jobs = append(jobs, job)
|
// while we're at it. Relocations can only be compressed when debug
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
// information is stripped.
|
||||||
case "wasi-libc":
|
ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
} else if config.Target.Linker == "ld.lld" {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
// ld.lld is also used on Linux.
|
||||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
ldflags = append(ldflags, "--strip-debug")
|
||||||
|
} else {
|
||||||
|
switch config.GOOS() {
|
||||||
|
case "linux":
|
||||||
|
// Either real linux or an embedded system (like AVR) that
|
||||||
|
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||||
|
// case (not ld.lld - that case is handled above).
|
||||||
|
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||||
|
case "darwin":
|
||||||
|
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||||
|
// information. Apple expects you to use the strip command
|
||||||
|
// instead.
|
||||||
|
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||||
|
default:
|
||||||
|
// Other OSes may have different flags.
|
||||||
|
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
job := dummyCompileJob(path)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
case "":
|
|
||||||
// no library specified, so nothing to do
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a linker job, which links all object files together and does some
|
// Create a linker job, which links all object files together and does some
|
||||||
// extra stuff that can only be done after linking.
|
// extra stuff that can only be done after linking.
|
||||||
jobs = append(jobs, &compileJob{
|
linkJob := &compileJob{
|
||||||
description: "link",
|
description: "link",
|
||||||
dependencies: linkerDependencies,
|
dependencies: linkerDependencies,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
@@ -588,22 +659,31 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print code size if requested.
|
||||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||||
sizes, err := loadProgramSize(executable)
|
packagePathMap := make(map[string]string, len(lprogram.Packages))
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||||
|
}
|
||||||
|
sizes, err := loadProgramSize(executable, packagePathMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if config.Options.PrintSizes == "short" {
|
if config.Options.PrintSizes == "short" {
|
||||||
fmt.Printf(" code data bss | flash ram\n")
|
fmt.Printf(" code data bss | flash ram\n")
|
||||||
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
|
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM())
|
||||||
} else {
|
} else {
|
||||||
|
if !config.Debug() {
|
||||||
|
fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail")
|
||||||
|
}
|
||||||
fmt.Printf(" code rodata data bss | flash ram | package\n")
|
fmt.Printf(" code rodata data bss | flash ram | package\n")
|
||||||
|
fmt.Printf("------------------------------- | --------------- | -------\n")
|
||||||
for _, name := range sizes.sortedPackageNames() {
|
for _, name := range sizes.sortedPackageNames() {
|
||||||
pkgSize := sizes.Packages[name]
|
pkgSize := sizes.Packages[name]
|
||||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
|
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
|
||||||
}
|
}
|
||||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | (sum)\n", sizes.Sum.Code, sizes.Sum.ROData, sizes.Sum.Data, sizes.Sum.BSS, sizes.Sum.Flash(), sizes.Sum.RAM())
|
fmt.Printf("------------------------------- | --------------- | -------\n")
|
||||||
fmt.Printf("%7d - %7d %7d | %7d %7d | (all)\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
|
fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,12 +694,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
|
||||||
// Run all jobs to compile and link the program.
|
// Run all jobs to compile and link the program.
|
||||||
// Do this now (instead of after elf-to-hex and similar conversions) as it
|
// Do this now (instead of after elf-to-hex and similar conversions) as it
|
||||||
// is simpler and cannot be parallelized.
|
// is simpler and cannot be parallelized.
|
||||||
err = runJobs(jobs)
|
err = runJobs(linkJob, config.Options.Parallelism)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -644,7 +724,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "esp32", "esp8266":
|
case "esp32", "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)
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
@@ -711,7 +791,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
// stack-allocated values.
|
// stack-allocated values.
|
||||||
// Use -wasm-abi=generic to disable this behaviour.
|
// Use -wasm-abi=generic to disable this behaviour.
|
||||||
if config.WasmAbi() == "js" {
|
if config.WasmAbi() == "js" {
|
||||||
err := transform.ExternalInt64AsPtr(mod)
|
err := transform.ExternalInt64AsPtr(mod, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
package builder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return the newest timestamp of all the file paths passed in. Used to check
|
|
||||||
// for stale caches.
|
|
||||||
func cacheTimestamp(paths []string) (time.Time, error) {
|
|
||||||
var timestamp time.Time
|
|
||||||
for _, path := range paths {
|
|
||||||
st, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if timestamp.IsZero() {
|
|
||||||
timestamp = st.ModTime()
|
|
||||||
} else if timestamp.Before(st.ModTime()) {
|
|
||||||
timestamp = st.ModTime()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return timestamp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to load a given file from the cache. Return "", nil if no cached file can
|
|
||||||
// be found (or the file is stale), return the absolute path if there is a cache
|
|
||||||
// and return an error on I/O errors.
|
|
||||||
func cacheLoad(name string, sourceFiles []string) (string, error) {
|
|
||||||
cachepath := filepath.Join(goenv.Get("GOCACHE"), name)
|
|
||||||
cacheStat, err := os.Stat(cachepath)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return "", nil // does not exist
|
|
||||||
} else if err != nil {
|
|
||||||
return "", err // cannot stat cache file
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceTimestamp, err := cacheTimestamp(sourceFiles)
|
|
||||||
if err != nil {
|
|
||||||
return "", err // cannot stat source files
|
|
||||||
}
|
|
||||||
|
|
||||||
if cacheStat.ModTime().After(sourceTimestamp) {
|
|
||||||
return cachepath, nil
|
|
||||||
} else {
|
|
||||||
os.Remove(cachepath)
|
|
||||||
// stale cache
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the file located at tmppath in the cache with the given name. The
|
|
||||||
// tmppath may or may not be gone afterwards.
|
|
||||||
func cacheStore(tmppath, name string, sourceFiles []string) (string, error) {
|
|
||||||
// get the last modified time
|
|
||||||
if len(sourceFiles) == 0 {
|
|
||||||
panic("cache: no source files")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check the config key
|
|
||||||
|
|
||||||
dir := goenv.Get("GOCACHE")
|
|
||||||
err := os.MkdirAll(dir, 0777)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
cachepath := filepath.Join(dir, name)
|
|
||||||
err = copyFile(tmppath, cachepath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return cachepath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyFile copies the given file from src to dst. It can copy over
|
|
||||||
// a possibly already existing file at the destination.
|
|
||||||
func copyFile(src, dst string) error {
|
|
||||||
inf, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer inf.Close()
|
|
||||||
outpath := dst + ".tmp"
|
|
||||||
outf, err := os.Create(outpath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.Copy(outf, inf)
|
|
||||||
if err != nil {
|
|
||||||
os.Remove(outpath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = outf.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.Rename(dst+".tmp", dst)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test whether the Clang generated "target-cpu" attribute matches the CPU
|
||||||
|
// property in TinyGo target files.
|
||||||
|
func TestClangAttributes(t *testing.T) {
|
||||||
|
var targetNames = []string{
|
||||||
|
// Please keep this list sorted!
|
||||||
|
"atmega328p",
|
||||||
|
"atmega1280",
|
||||||
|
"atmega1284p",
|
||||||
|
"atmega2560",
|
||||||
|
"attiny85",
|
||||||
|
"cortex-m0",
|
||||||
|
"cortex-m0plus",
|
||||||
|
"cortex-m3",
|
||||||
|
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
||||||
|
"cortex-m4",
|
||||||
|
"cortex-m7",
|
||||||
|
"esp32c3",
|
||||||
|
"fe310",
|
||||||
|
"gameboy-advance",
|
||||||
|
"k210",
|
||||||
|
"nintendoswitch",
|
||||||
|
"riscv-qemu",
|
||||||
|
"wasi",
|
||||||
|
"wasm",
|
||||||
|
}
|
||||||
|
if hasBuiltinTools {
|
||||||
|
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
|
||||||
|
// which also implies it was built with Xtensa support.
|
||||||
|
targetNames = append(targetNames, "esp32", "esp8266")
|
||||||
|
}
|
||||||
|
for _, targetName := range targetNames {
|
||||||
|
targetName := targetName
|
||||||
|
t.Run(targetName, func(t *testing.T) {
|
||||||
|
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, options := range []*compileopts.Options{
|
||||||
|
{GOOS: "linux", GOARCH: "386"},
|
||||||
|
{GOOS: "linux", GOARCH: "amd64"},
|
||||||
|
{GOOS: "linux", GOARCH: "arm"},
|
||||||
|
{GOOS: "linux", GOARCH: "arm64"},
|
||||||
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
|
} {
|
||||||
|
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
|
||||||
|
testClangAttributes(t, options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||||
|
testDir := t.TempDir()
|
||||||
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
defer ctx.Dispose()
|
||||||
|
|
||||||
|
target, err := compileopts.LoadTarget(options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not load target: %s", err)
|
||||||
|
}
|
||||||
|
config := compileopts.Config{
|
||||||
|
Options: options,
|
||||||
|
Target: target,
|
||||||
|
ClangHeaders: clangHeaderPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a very simple C input file.
|
||||||
|
srcpath := filepath.Join(testDir, "test.c")
|
||||||
|
err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not write target file %s: %s", srcpath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile this file using Clang.
|
||||||
|
outpath := filepath.Join(testDir, "test.bc")
|
||||||
|
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
|
||||||
|
if config.GOOS() == "darwin" {
|
||||||
|
// Silence some warnings that happen when testing GOOS=darwin on
|
||||||
|
// something other than MacOS.
|
||||||
|
flags = append(flags, "-Wno-missing-sysroot", "-Wno-incompatible-sysroot")
|
||||||
|
}
|
||||||
|
err = runCCompiler(flags...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to compile %s: %s", srcpath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the resulting LLVM bitcode.
|
||||||
|
mod, err := ctx.ParseBitcodeFile(outpath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not parse bitcode file %s: %s", outpath, err)
|
||||||
|
}
|
||||||
|
defer mod.Dispose()
|
||||||
|
|
||||||
|
// Check whether the LLVM target matches.
|
||||||
|
if mod.Target() != config.Triple() {
|
||||||
|
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the "target-cpu" string attribute of the add function.
|
||||||
|
add := mod.NamedFunction("add")
|
||||||
|
var cpu string
|
||||||
|
cpuAttr := add.GetStringAttributeAtIndex(-1, "target-cpu")
|
||||||
|
if !cpuAttr.IsNil() {
|
||||||
|
cpu = cpuAttr.GetStringValue()
|
||||||
|
}
|
||||||
|
if cpu != config.CPU() {
|
||||||
|
t.Errorf("target has CPU %#v but Clang makes it CPU %#v", config.CPU(), cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||||
|
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||||
|
// to recursive tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if len(os.Args) >= 2 {
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "clang", "ld.lld", "wasm-ld":
|
||||||
|
// Invoke a specific tool.
|
||||||
|
err := RunTool(os.Args[1], os.Args[2:]...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run normal tests.
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
+5
-3
@@ -157,10 +157,12 @@ var aeabiBuiltins = []string{
|
|||||||
//
|
//
|
||||||
// For more information, see: https://compiler-rt.llvm.org/
|
// For more information, see: https://compiler-rt.llvm.org/
|
||||||
var CompilerRT = Library{
|
var CompilerRT = Library{
|
||||||
name: "compiler-rt",
|
name: "compiler-rt",
|
||||||
cflags: func() []string { return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"} },
|
cflags: func(target, headerPath string) []string {
|
||||||
|
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
|
||||||
|
},
|
||||||
sourceDir: "lib/compiler-rt/lib/builtins",
|
sourceDir: "lib/compiler-rt/lib/builtins",
|
||||||
sources: func(target string) []string {
|
librarySources: func(target string) []string {
|
||||||
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...)
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
|
|
||||||
// Write dependencies file.
|
// Write dependencies file.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // shouldn't happen
|
panic(err) // shouldn't happen
|
||||||
|
|||||||
+25
-11
@@ -21,6 +21,7 @@ func init() {
|
|||||||
commands["clang"] = []string{"clang-" + llvmMajor}
|
commands["clang"] = []string{"clang-" + llvmMajor}
|
||||||
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
||||||
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
||||||
|
commands["lldb"] = []string{"lldb-" + llvmMajor, "lldb"}
|
||||||
// Add the path to a Homebrew-installed LLVM for ease of use (no need to
|
// Add the path to a Homebrew-installed LLVM for ease of use (no need to
|
||||||
// manually set $PATH).
|
// manually set $PATH).
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
@@ -28,6 +29,7 @@ func init() {
|
|||||||
commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor)
|
commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor)
|
||||||
commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld")
|
commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld")
|
||||||
commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld")
|
commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld")
|
||||||
|
commands["lldb"] = append(commands["lldb"], prefix+"lldb")
|
||||||
}
|
}
|
||||||
// Add the path for when LLVM was installed with the installer from
|
// Add the path for when LLVM was installed with the installer from
|
||||||
// llvm.org, which by default doesn't add LLVM to the $PATH environment
|
// llvm.org, which by default doesn't add LLVM to the $PATH environment
|
||||||
@@ -36,6 +38,7 @@ func init() {
|
|||||||
commands["clang"] = append(commands["clang"], "clang", "C:\\Program Files\\LLVM\\bin\\clang.exe")
|
commands["clang"] = append(commands["clang"], "clang", "C:\\Program Files\\LLVM\\bin\\clang.exe")
|
||||||
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
||||||
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
||||||
|
commands["lldb"] = append(commands["lldb"], "C:\\Program Files\\LLVM\\bin\\lldb.exe")
|
||||||
}
|
}
|
||||||
// Add the path to LLVM installed from ports.
|
// Add the path to LLVM installed from ports.
|
||||||
if runtime.GOOS == "freebsd" {
|
if runtime.GOOS == "freebsd" {
|
||||||
@@ -43,23 +46,34 @@ func init() {
|
|||||||
commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor)
|
commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor)
|
||||||
commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld")
|
commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld")
|
||||||
commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld")
|
commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld")
|
||||||
|
commands["lldb"] = append(commands["lldb"], prefix+"lldb")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func execCommand(cmdNames []string, args ...string) error {
|
// LookupCommand looks up the executable name for a given LLVM tool such as
|
||||||
for _, cmdName := range cmdNames {
|
// clang or wasm-ld. It returns the (relative) command that can be used to
|
||||||
cmd := exec.Command(cmdName, args...)
|
// invoke the tool or an error if it could not be found.
|
||||||
cmd.Stdout = os.Stdout
|
func LookupCommand(name string) (string, error) {
|
||||||
cmd.Stderr = os.Stderr
|
for _, cmdName := range commands[name] {
|
||||||
err := cmd.Run()
|
_, err := exec.LookPath(cmdName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, ok := err.(*exec.Error); ok && (err.Err == exec.ErrNotFound || err.Err.Error() == "file does not exist") {
|
if errors.Unwrap(err) == exec.ErrNotFound {
|
||||||
// this command was not found, try the next
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return cmdName, err
|
||||||
}
|
}
|
||||||
return nil
|
return cmdName, nil
|
||||||
}
|
}
|
||||||
return errors.New("none of these commands were found in your $PATH: " + strings.Join(cmdNames, " "))
|
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func execCommand(name string, args ...string) error {
|
||||||
|
name, err := LookupCommand(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command(name, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@ import (
|
|||||||
// uses the currently active GOPATH (from the goenv package) to determine the Go
|
// uses the currently active GOPATH (from the goenv package) to determine the Go
|
||||||
// version to use.
|
// version to use.
|
||||||
func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||||
spec, err := compileopts.LoadTarget(options.Target)
|
spec, err := compileopts.LoadTarget(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -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 < 13 || minor > 16 {
|
if major != 1 || minor < 15 || minor > 17 {
|
||||||
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|||||||
+19
-6
@@ -78,11 +78,21 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
// An added benefit is that we don't need to check for errors all the time.
|
// An added benefit is that we don't need to check for errors all the time.
|
||||||
outf := &bytes.Buffer{}
|
outf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Chip IDs. Source:
|
||||||
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||||
|
chip_id := map[string]uint16{
|
||||||
|
"esp32": 0x0000,
|
||||||
|
"esp32c3": 0x0005,
|
||||||
|
}[format]
|
||||||
|
|
||||||
// Image header.
|
// Image header.
|
||||||
switch format {
|
switch format {
|
||||||
case "esp32":
|
case "esp32", "esp32c3":
|
||||||
// Header format:
|
// Header format:
|
||||||
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||||
|
// Note: not adding a SHA256 hash as the binary is modified by
|
||||||
|
// esptool.py while flashing and therefore the hash won't be valid
|
||||||
|
// anymore.
|
||||||
binary.Write(outf, binary.LittleEndian, struct {
|
binary.Write(outf, binary.LittleEndian, struct {
|
||||||
magic uint8
|
magic uint8
|
||||||
segment_count uint8
|
segment_count uint8
|
||||||
@@ -91,15 +101,18 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
entry_addr uint32
|
entry_addr uint32
|
||||||
wp_pin uint8
|
wp_pin uint8
|
||||||
spi_pin_drv [3]uint8
|
spi_pin_drv [3]uint8
|
||||||
reserved [11]uint8
|
chip_id uint16
|
||||||
|
min_chip_rev uint8
|
||||||
|
reserved [8]uint8
|
||||||
hash_appended bool
|
hash_appended bool
|
||||||
}{
|
}{
|
||||||
magic: 0xE9,
|
magic: 0xE9,
|
||||||
segment_count: byte(len(segments)),
|
segment_count: byte(len(segments)),
|
||||||
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||||
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
|
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
||||||
entry_addr: uint32(inf.Entry),
|
entry_addr: uint32(inf.Entry),
|
||||||
wp_pin: 0xEE, // disable WP pin
|
wp_pin: 0xEE, // disable WP pin
|
||||||
|
chip_id: chip_id,
|
||||||
hash_appended: true, // add a SHA256 hash
|
hash_appended: true, // add a SHA256 hash
|
||||||
})
|
})
|
||||||
case "esp8266":
|
case "esp8266":
|
||||||
@@ -142,7 +155,7 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||||
outf.WriteByte(checksum)
|
outf.WriteByte(checksum)
|
||||||
|
|
||||||
if format == "esp32" {
|
if format != "esp8266" {
|
||||||
// SHA256 hash (to protect against image corruption, not for security).
|
// SHA256 hash (to protect against image corruption, not for security).
|
||||||
hash := sha256.Sum256(outf.Bytes())
|
hash := sha256.Sum256(outf.Bytes())
|
||||||
outf.Write(hash[:])
|
outf.Write(hash[:])
|
||||||
|
|||||||
+34
-8
@@ -65,19 +65,45 @@ func (job *compileJob) readyToRun() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// runJobs runs all the jobs indicated in the jobs slice and returns the error
|
// runJobs runs the indicated job and all its dependencies. For every job, all
|
||||||
// of the first job that fails to run.
|
// the dependencies are run first. It returns the error of the first job that
|
||||||
// It runs all jobs in the order of the slice, as long as all dependencies have
|
// fails.
|
||||||
// already run. Therefore, if some jobs are preferred to run before others, they
|
// It runs all jobs in the order of the dependencies slice, depth-first.
|
||||||
// should be ordered as such in this slice.
|
// Therefore, if some jobs are preferred to run before others, they should be
|
||||||
func runJobs(jobs []*compileJob) error {
|
// ordered as such in the job dependencies.
|
||||||
|
func runJobs(job *compileJob, parallelism int) error {
|
||||||
|
if parallelism == 0 {
|
||||||
|
// Have a default, if the parallelism isn't set. This is useful for
|
||||||
|
// tests.
|
||||||
|
parallelism = runtime.NumCPU()
|
||||||
|
}
|
||||||
|
if parallelism < 1 {
|
||||||
|
return fmt.Errorf("-p flag must be at least 1, provided -p=%d", parallelism)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a slice of jobs to run, where all dependencies are run in order.
|
||||||
|
jobs := []*compileJob{}
|
||||||
|
addedJobs := map[*compileJob]struct{}{}
|
||||||
|
var addJobs func(*compileJob)
|
||||||
|
addJobs = func(job *compileJob) {
|
||||||
|
if _, ok := addedJobs[job]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, dep := range job.dependencies {
|
||||||
|
addJobs(dep)
|
||||||
|
}
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
addedJobs[job] = struct{}{}
|
||||||
|
}
|
||||||
|
addJobs(job)
|
||||||
|
|
||||||
// Create channels to communicate with the workers.
|
// Create channels to communicate with the workers.
|
||||||
doneChan := make(chan *compileJob)
|
doneChan := make(chan *compileJob)
|
||||||
workerChan := make(chan *compileJob)
|
workerChan := make(chan *compileJob)
|
||||||
defer close(workerChan)
|
defer close(workerChan)
|
||||||
|
|
||||||
// Start a number of workers.
|
// Start a number of workers.
|
||||||
for i := 0; i < runtime.NumCPU(); i++ {
|
for i := 0; i < parallelism; i++ {
|
||||||
if jobRunnerDebug {
|
if jobRunnerDebug {
|
||||||
fmt.Println("## starting worker", i)
|
fmt.Println("## starting worker", i)
|
||||||
}
|
}
|
||||||
@@ -92,7 +118,7 @@ func runJobs(jobs []*compileJob) error {
|
|||||||
for {
|
for {
|
||||||
// If there are free workers, try starting a new job (if one is
|
// If there are free workers, try starting a new job (if one is
|
||||||
// available). If it succeeds, try again to fill the entire worker pool.
|
// available). If it succeeds, try again to fill the entire worker pool.
|
||||||
if numRunningJobs < runtime.NumCPU() {
|
if numRunningJobs < parallelism {
|
||||||
jobToRun := nextJob(jobs)
|
jobToRun := nextJob(jobs)
|
||||||
if jobToRun != nil {
|
if jobToRun != nil {
|
||||||
// Start job.
|
// Start job.
|
||||||
|
|||||||
+113
-51
@@ -1,10 +1,12 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,72 +16,87 @@ type Library struct {
|
|||||||
// The library name, such as compiler-rt or picolibc.
|
// The library name, such as compiler-rt or picolibc.
|
||||||
name string
|
name string
|
||||||
|
|
||||||
cflags func() []string
|
// makeHeaders creates a header include dir for the library
|
||||||
|
makeHeaders func(target, includeDir string) error
|
||||||
|
|
||||||
|
// cflags returns the C flags specific to this library
|
||||||
|
cflags func(target, headerPath string) []string
|
||||||
|
|
||||||
// The source directory, relative to TINYGOROOT.
|
// The source directory, relative to TINYGOROOT.
|
||||||
sourceDir string
|
sourceDir string
|
||||||
|
|
||||||
// The source files, relative to sourceDir.
|
// The source files, relative to sourceDir.
|
||||||
sources func(target string) []string
|
librarySources func(target string) []string
|
||||||
}
|
|
||||||
|
|
||||||
// fullPath returns the full path to the source directory.
|
// The source code for the crt1.o file, relative to sourceDir.
|
||||||
func (l *Library) fullPath() string {
|
crt1Source string
|
||||||
return filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sourcePaths returns a slice with the full paths to the source files.
|
|
||||||
func (l *Library) sourcePaths(target string) []string {
|
|
||||||
sources := l.sources(target)
|
|
||||||
paths := make([]string, len(sources))
|
|
||||||
for i, name := range sources {
|
|
||||||
paths[i] = filepath.Join(l.fullPath(), name)
|
|
||||||
}
|
|
||||||
return paths
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the library archive, possibly generating and caching it if needed.
|
// Load the library archive, possibly generating and caching it if needed.
|
||||||
// The resulting file is stored in the provided tmpdir, which is expected to be
|
// The resulting directory may be stored in the provided tmpdir, which is
|
||||||
// removed after the Load call.
|
// expected to be removed after the Load call.
|
||||||
func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
func (l *Library) Load(config *compileopts.Config, tmpdir string) (dir string, err error) {
|
||||||
job, err := l.load(target, "", tmpdir)
|
job, err := l.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
jobs := append([]*compileJob{job}, job.dependencies...)
|
err = runJobs(job, config.Options.Parallelism)
|
||||||
err = runJobs(jobs)
|
return filepath.Dir(job.result), err
|
||||||
return job.result, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load returns a compile job to build this library file for the given target
|
// load returns a compile job to build this library file for the given target
|
||||||
// and CPU. It may return a dummy compileJob if the library build is already
|
// and CPU. It may return a dummy compileJob if the library build is already
|
||||||
// cached. The path is stored as job.result but is only valid if the job and
|
// cached. The path is stored as job.result but is only valid after the job has
|
||||||
// job.dependencies have been run.
|
// been run.
|
||||||
// The provided tmpdir will be used to store intermediary files and possibly the
|
// The provided tmpdir will be used to store intermediary files and possibly the
|
||||||
// output archive file, it is expected to be removed after use.
|
// output archive file, it is expected to be removed after use.
|
||||||
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
|
// As a side effect, this call creates the library header files if they didn't
|
||||||
// Try to load a precompiled library.
|
// exist yet.
|
||||||
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, err error) {
|
||||||
if _, err := os.Stat(precompiledPath); err == nil {
|
outdir, precompiled := config.LibcPath(l.name)
|
||||||
|
archiveFilePath := filepath.Join(outdir, "lib.a")
|
||||||
|
if precompiled {
|
||||||
// Found a precompiled library for this OS/architecture. Return the path
|
// Found a precompiled library for this OS/architecture. Return the path
|
||||||
// directly.
|
// directly.
|
||||||
return dummyCompileJob(precompiledPath), nil
|
return dummyCompileJob(archiveFilePath), nil
|
||||||
}
|
|
||||||
|
|
||||||
var outfile string
|
|
||||||
if cpu != "" {
|
|
||||||
outfile = l.name + "-" + target + "-" + cpu + ".a"
|
|
||||||
} else {
|
|
||||||
outfile = l.name + "-" + target + ".a"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to fetch this library from the cache.
|
// Try to fetch this library from the cache.
|
||||||
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
|
if _, err := os.Stat(archiveFilePath); err == nil {
|
||||||
// Cache hit.
|
return dummyCompileJob(archiveFilePath), nil
|
||||||
return dummyCompileJob(path), nil
|
|
||||||
}
|
}
|
||||||
// Cache miss, build it now.
|
// Cache miss, build it now.
|
||||||
|
|
||||||
|
// Create the destination directory where the components of this library
|
||||||
|
// (lib.a file, include directory) are placed.
|
||||||
|
outname := filepath.Base(outdir)
|
||||||
|
err = os.MkdirAll(filepath.Join(goenv.Get("GOCACHE"), outname), 0o777)
|
||||||
|
if err != nil {
|
||||||
|
// Could not create directory (and not because it already exists).
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make headers if needed.
|
||||||
|
headerPath := filepath.Join(outdir, "include")
|
||||||
|
target := config.Triple()
|
||||||
|
if l.makeHeaders != nil {
|
||||||
|
if _, err = os.Stat(headerPath); err != nil {
|
||||||
|
temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(temporaryHeaderPath)
|
||||||
|
err = l.makeHeaders(target, temporaryHeaderPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = os.Rename(temporaryHeaderPath, headerPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
remapDir := filepath.Join(os.TempDir(), "tinygo-"+l.name)
|
remapDir := filepath.Join(os.TempDir(), "tinygo-"+l.name)
|
||||||
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
||||||
err = os.Mkdir(dir, 0777)
|
err = os.Mkdir(dir, 0777)
|
||||||
@@ -91,9 +108,16 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
|
|||||||
// 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(), "-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", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||||
|
cpu := config.CPU()
|
||||||
if cpu != "" {
|
if cpu != "" {
|
||||||
args = append(args, "-mcpu="+cpu)
|
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
||||||
|
// However, ARM has not done this.
|
||||||
|
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||||
|
args = append(args, "-march="+cpu)
|
||||||
|
} else {
|
||||||
|
args = append(args, "-mcpu="+cpu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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")
|
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
|
||||||
@@ -108,27 +132,39 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
|
|||||||
// Create job to put all the object files in a single archive. This archive
|
// Create job to put all the object files in a single archive. This archive
|
||||||
// file is the (static) library file.
|
// file is the (static) library file.
|
||||||
var objs []string
|
var objs []string
|
||||||
arpath := filepath.Join(dir, l.name+".a")
|
|
||||||
job = &compileJob{
|
job = &compileJob{
|
||||||
description: "ar " + l.name + ".a",
|
description: "ar " + l.name + "/lib.a",
|
||||||
result: arpath,
|
result: filepath.Join(goenv.Get("GOCACHE"), outname, "lib.a"),
|
||||||
run: func(*compileJob) error {
|
run: func(*compileJob) error {
|
||||||
// Create an archive of all object files.
|
// Create an archive of all object files.
|
||||||
err := makeArchive(arpath, objs)
|
f, err := ioutil.TempFile(outdir, "libc.a.tmp*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = makeArchive(f, objs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Store this archive in the cache.
|
// Store this archive in the cache.
|
||||||
_, err = cacheStore(arpath, outfile, l.sourcePaths(target))
|
return os.Rename(f.Name(), archiveFilePath)
|
||||||
return err
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 _, srcpath := range l.sourcePaths(target) {
|
for _, path := range l.librarySources(target) {
|
||||||
srcpath := srcpath // avoid concurrency issues by redefining inside the loop
|
// Strip leading "../" parts off the path.
|
||||||
objpath := filepath.Join(dir, filepath.Base(srcpath)+".o")
|
cleanpath := path
|
||||||
|
for strings.HasPrefix(cleanpath, "../") {
|
||||||
|
cleanpath = cleanpath[3:]
|
||||||
|
}
|
||||||
|
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
|
||||||
|
objpath := filepath.Join(dir, cleanpath+".o")
|
||||||
|
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||||
objs = append(objs, objpath)
|
objs = append(objs, objpath)
|
||||||
job.dependencies = append(job.dependencies, &compileJob{
|
job.dependencies = append(job.dependencies, &compileJob{
|
||||||
description: "compile " + srcpath,
|
description: "compile " + srcpath,
|
||||||
@@ -145,5 +181,31 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create crt1.o job, if needed.
|
||||||
|
// Add this as a (fake) dependency to the ar file so it gets compiled.
|
||||||
|
// (It could be done in parallel with creating the ar file, but it probably
|
||||||
|
// won't make much of a difference in speed).
|
||||||
|
if l.crt1Source != "" {
|
||||||
|
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
|
||||||
|
job.dependencies = append(job.dependencies, &compileJob{
|
||||||
|
description: "compile " + srcpath,
|
||||||
|
run: func(*compileJob) error {
|
||||||
|
var compileArgs []string
|
||||||
|
compileArgs = append(compileArgs, args...)
|
||||||
|
tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpfile.Close()
|
||||||
|
compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath)
|
||||||
|
err = runCCompiler(compileArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return &commandError{"failed to build", srcpath, err}
|
||||||
|
}
|
||||||
|
return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o"))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return job, nil
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Musl = Library{
|
||||||
|
name: "musl",
|
||||||
|
makeHeaders: func(target, includeDir string) error {
|
||||||
|
bits := filepath.Join(includeDir, "bits")
|
||||||
|
err := os.Mkdir(bits, 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
arch := compileopts.MuslArchitecture(target)
|
||||||
|
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl")
|
||||||
|
|
||||||
|
// Create the file alltypes.h.
|
||||||
|
f, err := os.Create(filepath.Join(bits, "alltypes.h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
infiles := []string{
|
||||||
|
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
|
||||||
|
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||||
|
}
|
||||||
|
for _, infile := range infiles {
|
||||||
|
data, err := ioutil.ReadFile(infile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||||
|
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||||
|
value := matches[1]
|
||||||
|
name := matches[2]
|
||||||
|
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "STRUCT ") {
|
||||||
|
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
|
||||||
|
name := matches[1]
|
||||||
|
value := matches[2]
|
||||||
|
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
|
||||||
|
}
|
||||||
|
f.WriteString(line + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
// Create the file syscall.h.
|
||||||
|
f, err = os.Create(filepath.Join(bits, "syscall.h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.Write(bytes.ReplaceAll(data, []byte("__NR_"), []byte("SYS_")))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
cflags: func(target, headerPath string) []string {
|
||||||
|
arch := compileopts.MuslArchitecture(target)
|
||||||
|
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl")
|
||||||
|
return []string{
|
||||||
|
"-std=c99", // same as in musl
|
||||||
|
"-D_XOPEN_SOURCE=700", // same as in musl
|
||||||
|
// Musl triggers some warnings and we don't want to show any
|
||||||
|
// warnings while compiling (only errors or silence), so disable
|
||||||
|
// specific warnings that are triggered in musl.
|
||||||
|
"-Werror",
|
||||||
|
"-Wno-logical-op-parentheses",
|
||||||
|
"-Wno-bitwise-op-parentheses",
|
||||||
|
"-Wno-shift-op-parentheses",
|
||||||
|
"-Wno-ignored-attributes",
|
||||||
|
"-Wno-string-plus-int",
|
||||||
|
"-Qunused-arguments",
|
||||||
|
// Select include dirs. Don't include standard library includes
|
||||||
|
// (that would introduce host dependencies and other complications),
|
||||||
|
// but do include all the include directories expected by musl.
|
||||||
|
"-nostdlibinc",
|
||||||
|
"-I" + muslDir + "/arch/" + arch,
|
||||||
|
"-I" + muslDir + "/arch/generic",
|
||||||
|
"-I" + muslDir + "/src/include",
|
||||||
|
"-I" + muslDir + "/src/internal",
|
||||||
|
"-I" + headerPath,
|
||||||
|
"-I" + muslDir + "/include",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sourceDir: "lib/musl/src",
|
||||||
|
librarySources: func(target string) []string {
|
||||||
|
arch := compileopts.MuslArchitecture(target)
|
||||||
|
globs := []string{
|
||||||
|
"env/*.c",
|
||||||
|
"errno/*.c",
|
||||||
|
"exit/*.c",
|
||||||
|
"internal/defsysinfo.c",
|
||||||
|
"internal/libc.c",
|
||||||
|
"internal/syscall_ret.c",
|
||||||
|
"internal/vdso.c",
|
||||||
|
"malloc/*.c",
|
||||||
|
"mman/*.c",
|
||||||
|
"signal/*.c",
|
||||||
|
"stdio/*.c",
|
||||||
|
"string/*.c",
|
||||||
|
"thread/" + arch + "/*.s",
|
||||||
|
"thread/" + arch + "/*.c",
|
||||||
|
"thread/*.c",
|
||||||
|
"time/*.c",
|
||||||
|
"unistd/*.c",
|
||||||
|
}
|
||||||
|
|
||||||
|
var sources []string
|
||||||
|
seenSources := map[string]struct{}{}
|
||||||
|
basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/"
|
||||||
|
for _, pattern := range globs {
|
||||||
|
matches, err := filepath.Glob(basepath + pattern)
|
||||||
|
if err != nil {
|
||||||
|
// From the documentation:
|
||||||
|
// > Glob ignores file system errors such as I/O errors reading
|
||||||
|
// > directories. The only possible returned error is
|
||||||
|
// > ErrBadPattern, when pattern is malformed.
|
||||||
|
// So the only possible error is when the (statically defined)
|
||||||
|
// pattern is wrong. In other words, a programming bug.
|
||||||
|
panic("could not glob source dirs: " + err.Error())
|
||||||
|
}
|
||||||
|
for _, match := range matches {
|
||||||
|
relpath, err := filepath.Rel(basepath, match)
|
||||||
|
if err != nil {
|
||||||
|
// Not sure if this is even possible.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Make sure architecture specific files override generic files.
|
||||||
|
id := strings.ReplaceAll(relpath, "/"+arch+"/", "/")
|
||||||
|
if _, ok := seenSources[id]; ok {
|
||||||
|
// Already seen this file, skipping this (generic) file.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seenSources[id] = struct{}{}
|
||||||
|
sources = append(sources, relpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources
|
||||||
|
},
|
||||||
|
crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c
|
||||||
|
}
|
||||||
+121
-3
@@ -1,6 +1,7 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
@@ -10,17 +11,134 @@ import (
|
|||||||
// based on newlib.
|
// based on newlib.
|
||||||
var Picolibc = Library{
|
var Picolibc = Library{
|
||||||
name: "picolibc",
|
name: "picolibc",
|
||||||
cflags: func() []string {
|
makeHeaders: func(target, includeDir string) error {
|
||||||
|
f, err := os.Create(filepath.Join(includeDir, "picolibc.h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.Close()
|
||||||
|
},
|
||||||
|
cflags: func(target, headerPath string) []string {
|
||||||
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
||||||
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include", "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
|
return []string{
|
||||||
|
"-Werror",
|
||||||
|
"-Wall",
|
||||||
|
"-std=gnu11",
|
||||||
|
"-D_COMPILING_NEWLIB",
|
||||||
|
"-DHAVE_ALIAS_ATTRIBUTE",
|
||||||
|
"-DTINY_STDIO",
|
||||||
|
"-nostdlibinc",
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include",
|
||||||
|
"-I" + picolibcDir + "/tinystdio",
|
||||||
|
"-I" + headerPath,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sourceDir: "lib/picolibc/newlib/libc",
|
sourceDir: "lib/picolibc/newlib/libc",
|
||||||
sources: func(target string) []string {
|
librarySources: func(target string) []string {
|
||||||
return picolibcSources
|
return picolibcSources
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var picolibcSources = []string{
|
var picolibcSources = []string{
|
||||||
|
"../../../picolibc-stdio.c",
|
||||||
|
|
||||||
|
"tinystdio/asprintf.c",
|
||||||
|
"tinystdio/atod_engine.c",
|
||||||
|
"tinystdio/atod_ryu.c",
|
||||||
|
"tinystdio/atof_engine.c",
|
||||||
|
"tinystdio/atof_ryu.c",
|
||||||
|
//"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
|
||||||
|
"tinystdio/clearerr.c",
|
||||||
|
"tinystdio/compare_exchange.c",
|
||||||
|
"tinystdio/dtoa_data.c",
|
||||||
|
"tinystdio/dtoa_engine.c",
|
||||||
|
"tinystdio/dtoa_ryu.c",
|
||||||
|
"tinystdio/ecvtbuf.c",
|
||||||
|
"tinystdio/ecvt.c",
|
||||||
|
"tinystdio/ecvt_data.c",
|
||||||
|
"tinystdio/ecvtfbuf.c",
|
||||||
|
"tinystdio/ecvtf.c",
|
||||||
|
"tinystdio/ecvtf_data.c",
|
||||||
|
"tinystdio/exchange.c",
|
||||||
|
//"tinystdio/fclose.c", // posix-io
|
||||||
|
"tinystdio/fcvtbuf.c",
|
||||||
|
"tinystdio/fcvt.c",
|
||||||
|
"tinystdio/fcvtfbuf.c",
|
||||||
|
"tinystdio/fcvtf.c",
|
||||||
|
"tinystdio/fdevopen.c",
|
||||||
|
//"tinystdio/fdopen.c", // posix-io
|
||||||
|
"tinystdio/feof.c",
|
||||||
|
"tinystdio/ferror.c",
|
||||||
|
"tinystdio/fflush.c",
|
||||||
|
"tinystdio/fgetc.c",
|
||||||
|
"tinystdio/fgets.c",
|
||||||
|
"tinystdio/fileno.c",
|
||||||
|
"tinystdio/filestrget.c",
|
||||||
|
"tinystdio/filestrputalloc.c",
|
||||||
|
"tinystdio/filestrput.c",
|
||||||
|
//"tinystdio/fopen.c", // posix-io
|
||||||
|
"tinystdio/fprintf.c",
|
||||||
|
"tinystdio/fputc.c",
|
||||||
|
"tinystdio/fputs.c",
|
||||||
|
"tinystdio/fread.c",
|
||||||
|
"tinystdio/fscanf.c",
|
||||||
|
"tinystdio/fseek.c",
|
||||||
|
"tinystdio/ftell.c",
|
||||||
|
"tinystdio/ftoa_data.c",
|
||||||
|
"tinystdio/ftoa_engine.c",
|
||||||
|
"tinystdio/ftoa_ryu.c",
|
||||||
|
"tinystdio/fwrite.c",
|
||||||
|
"tinystdio/gcvtbuf.c",
|
||||||
|
"tinystdio/gcvt.c",
|
||||||
|
"tinystdio/gcvtfbuf.c",
|
||||||
|
"tinystdio/gcvtf.c",
|
||||||
|
"tinystdio/getchar.c",
|
||||||
|
"tinystdio/gets.c",
|
||||||
|
"tinystdio/matchcaseprefix.c",
|
||||||
|
"tinystdio/perror.c",
|
||||||
|
//"tinystdio/posixiob.c", // posix-io
|
||||||
|
//"tinystdio/posixio.c", // posix-io
|
||||||
|
"tinystdio/printf.c",
|
||||||
|
"tinystdio/putchar.c",
|
||||||
|
"tinystdio/puts.c",
|
||||||
|
"tinystdio/ryu_divpow2.c",
|
||||||
|
"tinystdio/ryu_log10.c",
|
||||||
|
"tinystdio/ryu_log2pow5.c",
|
||||||
|
"tinystdio/ryu_pow5bits.c",
|
||||||
|
"tinystdio/ryu_table.c",
|
||||||
|
"tinystdio/ryu_umul128.c",
|
||||||
|
"tinystdio/scanf.c",
|
||||||
|
"tinystdio/setbuf.c",
|
||||||
|
"tinystdio/setvbuf.c",
|
||||||
|
//"tinystdio/sflags.c", // posix-io
|
||||||
|
"tinystdio/snprintf.c",
|
||||||
|
"tinystdio/snprintfd.c",
|
||||||
|
"tinystdio/snprintff.c",
|
||||||
|
"tinystdio/sprintf.c",
|
||||||
|
"tinystdio/sprintfd.c",
|
||||||
|
"tinystdio/sprintff.c",
|
||||||
|
"tinystdio/sscanf.c",
|
||||||
|
"tinystdio/strfromd.c",
|
||||||
|
"tinystdio/strfromf.c",
|
||||||
|
"tinystdio/strtod.c",
|
||||||
|
"tinystdio/strtod_l.c",
|
||||||
|
"tinystdio/strtof.c",
|
||||||
|
//"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
|
||||||
|
//"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
|
||||||
|
"tinystdio/ungetc.c",
|
||||||
|
"tinystdio/vasprintf.c",
|
||||||
|
"tinystdio/vfiprintf.c",
|
||||||
|
"tinystdio/vfiscanf.c",
|
||||||
|
"tinystdio/vfprintf.c",
|
||||||
|
"tinystdio/vfprintff.c",
|
||||||
|
"tinystdio/vfscanf.c",
|
||||||
|
"tinystdio/vfscanff.c",
|
||||||
|
"tinystdio/vprintf.c",
|
||||||
|
"tinystdio/vscanf.c",
|
||||||
|
"tinystdio/vsnprintf.c",
|
||||||
|
"tinystdio/vsprintf.c",
|
||||||
|
"tinystdio/vsscanf.c",
|
||||||
|
|
||||||
"string/bcmp.c",
|
"string/bcmp.c",
|
||||||
"string/bcopy.c",
|
"string/bcopy.c",
|
||||||
"string/bzero.c",
|
"string/bzero.c",
|
||||||
|
|||||||
+524
-109
@@ -1,16 +1,30 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aykevl/go-wasm"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Set to true to print extra debug logs.
|
||||||
|
const sizesDebug = false
|
||||||
|
|
||||||
// programSize contains size statistics per package of a compiled program.
|
// programSize contains size statistics per package of a compiled program.
|
||||||
type programSize struct {
|
type programSize struct {
|
||||||
Packages map[string]*packageSize
|
Packages map[string]packageSize
|
||||||
Sum *packageSize
|
|
||||||
Code uint64
|
Code uint64
|
||||||
|
ROData uint64
|
||||||
Data uint64
|
Data uint64
|
||||||
BSS uint64
|
BSS uint64
|
||||||
}
|
}
|
||||||
@@ -26,6 +40,16 @@ func (ps *programSize) sortedPackageNames() []string {
|
|||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flash usage in regular microcontrollers.
|
||||||
|
func (ps *programSize) Flash() uint64 {
|
||||||
|
return ps.Code + ps.ROData + ps.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static RAM usage in regular microcontrollers.
|
||||||
|
func (ps *programSize) RAM() uint64 {
|
||||||
|
return ps.Data + ps.BSS
|
||||||
|
}
|
||||||
|
|
||||||
// packageSize contains the size of a package, calculated from the linked object
|
// packageSize contains the size of a package, calculated from the linked object
|
||||||
// file.
|
// file.
|
||||||
type packageSize struct {
|
type packageSize struct {
|
||||||
@@ -45,129 +69,520 @@ func (ps *packageSize) RAM() uint64 {
|
|||||||
return ps.Data + ps.BSS
|
return ps.Data + ps.BSS
|
||||||
}
|
}
|
||||||
|
|
||||||
type symbolList []elf.Symbol
|
// A mapping of a single chunk of code or data to a file path.
|
||||||
|
type addressLine struct {
|
||||||
func (l symbolList) Len() int {
|
Address uint64
|
||||||
return len(l)
|
Length uint64 // length of this chunk
|
||||||
|
File string // file path as stored in DWARF
|
||||||
|
IsVariable bool // true if this is a variable (or constant), false if it is code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l symbolList) Less(i, j int) bool {
|
// Sections defined in the input file. This struct defines them in a
|
||||||
bind_i := elf.ST_BIND(l[i].Info)
|
// filetype-agnostic way but roughly follow the ELF types (.text, .data, .bss,
|
||||||
bind_j := elf.ST_BIND(l[j].Info)
|
// etc).
|
||||||
if l[i].Value == l[j].Value && bind_i != elf.STB_WEAK && bind_j == elf.STB_WEAK {
|
type memorySection struct {
|
||||||
// sort weak symbols after non-weak symbols
|
Type memoryType
|
||||||
return true
|
Address uint64
|
||||||
|
Size uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
memoryCode memoryType = iota + 1
|
||||||
|
memoryData
|
||||||
|
memoryROData
|
||||||
|
memoryBSS
|
||||||
|
memoryStack
|
||||||
|
)
|
||||||
|
|
||||||
|
// Regular expressions to match particular symbol names. These are not stored as
|
||||||
|
// DWARF variables because they have no mapping to source code global variables.
|
||||||
|
var (
|
||||||
|
// Various globals that aren't a variable but nonetheless need to be stored
|
||||||
|
// somewhere:
|
||||||
|
// alloc: heap allocations during init interpretation
|
||||||
|
// pack: data created when storing a constant in an interface for example
|
||||||
|
// string: buffer behind strings
|
||||||
|
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
||||||
|
|
||||||
|
// Reflect sidetables. Created by the reflect lowering pass.
|
||||||
|
// See src/reflect/sidetables.go.
|
||||||
|
reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// readProgramSizeFromDWARF reads the source location for each line of code and
|
||||||
|
// each variable in the program, as far as this is stored in the DWARF debug
|
||||||
|
// information.
|
||||||
|
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
|
||||||
|
r := data.Reader()
|
||||||
|
var lines []*dwarf.LineFile
|
||||||
|
var addresses []addressLine
|
||||||
|
for {
|
||||||
|
e, err := r.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch e.Tag {
|
||||||
|
case dwarf.TagCompileUnit:
|
||||||
|
// Found a compile unit.
|
||||||
|
// We can read the .debug_line section using it, which contains a
|
||||||
|
// mapping for most instructions to their file/line/column - even
|
||||||
|
// for inlined functions!
|
||||||
|
lr, err := data.LineReader(e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lines = lr.Files()
|
||||||
|
var lineEntry = dwarf.LineEntry{
|
||||||
|
EndSequence: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line tables are organized as sequences of line entries until an
|
||||||
|
// end sequence. A single line table can contain multiple such
|
||||||
|
// sequences. The last line entry is an EndSequence to indicate the
|
||||||
|
// end.
|
||||||
|
for {
|
||||||
|
// Read the next .debug_line entry.
|
||||||
|
prevLineEntry := lineEntry
|
||||||
|
err := lr.Next(&lineEntry)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevLineEntry.EndSequence && lineEntry.Address == 0 {
|
||||||
|
// Tombstone value. This symbol has been removed, for
|
||||||
|
// example by the --gc-sections linker flag. It is still
|
||||||
|
// here in the debug information because the linker can't
|
||||||
|
// just remove this reference.
|
||||||
|
// Read until the next EndSequence so that this sequence is
|
||||||
|
// skipped.
|
||||||
|
// For more details, see (among others):
|
||||||
|
// https://reviews.llvm.org/D84825
|
||||||
|
for {
|
||||||
|
err := lr.Next(&lineEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if lineEntry.EndSequence {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !prevLineEntry.EndSequence {
|
||||||
|
// The chunk describes the code from prevLineEntry to
|
||||||
|
// lineEntry.
|
||||||
|
line := addressLine{
|
||||||
|
Address: prevLineEntry.Address + codeOffset,
|
||||||
|
Length: lineEntry.Address - prevLineEntry.Address,
|
||||||
|
File: prevLineEntry.File.Name,
|
||||||
|
}
|
||||||
|
if line.Length != 0 {
|
||||||
|
addresses = append(addresses, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case dwarf.TagVariable:
|
||||||
|
// Global variable (or constant). Most of these are not actually
|
||||||
|
// stored in the binary, because they have been optimized out. Only
|
||||||
|
// the ones with a location are still present.
|
||||||
|
r.SkipChildren()
|
||||||
|
|
||||||
|
file := e.AttrField(dwarf.AttrDeclFile)
|
||||||
|
location := e.AttrField(dwarf.AttrLocation)
|
||||||
|
globalType := e.AttrField(dwarf.AttrType)
|
||||||
|
if file == nil || location == nil || globalType == nil {
|
||||||
|
// Doesn't contain the requested information.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse the location. While this could in theory be a very
|
||||||
|
// complex expression, usually it's just a DW_OP_addr opcode
|
||||||
|
// followed by an address.
|
||||||
|
locationCode := location.Val.([]uint8)
|
||||||
|
if locationCode[0] != 3 { // DW_OP_addr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var addr uint64
|
||||||
|
switch len(locationCode) {
|
||||||
|
case 1 + 2:
|
||||||
|
addr = uint64(binary.LittleEndian.Uint16(locationCode[1:]))
|
||||||
|
case 1 + 4:
|
||||||
|
addr = uint64(binary.LittleEndian.Uint32(locationCode[1:]))
|
||||||
|
case 1 + 8:
|
||||||
|
addr = binary.LittleEndian.Uint64(locationCode[1:])
|
||||||
|
default:
|
||||||
|
continue // unknown address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the type of the global variable, which (importantly)
|
||||||
|
// contains the variable size. We're not interested in the type,
|
||||||
|
// only in the size.
|
||||||
|
typ, err := data.Type(globalType.Val.(dwarf.Offset))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses = append(addresses, addressLine{
|
||||||
|
Address: addr,
|
||||||
|
Length: uint64(typ.Size()),
|
||||||
|
File: lines[file.Val.(int64)].Name,
|
||||||
|
IsVariable: true,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
r.SkipChildren()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return l[i].Value < l[j].Value
|
return addresses, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (l symbolList) Swap(i, j int) {
|
|
||||||
l[i], l[j] = l[j], l[i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadProgramSize calculate a program/data size breakdown of each package for a
|
// loadProgramSize calculate a program/data size breakdown of each package for a
|
||||||
// given ELF file.
|
// given ELF file.
|
||||||
func loadProgramSize(path string) (*programSize, error) {
|
// If the file doesn't contain DWARF debug information, the returned program
|
||||||
file, err := elf.Open(path)
|
// size will still have valid summaries but won't have complete size information
|
||||||
|
// per package.
|
||||||
|
func loadProgramSize(path string, packagePathMap map[string]string) (*programSize, error) {
|
||||||
|
// Open the binary file.
|
||||||
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var sumCode uint64
|
// This stores all chunks of addresses found in the binary.
|
||||||
var sumData uint64
|
var addresses []addressLine
|
||||||
var sumBSS uint64
|
|
||||||
for _, section := range file.Sections {
|
|
||||||
if section.Flags&elf.SHF_ALLOC == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if section.Name == ".stack" {
|
|
||||||
// HACK: this works around a bug in ld.lld from LLVM 10. The linker
|
|
||||||
// marks sections with no input symbols (such as is the case for the
|
|
||||||
// .stack section) as SHT_PROGBITS instead of SHT_NOBITS. While it
|
|
||||||
// doesn't affect the generated binaries (.hex and .bin), it does
|
|
||||||
// affect the reported size.
|
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=45336
|
|
||||||
// https://reviews.llvm.org/D76981
|
|
||||||
// It has been merged in master, but it has not (yet) been
|
|
||||||
// backported to the LLVM 10 release branch.
|
|
||||||
sumBSS += section.Size
|
|
||||||
} else if section.Type == elf.SHT_NOBITS {
|
|
||||||
sumBSS += section.Size
|
|
||||||
} else if section.Flags&elf.SHF_EXECINSTR != 0 {
|
|
||||||
sumCode += section.Size
|
|
||||||
} else if section.Flags&elf.SHF_WRITE != 0 {
|
|
||||||
sumData += section.Size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allSymbols, err := file.Symbols()
|
// Load the binary file, which could be in a number of file formats.
|
||||||
if err != nil {
|
var sections []memorySection
|
||||||
return nil, err
|
if file, err := elf.NewFile(f); err == nil {
|
||||||
}
|
// Read DWARF information. The error is intentionally ignored.
|
||||||
symbols := make([]elf.Symbol, 0, len(allSymbols))
|
data, _ := file.DWARF()
|
||||||
for _, symbol := range allSymbols {
|
if data != nil {
|
||||||
symType := elf.ST_TYPE(symbol.Info)
|
addresses, err = readProgramSizeFromDWARF(data, 0)
|
||||||
if symbol.Size == 0 {
|
if err != nil {
|
||||||
continue
|
// However, _do_ report an error here. Something must have gone
|
||||||
}
|
// wrong while trying to parse DWARF data.
|
||||||
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
|
return nil, err
|
||||||
continue
|
|
||||||
}
|
|
||||||
if symbol.Section >= elf.SectionIndex(len(file.Sections)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
section := file.Sections[symbol.Section]
|
|
||||||
if section.Flags&elf.SHF_ALLOC == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
symbols = append(symbols, symbol)
|
|
||||||
}
|
|
||||||
sort.Sort(symbolList(symbols))
|
|
||||||
|
|
||||||
sizes := map[string]*packageSize{}
|
|
||||||
var lastSymbolValue uint64
|
|
||||||
for _, symbol := range symbols {
|
|
||||||
symType := elf.ST_TYPE(symbol.Info)
|
|
||||||
//bind := elf.ST_BIND(symbol.Info)
|
|
||||||
section := file.Sections[symbol.Section]
|
|
||||||
pkgName := "(bootstrap)"
|
|
||||||
symName := strings.TrimLeft(symbol.Name, "(*")
|
|
||||||
dot := strings.IndexByte(symName, '.')
|
|
||||||
if dot > 0 {
|
|
||||||
pkgName = symName[:dot]
|
|
||||||
}
|
|
||||||
pkgSize := sizes[pkgName]
|
|
||||||
if pkgSize == nil {
|
|
||||||
pkgSize = &packageSize{}
|
|
||||||
sizes[pkgName] = pkgSize
|
|
||||||
}
|
|
||||||
if lastSymbolValue != symbol.Value || lastSymbolValue == 0 {
|
|
||||||
if symType == elf.STT_FUNC {
|
|
||||||
pkgSize.Code += symbol.Size
|
|
||||||
} else if section.Flags&elf.SHF_WRITE != 0 {
|
|
||||||
if section.Type == elf.SHT_NOBITS {
|
|
||||||
pkgSize.BSS += symbol.Size
|
|
||||||
} else {
|
|
||||||
pkgSize.Data += symbol.Size
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pkgSize.ROData += symbol.Size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastSymbolValue = symbol.Value
|
|
||||||
|
// Read the ELF symbols for some more chunks of location information.
|
||||||
|
// Some globals (such as strings) aren't stored in the DWARF debug
|
||||||
|
// information and therefore need to be obtained in a different way.
|
||||||
|
allSymbols, err := file.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, symbol := range allSymbols {
|
||||||
|
symType := elf.ST_TYPE(symbol.Info)
|
||||||
|
if symbol.Size == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
section := file.Sections[symbol.Section]
|
||||||
|
if section.Flags&elf.SHF_ALLOC == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
|
||||||
|
addresses = append(addresses, addressLine{
|
||||||
|
Address: symbol.Value,
|
||||||
|
Length: symbol.Size,
|
||||||
|
File: symbol.Name,
|
||||||
|
IsVariable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load allocated sections.
|
||||||
|
for _, section := range file.Sections {
|
||||||
|
if section.Flags&elf.SHF_ALLOC == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if section.Type == elf.SHT_NOBITS {
|
||||||
|
if section.Name == ".stack" {
|
||||||
|
// TinyGo emits stack sections on microcontroller using the
|
||||||
|
// ".stack" name.
|
||||||
|
// This is a bit ugly, but I don't think there is a way to
|
||||||
|
// mark the stack section in a linker script.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: section.Size,
|
||||||
|
Type: memoryStack,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Regular .bss section.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: section.Size,
|
||||||
|
Type: memoryBSS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_EXECINSTR != 0 {
|
||||||
|
// .text
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: section.Size,
|
||||||
|
Type: memoryCode,
|
||||||
|
})
|
||||||
|
} else if section.Type == elf.SHT_PROGBITS && section.Flags&elf.SHF_WRITE != 0 {
|
||||||
|
// .data
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: section.Size,
|
||||||
|
Type: memoryData,
|
||||||
|
})
|
||||||
|
} else if section.Type == elf.SHT_PROGBITS {
|
||||||
|
// .rodata
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: section.Size,
|
||||||
|
Type: memoryROData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if file, err := wasm.Parse(f); err == nil {
|
||||||
|
// File is in WebAssembly format.
|
||||||
|
|
||||||
|
// Put code at a very high address, so that it won't conflict with the
|
||||||
|
// data in the memory section.
|
||||||
|
const codeOffset = 0x8000_0000_0000_0000
|
||||||
|
|
||||||
|
// Read DWARF information. The error is intentionally ignored.
|
||||||
|
data, err := file.DWARF()
|
||||||
|
if data != nil {
|
||||||
|
addresses, err = readProgramSizeFromDWARF(data, codeOffset)
|
||||||
|
if err != nil {
|
||||||
|
// However, _do_ report an error here. Something must have gone
|
||||||
|
// wrong while trying to parse DWARF data.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var linearMemorySize uint64
|
||||||
|
for _, section := range file.Sections {
|
||||||
|
switch section := section.(type) {
|
||||||
|
case *wasm.SectionCode:
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: codeOffset,
|
||||||
|
Size: uint64(section.Size()),
|
||||||
|
Type: memoryCode,
|
||||||
|
})
|
||||||
|
case *wasm.SectionMemory:
|
||||||
|
// This value is used when processing *wasm.SectionData (which
|
||||||
|
// always comes after *wasm.SectionMemory).
|
||||||
|
linearMemorySize = uint64(section.Entries[0].Limits.Initial) * 64 * 1024
|
||||||
|
case *wasm.SectionData:
|
||||||
|
// Data sections contain initial values for linear memory.
|
||||||
|
// First load the list of data sections, and sort them by
|
||||||
|
// address for easier processing.
|
||||||
|
var dataSections []memorySection
|
||||||
|
for _, entry := range section.Entries {
|
||||||
|
address, err := wasm.Eval(bytes.NewBuffer(entry.Offset))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse data section address: %w", err)
|
||||||
|
}
|
||||||
|
dataSections = append(dataSections, memorySection{
|
||||||
|
Address: uint64(address[0].(int32)),
|
||||||
|
Size: uint64(len(entry.Data)),
|
||||||
|
Type: memoryData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(dataSections, func(i, j int) bool {
|
||||||
|
return dataSections[i].Address < dataSections[j].Address
|
||||||
|
})
|
||||||
|
|
||||||
|
// And now add all data sections for linear memory.
|
||||||
|
// Parts that are in the slice of data sections are added as
|
||||||
|
// memoryData, and parts that are not are added as memoryBSS.
|
||||||
|
addr := uint64(0)
|
||||||
|
for _, section := range dataSections {
|
||||||
|
if addr < section.Address {
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: addr,
|
||||||
|
Size: section.Address - addr,
|
||||||
|
Type: memoryBSS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if addr > section.Address {
|
||||||
|
// This might be allowed, I'm not sure.
|
||||||
|
// It certainly doesn't make a lot of sense.
|
||||||
|
return nil, fmt.Errorf("overlapping data section")
|
||||||
|
}
|
||||||
|
// addr == section.Address
|
||||||
|
sections = append(sections, section)
|
||||||
|
addr = section.Address + section.Size
|
||||||
|
}
|
||||||
|
if addr < linearMemorySize {
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: addr,
|
||||||
|
Size: linearMemorySize - addr,
|
||||||
|
Type: memoryBSS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("could not parse file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sum := &packageSize{}
|
// Sort the slice of address chunks by address, so that we can iterate
|
||||||
|
// through it to calculate section sizes.
|
||||||
|
sort.Slice(addresses, func(i, j int) bool {
|
||||||
|
if addresses[i].Address == addresses[j].Address {
|
||||||
|
// Very rarely, there might be duplicate addresses.
|
||||||
|
// If that happens, sort the largest chunks first.
|
||||||
|
return addresses[i].Length > addresses[j].Length
|
||||||
|
}
|
||||||
|
return addresses[i].Address < addresses[j].Address
|
||||||
|
})
|
||||||
|
|
||||||
|
// Now finally determine the binary/RAM size usage per package by going
|
||||||
|
// through each allocated section.
|
||||||
|
sizes := make(map[string]packageSize)
|
||||||
|
for _, section := range sections {
|
||||||
|
switch section.Type {
|
||||||
|
case memoryCode:
|
||||||
|
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||||
|
field := sizes[path]
|
||||||
|
if isVariable {
|
||||||
|
field.ROData += size
|
||||||
|
} else {
|
||||||
|
field.Code += size
|
||||||
|
}
|
||||||
|
sizes[path] = field
|
||||||
|
}, packagePathMap)
|
||||||
|
case memoryROData:
|
||||||
|
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||||
|
field := sizes[path]
|
||||||
|
field.ROData += size
|
||||||
|
sizes[path] = field
|
||||||
|
}, packagePathMap)
|
||||||
|
case memoryData:
|
||||||
|
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||||
|
field := sizes[path]
|
||||||
|
field.Data += size
|
||||||
|
sizes[path] = field
|
||||||
|
}, packagePathMap)
|
||||||
|
case memoryBSS:
|
||||||
|
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
||||||
|
field := sizes[path]
|
||||||
|
field.BSS += size
|
||||||
|
sizes[path] = field
|
||||||
|
}, packagePathMap)
|
||||||
|
case memoryStack:
|
||||||
|
// We store the C stack as a pseudo-package.
|
||||||
|
sizes["C stack"] = packageSize{
|
||||||
|
BSS: section.Size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and summarize the results.
|
||||||
|
program := &programSize{
|
||||||
|
Packages: sizes,
|
||||||
|
}
|
||||||
for _, pkg := range sizes {
|
for _, pkg := range sizes {
|
||||||
sum.Code += pkg.Code
|
program.Code += pkg.Code
|
||||||
sum.ROData += pkg.ROData
|
program.ROData += pkg.ROData
|
||||||
sum.Data += pkg.Data
|
program.Data += pkg.Data
|
||||||
sum.BSS += pkg.BSS
|
program.BSS += pkg.BSS
|
||||||
}
|
}
|
||||||
|
return program, nil
|
||||||
return &programSize{Packages: sizes, Code: sumCode, Data: sumData, BSS: sumBSS, Sum: sum}, nil
|
}
|
||||||
|
|
||||||
|
// readSection determines for each byte in this section to which package it
|
||||||
|
// belongs. It reports this usage through the addSize callback.
|
||||||
|
func readSection(section memorySection, addresses []addressLine, addSize func(string, uint64, bool), packagePathMap map[string]string) {
|
||||||
|
// The addr variable tracks at which address we are while going through this
|
||||||
|
// section. We start at the beginning.
|
||||||
|
addr := section.Address
|
||||||
|
sectionEnd := section.Address + section.Size
|
||||||
|
for _, line := range addresses {
|
||||||
|
if line.Address < section.Address || line.Address+line.Length >= sectionEnd {
|
||||||
|
// Check that this line is entirely within the section.
|
||||||
|
// Don't bother dealing with line entries that cross sections (that
|
||||||
|
// seems rather unlikely anyway).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if addr < line.Address {
|
||||||
|
// There is a gap: there is a space between the current and the
|
||||||
|
// previous line entry.
|
||||||
|
addSize("(unknown)", line.Address-addr, false)
|
||||||
|
if sizesDebug {
|
||||||
|
fmt.Printf("%08x..%08x %4d: unknown (gap)\n", addr, line.Address, line.Address-addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if addr > line.Address+line.Length {
|
||||||
|
// The current line is already covered by a previous line entry.
|
||||||
|
// Simply skip it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// At this point, addr falls within the current line (probably at the
|
||||||
|
// start).
|
||||||
|
length := line.Length
|
||||||
|
if addr > line.Address {
|
||||||
|
// There is some overlap: the previous line entry already covered
|
||||||
|
// part of this line entry. So reduce the length to add to the
|
||||||
|
// remaining bit of the line entry.
|
||||||
|
length = line.Length - (addr - line.Address)
|
||||||
|
}
|
||||||
|
// Finally, mark this chunk of memory as used by the given package.
|
||||||
|
addSize(findPackagePath(line.File, packagePathMap), length, line.IsVariable)
|
||||||
|
addr = line.Address + line.Length
|
||||||
|
}
|
||||||
|
if addr < sectionEnd {
|
||||||
|
// There is a gap at the end of the section.
|
||||||
|
addSize("(unknown)", sectionEnd-addr, false)
|
||||||
|
if sizesDebug {
|
||||||
|
fmt.Printf("%08x..%08x %4d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPackagePath returns the Go package (or a pseudo package) for the given
|
||||||
|
// path. It uses some heuristics, for example for some C libraries.
|
||||||
|
func findPackagePath(path string, packagePathMap map[string]string) string {
|
||||||
|
// Check whether this path is part of one of the compiled packages.
|
||||||
|
packagePath, ok := packagePathMap[filepath.Dir(path)]
|
||||||
|
if !ok {
|
||||||
|
if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")) {
|
||||||
|
// Emit C libraries (in the lib subdirectory of TinyGo) as a single
|
||||||
|
// package, with a "C" prefix. For example: "C compiler-rt" for the
|
||||||
|
// compiler runtime library from LLVM.
|
||||||
|
packagePath = "C " + strings.Split(strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")), string(os.PathSeparator))[1]
|
||||||
|
} else if packageSymbolRegexp.MatchString(path) {
|
||||||
|
// Parse symbol names like main$alloc or runtime$string.
|
||||||
|
packagePath = path[:strings.LastIndex(path, "$")]
|
||||||
|
} else if reflectDataRegexp.MatchString(path) {
|
||||||
|
// Parse symbol names like reflect.structTypesSidetable.
|
||||||
|
packagePath = "Go reflect data"
|
||||||
|
} else if path == "<Go interface assert>" {
|
||||||
|
// Interface type assert, generated by the interface lowering pass.
|
||||||
|
packagePath = "Go interface assert"
|
||||||
|
} else if path == "<Go interface method>" {
|
||||||
|
// Interface method wrapper (switch over all concrete types),
|
||||||
|
// generated by the interface lowering pass.
|
||||||
|
packagePath = "Go interface method"
|
||||||
|
} else if path == "<stdin>" {
|
||||||
|
// This can happen when the source code (in Go) doesn't have a
|
||||||
|
// source file and uses "-" as the location. Somewhere this is
|
||||||
|
// converted to "<stdin>".
|
||||||
|
// Convert this back to the "-" string. Eventually, this should be
|
||||||
|
// fixed in the compiler.
|
||||||
|
packagePath = "-"
|
||||||
|
} else {
|
||||||
|
// This is some other path. Not sure what it is, so just emit its directory.
|
||||||
|
packagePath = filepath.Dir(path) // fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packagePath
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -24,7 +24,7 @@ func runCCompiler(flags ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compile this with an external invocation of the Clang compiler.
|
// Compile this with an external invocation of the Clang compiler.
|
||||||
return execCommand(commands["clang"], flags...)
|
return execCommand("clang", flags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// link invokes a linker with the given name and flags.
|
// link invokes a linker with the given name and flags.
|
||||||
@@ -38,8 +38,8 @@ func link(linker string, flags ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to external command.
|
// Fall back to external command.
|
||||||
if cmdNames, ok := commands[linker]; ok {
|
if _, ok := commands[linker]; ok {
|
||||||
return execCommand(cmdNames, flags...)
|
return execCommand(linker, flags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(linker, flags...)
|
cmd := exec.Command(linker, flags...)
|
||||||
|
|||||||
+3
-1
@@ -141,11 +141,13 @@ func split(input []byte, limit int) [][]byte {
|
|||||||
var block []byte
|
var block []byte
|
||||||
output := make([][]byte, 0, len(input)/limit+1)
|
output := make([][]byte, 0, len(input)/limit+1)
|
||||||
for len(input) >= limit {
|
for len(input) >= limit {
|
||||||
|
// add all blocks
|
||||||
block, input = input[:limit], input[limit:]
|
block, input = input[:limit], input[limit:]
|
||||||
output = append(output, block)
|
output = append(output, block)
|
||||||
}
|
}
|
||||||
if len(input) > 0 {
|
if len(input) > 0 {
|
||||||
output = append(output, input[:len(input)])
|
// add remaining block (that isn't full sized)
|
||||||
|
output = append(output, input)
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|||||||
+167
-147
@@ -25,12 +25,19 @@ import (
|
|||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version of the cgo package. It must be incremented whenever the cgo package
|
||||||
|
// is changed in a way that affects the output so that cached package builds
|
||||||
|
// will be invalidated.
|
||||||
|
// This version is independent of the TinyGo version number.
|
||||||
|
const Version = 1 // last change: run libclang once per Go file
|
||||||
|
|
||||||
// cgoPackage holds all CGo-related information of a package.
|
// cgoPackage holds all CGo-related information of a package.
|
||||||
type cgoPackage struct {
|
type cgoPackage struct {
|
||||||
generated *ast.File
|
generated *ast.File
|
||||||
generatedPos token.Pos
|
generatedPos token.Pos
|
||||||
errors []error
|
errors []error
|
||||||
dir string
|
currentDir string // current working directory
|
||||||
|
packageDir string // full path to the package to process
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
tokenFiles map[string]*token.File
|
tokenFiles map[string]*token.File
|
||||||
missingSymbols map[string]struct{}
|
missingSymbols map[string]struct{}
|
||||||
@@ -124,17 +131,17 @@ var cgoAliases = map[string]string{
|
|||||||
// builtinAliases are handled specially because they only exist on the Go side
|
// builtinAliases are handled specially because they only exist on the Go side
|
||||||
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
||||||
var builtinAliases = map[string]struct{}{
|
var builtinAliases = map[string]struct{}{
|
||||||
"char": struct{}{},
|
"char": {},
|
||||||
"schar": struct{}{},
|
"schar": {},
|
||||||
"uchar": struct{}{},
|
"uchar": {},
|
||||||
"short": struct{}{},
|
"short": {},
|
||||||
"ushort": struct{}{},
|
"ushort": {},
|
||||||
"int": struct{}{},
|
"int": {},
|
||||||
"uint": struct{}{},
|
"uint": {},
|
||||||
"long": struct{}{},
|
"long": {},
|
||||||
"ulong": struct{}{},
|
"ulong": {},
|
||||||
"longlong": struct{}{},
|
"longlong": {},
|
||||||
"ulonglong": struct{}{},
|
"ulonglong": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
||||||
@@ -158,12 +165,13 @@ typedef unsigned long long _Cgo_ulonglong;
|
|||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
// with libclang, and modifies the AST to use this information. It returns a
|
// with libclang, and modifies the AST to use this information. It returns a
|
||||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
// newly created *ast.File that should be added to the list of to-be-parsed
|
||||||
// files, the CFLAGS and LDFLAGS found in #cgo lines, and a map of file hashes
|
// files, the CGo header snippets that should be compiled (for inline
|
||||||
// of the accessed C header files. If there is one or more error, it returns
|
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||||
// these in the []error slice but still modifies the AST.
|
// hashes of the accessed C header files. If there is one or more error, it
|
||||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, map[string][]byte, []error) {
|
// returns these in the []error slice but still modifies the AST.
|
||||||
|
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
dir: dir,
|
currentDir: dir,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
tokenFiles: map[string]*token.File{},
|
tokenFiles: map[string]*token.File{},
|
||||||
missingSymbols: map[string]struct{}{},
|
missingSymbols: map[string]struct{}{},
|
||||||
@@ -184,14 +192,14 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
// Find the absolute path for this package.
|
// Find the absolute path for this package.
|
||||||
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, []error{
|
return nil, nil, nil, nil, nil, []error{
|
||||||
scanner.Error{
|
scanner.Error{
|
||||||
Pos: fset.Position(files[0].Pos()),
|
Pos: fset.Position(files[0].Pos()),
|
||||||
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagePath = filepath.Dir(packagePath)
|
p.packageDir = filepath.Dir(packagePath)
|
||||||
|
|
||||||
// Construct a new in-memory AST for CGo declarations of this package.
|
// Construct a new in-memory AST for CGo declarations of this package.
|
||||||
unsafeImport := &ast.ImportSpec{
|
unsafeImport := &ast.ImportSpec{
|
||||||
@@ -224,7 +232,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
Specs: []ast.Spec{
|
Specs: []ast.Spec{
|
||||||
&ast.ValueSpec{
|
&ast.ValueSpec{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
Name: "_",
|
Name: "_",
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
@@ -255,9 +263,10 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
p.missingSymbols["_Cgo_"+name] = struct{}{}
|
p.missingSymbols["_Cgo_"+name] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find `import "C"` statements in the file.
|
// Find `import "C"` C fragments in the file.
|
||||||
var statements []*ast.GenDecl
|
cgoHeaders := make([]string, len(files)) // combined CGo header fragment for each file
|
||||||
for _, f := range files {
|
for i, f := range files {
|
||||||
|
var cgoHeader string
|
||||||
for i := 0; i < len(f.Decls); i++ {
|
for i := 0; i < len(f.Decls); i++ {
|
||||||
decl := f.Decls[i]
|
decl := f.Decls[i]
|
||||||
genDecl, ok := decl.(*ast.GenDecl)
|
genDecl, ok := decl.(*ast.GenDecl)
|
||||||
@@ -281,105 +290,40 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Found a CGo statement.
|
|
||||||
statements = append(statements, genDecl)
|
|
||||||
|
|
||||||
// Remove this import declaration.
|
// Remove this import declaration.
|
||||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||||
i--
|
i--
|
||||||
}
|
|
||||||
|
|
||||||
// Print the AST, for debugging.
|
if genDecl.Doc == nil {
|
||||||
//ast.Print(fset, f)
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
// Find all #cgo lines.
|
|
||||||
for _, genDecl := range statements {
|
|
||||||
if genDecl.Doc == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, comment := range genDecl.Doc.List {
|
|
||||||
for {
|
|
||||||
// Extract the #cgo line, and replace it with spaces.
|
|
||||||
// Replacing with spaces makes sure that error locations are
|
|
||||||
// still correct, while not interfering with parsing in any way.
|
|
||||||
lineStart := strings.Index(comment.Text, "#cgo ")
|
|
||||||
if lineStart < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
lineLen := strings.IndexByte(comment.Text[lineStart:], '\n')
|
|
||||||
if lineLen < 0 {
|
|
||||||
lineLen = len(comment.Text) - lineStart
|
|
||||||
}
|
|
||||||
lineEnd := lineStart + lineLen
|
|
||||||
line := comment.Text[lineStart:lineEnd]
|
|
||||||
spaces := make([]byte, len(line))
|
|
||||||
for i := range spaces {
|
|
||||||
spaces[i] = ' '
|
|
||||||
}
|
|
||||||
lenBefore := len(comment.Text)
|
|
||||||
comment.Text = comment.Text[:lineStart] + string(spaces) + comment.Text[lineEnd:]
|
|
||||||
if len(comment.Text) != lenBefore {
|
|
||||||
println(lenBefore, len(comment.Text))
|
|
||||||
panic("length of preamble changed!")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the text before the colon in the #cgo directive.
|
|
||||||
colon := strings.IndexByte(line, ':')
|
|
||||||
if colon < 0 {
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart], "missing colon in #cgo line")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the fields before the colon. These fields are a list
|
|
||||||
// of build tags and the C environment variable.
|
|
||||||
fields := strings.Fields(line[4:colon])
|
|
||||||
if len(fields) == 0 {
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon-1], "invalid #cgo line")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fields) > 1 {
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+5], "not implemented: build constraints in #cgo line")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
name := fields[len(fields)-1]
|
|
||||||
value := line[colon+1:]
|
|
||||||
switch name {
|
|
||||||
case "CFLAGS":
|
|
||||||
flags, err := shlex.Split(value)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: find the exact location where the error happened.
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkCompilerFlags(name, flags); err != nil {
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
makePathsAbsolute(flags, packagePath)
|
|
||||||
p.cflags = append(p.cflags, flags...)
|
|
||||||
case "LDFLAGS":
|
|
||||||
flags, err := shlex.Split(value)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: find the exact location where the error happened.
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkLinkerFlags(name, flags); err != nil {
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
makePathsAbsolute(flags, packagePath)
|
|
||||||
p.ldflags = append(p.ldflags, flags...)
|
|
||||||
default:
|
|
||||||
startPos := strings.LastIndex(line[4:colon], name) + 4
|
|
||||||
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+startPos], "invalid #cgo line: "+name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate through all parts of the CGo header. Note that every //
|
||||||
|
// line is a new comment.
|
||||||
|
position := fset.Position(genDecl.Doc.Pos())
|
||||||
|
fragment := fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)
|
||||||
|
for _, comment := range genDecl.Doc.List {
|
||||||
|
// Find all #cgo lines, extract and use their contents, and
|
||||||
|
// replace the lines with spaces (to preserve locations).
|
||||||
|
c := p.parseCGoPreprocessorLines(comment.Text, comment.Slash)
|
||||||
|
|
||||||
|
// Change the comment (which is still in Go syntax, with // and
|
||||||
|
// /* */ ) to a regular string by replacing the start/end
|
||||||
|
// markers of comments with spaces.
|
||||||
|
// It is similar to the Text() method but differs in that it
|
||||||
|
// doesn't strip anything and tries to keep all offsets correct
|
||||||
|
// by adding spaces and newlines where necessary.
|
||||||
|
if c[1] == '/' { /* comment */
|
||||||
|
c = " " + c[2:]
|
||||||
|
} else { // comment
|
||||||
|
c = " " + c[2:len(c)-2]
|
||||||
|
}
|
||||||
|
fragment += c + "\n"
|
||||||
|
}
|
||||||
|
cgoHeader += fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cgoHeaders[i] = cgoHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define CFlags that will be used while parsing the package.
|
// Define CFlags that will be used while parsing the package.
|
||||||
@@ -389,16 +333,9 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
|||||||
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
||||||
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
||||||
|
|
||||||
// Process all CGo imports.
|
// Process CGo imports for each file.
|
||||||
for _, genDecl := range statements {
|
for i, f := range files {
|
||||||
cgoComment := genDecl.Doc.Text()
|
p.parseFragment(cgoHeaders[i]+cgoTypes, cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()))
|
||||||
|
|
||||||
pos := genDecl.Pos()
|
|
||||||
if genDecl.Doc != nil {
|
|
||||||
pos = genDecl.Doc.Pos()
|
|
||||||
}
|
|
||||||
position := fset.PositionFor(pos, true)
|
|
||||||
p.parseFragment(cgoComment+cgoTypes, cflagsForCGo, position.Filename, position.Line)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declare functions found by libclang.
|
// Declare functions found by libclang.
|
||||||
@@ -433,18 +370,18 @@ 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, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
||||||
// relative flags into absolute flags, if they are relative. This is necessary
|
// relative flags into absolute flags, if they are relative. This is necessary
|
||||||
// because the C compiler is usually not invoked from the package path.
|
// because the C compiler is usually not invoked from the package path.
|
||||||
func makePathsAbsolute(args []string, packagePath string) {
|
func (p *cgoPackage) makePathsAbsolute(args []string) {
|
||||||
nextIsPath := false
|
nextIsPath := false
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
if nextIsPath {
|
if nextIsPath {
|
||||||
if !filepath.IsAbs(arg) {
|
if !filepath.IsAbs(arg) {
|
||||||
args[i] = filepath.Join(packagePath, arg)
|
args[i] = filepath.Join(p.packageDir, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if arg == "-I" || arg == "-L" {
|
if arg == "-I" || arg == "-L" {
|
||||||
@@ -454,12 +391,95 @@ func makePathsAbsolute(args []string, packagePath string) {
|
|||||||
if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
|
if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
|
||||||
path := arg[2:]
|
path := arg[2:]
|
||||||
if !filepath.IsAbs(path) {
|
if !filepath.IsAbs(path) {
|
||||||
args[i] = arg[:2] + filepath.Join(packagePath, path)
|
args[i] = arg[:2] + filepath.Join(p.packageDir, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseCGoPreprocessorLines reads #cgo pseudo-preprocessor lines in the source
|
||||||
|
// text (import "C" fragment), stores their information such as CFLAGS, and
|
||||||
|
// returns the same text but with those #cgo lines replaced by spaces (to keep
|
||||||
|
// position offsets the same).
|
||||||
|
func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) string {
|
||||||
|
for {
|
||||||
|
// Extract the #cgo line, and replace it with spaces.
|
||||||
|
// Replacing with spaces makes sure that error locations are
|
||||||
|
// still correct, while not interfering with parsing in any way.
|
||||||
|
lineStart := strings.Index(text, "#cgo ")
|
||||||
|
if lineStart < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lineLen := strings.IndexByte(text[lineStart:], '\n')
|
||||||
|
if lineLen < 0 {
|
||||||
|
lineLen = len(text) - lineStart
|
||||||
|
}
|
||||||
|
lineEnd := lineStart + lineLen
|
||||||
|
line := text[lineStart:lineEnd]
|
||||||
|
spaces := make([]byte, len(line))
|
||||||
|
for i := range spaces {
|
||||||
|
spaces[i] = ' '
|
||||||
|
}
|
||||||
|
text = text[:lineStart] + string(spaces) + text[lineEnd:]
|
||||||
|
|
||||||
|
// Get the text before the colon in the #cgo directive.
|
||||||
|
colon := strings.IndexByte(line, ':')
|
||||||
|
if colon < 0 {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart], "missing colon in #cgo line")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the fields before the colon. These fields are a list
|
||||||
|
// of build tags and the C environment variable.
|
||||||
|
fields := strings.Fields(line[4:colon])
|
||||||
|
if len(fields) == 0 {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+colon-1], "invalid #cgo line")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fields) > 1 {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+5], "not implemented: build constraints in #cgo line")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := fields[len(fields)-1]
|
||||||
|
value := line[colon+1:]
|
||||||
|
switch name {
|
||||||
|
case "CFLAGS":
|
||||||
|
flags, err := shlex.Split(value)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: find the exact location where the error happened.
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := checkCompilerFlags(name, flags); err != nil {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+colon+1], err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.makePathsAbsolute(flags)
|
||||||
|
p.cflags = append(p.cflags, flags...)
|
||||||
|
case "LDFLAGS":
|
||||||
|
flags, err := shlex.Split(value)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: find the exact location where the error happened.
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := checkLinkerFlags(name, flags); err != nil {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+colon+1], err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.makePathsAbsolute(flags)
|
||||||
|
p.ldflags = append(p.ldflags, flags...)
|
||||||
|
default:
|
||||||
|
startPos := strings.LastIndex(line[4:colon], name) + 4
|
||||||
|
p.addErrorAfter(pos, text[:lineStart+startPos], "invalid #cgo line: "+name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
// addFuncDecls adds the C function declarations found by libclang in the
|
// addFuncDecls adds the C function declarations found by libclang in the
|
||||||
// comment above the `import "C"` statement.
|
// comment above the `import "C"` statement.
|
||||||
func (p *cgoPackage) addFuncDecls() {
|
func (p *cgoPackage) addFuncDecls() {
|
||||||
@@ -494,7 +514,7 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
if fn.variadic {
|
if fn.variadic {
|
||||||
decl.Doc = &ast.CommentGroup{
|
decl.Doc = &ast.CommentGroup{
|
||||||
List: []*ast.Comment{
|
List: []*ast.Comment{
|
||||||
&ast.Comment{
|
{
|
||||||
Slash: fn.pos,
|
Slash: fn.pos,
|
||||||
Text: "//go:variadic",
|
Text: "//go:variadic",
|
||||||
},
|
},
|
||||||
@@ -505,7 +525,7 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
for i, arg := range fn.args {
|
for i, arg := range fn.args {
|
||||||
args[i] = &ast.Field{
|
args[i] = &ast.Field{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: fn.pos,
|
NamePos: fn.pos,
|
||||||
Name: arg.name,
|
Name: arg.name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
@@ -553,7 +573,7 @@ func (p *cgoPackage) addFuncPtrDecls() {
|
|||||||
Name: "C." + name + "$funcaddr",
|
Name: "C." + name + "$funcaddr",
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
Names: []*ast.Ident{{
|
||||||
NamePos: fn.pos,
|
NamePos: fn.pos,
|
||||||
Name: "C." + name + "$funcaddr",
|
Name: "C." + name + "$funcaddr",
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
@@ -603,7 +623,7 @@ func (p *cgoPackage) addConstDecls() {
|
|||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
Names: []*ast.Ident{{
|
||||||
NamePos: constVal.pos,
|
NamePos: constVal.pos,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
@@ -644,7 +664,7 @@ func (p *cgoPackage) addVarDecls() {
|
|||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
Names: []*ast.Ident{{
|
||||||
NamePos: global.pos,
|
NamePos: global.pos,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
@@ -845,9 +865,9 @@ func (p *cgoPackage) makeUnionField(typ *elaboratedTypeInfo) *ast.StructType {
|
|||||||
Struct: typ.typeExpr.Struct,
|
Struct: typ.typeExpr.Struct,
|
||||||
Fields: &ast.FieldList{
|
Fields: &ast.FieldList{
|
||||||
Opening: typ.typeExpr.Fields.Opening,
|
Opening: typ.typeExpr.Fields.Opening,
|
||||||
List: []*ast.Field{&ast.Field{
|
List: []*ast.Field{{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: typ.typeExpr.Fields.Opening,
|
NamePos: typ.typeExpr.Fields.Opening,
|
||||||
Name: "$union",
|
Name: "$union",
|
||||||
},
|
},
|
||||||
@@ -928,9 +948,9 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
|||||||
Recv: &ast.FieldList{
|
Recv: &ast.FieldList{
|
||||||
Opening: pos,
|
Opening: pos,
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "union",
|
Name: "union",
|
||||||
},
|
},
|
||||||
@@ -959,7 +979,7 @@ func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
|||||||
},
|
},
|
||||||
Results: &ast.FieldList{
|
Results: &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Type: &ast.StarExpr{
|
Type: &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: field.Type,
|
X: field.Type,
|
||||||
@@ -1038,9 +1058,9 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
|||||||
Recv: &ast.FieldList{
|
Recv: &ast.FieldList{
|
||||||
Opening: bitfield.pos,
|
Opening: bitfield.pos,
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
@@ -1074,7 +1094,7 @@ func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string
|
|||||||
},
|
},
|
||||||
Results: &ast.FieldList{
|
Results: &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Type: bitfield.field.Type,
|
Type: bitfield.field.Type,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1191,9 +1211,9 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
Recv: &ast.FieldList{
|
Recv: &ast.FieldList{
|
||||||
Opening: bitfield.pos,
|
Opening: bitfield.pos,
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "s",
|
Name: "s",
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
@@ -1224,9 +1244,9 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string
|
|||||||
Params: &ast.FieldList{
|
Params: &ast.FieldList{
|
||||||
Opening: bitfield.pos,
|
Opening: bitfield.pos,
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: bitfield.pos,
|
NamePos: bitfield.pos,
|
||||||
Name: "value",
|
Name: "value",
|
||||||
Obj: nil,
|
Obj: nil,
|
||||||
|
|||||||
+3
-12
@@ -12,7 +12,6 @@ import (
|
|||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -25,19 +24,11 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
|||||||
// platforms and Go versions.
|
// platforms and Go versions.
|
||||||
func normalizeResult(result string) string {
|
func normalizeResult(result string) string {
|
||||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
||||||
|
|
||||||
// Make sure all functions are wrapped, even those that would otherwise be
|
|
||||||
// single-line functions. This is necessary because Go 1.14 changed the way
|
|
||||||
// such functions are wrapped and it's important to have consistent test
|
|
||||||
// results.
|
|
||||||
re := regexp.MustCompile(`func \((.+)\)( .*?) +{ (.+) }`)
|
|
||||||
actual = re.ReplaceAllString(actual, "func ($1)$2 {\n\t$3\n}")
|
|
||||||
|
|
||||||
return actual
|
return actual
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCGo(t *testing.T) {
|
func TestCGo(t *testing.T) {
|
||||||
var cflags = []string{"--target=armv6m-none-eabi"}
|
var cflags = []string{"--target=armv6m-unknown-unknown-eabi"}
|
||||||
|
|
||||||
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
||||||
name := name // avoid a race condition
|
name := name // avoid a race condition
|
||||||
@@ -65,7 +56,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", fset, cflags)
|
||||||
|
|
||||||
// Check the AST for type errors.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
@@ -105,7 +96,7 @@ 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(string(buf.Bytes()))
|
actual := normalizeResult(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")
|
||||||
|
|||||||
+48
-12
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <clang-c/Index.h> // if this fails, install libclang-10-dev
|
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -72,17 +72,14 @@ var diagnosticSeverity = [...]string{
|
|||||||
C.CXDiagnostic_Fatal: "fatal",
|
C.CXDiagnostic_Fatal: "fatal",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename string, posLine int) {
|
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
// pretend to be a .c file
|
// pretend to be a .c file
|
||||||
filenameC := C.CString(posFilename + "!cgo.c")
|
filenameC := C.CString(filename + "!cgo.c")
|
||||||
defer C.free(unsafe.Pointer(filenameC))
|
defer C.free(unsafe.Pointer(filenameC))
|
||||||
|
|
||||||
// fix up error locations
|
|
||||||
fragment = fmt.Sprintf("# %d %#v\n", posLine+1, posFilename) + fragment
|
|
||||||
|
|
||||||
fragmentC := C.CString(fragment)
|
fragmentC := C.CString(fragment)
|
||||||
defer C.free(unsafe.Pointer(fragmentC))
|
defer C.free(unsafe.Pointer(fragmentC))
|
||||||
|
|
||||||
@@ -196,14 +193,14 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
fn.args = append(fn.args, paramInfo{
|
||||||
name: argName,
|
name: argName,
|
||||||
typeExpr: p.makeASTType(argType, pos),
|
typeExpr: p.makeDecayingASTType(argType, pos),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||||
if resultType.kind != C.CXType_Void {
|
if resultType.kind != C.CXType_Void {
|
||||||
fn.results = &ast.FieldList{
|
fn.results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
{
|
||||||
Type: p.makeASTType(resultType, pos),
|
Type: p.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -380,7 +377,7 @@ func (p *cgoPackage) addErrorAfter(pos token.Pos, after, msg string) {
|
|||||||
func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
||||||
if filepath.IsAbs(position.Filename) {
|
if filepath.IsAbs(position.Filename) {
|
||||||
// Relative paths for readability, like other Go parser errors.
|
// Relative paths for readability, like other Go parser errors.
|
||||||
relpath, err := filepath.Rel(p.dir, position.Filename)
|
relpath, err := filepath.Rel(p.currentDir, position.Filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
position.Filename = relpath
|
position.Filename = relpath
|
||||||
}
|
}
|
||||||
@@ -391,6 +388,41 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||||
|
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||||
|
// makeASTType.
|
||||||
|
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
|
// Strip typedefs, if any.
|
||||||
|
underlyingType := typ
|
||||||
|
if underlyingType.kind == C.CXType_Typedef {
|
||||||
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
|
underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
|
// TODO: support a chain of typedefs. At the moment, it seems to get
|
||||||
|
// stuck in an endless loop when trying to get to the most underlying
|
||||||
|
// type.
|
||||||
|
}
|
||||||
|
// Check for decaying type. An example would be an array type in a
|
||||||
|
// parameter. This declaration:
|
||||||
|
// void foo(char buf[6]);
|
||||||
|
// is the same as this one:
|
||||||
|
// void foo(char *buf);
|
||||||
|
// But this one:
|
||||||
|
// void bar(char buf[6][4]);
|
||||||
|
// equals this:
|
||||||
|
// void bar(char *buf[4]);
|
||||||
|
// so not all array dimensions should be stripped, just the first one.
|
||||||
|
// TODO: there are more kinds of decaying types.
|
||||||
|
if underlyingType.kind == C.CXType_ConstantArray {
|
||||||
|
// Apply type decaying.
|
||||||
|
pointeeType := C.clang_getElementType(underlyingType)
|
||||||
|
return &ast.StarExpr{
|
||||||
|
Star: pos,
|
||||||
|
X: p.makeASTType(pointeeType, pos),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.makeASTType(typ, pos)
|
||||||
|
}
|
||||||
|
|
||||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||||
// it converts a libclang type to a type in the Go AST.
|
// it converts a libclang type to a type in the Go AST.
|
||||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
@@ -775,7 +807,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
}
|
}
|
||||||
*inBitfield = false
|
*inBitfield = false
|
||||||
field.Names = []*ast.Ident{
|
field.Names = []*ast.Ident{
|
||||||
&ast.Ident{
|
{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
@@ -796,8 +828,12 @@ func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientDat
|
|||||||
pos := p.getCursorPosition(c)
|
pos := p.getCursorPosition(c)
|
||||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||||
p.constants[name] = constantInfo{
|
p.constants[name] = constantInfo{
|
||||||
expr: &ast.BasicLit{pos, token.INT, strconv.FormatInt(int64(value), 10)},
|
expr: &ast.BasicLit{
|
||||||
pos: pos,
|
ValuePos: pos,
|
||||||
|
Kind: token.INT,
|
||||||
|
Value: strconv.FormatInt(int64(value), 10),
|
||||||
|
},
|
||||||
|
pos: pos,
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// +build !byollvm
|
// +build !byollvm
|
||||||
// +build !llvm10
|
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
// +build !byollvm
|
|
||||||
// +build llvm10
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-10/include
|
|
||||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include
|
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm10/include
|
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang
|
|
||||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi
|
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// are slightly different from the ones defined in libclang.go, but they
|
// are slightly different from the ones defined in libclang.go, but they
|
||||||
// should be ABI compatible.
|
// should be ABI compatible.
|
||||||
|
|
||||||
#include <clang-c/Index.h> // if this fails, install libclang-10-dev
|
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||||
|
|
||||||
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
|
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
|
||||||
return clang_getTranslationUnitCursor(tu);
|
return clang_getTranslationUnitCursor(tu);
|
||||||
|
|||||||
Vendored
+8
@@ -14,6 +14,12 @@ typedef someType noType; // undefined type
|
|||||||
#define SOME_CONST_2 6) // const not used (so no error)
|
#define SOME_CONST_2 6) // const not used (so no error)
|
||||||
#define SOME_CONST_3 1234 // const too large for byte
|
#define SOME_CONST_3 1234 // const too large for byte
|
||||||
*/
|
*/
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// #define SOME_CONST_4 8) // after some empty lines
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// #warning another warning
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// Make sure that errors for the following lines won't change with future
|
// Make sure that errors for the following lines won't change with future
|
||||||
@@ -30,4 +36,6 @@ var (
|
|||||||
_ = C.SOME_CONST_1
|
_ = C.SOME_CONST_1
|
||||||
|
|
||||||
_ byte = C.SOME_CONST_3
|
_ byte = C.SOME_CONST_3
|
||||||
|
|
||||||
|
_ = C.SOME_CONST_4
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
+3
@@ -1,13 +1,16 @@
|
|||||||
// CGo errors:
|
// CGo errors:
|
||||||
// testdata/errors.go:4:2: warning: some warning
|
// testdata/errors.go:4:2: warning: some warning
|
||||||
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
||||||
|
// testdata/errors.go:22:5: warning: another warning
|
||||||
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
||||||
|
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||||
|
|
||||||
// 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 uint8 value in variable declaration (overflows)
|
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 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: undeclared name: 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
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|||||||
Vendored
+4
@@ -111,6 +111,10 @@ void variadic2(int x, int y, ...);
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
// // Test that we can refer from this CGo fragment to the fragment above.
|
||||||
|
// typedef myint myint2;
|
||||||
|
import "C"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Simple typedefs.
|
// Simple typedefs.
|
||||||
_ C.myint
|
_ C.myint
|
||||||
|
|||||||
Vendored
+5
-15
@@ -67,9 +67,7 @@ type C.union3_t = C.union_1
|
|||||||
type C.union_nested_t = C.union_3
|
type C.union_nested_t = C.union_3
|
||||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||||
|
|
||||||
func (s *C.struct_4) bitfield_a() C.uchar {
|
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||||
return s.__bitfield_1 & 0x1f
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||||
}
|
}
|
||||||
@@ -105,15 +103,9 @@ type C.struct_type1 struct {
|
|||||||
}
|
}
|
||||||
type C.struct_type2 struct{ _type C.int }
|
type C.struct_type2 struct{ _type C.int }
|
||||||
|
|
||||||
func (union *C.union_1) unionfield_i() *C.int {
|
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||||
}
|
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||||
func (union *C.union_1) unionfield_d() *float64 {
|
|
||||||
return (*float64)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_1) unionfield_s() *C.short {
|
|
||||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
|
|
||||||
type C.union_1 struct{ $union uint64 }
|
type C.union_1 struct{ $union uint64 }
|
||||||
|
|
||||||
@@ -138,9 +130,7 @@ func (union *C.union_3) unionfield_thing() *C.union3_t {
|
|||||||
|
|
||||||
type C.union_3 struct{ $union [2]uint64 }
|
type C.union_3 struct{ $union [2]uint64 }
|
||||||
|
|
||||||
func (union *C.union_union2d) unionfield_i() *C.int {
|
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-9
@@ -5,6 +5,7 @@ package compileopts
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -21,7 +22,7 @@ type Config struct {
|
|||||||
TestConfig TestConfig
|
TestConfig TestConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triple returns the LLVM target triple, like armv6m-none-eabi.
|
// Triple returns the LLVM target triple, like armv6m-unknown-unknown-eabi.
|
||||||
func (c *Config) Triple() string {
|
func (c *Config) Triple() string {
|
||||||
return c.Target.Triple
|
return c.Target.Triple
|
||||||
}
|
}
|
||||||
@@ -55,7 +56,7 @@ func (c *Config) GOARCH() 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", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
tags := append(c.Target.BuildTags, []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))
|
||||||
}
|
}
|
||||||
@@ -197,6 +198,39 @@ func (c *Config) RP2040BootPatch() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MuslArchitecture returns the architecture name as used in musl libc. It is
|
||||||
|
// usually the same as the first part of the LLVM triple, but not always.
|
||||||
|
func MuslArchitecture(triple string) string {
|
||||||
|
arch := strings.Split(triple, "-")[0]
|
||||||
|
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||||
|
arch = "arm"
|
||||||
|
}
|
||||||
|
return arch
|
||||||
|
}
|
||||||
|
|
||||||
|
// LibcPath returns the path to the libc directory. The libc path will be either
|
||||||
|
// a precompiled libc shipped with a TinyGo build, or a libc path in the cache
|
||||||
|
// directory (which might not yet be built).
|
||||||
|
func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||||
|
// Try to load a precompiled library.
|
||||||
|
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", c.Triple(), name)
|
||||||
|
if _, err := os.Stat(precompiledDir); err == nil {
|
||||||
|
// Found a precompiled library for this OS/architecture. Return the path
|
||||||
|
// directly.
|
||||||
|
return precompiledDir, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// No precompiled library found. Determine the path name that will be used
|
||||||
|
// in the build cache.
|
||||||
|
var outname string
|
||||||
|
if c.CPU() != "" {
|
||||||
|
outname = name + "-" + c.Triple() + "-" + c.CPU()
|
||||||
|
} else {
|
||||||
|
outname = name + "-" + c.Triple()
|
||||||
|
}
|
||||||
|
return filepath.Join(goenv.Get("GOCACHE"), outname), false
|
||||||
|
}
|
||||||
|
|
||||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||||
// preprocessing.
|
// preprocessing.
|
||||||
func (c *Config) CFlags() []string {
|
func (c *Config) CFlags() []string {
|
||||||
@@ -204,13 +238,53 @@ func (c *Config) CFlags() []string {
|
|||||||
for _, flag := range c.Target.CFlags {
|
for _, flag := range c.Target.CFlags {
|
||||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||||
}
|
}
|
||||||
if c.Target.Libc == "picolibc" {
|
switch c.Target.Libc {
|
||||||
|
case "picolibc":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||||
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
path, _ := c.LibcPath("picolibc")
|
||||||
|
cflags = append(cflags,
|
||||||
|
"--sysroot="+path,
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "include"),
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "tinystdio"),
|
||||||
|
)
|
||||||
|
case "musl":
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
path, _ := c.LibcPath("musl")
|
||||||
|
arch := MuslArchitecture(c.Triple())
|
||||||
|
cflags = append(cflags,
|
||||||
|
"--sysroot="+path,
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "musl", "include"),
|
||||||
|
)
|
||||||
|
case "wasi-libc":
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
|
||||||
|
case "":
|
||||||
|
// No libc specified, nothing to add.
|
||||||
|
default:
|
||||||
|
// Incorrect configuration. This could be handled in a better way, but
|
||||||
|
// usually this will be found by developers (not by TinyGo users).
|
||||||
|
panic("unknown libc: " + c.Target.Libc)
|
||||||
}
|
}
|
||||||
if c.Debug() {
|
// Always emit debug information. It is optionally stripped at link time.
|
||||||
cflags = append(cflags, "-g")
|
cflags = append(cflags, "-g")
|
||||||
|
// Use the same optimization level as TinyGo.
|
||||||
|
cflags = append(cflags, "-O"+c.Options.Opt)
|
||||||
|
// Set the LLVM target triple.
|
||||||
|
cflags = append(cflags, "--target="+c.Triple())
|
||||||
|
// Set the -mcpu (or similar) flag.
|
||||||
|
if c.Target.CPU != "" {
|
||||||
|
if c.GOARCH() == "amd64" || c.GOARCH() == "386" {
|
||||||
|
// x86 prefers the -march flag (-mcpu is deprecated there).
|
||||||
|
cflags = append(cflags, "-march="+c.Target.CPU)
|
||||||
|
} else if strings.HasPrefix(c.Triple(), "avr") {
|
||||||
|
// AVR MCUs use -mmcu instead of -mcpu.
|
||||||
|
cflags = append(cflags, "-mmcu="+c.Target.CPU)
|
||||||
|
} else {
|
||||||
|
// The rest just uses -mcpu.
|
||||||
|
cflags = append(cflags, "-mcpu="+c.Target.CPU)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cflags
|
return cflags
|
||||||
}
|
}
|
||||||
@@ -250,8 +324,9 @@ func (c *Config) VerifyIR() bool {
|
|||||||
return c.Options.VerifyIR
|
return c.Options.VerifyIR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
|
// Debug returns whether debug (DWARF) information should be retained by the
|
||||||
// and similar.
|
// linker. By default, debug information is retained but it can be removed with
|
||||||
|
// the -no-debug flag.
|
||||||
func (c *Config) Debug() bool {
|
func (c *Config) Debug() bool {
|
||||||
return c.Options.Debug
|
return c.Options.Debug
|
||||||
}
|
}
|
||||||
@@ -297,6 +372,9 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
|||||||
case "openocd", "msd", "command":
|
case "openocd", "msd", "command":
|
||||||
// The -programmer flag only specifies the flash method.
|
// The -programmer flag only specifies the flash method.
|
||||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||||
|
case "bmp":
|
||||||
|
// The -programmer flag only specifies the flash method.
|
||||||
|
return c.Options.Programmer, ""
|
||||||
default:
|
default:
|
||||||
// The -programmer flag specifies something else, assume it specifies
|
// The -programmer flag specifies something else, assume it specifies
|
||||||
// the OpenOCD interface name.
|
// the OpenOCD interface name.
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Options contains extra options to give to the compiler. These options are
|
// Options contains extra options to give to the compiler. These options are
|
||||||
// usually passed from the command line.
|
// usually passed from the command line, but can also be passed in environment
|
||||||
|
// variables for example.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
GOOS string // environment variable
|
||||||
|
GOARCH string // environment variable
|
||||||
Target string
|
Target string
|
||||||
Opt string
|
Opt string
|
||||||
GC string
|
GC string
|
||||||
@@ -28,6 +31,7 @@ type Options struct {
|
|||||||
DumpSSA bool
|
DumpSSA bool
|
||||||
VerifyIR bool
|
VerifyIR bool
|
||||||
PrintCommands func(cmd string, args ...string)
|
PrintCommands func(cmd string, args ...string)
|
||||||
|
Parallelism int // -p flag
|
||||||
Debug bool
|
Debug bool
|
||||||
PrintSizes string
|
PrintSizes string
|
||||||
PrintAllocs *regexp.Regexp // regexp string
|
PrintAllocs *regexp.Regexp // regexp string
|
||||||
|
|||||||
+48
-63
@@ -161,75 +161,54 @@ func (spec *TargetSpec) resolveInherits() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load a target specification.
|
// Load a target specification.
|
||||||
func LoadTarget(target string) (*TargetSpec, error) {
|
func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||||
if target == "" {
|
if options.Target == "" {
|
||||||
// Configure based on GOOS/GOARCH environment variables (falling back to
|
// Configure based on GOOS/GOARCH environment variables (falling back to
|
||||||
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
||||||
goos := goenv.Get("GOOS")
|
|
||||||
goarch := goenv.Get("GOARCH")
|
|
||||||
llvmos := goos
|
|
||||||
llvmarch := map[string]string{
|
llvmarch := map[string]string{
|
||||||
"386": "i386",
|
"386": "i386",
|
||||||
"amd64": "x86_64",
|
"amd64": "x86_64",
|
||||||
"arm64": "aarch64",
|
"arm64": "aarch64",
|
||||||
}[goarch]
|
"arm": "armv7",
|
||||||
|
}[options.GOARCH]
|
||||||
if llvmarch == "" {
|
if llvmarch == "" {
|
||||||
llvmarch = goarch
|
llvmarch = options.GOARCH
|
||||||
}
|
}
|
||||||
target = llvmarch + "--" + llvmos
|
llvmos := options.GOOS
|
||||||
if goarch == "arm" {
|
if llvmos == "darwin" {
|
||||||
|
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||||
|
// to iOS!
|
||||||
|
llvmos = "macosx10.12.0"
|
||||||
|
if llvmarch == "aarch64" {
|
||||||
|
// Looks like Apple prefers to call this architecture ARM64
|
||||||
|
// instead of AArch64.
|
||||||
|
llvmarch = "arm64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Target triples (which actually have four components, but are called
|
||||||
|
// triples for historical reasons) have the form:
|
||||||
|
// arch-vendor-os-environment
|
||||||
|
target := llvmarch + "-unknown-" + llvmos
|
||||||
|
if options.GOARCH == "arm" {
|
||||||
target += "-gnueabihf"
|
target += "-gnueabihf"
|
||||||
}
|
}
|
||||||
return defaultTarget(goos, goarch, target)
|
return defaultTarget(options.GOOS, options.GOARCH, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See whether there is a target specification for this target (e.g.
|
// See whether there is a target specification for this target (e.g.
|
||||||
// Arduino).
|
// Arduino).
|
||||||
spec := &TargetSpec{}
|
spec := &TargetSpec{}
|
||||||
err := spec.loadFromGivenStr(target)
|
err := spec.loadFromGivenStr(options.Target)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
// Successfully loaded this target from a built-in .json file. Make sure
|
|
||||||
// it includes all parents as specified in the "inherits" key.
|
|
||||||
err = spec.resolveInherits()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return spec, nil
|
|
||||||
} else if !os.IsNotExist(err) {
|
|
||||||
// Expected a 'file not found' error, got something else. Report it as
|
|
||||||
// an error.
|
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
|
||||||
// Load target from given triple, ignore GOOS/GOARCH environment
|
|
||||||
// variables.
|
|
||||||
tripleSplit := strings.Split(target, "-")
|
|
||||||
if len(tripleSplit) < 3 {
|
|
||||||
return nil, errors.New("expected a full LLVM target or a custom target in -target flag")
|
|
||||||
}
|
|
||||||
if tripleSplit[0] == "arm" {
|
|
||||||
// LLVM and Clang have a different idea of what "arm" means, so
|
|
||||||
// upgrade to a slightly more modern ARM. In fact, when you pass
|
|
||||||
// --target=arm--linux-gnueabihf to Clang, it will convert that
|
|
||||||
// internally to armv7-unknown-linux-gnueabihf. Changing the
|
|
||||||
// architecture to armv7 will keep things consistent.
|
|
||||||
tripleSplit[0] = "armv7"
|
|
||||||
}
|
|
||||||
goos := tripleSplit[2]
|
|
||||||
if strings.HasPrefix(goos, "darwin") {
|
|
||||||
goos = "darwin"
|
|
||||||
}
|
|
||||||
goarch := map[string]string{ // map from LLVM arch to Go arch
|
|
||||||
"i386": "386",
|
|
||||||
"i686": "386",
|
|
||||||
"x86_64": "amd64",
|
|
||||||
"aarch64": "arm64",
|
|
||||||
"armv7": "arm",
|
|
||||||
}[tripleSplit[0]]
|
|
||||||
if goarch == "" {
|
|
||||||
goarch = tripleSplit[0]
|
|
||||||
}
|
|
||||||
return defaultTarget(goos, goarch, strings.Join(tripleSplit, "-"))
|
|
||||||
}
|
}
|
||||||
|
// Successfully loaded this target from a built-in .json file. Make sure
|
||||||
|
// it includes all parents as specified in the "inherits" key.
|
||||||
|
err = spec.resolveInherits()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
|
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
|
||||||
@@ -249,13 +228,27 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
Scheduler: "tasks",
|
Scheduler: "tasks",
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
DefaultStackSize: 1024 * 64, // 64kB
|
DefaultStackSize: 1024 * 64, // 64kB
|
||||||
CFlags: []string{"--target=" + triple},
|
|
||||||
GDB: []string{"gdb"},
|
GDB: []string{"gdb"},
|
||||||
PortReset: "false",
|
PortReset: "false",
|
||||||
}
|
}
|
||||||
|
switch goarch {
|
||||||
|
case "386":
|
||||||
|
spec.CPU = "pentium4"
|
||||||
|
case "amd64":
|
||||||
|
spec.CPU = "x86-64"
|
||||||
|
case "arm":
|
||||||
|
spec.CPU = "generic"
|
||||||
|
case "arm64":
|
||||||
|
spec.CPU = "generic"
|
||||||
|
}
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||||
|
} else if goos == "linux" {
|
||||||
|
spec.Linker = "ld.lld"
|
||||||
|
spec.RTLib = "compiler-rt"
|
||||||
|
spec.Libc = "musl"
|
||||||
|
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
||||||
} else {
|
} else {
|
||||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||||
}
|
}
|
||||||
@@ -267,18 +260,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
// Some educated guesses as to how to invoke helper programs.
|
// Some educated guesses as to how to invoke helper programs.
|
||||||
spec.GDB = []string{"gdb-multiarch"}
|
spec.GDB = []string{"gdb-multiarch"}
|
||||||
if goarch == "arm" && goos == "linux" {
|
if goarch == "arm" && goos == "linux" {
|
||||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
spec.Emulator = []string{"qemu-arm"}
|
||||||
spec.Linker = "arm-linux-gnueabihf-gcc"
|
|
||||||
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
|
|
||||||
}
|
}
|
||||||
if goarch == "arm64" && goos == "linux" {
|
if goarch == "arm64" && goos == "linux" {
|
||||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu")
|
spec.Emulator = []string{"qemu-aarch64"}
|
||||||
spec.Linker = "aarch64-linux-gnu-gcc"
|
|
||||||
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
|
|
||||||
}
|
|
||||||
if goarch == "386" && runtime.GOARCH == "amd64" {
|
|
||||||
spec.CFlags = append(spec.CFlags, "-m32")
|
|
||||||
spec.LDFlags = append(spec.LDFlags, "-m32")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &spec, nil
|
return &spec, nil
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
package compileopts
|
package compileopts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoadTarget(t *testing.T) {
|
func TestLoadTarget(t *testing.T) {
|
||||||
_, err := LoadTarget("arduino")
|
_, err := LoadTarget(&Options{Target: "arduino"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("LoadTarget test failed:", err)
|
t.Error("LoadTarget test failed:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = LoadTarget("notexist")
|
_, err = LoadTarget(&Options{Target: "notexist"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("LoadTarget should have failed with non existing target")
|
t.Error("LoadTarget should have failed with non existing target")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err.Error() != "expected a full LLVM target or a custom target in -target flag" {
|
if !os.IsNotExist(err) {
|
||||||
t.Error("LoadTarget failed for wrong reason:", err)
|
t.Error("LoadTarget failed for wrong reason:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// This file defines alias functions for functions that are normally defined in
|
||||||
|
// Go assembly.
|
||||||
|
//
|
||||||
|
// The Go toolchain defines many performance critical functions in assembly
|
||||||
|
// instead of plain Go. This is a problem for TinyGo as it currently (as of
|
||||||
|
// august 2021) is not able to compile these assembly files and even if it
|
||||||
|
// could, it would not be able to make use of them for many targets that are
|
||||||
|
// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these
|
||||||
|
// functions are aliased to their generic Go implementation.
|
||||||
|
// This results in slower than possible implementations, but at least they are
|
||||||
|
// usable.
|
||||||
|
|
||||||
|
import "tinygo.org/x/go-llvm"
|
||||||
|
|
||||||
|
var stdlibAliases = map[string]string{
|
||||||
|
// crypto packages
|
||||||
|
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||||
|
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||||
|
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||||
|
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||||
|
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||||
|
|
||||||
|
// math package
|
||||||
|
"math.Asin": "math.asin",
|
||||||
|
"math.Asinh": "math.asinh",
|
||||||
|
"math.Acos": "math.acos",
|
||||||
|
"math.Acosh": "math.acosh",
|
||||||
|
"math.Atan": "math.atan",
|
||||||
|
"math.Atanh": "math.atanh",
|
||||||
|
"math.Atan2": "math.atan2",
|
||||||
|
"math.Cbrt": "math.cbrt",
|
||||||
|
"math.Ceil": "math.ceil",
|
||||||
|
"math.archCeil": "math.ceil",
|
||||||
|
"math.Cos": "math.cos",
|
||||||
|
"math.Cosh": "math.cosh",
|
||||||
|
"math.Erf": "math.erf",
|
||||||
|
"math.Erfc": "math.erfc",
|
||||||
|
"math.Exp": "math.exp",
|
||||||
|
"math.archExp": "math.exp",
|
||||||
|
"math.Expm1": "math.expm1",
|
||||||
|
"math.Exp2": "math.exp2",
|
||||||
|
"math.archExp2": "math.exp2",
|
||||||
|
"math.Floor": "math.floor",
|
||||||
|
"math.archFloor": "math.floor",
|
||||||
|
"math.Frexp": "math.frexp",
|
||||||
|
"math.Hypot": "math.hypot",
|
||||||
|
"math.archHypot": "math.hypot",
|
||||||
|
"math.Ldexp": "math.ldexp",
|
||||||
|
"math.Log": "math.log",
|
||||||
|
"math.archLog": "math.log",
|
||||||
|
"math.Log1p": "math.log1p",
|
||||||
|
"math.Log10": "math.log10",
|
||||||
|
"math.Log2": "math.log2",
|
||||||
|
"math.Max": "math.max",
|
||||||
|
"math.archMax": "math.max",
|
||||||
|
"math.Min": "math.min",
|
||||||
|
"math.archMin": "math.min",
|
||||||
|
"math.Mod": "math.mod",
|
||||||
|
"math.Modf": "math.modf",
|
||||||
|
"math.archModf": "math.modf",
|
||||||
|
"math.Pow": "math.pow",
|
||||||
|
"math.Remainder": "math.remainder",
|
||||||
|
"math.Sin": "math.sin",
|
||||||
|
"math.Sinh": "math.sinh",
|
||||||
|
"math.Sqrt": "math.sqrt",
|
||||||
|
"math.archSqrt": "math.sqrt",
|
||||||
|
"math.Tan": "math.tan",
|
||||||
|
"math.Tanh": "math.tanh",
|
||||||
|
"math.Trunc": "math.trunc",
|
||||||
|
"math.archTrunc": "math.trunc",
|
||||||
|
}
|
||||||
|
|
||||||
|
// createAlias implements the function (in the builder) as a call to the alias
|
||||||
|
// function.
|
||||||
|
func (b *builder) createAlias(alias llvm.Value) {
|
||||||
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
|
|
||||||
|
if b.Debug {
|
||||||
|
if b.fn.Syntax() != nil {
|
||||||
|
// Create debug info file if present.
|
||||||
|
b.difunc = b.attachDebugInfo(b.fn)
|
||||||
|
}
|
||||||
|
pos := b.program.Fset.Position(b.fn.Pos())
|
||||||
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||||
|
}
|
||||||
|
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||||
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
|
if b.llvmFn.Type() != alias.Type() {
|
||||||
|
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||||
|
b.CreateUnreachable()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
||||||
|
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||||
|
b.CreateRetVoid()
|
||||||
|
} else {
|
||||||
|
b.CreateRet(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
-2
@@ -101,6 +101,55 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
|||||||
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
|
||||||
|
// conversions. This conversion was added in Go 1.17. For details, see:
|
||||||
|
// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
|
||||||
|
func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen int64) {
|
||||||
|
// From the spec:
|
||||||
|
// > If the length of the slice is less than the length of the array, a
|
||||||
|
// > run-time panic occurs.
|
||||||
|
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||||
|
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
|
||||||
|
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||||
|
}
|
||||||
|
|
||||||
|
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
||||||
|
// function must panic if the ptr/len parameters are invalid.
|
||||||
|
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
||||||
|
// From the documentation of unsafe.Slice:
|
||||||
|
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||||
|
// > zero, a run-time panic occurs.
|
||||||
|
// However, in practice, it is also necessary to check that the length is
|
||||||
|
// not too big that a GEP wouldn't be possible without wrapping the pointer.
|
||||||
|
// These two checks (non-negative and not too big) can be merged into one
|
||||||
|
// using an unsiged greater than.
|
||||||
|
|
||||||
|
// Make sure the len value is at least as big as a uintptr.
|
||||||
|
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||||
|
if lenType.Info()&types.IsUnsigned != 0 {
|
||||||
|
len = b.CreateZExt(len, b.uintptrType, "")
|
||||||
|
} else {
|
||||||
|
len = b.CreateSExt(len, b.uintptrType, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the maximum slice size, and therefore the maximum value of the
|
||||||
|
// len parameter.
|
||||||
|
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
||||||
|
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||||
|
|
||||||
|
// Do the check. By using unsigned greater than for the length check, signed
|
||||||
|
// negative values are also checked (which are very large numbers when
|
||||||
|
// interpreted as signed values).
|
||||||
|
zero := llvm.ConstInt(len.Type(), 0, false)
|
||||||
|
lenOutOfBounds := b.CreateICmp(llvm.IntUGT, len, maxSizeValue, "")
|
||||||
|
ptrIsNil := b.CreateICmp(llvm.IntEQ, ptr, llvm.ConstNull(ptr.Type()), "")
|
||||||
|
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||||
|
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||||
|
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||||
|
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
||||||
|
}
|
||||||
|
|
||||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||||
// check that the value is not too big for runtime.chanMake.
|
// check that the value is not too big for runtime.chanMake.
|
||||||
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
||||||
@@ -115,11 +164,9 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
|||||||
if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||||
if bufSizeType.Info()&types.IsUnsigned != 0 {
|
if bufSizeType.Info()&types.IsUnsigned != 0 {
|
||||||
// Unsigned, so zero-extend to uint type.
|
// Unsigned, so zero-extend to uint type.
|
||||||
bufSizeType = types.Typ[types.Uint]
|
|
||||||
bufSize = b.CreateZExt(bufSize, b.intType, "")
|
bufSize = b.CreateZExt(bufSize, b.intType, "")
|
||||||
} else {
|
} else {
|
||||||
// Signed, so sign-extend to int type.
|
// Signed, so sign-extend to int type.
|
||||||
bufSizeType = types.Typ[types.Int]
|
|
||||||
bufSize = b.CreateSExt(bufSize, b.intType, "")
|
bufSize = b.CreateSExt(bufSize, b.intType, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,6 +253,19 @@ func (b *builder) createNegativeShiftCheck(shift llvm.Value) {
|
|||||||
b.createRuntimeAssert(isNegative, "shift", "negativeShiftPanic")
|
b.createRuntimeAssert(isNegative, "shift", "negativeShiftPanic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createDivideByZeroCheck asserts that y is not zero. If it is, a runtime panic
|
||||||
|
// will be emitted. This follows the Go specification which says that a divide
|
||||||
|
// by zero must cause a run time panic.
|
||||||
|
func (b *builder) createDivideByZeroCheck(y llvm.Value) {
|
||||||
|
if b.info.nobounds {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// isZero = y == 0
|
||||||
|
isZero := b.CreateICmp(llvm.IntEQ, y, llvm.ConstInt(y.Type(), 0, false), "")
|
||||||
|
b.createRuntimeAssert(isZero, "divbyzero", "divideByZeroPanic")
|
||||||
|
}
|
||||||
|
|
||||||
// createRuntimeAssert is a common function to create a new branch on an assert
|
// createRuntimeAssert is a common function to create a new branch on an assert
|
||||||
// bool, calling an assert func if the assert value is true (1).
|
// bool, calling an assert func if the assert value is true (1).
|
||||||
func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string) {
|
func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string) {
|
||||||
|
|||||||
+2
-3
@@ -38,11 +38,10 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
|||||||
old := b.getValue(call.Args[1])
|
old := b.getValue(call.Args[1])
|
||||||
newVal := b.getValue(call.Args[2])
|
newVal := b.getValue(call.Args[2])
|
||||||
if strings.HasSuffix(name, "64") {
|
if strings.HasSuffix(name, "64") {
|
||||||
arch := strings.Split(b.Triple, "-")[0]
|
if strings.HasPrefix(b.Triple, "thumb") {
|
||||||
if strings.HasPrefix(arch, "arm") && strings.HasSuffix(arch, "m") {
|
|
||||||
// Work around a bug in LLVM, at least LLVM 11:
|
// Work around a bug in LLVM, at least LLVM 11:
|
||||||
// https://reviews.llvm.org/D95891
|
// https://reviews.llvm.org/D95891
|
||||||
// Check for armv6m, armv7, armv7em, and perhaps others.
|
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
||||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
||||||
if compareAndSwap.IsNil() {
|
if compareAndSwap.IsNil() {
|
||||||
|
|||||||
+2
-2
@@ -63,10 +63,10 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType
|
|||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldInfos := c.flattenAggregateType(t, name, goType)
|
fieldInfos := c.flattenAggregateType(t, name, goType)
|
||||||
if len(fieldInfos) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
|
// managed to expand this parameter
|
||||||
return fieldInfos
|
return fieldInfos
|
||||||
} else {
|
|
||||||
// failed to lower
|
|
||||||
}
|
}
|
||||||
|
// failed to expand this parameter: too many fields
|
||||||
}
|
}
|
||||||
// TODO: split small arrays
|
// TODO: split small arrays
|
||||||
return []paramInfo{
|
return []paramInfo{
|
||||||
|
|||||||
+28
-9
@@ -32,8 +32,15 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
|
|
||||||
// store value-to-send
|
// store value-to-send
|
||||||
valueType := b.getLLVMType(instr.X.Type())
|
valueType := b.getLLVMType(instr.X.Type())
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
b.CreateStore(chanValue, valueAlloca)
|
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||||
|
if isZeroSize {
|
||||||
|
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||||
|
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||||
|
} else {
|
||||||
|
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
|
b.CreateStore(chanValue, valueAlloca)
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||||
@@ -46,7 +53,9 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
if !isZeroSize {
|
||||||
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||||
@@ -56,7 +65,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
ch := b.getValue(unop.X)
|
ch := b.getValue(unop.X)
|
||||||
|
|
||||||
// Allocate memory to receive into.
|
// Allocate memory to receive into.
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
|
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||||
|
if isZeroSize {
|
||||||
|
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||||
|
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||||
|
} else {
|
||||||
|
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||||
@@ -64,9 +80,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||||
received := b.CreateLoad(valueAlloca, "chan.received")
|
var received llvm.Value
|
||||||
|
if isZeroSize {
|
||||||
|
received = llvm.ConstNull(valueType)
|
||||||
|
} else {
|
||||||
|
received = b.CreateLoad(valueAlloca, "chan.received")
|
||||||
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
|
}
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
|
||||||
|
|
||||||
if unop.CommaOk {
|
if unop.CommaOk {
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||||
@@ -116,7 +137,6 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
// determine the receive buffer size and alignment.
|
// determine the receive buffer size and alignment.
|
||||||
recvbufSize := uint64(0)
|
recvbufSize := uint64(0)
|
||||||
recvbufAlign := 0
|
recvbufAlign := 0
|
||||||
hasReceives := false
|
|
||||||
var selectStates []llvm.Value
|
var selectStates []llvm.Value
|
||||||
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
|
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
|
||||||
for _, state := range expr.States {
|
for _, state := range expr.States {
|
||||||
@@ -133,7 +153,6 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
|
if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
|
||||||
recvbufAlign = align
|
recvbufAlign = align
|
||||||
}
|
}
|
||||||
hasReceives = true
|
|
||||||
case types.SendOnly:
|
case types.SendOnly:
|
||||||
// Store this value in an alloca and put a pointer to this alloca
|
// Store this value in an alloca and put a pointer to this alloca
|
||||||
// in the send state.
|
// in the send state.
|
||||||
@@ -150,7 +169,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
|
|
||||||
// Create a receive buffer, where the received value will be stored.
|
// Create a receive buffer, where the received value will be stored.
|
||||||
recvbuf := llvm.Undef(b.i8ptrType)
|
recvbuf := llvm.Undef(b.i8ptrType)
|
||||||
if hasReceives {
|
if recvbufSize != 0 {
|
||||||
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)
|
||||||
|
|||||||
+216
-56
@@ -23,7 +23,7 @@ import (
|
|||||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
// package changes in a way that affects the generated LLVM module.
|
// package changes in a way that affects the generated LLVM module.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 12 // last change: implement syscall.rawSyscallNoError
|
const Version = 24 // last change: add layout param to runtime.alloc calls
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
@@ -33,9 +33,6 @@ func init() {
|
|||||||
llvm.InitializeAllAsmPrinters()
|
llvm.InitializeAllAsmPrinters()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The TinyGo import path.
|
|
||||||
const tinygoPath = "github.com/tinygo-org/tinygo"
|
|
||||||
|
|
||||||
// Config is the configuration for the compiler. Most settings should be copied
|
// Config is the configuration for the compiler. Most settings should be copied
|
||||||
// directly from compileopts.Config, it recreated here to decouple the compiler
|
// directly from compileopts.Config, it recreated here to decouple the compiler
|
||||||
// package a bit and because it makes caching easier.
|
// package a bit and because it makes caching easier.
|
||||||
@@ -51,6 +48,7 @@ type Config struct {
|
|||||||
GOARCH string
|
GOARCH string
|
||||||
CodeModel string
|
CodeModel string
|
||||||
RelocationModel string
|
RelocationModel string
|
||||||
|
SizeLevel int
|
||||||
|
|
||||||
// Various compiler options that determine how code is generated.
|
// Various compiler options that determine how code is generated.
|
||||||
Scheduler string
|
Scheduler string
|
||||||
@@ -74,15 +72,18 @@ type compilerContext struct {
|
|||||||
cu llvm.Metadata
|
cu llvm.Metadata
|
||||||
difiles map[string]llvm.Metadata
|
difiles map[string]llvm.Metadata
|
||||||
ditypes map[types.Type]llvm.Metadata
|
ditypes map[types.Type]llvm.Metadata
|
||||||
|
llvmTypes map[types.Type]llvm.Type
|
||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
i8ptrType llvm.Type // for convenience
|
i8ptrType llvm.Type // for convenience
|
||||||
|
rawVoidFuncType llvm.Type // for convenience
|
||||||
funcPtrAddrSpace int
|
funcPtrAddrSpace int
|
||||||
uintptrType llvm.Type
|
uintptrType llvm.Type
|
||||||
program *ssa.Program
|
program *ssa.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
astComments map[string]*ast.CommentGroup
|
astComments map[string]*ast.CommentGroup
|
||||||
|
pkg *types.Package
|
||||||
runtimePkg *types.Package
|
runtimePkg *types.Package
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
|||||||
DumpSSA: dumpSSA,
|
DumpSSA: dumpSSA,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
ditypes: make(map[types.Type]llvm.Metadata),
|
ditypes: make(map[types.Type]llvm.Metadata),
|
||||||
|
llvmTypes: make(map[types.Type]llvm.Type),
|
||||||
machine: machine,
|
machine: machine,
|
||||||
targetData: machine.CreateTargetData(),
|
targetData: machine.CreateTargetData(),
|
||||||
astComments: map[string]*ast.CommentGroup{},
|
astComments: map[string]*ast.CommentGroup{},
|
||||||
@@ -122,6 +124,7 @@ 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.rawVoidFuncType = dummyFunc.Type()
|
||||||
dummyFunc.EraseFromParentAsFunction()
|
dummyFunc.EraseFromParentAsFunction()
|
||||||
|
|
||||||
return c
|
return c
|
||||||
@@ -139,7 +142,6 @@ type builder struct {
|
|||||||
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
|
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
|
||||||
currentBlock *ssa.BasicBlock
|
currentBlock *ssa.BasicBlock
|
||||||
phis []phiNode
|
phis []phiNode
|
||||||
taskHandle llvm.Value
|
|
||||||
deferPtr llvm.Value
|
deferPtr llvm.Value
|
||||||
difunc llvm.Metadata
|
difunc llvm.Metadata
|
||||||
dilocals map[*types.Var]llvm.Metadata
|
dilocals map[*types.Var]llvm.Metadata
|
||||||
@@ -231,10 +233,6 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
targetData := machine.CreateTargetData()
|
targetData := machine.CreateTargetData()
|
||||||
defer targetData.Dispose()
|
defer targetData.Dispose()
|
||||||
|
|
||||||
intPtrType := targetData.IntPtrType()
|
|
||||||
if intPtrType.IntTypeWidth()/8 <= 32 {
|
|
||||||
}
|
|
||||||
|
|
||||||
var intWidth int
|
var intWidth int
|
||||||
if targetData.PointerSize() <= 4 {
|
if targetData.PointerSize() <= 4 {
|
||||||
// 8, 16, 32 bits targets
|
// 8, 16, 32 bits targets
|
||||||
@@ -249,13 +247,14 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
return &stdSizes{
|
return &stdSizes{
|
||||||
IntSize: int64(intWidth / 8),
|
IntSize: int64(intWidth / 8),
|
||||||
PtrSize: int64(targetData.PointerSize()),
|
PtrSize: int64(targetData.PointerSize()),
|
||||||
MaxAlign: int64(targetData.PrefTypeAlignment(intPtrType)),
|
MaxAlign: int64(targetData.PrefTypeAlignment(targetData.IntPtrType())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
c.pkg = pkg.Pkg
|
||||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c.program = ssaPkg.Prog
|
c.program = ssaPkg.Prog
|
||||||
|
|
||||||
@@ -289,14 +288,14 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
if c.Debug {
|
if c.Debug {
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
llvm.ConstInt(c.ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
|
||||||
c.ctx.MDString("Debug Info Version"),
|
c.ctx.MDString("Debug Info Version"),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
llvm.ConstInt(c.ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
|
||||||
c.ctx.MDString("Dwarf Version"),
|
c.ctx.MDString("Dwarf Version"),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
}),
|
}),
|
||||||
@@ -315,10 +314,23 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
|||||||
return c.getLLVMType(typ)
|
return c.getLLVMType(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
|
||||||
// named struct types (or Go types implemented as named LLVM structs such as
|
// created types. This is somewhat important for performance, but especially
|
||||||
// strings) it also creates it first if necessary.
|
// important for named struct types (which should only be created once).
|
||||||
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||||
|
// Try to load the LLVM type from the cache.
|
||||||
|
if t, ok := c.llvmTypes[goType]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
// Not already created, so adding this type to the cache.
|
||||||
|
llvmType := c.makeLLVMType(goType)
|
||||||
|
c.llvmTypes[goType] = llvmType
|
||||||
|
return llvmType
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||||
|
// getLLVMType instead.
|
||||||
|
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||||
switch typ := goType.(type) {
|
switch typ := goType.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
@@ -367,12 +379,10 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
|||||||
// LLVM. This is because it is otherwise impossible to create
|
// LLVM. This is because it is otherwise impossible to create
|
||||||
// self-referencing types such as linked lists.
|
// self-referencing types such as linked lists.
|
||||||
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
||||||
llvmType := c.mod.GetTypeByName(llvmName)
|
llvmType := c.ctx.StructCreateNamed(llvmName)
|
||||||
if llvmType.IsNil() {
|
c.llvmTypes[goType] = llvmType // avoid infinite recursion
|
||||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
underlying := c.getLLVMType(st)
|
||||||
underlying := c.getLLVMType(st)
|
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
|
||||||
}
|
|
||||||
return llvmType
|
return llvmType
|
||||||
}
|
}
|
||||||
return c.getLLVMType(typ.Underlying())
|
return c.getLLVMType(typ.Underlying())
|
||||||
@@ -439,7 +449,7 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
|||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
ElementType: c.getDIType(typ.Elem()),
|
ElementType: c.getDIType(typ.Elem()),
|
||||||
Subscripts: []llvm.DISubrange{
|
Subscripts: []llvm.DISubrange{
|
||||||
llvm.DISubrange{
|
{
|
||||||
Lo: 0,
|
Lo: 0,
|
||||||
Count: typ.Len(),
|
Count: typ.Len(),
|
||||||
},
|
},
|
||||||
@@ -768,6 +778,29 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add forwarding functions for functions that would otherwise be
|
||||||
|
// implemented in assembly.
|
||||||
|
for _, name := range members {
|
||||||
|
member := pkg.Members[name]
|
||||||
|
switch member := member.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
if member.Blocks != nil {
|
||||||
|
continue // external function
|
||||||
|
}
|
||||||
|
info := c.getFunctionInfo(member)
|
||||||
|
if aliasName, ok := stdlibAliases[info.linkName]; ok {
|
||||||
|
alias := c.mod.NamedFunction(aliasName)
|
||||||
|
if alias.IsNil() {
|
||||||
|
// Shouldn't happen, but perhaps best to just ignore.
|
||||||
|
// The error will be a link error, if there is an error.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b := newBuilder(c, irbuilder, member)
|
||||||
|
b.createAlias(alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createFunction builds the LLVM IR implementation for this function. The
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
@@ -786,6 +819,7 @@ func (b *builder) createFunction() {
|
|||||||
b.addError(b.fn.Pos(), errValue)
|
b.addError(b.fn.Pos(), errValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
b.addStandardDefinedAttributes(b.llvmFn)
|
||||||
if !b.info.exported {
|
if !b.info.exported {
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
@@ -812,6 +846,17 @@ func (b *builder) createFunction() {
|
|||||||
b.llvmFn.AddFunctionAttr(noinline)
|
b.llvmFn.AddFunctionAttr(noinline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.info.interrupt {
|
||||||
|
// Mark this function as an interrupt.
|
||||||
|
// This is necessary on MCUs that don't push caller saved registers when
|
||||||
|
// entering an interrupt, such as on AVR.
|
||||||
|
if strings.HasPrefix(b.Triple, "avr") {
|
||||||
|
b.llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("signal", ""))
|
||||||
|
} else {
|
||||||
|
b.addError(b.fn.Pos(), "//go:interrupt not supported on this architecture")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add debug info, if needed.
|
// Add debug info, if needed.
|
||||||
if b.Debug {
|
if b.Debug {
|
||||||
if b.fn.Synthetic == "package initializer" {
|
if b.fn.Synthetic == "package initializer" {
|
||||||
@@ -1270,6 +1315,38 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
case "ssa:wrapnilchk":
|
case "ssa:wrapnilchk":
|
||||||
// TODO: do an actual nil check?
|
// TODO: do an actual nil check?
|
||||||
return argValues[0], nil
|
return argValues[0], nil
|
||||||
|
|
||||||
|
// Builtins from the unsafe package.
|
||||||
|
case "Add": // unsafe.Add
|
||||||
|
// This is basically just a GEP operation.
|
||||||
|
// Note: the pointer is always of type *i8.
|
||||||
|
ptr := argValues[0]
|
||||||
|
len := argValues[1]
|
||||||
|
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
||||||
|
case "Slice": // unsafe.Slice
|
||||||
|
// This creates a slice from a pointer and a length.
|
||||||
|
// Note that the exception mentioned in the documentation (if the
|
||||||
|
// pointer and length are nil, the slice is also nil) is trivially
|
||||||
|
// already the case.
|
||||||
|
ptr := argValues[0]
|
||||||
|
len := argValues[1]
|
||||||
|
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
|
||||||
|
ptr.Type(),
|
||||||
|
b.uintptrType,
|
||||||
|
b.uintptrType,
|
||||||
|
}, false))
|
||||||
|
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
|
||||||
|
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||||
|
// Too small, zero-extend len.
|
||||||
|
len = b.CreateZExt(len, b.uintptrType, "")
|
||||||
|
} else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||||
|
// Too big, truncate len.
|
||||||
|
len = b.CreateTrunc(len, b.uintptrType, "")
|
||||||
|
}
|
||||||
|
slice = b.CreateInsertValue(slice, ptr, 0, "")
|
||||||
|
slice = b.CreateInsertValue(slice, len, 1, "")
|
||||||
|
slice = b.CreateInsertValue(slice, len, 2, "")
|
||||||
|
return slice, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
||||||
}
|
}
|
||||||
@@ -1281,9 +1358,9 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
//
|
//
|
||||||
// This is also where compiler intrinsics are implemented.
|
// This is also where compiler intrinsics are implemented.
|
||||||
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
if instr.IsInvoke() {
|
var params []llvm.Value
|
||||||
fnCast, args := b.getInvokeCall(instr)
|
for _, param := range instr.Args {
|
||||||
return b.createCall(fnCast, args, ""), nil
|
params = append(params, b.getValue(param))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
// Try to call the function directly for trivially static calls.
|
||||||
@@ -1298,6 +1375,11 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return b.createMemoryCopyCall(fn, instr.Args)
|
return b.createMemoryCopyCall(fn, instr.Args)
|
||||||
case name == "runtime.memzero":
|
case name == "runtime.memzero":
|
||||||
return b.createMemoryZeroCall(instr.Args)
|
return b.createMemoryZeroCall(instr.Args)
|
||||||
|
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":
|
||||||
@@ -1349,12 +1431,20 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||||
// Builtin function (append, close, delete, etc.).)
|
// Builtin function (append, close, delete, etc.).)
|
||||||
var argTypes []types.Type
|
var argTypes []types.Type
|
||||||
var argValues []llvm.Value
|
|
||||||
for _, arg := range instr.Args {
|
for _, arg := range instr.Args {
|
||||||
argTypes = append(argTypes, arg.Type())
|
argTypes = append(argTypes, arg.Type())
|
||||||
argValues = append(argValues, b.getValue(arg))
|
|
||||||
}
|
}
|
||||||
return b.createBuiltin(argTypes, argValues, call.Name(), instr.Pos())
|
return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
|
||||||
|
} else if instr.IsInvoke() {
|
||||||
|
// Interface method call (aka invoke call).
|
||||||
|
itf := b.getValue(instr.Value) // interface value (runtime._interface)
|
||||||
|
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||||
|
value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver
|
||||||
|
// Prefix the params with receiver value and suffix with typecode.
|
||||||
|
params = append([]llvm.Value{value}, params...)
|
||||||
|
params = append(params, typecode)
|
||||||
|
callee = b.getInvokeFunction(instr)
|
||||||
|
context = llvm.Undef(b.i8ptrType)
|
||||||
} else {
|
} else {
|
||||||
// Function pointer.
|
// Function pointer.
|
||||||
value := b.getValue(instr.Value)
|
value := b.getValue(instr.Value)
|
||||||
@@ -1364,11 +1454,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
b.createNilCheck(instr.Value, callee, "fpcall")
|
b.createNilCheck(instr.Value, callee, "fpcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
var params []llvm.Value
|
|
||||||
for _, param := range instr.Args {
|
|
||||||
params = append(params, b.getValue(param))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exported {
|
if !exported {
|
||||||
// This function takes a context parameter.
|
// This function takes a context parameter.
|
||||||
// Add it to the end of the parameter list.
|
// Add it to the end of the parameter list.
|
||||||
@@ -1386,7 +1471,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
||||||
switch expr := expr.(type) {
|
switch expr := expr.(type) {
|
||||||
case *ssa.Const:
|
case *ssa.Const:
|
||||||
return b.createConst(b.info.linkName, expr)
|
return b.createConst(expr)
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
if b.getFunctionInfo(expr).exported {
|
if b.getFunctionInfo(expr).exported {
|
||||||
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())
|
||||||
@@ -1411,6 +1496,28 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maxSliceSize determines the maximum size a slice of the given element type
|
||||||
|
// can be.
|
||||||
|
func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
||||||
|
// Calculate ^uintptr(0), which is the max value that fits in uintptr.
|
||||||
|
maxPointerValue := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
|
||||||
|
// Calculate (^uint(0))/2, which is the max value that fits in an int.
|
||||||
|
maxIntegerValue := llvm.ConstNot(llvm.ConstInt(c.intType, 0, false)).ZExtValue() / 2
|
||||||
|
|
||||||
|
// Determine the maximum allowed size for a slice. The biggest possible
|
||||||
|
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
||||||
|
// divide by the element type to get the real maximum size.
|
||||||
|
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
|
||||||
|
|
||||||
|
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||||
|
// an int.
|
||||||
|
if maxSize > maxIntegerValue {
|
||||||
|
maxSize = maxIntegerValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSize
|
||||||
|
}
|
||||||
|
|
||||||
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
|
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
|
||||||
// or multiple LLVM IR instructions and/or runtime calls.
|
// or multiple LLVM IR instructions and/or runtime calls.
|
||||||
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||||
@@ -1431,7 +1538,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size))
|
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size))
|
||||||
}
|
}
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment)
|
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
||||||
|
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
||||||
buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
||||||
return buf, nil
|
return buf, nil
|
||||||
} else {
|
} else {
|
||||||
@@ -1624,10 +1732,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
elemSize := b.targetData.TypeAllocSize(llvmElemType)
|
elemSize := b.targetData.TypeAllocSize(llvmElemType)
|
||||||
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
|
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
|
||||||
|
|
||||||
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in
|
maxSize := b.maxSliceSize(llvmElemType)
|
||||||
// uintptr if uintptr were signed.
|
if elemSize > maxSize {
|
||||||
maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
|
|
||||||
if elemSize > maxSize.ZExtValue() {
|
|
||||||
// This seems to be checked by the typechecker already, but let's
|
// This seems to be checked by the typechecker already, but let's
|
||||||
// check it again just to be sure.
|
// check it again just to be sure.
|
||||||
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
|
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
|
||||||
@@ -1636,7 +1742,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
// Bounds checking.
|
// Bounds checking.
|
||||||
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
||||||
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
||||||
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
maxSizeValue := llvm.ConstInt(b.uintptrType, maxSize, false)
|
||||||
|
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||||
|
|
||||||
// Allocate the backing array.
|
// Allocate the backing array.
|
||||||
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||||
@@ -1644,7 +1751,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
||||||
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize}, "makeslice.buf")
|
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
||||||
|
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
||||||
slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array")
|
slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array")
|
||||||
|
|
||||||
// Extend or truncate if necessary. This is safe as we've already done
|
// Extend or truncate if necessary. This is safe as we've already done
|
||||||
@@ -1879,6 +1987,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
||||||
}
|
}
|
||||||
|
case *ssa.SliceToArrayPointer:
|
||||||
|
// Conversion from a slice to an array pointer, as the name clearly
|
||||||
|
// says. This requires a runtime check to make sure the slice is at
|
||||||
|
// least as big as the array.
|
||||||
|
slice := b.getValue(expr.X)
|
||||||
|
sliceLen := b.CreateExtractValue(slice, 1, "")
|
||||||
|
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
||||||
|
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
|
||||||
|
ptr := b.CreateExtractValue(slice, 0, "")
|
||||||
|
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
|
||||||
|
return ptr, nil
|
||||||
case *ssa.TypeAssert:
|
case *ssa.TypeAssert:
|
||||||
return b.createTypeAssert(expr), nil
|
return b.createTypeAssert(expr), nil
|
||||||
case *ssa.UnOp:
|
case *ssa.UnOp:
|
||||||
@@ -1907,17 +2026,58 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
|
|||||||
return b.CreateSub(x, y, ""), nil
|
return b.CreateSub(x, y, ""), nil
|
||||||
case token.MUL: // *
|
case token.MUL: // *
|
||||||
return b.CreateMul(x, y, ""), nil
|
return b.CreateMul(x, y, ""), nil
|
||||||
case token.QUO: // /
|
case token.QUO, token.REM: // /, %
|
||||||
|
// Check for a divide by zero. If y is zero, the Go
|
||||||
|
// specification says that a runtime error must be triggered.
|
||||||
|
b.createDivideByZeroCheck(y)
|
||||||
|
|
||||||
if signed {
|
if signed {
|
||||||
return b.CreateSDiv(x, y, ""), nil
|
// Deal with signed division overflow.
|
||||||
|
// The LLVM LangRef says:
|
||||||
|
//
|
||||||
|
// Overflow also leads to undefined behavior; this is a
|
||||||
|
// rare case, but can occur, for example, by doing a
|
||||||
|
// 32-bit division of -2147483648 by -1.
|
||||||
|
//
|
||||||
|
// The Go specification however says this about division:
|
||||||
|
//
|
||||||
|
// The one exception to this rule is that if the dividend
|
||||||
|
// x is the most negative value for the int type of x, the
|
||||||
|
// quotient q = x / -1 is equal to x (and r = 0) due to
|
||||||
|
// two's-complement integer overflow.
|
||||||
|
//
|
||||||
|
// In other words, in the special case that the lowest
|
||||||
|
// possible signed integer is divided by -1, the result of
|
||||||
|
// the division is the same as x (the dividend).
|
||||||
|
// This is implemented by checking for this condition and
|
||||||
|
// changing y to 1 if it occurs, for example for 32-bit
|
||||||
|
// ints:
|
||||||
|
//
|
||||||
|
// if x == -2147483648 && y == -1 {
|
||||||
|
// y = 1
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Dividing x by 1 obviously returns x, therefore satisfying
|
||||||
|
// the Go specification without a branch.
|
||||||
|
llvmType := x.Type()
|
||||||
|
minusOne := llvm.ConstSub(llvm.ConstInt(llvmType, 0, false), llvm.ConstInt(llvmType, 1, false))
|
||||||
|
lowestInteger := llvm.ConstInt(x.Type(), 1<<(llvmType.IntTypeWidth()-1), false)
|
||||||
|
yIsMinusOne := b.CreateICmp(llvm.IntEQ, y, minusOne, "")
|
||||||
|
xIsLowestInteger := b.CreateICmp(llvm.IntEQ, x, lowestInteger, "")
|
||||||
|
hasOverflow := b.CreateAnd(yIsMinusOne, xIsLowestInteger, "")
|
||||||
|
y = b.CreateSelect(hasOverflow, llvm.ConstInt(llvmType, 1, true), y, "")
|
||||||
|
|
||||||
|
if op == token.QUO {
|
||||||
|
return b.CreateSDiv(x, y, ""), nil
|
||||||
|
} else {
|
||||||
|
return b.CreateSRem(x, y, ""), nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return b.CreateUDiv(x, y, ""), nil
|
if op == token.QUO {
|
||||||
}
|
return b.CreateUDiv(x, y, ""), nil
|
||||||
case token.REM: // %
|
} else {
|
||||||
if signed {
|
return b.CreateURem(x, y, ""), nil
|
||||||
return b.CreateSRem(x, y, ""), nil
|
}
|
||||||
} else {
|
|
||||||
return b.CreateURem(x, y, ""), nil
|
|
||||||
}
|
}
|
||||||
case token.AND: // &
|
case token.AND: // &
|
||||||
return b.CreateAnd(x, y, ""), nil
|
return b.CreateAnd(x, y, ""), nil
|
||||||
@@ -2264,7 +2424,7 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createConst creates a LLVM constant value from a Go constant.
|
// createConst creates a LLVM constant value from a Go constant.
|
||||||
func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
||||||
switch typ := expr.Type().Underlying().(type) {
|
switch typ := expr.Type().Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
llvmType := b.getLLVMType(typ)
|
llvmType := b.getLLVMType(typ)
|
||||||
@@ -2280,7 +2440,7 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
||||||
var strPtr llvm.Value
|
var strPtr llvm.Value
|
||||||
if str != "" {
|
if str != "" {
|
||||||
objname := prefix + "$string"
|
objname := b.pkg.Path() + "$string"
|
||||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
@@ -2310,15 +2470,15 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||||||
n, _ := constant.Float64Val(expr.Value)
|
n, _ := constant.Float64Val(expr.Value)
|
||||||
return llvm.ConstFloat(llvmType, n)
|
return llvm.ConstFloat(llvmType, n)
|
||||||
} else if typ.Kind() == types.Complex64 {
|
} else if typ.Kind() == types.Complex64 {
|
||||||
r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||||
i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx
|
return cplx
|
||||||
} else if typ.Kind() == types.Complex128 {
|
} else if typ.Kind() == types.Complex128 {
|
||||||
r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||||
i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
@@ -16,6 +17,11 @@ import (
|
|||||||
// Pass -update to go test to update the output of the test files.
|
// Pass -update to go test to update the output of the test files.
|
||||||
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
file string
|
||||||
|
target string
|
||||||
|
}
|
||||||
|
|
||||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||||
// the expected LLVM IR for regression testing.
|
// the expected LLVM IR for regression testing.
|
||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
@@ -34,10 +40,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []testCase{
|
||||||
file string
|
|
||||||
target string
|
|
||||||
}{
|
|
||||||
{"basic.go", ""},
|
{"basic.go", ""},
|
||||||
{"pointer.go", ""},
|
{"pointer.go", ""},
|
||||||
{"slice.go", ""},
|
{"slice.go", ""},
|
||||||
@@ -48,6 +51,18 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"pragma.go", ""},
|
{"pragma.go", ""},
|
||||||
{"goroutine.go", "wasm"},
|
{"goroutine.go", "wasm"},
|
||||||
{"goroutine.go", "cortex-m-qemu"},
|
{"goroutine.go", "cortex-m-qemu"},
|
||||||
|
{"channel.go", ""},
|
||||||
|
{"intrinsics.go", "cortex-m-qemu"},
|
||||||
|
{"intrinsics.go", "wasm"},
|
||||||
|
{"gc.go", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not read Go version:", err)
|
||||||
|
}
|
||||||
|
if minor >= 17 {
|
||||||
|
tests = append(tests, testCase{"go1.17.go", ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -59,12 +74,15 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
target, err := compileopts.LoadTarget(targetString)
|
options := &compileopts.Options{
|
||||||
|
Target: targetString,
|
||||||
|
}
|
||||||
|
target, err := compileopts.LoadTarget(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to load target:", err)
|
t.Fatal("failed to load target:", err)
|
||||||
}
|
}
|
||||||
config := &compileopts.Config{
|
config := &compileopts.Config{
|
||||||
Options: &compileopts.Options{},
|
Options: options,
|
||||||
Target: target,
|
Target: target,
|
||||||
}
|
}
|
||||||
compilerConfig := &Config{
|
compilerConfig := &Config{
|
||||||
|
|||||||
+6
-5
@@ -217,7 +217,8 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
// This may be hit a variable number of times, so use a heap allocation.
|
// This may be hit a variable number of times, so use a heap allocation.
|
||||||
size := b.targetData.TypeAllocSize(deferFrameType)
|
size := b.targetData.TypeAllocSize(deferFrameType)
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
nilPtr := llvm.ConstNull(b.i8ptrType)
|
||||||
|
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||||
}
|
}
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
@@ -331,10 +332,10 @@ func (b *builder) createRunDefers() {
|
|||||||
//Pass context
|
//Pass context
|
||||||
forwardParams = append(forwardParams, context)
|
forwardParams = append(forwardParams, context)
|
||||||
} else {
|
} else {
|
||||||
// Isolate the typecode.
|
// Move typecode from the start to the end of the list of
|
||||||
typecode := forwardParams[0]
|
// parameters.
|
||||||
forwardParams = forwardParams[1:]
|
forwardParams = append(forwardParams[1:], forwardParams[0])
|
||||||
fnPtr = b.getInvokePtr(callback, typecode)
|
fnPtr = b.getInvokeFunction(callback)
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
+1
-11
@@ -3,7 +3,6 @@ package compiler
|
|||||||
// This file contains some utility functions related to error handling.
|
// This file contains some utility functions related to error handling.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/scanner"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -20,20 +19,11 @@ func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addError adds a new compiler diagnostic with the given location and message.
|
||||||
func (c *compilerContext) addError(pos token.Pos, msg string) {
|
func (c *compilerContext) addError(pos token.Pos, msg string) {
|
||||||
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorAt returns an error value at the location of the instruction.
|
|
||||||
// The location information may not be complete as it depends on debug
|
|
||||||
// information in the IR.
|
|
||||||
func errorAt(inst llvm.Value, msg string) scanner.Error {
|
|
||||||
return scanner.Error{
|
|
||||||
Pos: getPosition(inst),
|
|
||||||
Msg: msg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPosition returns the position information for the given value, as far as
|
// getPosition returns the position information for the given value, as far as
|
||||||
// it is available.
|
// it is available.
|
||||||
func getPosition(val llvm.Value) token.Position {
|
func getPosition(val llvm.Value) token.Position {
|
||||||
|
|||||||
+16
-4
@@ -23,7 +23,7 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
|||||||
switch c.FuncImplementation {
|
switch c.FuncImplementation {
|
||||||
case "doubleword":
|
case "doubleword":
|
||||||
// Closure is: {context, function pointer}
|
// Closure is: {context, function pointer}
|
||||||
funcValueScalar = funcPtr
|
funcValueScalar = llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
|
||||||
case "switch":
|
case "switch":
|
||||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||||
@@ -80,8 +80,21 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
|
|||||||
context = b.CreateExtractValue(funcValue, 0, "")
|
context = b.CreateExtractValue(funcValue, 0, "")
|
||||||
switch b.FuncImplementation {
|
switch b.FuncImplementation {
|
||||||
case "doubleword":
|
case "doubleword":
|
||||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
bitcast := b.CreateExtractValue(funcValue, 1, "")
|
||||||
|
if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
|
||||||
|
funcPtr = bitcast.Operand(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
llvmSig := b.getRawFuncType(sig)
|
||||||
|
funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
|
||||||
case "switch":
|
case "switch":
|
||||||
|
if !funcValue.IsAConstant().IsNil() {
|
||||||
|
// If this is a constant func value, the underlying function is
|
||||||
|
// known and can be returned directly.
|
||||||
|
funcValueWithSignatureGlobal := llvm.ConstExtractValue(funcValue, []uint32{1}).Operand(0)
|
||||||
|
funcPtr = llvm.ConstExtractValue(funcValueWithSignatureGlobal.Initializer(), []uint32{0}).Operand(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
llvmSig := b.getRawFuncType(sig)
|
llvmSig := b.getRawFuncType(sig)
|
||||||
sigGlobal := b.getFuncSignatureID(sig)
|
sigGlobal := b.getFuncSignatureID(sig)
|
||||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||||
@@ -96,8 +109,7 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
|
|||||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||||
switch c.FuncImplementation {
|
switch c.FuncImplementation {
|
||||||
case "doubleword":
|
case "doubleword":
|
||||||
rawPtr := c.getRawFuncType(typ)
|
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
||||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
|
||||||
case "switch":
|
case "switch":
|
||||||
return c.getLLVMRuntimeType("funcValue")
|
return c.getLLVMRuntimeType("funcValue")
|
||||||
default:
|
default:
|
||||||
|
|||||||
+11
-4
@@ -79,7 +79,15 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
}
|
}
|
||||||
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
|
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
|
||||||
return
|
return
|
||||||
} else if !instr.Call.IsInvoke() {
|
} else if instr.Call.IsInvoke() {
|
||||||
|
// This is a method call on an interface value.
|
||||||
|
itf := b.getValue(instr.Call.Value)
|
||||||
|
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
||||||
|
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||||
|
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||||
|
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||||
|
params = append(params, itfTypeCode) // end with typecode
|
||||||
|
} else {
|
||||||
// This is a function pointer.
|
// This is a function pointer.
|
||||||
// At the moment, two extra params are passed to the newly started
|
// At the moment, two extra params are passed to the newly started
|
||||||
// goroutine:
|
// goroutine:
|
||||||
@@ -99,9 +107,6 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
panic("unknown scheduler type")
|
panic("unknown scheduler type")
|
||||||
}
|
}
|
||||||
prefix = b.fn.RelString(nil)
|
prefix = b.fn.RelString(nil)
|
||||||
} else {
|
|
||||||
b.addError(instr.Pos(), "todo: go on interface call")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
@@ -176,6 +181,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||||
|
c.addStandardAttributes(wrapper)
|
||||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||||
@@ -240,6 +246,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||||
|
c.addStandardAttributes(wrapper)
|
||||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
constraints := []string{}
|
constraints := []string{}
|
||||||
hasOutput := false
|
hasOutput := false
|
||||||
asmString = regexp.MustCompile("\\{\\}").ReplaceAllStringFunc(asmString, func(s string) string {
|
asmString = regexp.MustCompile(`\{\}`).ReplaceAllStringFunc(asmString, func(s string) string {
|
||||||
hasOutput = true
|
hasOutput = true
|
||||||
return "$0"
|
return "$0"
|
||||||
})
|
})
|
||||||
@@ -80,7 +80,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
constraints = append(constraints, "=&r")
|
constraints = append(constraints, "=&r")
|
||||||
registerNumbers[""] = 0
|
registerNumbers[""] = 0
|
||||||
}
|
}
|
||||||
asmString = regexp.MustCompile("\\{[a-zA-Z]+\\}").ReplaceAllStringFunc(asmString, func(s string) string {
|
asmString = regexp.MustCompile(`\{[a-zA-Z]+\}`).ReplaceAllStringFunc(asmString, func(s string) string {
|
||||||
// TODO: skip strings like {r4} etc. that look like ARM push/pop
|
// TODO: skip strings like {r4} etc. that look like ARM push/pop
|
||||||
// instructions.
|
// instructions.
|
||||||
name := s[1 : len(s)-1]
|
name := s[1 : len(s)-1]
|
||||||
|
|||||||
+71
-38
@@ -47,6 +47,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
var length int64
|
var length int64
|
||||||
var methodSet llvm.Value
|
var methodSet llvm.Value
|
||||||
var ptrTo llvm.Value
|
var ptrTo llvm.Value
|
||||||
|
var typeAssert llvm.Value
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
references = c.getTypeCode(typ.Underlying())
|
references = c.getTypeCode(typ.Underlying())
|
||||||
@@ -69,6 +70,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
}
|
}
|
||||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||||
methodSet = c.getTypeMethodSet(typ)
|
methodSet = c.getTypeMethodSet(typ)
|
||||||
|
} else {
|
||||||
|
typeAssert = c.getInterfaceImplementsFunc(typ)
|
||||||
|
typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
|
||||||
}
|
}
|
||||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
||||||
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
||||||
@@ -87,6 +91,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
if !ptrTo.IsNil() {
|
if !ptrTo.IsNil() {
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
||||||
}
|
}
|
||||||
|
if !typeAssert.IsNil() {
|
||||||
|
globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
|
||||||
|
}
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
@@ -193,7 +200,11 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
methods := make([]string, t.NumMethods())
|
methods := make([]string, t.NumMethods())
|
||||||
for i := 0; i < t.NumMethods(); i++ {
|
for i := 0; i < t.NumMethods(); i++ {
|
||||||
methods[i] = t.Method(i).Name() + ":" + getTypeCodeName(t.Method(i).Type())
|
name := t.Method(i).Name()
|
||||||
|
if !token.IsExported(name) {
|
||||||
|
name = t.Method(i).Pkg().Path() + "." + name
|
||||||
|
}
|
||||||
|
methods[i] = name + ":" + getTypeCodeName(t.Method(i).Type())
|
||||||
}
|
}
|
||||||
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
@@ -306,10 +317,9 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
|||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMethodSignature returns a global variable which is a reference to an
|
// getMethodSignatureName returns a unique name (that can be used as the name of
|
||||||
// external *i8 indicating the indicating the signature of this method. It is
|
// a global) for the given method.
|
||||||
// used during the interface lowering pass.
|
func (c *compilerContext) getMethodSignatureName(method *types.Func) string {
|
||||||
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
|
||||||
signature := methodSignature(method)
|
signature := methodSignature(method)
|
||||||
var globalName string
|
var globalName string
|
||||||
if token.IsExported(method.Name()) {
|
if token.IsExported(method.Name()) {
|
||||||
@@ -317,6 +327,14 @@ func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
|||||||
} else {
|
} else {
|
||||||
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
||||||
}
|
}
|
||||||
|
return globalName
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMethodSignature returns a global variable which is a reference to an
|
||||||
|
// external *i8 indicating the indicating the signature of this method. It is
|
||||||
|
// used during the interface lowering pass.
|
||||||
|
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
||||||
|
globalName := c.getMethodSignatureName(method)
|
||||||
signatureGlobal := c.mod.NamedGlobal(globalName)
|
signatureGlobal := c.mod.NamedGlobal(globalName)
|
||||||
if signatureGlobal.IsNil() {
|
if signatureGlobal.IsNil() {
|
||||||
// TODO: put something useful in these globals, such as the method
|
// TODO: put something useful in these globals, such as the method
|
||||||
@@ -345,15 +363,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
commaOk := llvm.Value{}
|
commaOk := llvm.Value{}
|
||||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||||
// Type assert on interface type.
|
// Type assert on interface type.
|
||||||
// This pseudo call will be lowered in the interface lowering pass to a
|
// This is a call to an interface type assert function.
|
||||||
// real call which checks whether the provided typecode is any of the
|
// The interface lowering pass will define this function by filling it
|
||||||
// concrete types that implements this interface.
|
// with a type switch over all concrete types that implement this
|
||||||
|
// interface, and returning whether it's one of the matched types.
|
||||||
// This is very different from how interface asserts are implemented in
|
// This is very different from how interface asserts are implemented in
|
||||||
// the main Go compiler, where the runtime checks whether the type
|
// the main Go compiler, where the runtime checks whether the type
|
||||||
// implements each method of the interface. See:
|
// implements each method of the interface. See:
|
||||||
// https://research.swtch.com/interfaces
|
// https://research.swtch.com/interfaces
|
||||||
methodSet := b.getInterfaceMethodSet(expr.AssertedType)
|
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
commaOk = b.CreateCall(fn, []llvm.Value{actualTypeNum}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
@@ -420,39 +439,52 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInvokePtr creates an interface function pointer lookup for the specified invoke instruction, using a specified typecode.
|
// getMethodsString returns a string to be used in the "tinygo-methods" string
|
||||||
func (b *builder) getInvokePtr(instr *ssa.CallCommon, typecode llvm.Value) llvm.Value {
|
// attribute for interface functions.
|
||||||
llvmFnType := b.getRawFuncType(instr.Method.Type().(*types.Signature))
|
func (c *compilerContext) getMethodsString(itf *types.Interface) string {
|
||||||
values := []llvm.Value{
|
methods := make([]string, itf.NumMethods())
|
||||||
typecode,
|
for i := range methods {
|
||||||
b.getInterfaceMethodSet(instr.Value.Type()),
|
methods[i] = c.getMethodSignatureName(itf.Method(i))
|
||||||
b.getMethodSignature(instr.Method),
|
|
||||||
}
|
}
|
||||||
fn := b.createRuntimeCall("interfaceMethod", values, "invoke.func")
|
return strings.Join(methods, "; ")
|
||||||
return b.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInvokeCall creates and returns the function pointer and parameters of an
|
// getInterfaceImplementsfunc returns a declared function that works as a type
|
||||||
// interface call.
|
// switch. The interface lowering pass will define this function.
|
||||||
func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value) {
|
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
|
||||||
// Call an interface method with dynamic dispatch.
|
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
|
||||||
itf := b.getValue(instr.Value) // interface
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
|
if llvmFn.IsNil() {
|
||||||
typecode := b.CreateExtractValue(itf, 0, "invoke.typecode")
|
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
|
||||||
fnCast := b.getInvokePtr(instr, typecode)
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
|
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||||
args := []llvm.Value{receiverValue}
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||||
for _, arg := range instr.Args {
|
|
||||||
args = append(args, b.getValue(arg))
|
|
||||||
}
|
}
|
||||||
// Add the context parameter. An interface call never takes a context but we
|
return llvmFn
|
||||||
// have to supply the parameter anyway.
|
}
|
||||||
args = append(args, llvm.Undef(b.i8ptrType))
|
|
||||||
// Add the parent goroutine handle.
|
|
||||||
args = append(args, llvm.Undef(b.i8ptrType))
|
|
||||||
|
|
||||||
return fnCast, args
|
// getInvokeFunction returns the thunk to call the given interface method. The
|
||||||
|
// thunk is declared, not defined: it will be defined by the interface lowering
|
||||||
|
// pass.
|
||||||
|
func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||||
|
fnName := getTypeCodeName(instr.Value.Type().Underlying()) + "." + instr.Method.Name() + "$invoke"
|
||||||
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
|
if llvmFn.IsNil() {
|
||||||
|
sig := instr.Method.Type().(*types.Signature)
|
||||||
|
var paramTuple []*types.Var
|
||||||
|
for i := 0; i < sig.Params().Len(); i++ {
|
||||||
|
paramTuple = append(paramTuple, sig.Params().At(i))
|
||||||
|
}
|
||||||
|
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
|
||||||
|
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)).ElementType()
|
||||||
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
||||||
|
methods := c.getMethodsString(instr.Value.Type().Underlying().(*types.Interface))
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||||
|
}
|
||||||
|
return llvmFn
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
|
// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
|
||||||
@@ -489,6 +521,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
|||||||
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
|
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||||
wrapFnType := llvm.FunctionType(fnType.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)
|
||||||
wrapper.LastParam().SetName("parentHandle")
|
wrapper.LastParam().SetName("parentHandle")
|
||||||
|
|
||||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ 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)
|
||||||
|
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
|
||||||
// type are lowered in the interrupt lowering pass.
|
// type are lowered in the interrupt lowering pass.
|
||||||
@@ -47,8 +49,9 @@ 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, funcValue, []uint32{0})
|
initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
|
||||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{1, 0})
|
initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
|
||||||
|
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
|
|
||||||
// Add debug info to the interrupt global.
|
// Add debug info to the interrupt global.
|
||||||
|
|||||||
@@ -48,3 +48,57 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
|||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFn, params, "")
|
||||||
return llvm.Value{}, nil
|
return llvm.Value{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mathToLLVMMapping = map[string]string{
|
||||||
|
"math.Sqrt": "llvm.sqrt.f64",
|
||||||
|
"math.Floor": "llvm.floor.f64",
|
||||||
|
"math.Ceil": "llvm.ceil.f64",
|
||||||
|
"math.Trunc": "llvm.trunc.f64",
|
||||||
|
}
|
||||||
|
|
||||||
|
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
||||||
|
// possible. It returns the call result if possible, and a boolean whether it
|
||||||
|
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
||||||
|
// intrinsic.
|
||||||
|
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||||
|
// Check whether this intrinsic is supported on the given GOARCH.
|
||||||
|
// If it is unsupported, this can have two reasons:
|
||||||
|
//
|
||||||
|
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
||||||
|
// the result doesn't pass the tests of the math package.
|
||||||
|
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
||||||
|
// libm function call, but the libm function call also fails the math
|
||||||
|
// package tests.
|
||||||
|
//
|
||||||
|
// Whatever the implementation, it must pass the tests in the math package
|
||||||
|
// so unfortunately only the below intrinsic+architecture combinations are
|
||||||
|
// supported.
|
||||||
|
name := call.StaticCallee().RelString(nil)
|
||||||
|
switch name {
|
||||||
|
case "math.Ceil", "math.Floor", "math.Trunc":
|
||||||
|
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
||||||
|
return llvm.Value{}, false
|
||||||
|
}
|
||||||
|
case "math.Sqrt":
|
||||||
|
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
||||||
|
return llvm.Value{}, false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return llvm.Value{}, false // only the above functions are supported.
|
||||||
|
}
|
||||||
|
|
||||||
|
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
||||||
|
if llvmFn.IsNil() {
|
||||||
|
// The intrinsic doesn't exist yet, so declare it.
|
||||||
|
// At the moment, all supported intrinsics have the form "double
|
||||||
|
// foo(double %x)" so we can hardcode the signature here.
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||||
|
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
||||||
|
}
|
||||||
|
// Create a call to the intrinsic.
|
||||||
|
args := make([]llvm.Value, len(call.Args))
|
||||||
|
for i, arg := range call.Args {
|
||||||
|
args[i] = b.getValue(arg)
|
||||||
|
}
|
||||||
|
return b.CreateCall(llvmFn, args, ""), true
|
||||||
|
}
|
||||||
|
|||||||
+202
-16
@@ -1,6 +1,11 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
@@ -8,21 +13,6 @@ import (
|
|||||||
// This file contains helper functions for LLVM that are not exposed in the Go
|
// This file contains helper functions for LLVM that are not exposed in the Go
|
||||||
// bindings.
|
// bindings.
|
||||||
|
|
||||||
// Return a list of values (actually, instructions) where this value is used as
|
|
||||||
// an operand.
|
|
||||||
func getUses(value llvm.Value) []llvm.Value {
|
|
||||||
if value.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var uses []llvm.Value
|
|
||||||
use := value.FirstUse()
|
|
||||||
for !use.IsNil() {
|
|
||||||
uses = append(uses, use.User())
|
|
||||||
use = use.NextUse()
|
|
||||||
}
|
|
||||||
return uses
|
|
||||||
}
|
|
||||||
|
|
||||||
// createTemporaryAlloca creates a new alloca in the entry block and adds
|
// createTemporaryAlloca creates a new alloca in the entry block and adds
|
||||||
// lifetime start information in the IR signalling that the alloca won't be used
|
// lifetime start information in the IR signalling that the alloca won't be used
|
||||||
// before this point.
|
// before this point.
|
||||||
@@ -44,7 +34,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
|||||||
// 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.
|
||||||
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.NeedsStackObjects, values)
|
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.pkg.Path(), b.NeedsStackObjects, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||||
@@ -67,3 +57,199 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createObjectLayout returns a LLVM value (of type i8*) that describes where
|
||||||
|
// there are pointers in the type t. If all the data fits in a word, it is
|
||||||
|
// returned as a word. Otherwise it will store the data in a global.
|
||||||
|
//
|
||||||
|
// The value contains two pieces of information: the length of the object and
|
||||||
|
// which words contain a pointer (indicated by setting the given bit to 1). For
|
||||||
|
// arrays, only the element is stored. This works because the GC knows the
|
||||||
|
// object size and can therefore know how this value is repeated in the object.
|
||||||
|
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||||
|
// Use the element type for arrays. This works even for nested arrays.
|
||||||
|
for {
|
||||||
|
kind := t.TypeKind()
|
||||||
|
if kind == llvm.ArrayTypeKind {
|
||||||
|
t = t.ElementType()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if kind == llvm.StructTypeKind {
|
||||||
|
fields := t.StructElementTypes()
|
||||||
|
if len(fields) == 1 {
|
||||||
|
t = fields[0]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a few checks to see whether we need to generate any object layout
|
||||||
|
// information at all.
|
||||||
|
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||||
|
pointerSize := c.targetData.TypeAllocSize(c.i8ptrType)
|
||||||
|
pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||||
|
if objectSizeBytes < pointerSize {
|
||||||
|
// Too small to contain a pointer.
|
||||||
|
layout := (uint64(1) << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
|
||||||
|
}
|
||||||
|
bitmap := c.getPointerBitmap(t, pos)
|
||||||
|
if bitmap.BitLen() == 0 {
|
||||||
|
// There are no pointers in this type, so we can simplify the layout.
|
||||||
|
// TODO: this can be done in many other cases, e.g. when allocating an
|
||||||
|
// array (like [4][]byte, which repeats a slice 4 times).
|
||||||
|
layout := (uint64(1) << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
|
||||||
|
}
|
||||||
|
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
||||||
|
// This shouldn't happen except for packed structs, which aren't
|
||||||
|
// currently used.
|
||||||
|
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
||||||
|
return llvm.ConstNull(c.i8ptrType)
|
||||||
|
}
|
||||||
|
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
||||||
|
|
||||||
|
pointerBits := pointerSize * 8
|
||||||
|
var sizeFieldBits uint64
|
||||||
|
switch pointerBits {
|
||||||
|
case 16:
|
||||||
|
sizeFieldBits = 4
|
||||||
|
case 32:
|
||||||
|
sizeFieldBits = 5
|
||||||
|
case 64:
|
||||||
|
sizeFieldBits = 6
|
||||||
|
default:
|
||||||
|
panic("unknown pointer size")
|
||||||
|
}
|
||||||
|
layoutFieldBits := pointerBits - 1 - sizeFieldBits
|
||||||
|
|
||||||
|
// Try to emit the value as an inline integer. This is possible in most
|
||||||
|
// cases.
|
||||||
|
if objectSizeWords < layoutFieldBits {
|
||||||
|
// If it can be stored directly in the pointer value, do so.
|
||||||
|
// The runtime knows that if the least significant bit of the pointer is
|
||||||
|
// set, the pointer contains the value itself.
|
||||||
|
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||||
|
// integer. Store it in a global instead.
|
||||||
|
|
||||||
|
// Try first whether the global already exists. All objects with a
|
||||||
|
// particular name have the same type, so this is possible.
|
||||||
|
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
||||||
|
global := c.mod.NamedGlobal(globalName)
|
||||||
|
if !global.IsNil() {
|
||||||
|
return llvm.ConstBitCast(global, c.i8ptrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the global initializer.
|
||||||
|
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
||||||
|
copy(bitmapBytes, bitmap.Bytes())
|
||||||
|
var bitmapByteValues []llvm.Value
|
||||||
|
for _, b := range bitmapBytes {
|
||||||
|
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
||||||
|
}
|
||||||
|
initializer := c.ctx.ConstStruct([]llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, objectSizeWords, false),
|
||||||
|
llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
||||||
|
global.SetInitializer(initializer)
|
||||||
|
global.SetUnnamedAddr(true)
|
||||||
|
global.SetGlobalConstant(true)
|
||||||
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
||||||
|
// AVR doesn't have alignment by default.
|
||||||
|
global.SetAlignment(2)
|
||||||
|
}
|
||||||
|
if c.Debug && pos != token.NoPos {
|
||||||
|
// Creating a fake global so that the value can be inspected in GDB.
|
||||||
|
// For example, the layout for strings.stringFinder (as of Go version
|
||||||
|
// 1.15) has the following type according to GDB:
|
||||||
|
// type = struct {
|
||||||
|
// uintptr numBits;
|
||||||
|
// uint8 data[33];
|
||||||
|
// }
|
||||||
|
// ...that's sort of a mixed C/Go type, but it is readable. More
|
||||||
|
// importantly, these object layout globals can be read and printed by
|
||||||
|
// GDB which may be useful for debugging.
|
||||||
|
position := c.program.Fset.Position(pos)
|
||||||
|
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[position.Filename], llvm.DIGlobalVariableExpression{
|
||||||
|
Name: globalName,
|
||||||
|
File: c.getDIFile(position.Filename),
|
||||||
|
Line: position.Line,
|
||||||
|
Type: c.getDIType(types.NewStruct([]*types.Var{
|
||||||
|
types.NewVar(pos, nil, "numBits", types.Typ[types.Uintptr]),
|
||||||
|
types.NewVar(pos, nil, "data", types.NewArray(types.Typ[types.Byte], int64(len(bitmapByteValues)))),
|
||||||
|
}, nil)),
|
||||||
|
LocalToUnit: false,
|
||||||
|
Expr: c.dibuilder.CreateExpression(nil),
|
||||||
|
})
|
||||||
|
global.AddMetadata(0, diglobal)
|
||||||
|
}
|
||||||
|
|
||||||
|
return llvm.ConstBitCast(global, c.i8ptrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||||
|
// bigint at the word offset that contains a pointer. This scan is recursive.
|
||||||
|
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
||||||
|
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||||
|
switch typ.TypeKind() {
|
||||||
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
|
return big.NewInt(0)
|
||||||
|
case llvm.PointerTypeKind:
|
||||||
|
return big.NewInt(1)
|
||||||
|
case llvm.StructTypeKind:
|
||||||
|
ptrs := big.NewInt(0)
|
||||||
|
if typ.StructName() == "runtime.funcValue" {
|
||||||
|
// Hack: the type runtime.funcValue contains an 'id' field which is
|
||||||
|
// of type uintptr, but before the LowerFuncValues pass it actually
|
||||||
|
// contains a pointer (ptrtoint) to a global. This trips up the
|
||||||
|
// interp package. Therefore, make the id field a pointer for now.
|
||||||
|
typ = c.ctx.StructType([]llvm.Type{c.i8ptrType, c.i8ptrType}, false)
|
||||||
|
}
|
||||||
|
for i, subtyp := range typ.StructElementTypes() {
|
||||||
|
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||||
|
if subptrs.BitLen() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
offset := c.targetData.ElementOffset(typ, i)
|
||||||
|
if offset%uint64(alignment) != 0 {
|
||||||
|
// This error will let the compilation fail, but by continuing
|
||||||
|
// the error can still easily be shown.
|
||||||
|
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||||
|
ptrs.Or(ptrs, subptrs)
|
||||||
|
}
|
||||||
|
return ptrs
|
||||||
|
case llvm.ArrayTypeKind:
|
||||||
|
subtyp := typ.ElementType()
|
||||||
|
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||||
|
ptrs := big.NewInt(0)
|
||||||
|
if subptrs.BitLen() == 0 {
|
||||||
|
return ptrs
|
||||||
|
}
|
||||||
|
elementSize := c.targetData.TypeAllocSize(subtyp)
|
||||||
|
if elementSize%uint64(alignment) != 0 {
|
||||||
|
// This error will let the compilation fail (but continues so that
|
||||||
|
// other errors can be shown).
|
||||||
|
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||||
|
return ptrs
|
||||||
|
}
|
||||||
|
for i := 0; i < typ.ArrayLength(); i++ {
|
||||||
|
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||||
|
ptrs.Or(ptrs, subptrs)
|
||||||
|
}
|
||||||
|
return ptrs
|
||||||
|
default:
|
||||||
|
// Should not happen.
|
||||||
|
panic("unknown LLVM type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
// 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.
|
// If the values are all constants, they are be stored in a constant global and deduplicated.
|
||||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
@@ -83,12 +83,11 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bo
|
|||||||
if constant {
|
if constant {
|
||||||
// The data is known at compile time, so store it in a constant global.
|
// 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.
|
// The global address is marked as unnamed, which allows LLVM to merge duplicates.
|
||||||
funcName := builder.GetInsertBlock().Parent().Name()
|
global := llvm.AddGlobal(mod, packedType, prefix+"$pack")
|
||||||
global := llvm.AddGlobal(mod, packedType, funcName+"$pack")
|
|
||||||
global.SetInitializer(ctx.ConstStruct(values, false))
|
global.SetInitializer(ctx.ConstStruct(values, false))
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
return llvm.ConstBitCast(global, i8ptrType)
|
return llvm.ConstBitCast(global, i8ptrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +96,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bo
|
|||||||
alloc := mod.NamedFunction("runtime.alloc")
|
alloc := mod.NamedFunction("runtime.alloc")
|
||||||
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
|
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
|
||||||
sizeValue,
|
sizeValue,
|
||||||
|
llvm.ConstNull(i8ptrType),
|
||||||
llvm.Undef(i8ptrType), // unused context parameter
|
llvm.Undef(i8ptrType), // unused context parameter
|
||||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||||
}, "")
|
}, "")
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ type functionInfo struct {
|
|||||||
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
||||||
section string // go:section - object file section name
|
section string // go:section - object file section name
|
||||||
exported bool // go:export, CGo
|
exported bool // go:export, CGo
|
||||||
|
interrupt bool // go:interrupt
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
variadic bool // go:variadic (CGo only)
|
variadic bool // go:variadic (CGo only)
|
||||||
inline inlineType // go:inline
|
inline inlineType // go:inline
|
||||||
@@ -108,6 +109,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
llvmFn.AddFunctionAttr(attr)
|
llvmFn.AddFunctionAttr(attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
|
|
||||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||||
for i, info := range paramInfos {
|
for i, info := range paramInfos {
|
||||||
@@ -250,6 +252,10 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
|||||||
|
|
||||||
importName = parts[1]
|
importName = parts[1]
|
||||||
info.exported = true
|
info.exported = true
|
||||||
|
case "//go:interrupt":
|
||||||
|
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||||
|
info.interrupt = true
|
||||||
|
}
|
||||||
case "//go:wasm-module":
|
case "//go:wasm-module":
|
||||||
// Alternative comment for setting the import module.
|
// Alternative comment for setting the import module.
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
@@ -323,6 +329,47 @@ func getParams(sig *types.Signature) []*types.Var {
|
|||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addStandardDeclaredAttributes adds attributes that are set for any function,
|
||||||
|
// whether declared or defined.
|
||||||
|
func (c *compilerContext) addStandardDeclaredAttributes(llvmFn llvm.Value) {
|
||||||
|
if c.SizeLevel >= 1 {
|
||||||
|
// Set the "optsize" attribute to make slightly smaller binaries at the
|
||||||
|
// cost of minimal performance loss (-Os in Clang).
|
||||||
|
kind := llvm.AttributeKindID("optsize")
|
||||||
|
attr := c.ctx.CreateEnumAttribute(kind, 0)
|
||||||
|
llvmFn.AddFunctionAttr(attr)
|
||||||
|
}
|
||||||
|
if c.SizeLevel >= 2 {
|
||||||
|
// Set the "minsize" attribute to reduce code size even further,
|
||||||
|
// regardless of performance loss (-Oz in Clang).
|
||||||
|
kind := llvm.AttributeKindID("minsize")
|
||||||
|
attr := c.ctx.CreateEnumAttribute(kind, 0)
|
||||||
|
llvmFn.AddFunctionAttr(attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addStandardDefinedAttributes adds the set of attributes that are added to
|
||||||
|
// every function defined by TinyGo (even thunks/wrappers), possibly depending
|
||||||
|
// on the architecture. It does not set attributes only set for declared
|
||||||
|
// functions, use addStandardDeclaredAttributes for this.
|
||||||
|
func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
|
||||||
|
// TinyGo does not currently raise exceptions, so set the 'nounwind' flag.
|
||||||
|
// This behavior matches Clang when compiling C source files.
|
||||||
|
// It reduces binary size on Linux a little bit on non-x86_64 targets by
|
||||||
|
// eliminating exception tables for these functions.
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
|
||||||
|
if strings.Split(c.Triple, "-")[0] == "x86_64" {
|
||||||
|
// Required by the ABI.
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addStandardAttribute adds all attributes added to defined functions.
|
||||||
|
func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) {
|
||||||
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
|
c.addStandardDefinedAttributes(llvmFn)
|
||||||
|
}
|
||||||
|
|
||||||
// globalInfo contains some information about a specific global. By default,
|
// globalInfo contains some information about a specific global. By default,
|
||||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||||
// some symbols this is different (due to //go:extern for example).
|
// some symbols this is different (due to //go:extern for example).
|
||||||
|
|||||||
Vendored
+31
@@ -10,6 +10,22 @@ func equalInt(x, y int) bool {
|
|||||||
return x == y
|
return x == y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func divInt(x, y int) int {
|
||||||
|
return x / y
|
||||||
|
}
|
||||||
|
|
||||||
|
func divUint(x, y uint) uint {
|
||||||
|
return x / y
|
||||||
|
}
|
||||||
|
|
||||||
|
func remInt(x, y int) int {
|
||||||
|
return x % y
|
||||||
|
}
|
||||||
|
|
||||||
|
func remUint(x, y uint) uint {
|
||||||
|
return x % y
|
||||||
|
}
|
||||||
|
|
||||||
func floatEQ(x, y float32) bool {
|
func floatEQ(x, y float32) bool {
|
||||||
return x == y
|
return x == y
|
||||||
}
|
}
|
||||||
@@ -55,3 +71,18 @@ func complexMul(x, y complex64) complex64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: complexDiv (requires runtime call)
|
// TODO: complexDiv (requires runtime call)
|
||||||
|
|
||||||
|
// A type 'kv' also exists in function foo. Test that these two types don't
|
||||||
|
// conflict with each other.
|
||||||
|
type kv struct {
|
||||||
|
v float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo(a *kv) {
|
||||||
|
// Define a new 'kv' type.
|
||||||
|
type kv struct {
|
||||||
|
v byte
|
||||||
|
}
|
||||||
|
// Use this type.
|
||||||
|
func(b *kv) {}(nil)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+118
-16
@@ -1,74 +1,159 @@
|
|||||||
; ModuleID = 'basic.go'
|
; ModuleID = 'basic.go'
|
||||||
source_filename = "basic.go"
|
source_filename = "basic.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
%main.kv = type { float }
|
||||||
|
%main.kv.0 = type { i8 }
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = add i32 %x, %y
|
%0 = add i32 %x, %y
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %x, %y
|
%0 = icmp eq i32 %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = icmp eq i32 %y, 0
|
||||||
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
divbyzero.next: ; preds = %entry
|
||||||
|
%1 = icmp eq i32 %y, -1
|
||||||
|
%2 = icmp eq i32 %x, -2147483648
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = select i1 %3, i32 1, i32 %y
|
||||||
|
%5 = sdiv i32 %x, %4
|
||||||
|
ret i32 %5
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.divideByZeroPanic(i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = icmp eq i32 %y, 0
|
||||||
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
divbyzero.next: ; preds = %entry
|
||||||
|
%1 = udiv i32 %x, %y
|
||||||
|
ret i32 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = icmp eq i32 %y, 0
|
||||||
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
divbyzero.next: ; preds = %entry
|
||||||
|
%1 = icmp eq i32 %y, -1
|
||||||
|
%2 = icmp eq i32 %x, -2147483648
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = select i1 %3, i32 1, i32 %y
|
||||||
|
%5 = srem i32 %x, %4
|
||||||
|
ret i32 %5
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = icmp eq i32 %y, 0
|
||||||
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
divbyzero.next: ; preds = %entry
|
||||||
|
%1 = urem i32 %x, %y
|
||||||
|
ret i32 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oeq float %x, %y
|
%0 = fcmp oeq float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp une float %x, %y
|
%0 = fcmp une float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt float %x, %y
|
%0 = fcmp olt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ole float %x, %y
|
%0 = fcmp ole float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ogt float %x, %y
|
%0 = fcmp ogt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oge float %x, %y
|
%0 = fcmp oge float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.r
|
ret float %x.r
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.i
|
ret float %x.i
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -77,7 +162,8 @@ entry:
|
|||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -86,7 +172,8 @@ entry:
|
|||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -98,3 +185,18 @@ entry:
|
|||||||
%7 = insertvalue { float, float } %6, float %5, 1
|
%7 = insertvalue { float, float } %6, float %5, 1
|
||||||
ret { float, float } %7
|
ret { float, float } %7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
Vendored
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func chanIntSend(ch chan int) {
|
||||||
|
ch <- 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanIntRecv(ch chan int) {
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanZeroSend(ch chan struct{}) {
|
||||||
|
ch <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanZeroRecv(ch chan struct{}) {
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectZeroRecv(ch1 chan int, ch2 chan struct{}) {
|
||||||
|
select {
|
||||||
|
case ch1 <- 1:
|
||||||
|
case <-ch2:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+121
@@ -0,0 +1,121 @@
|
|||||||
|
; ModuleID = 'channel.go'
|
||||||
|
source_filename = "channel.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||||
|
%"internal/task.state" = type { i8* }
|
||||||
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.value = alloca i32, align 4
|
||||||
|
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
store i32 3, i32* %chan.value, align 4
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind willreturn
|
||||||
|
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
|
||||||
|
|
||||||
|
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind willreturn
|
||||||
|
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.value = alloca i32, align 4
|
||||||
|
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null) #0
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||||
|
%select.send.value = alloca i32, align 4
|
||||||
|
store i32 1, i32* %select.send.value, align 4
|
||||||
|
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||||
|
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
|
||||||
|
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
|
||||||
|
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
|
||||||
|
%0 = bitcast i8** %.repack1 to i32**
|
||||||
|
store i32* %select.send.value, i32** %0, align 4
|
||||||
|
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
|
||||||
|
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
|
||||||
|
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
||||||
|
store i8* null, i8** %.repack4, align 4
|
||||||
|
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
|
||||||
|
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef, i8* null) #0
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||||
|
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||||
|
%2 = icmp eq i32 %1, 0
|
||||||
|
br i1 %2, label %select.done, label %select.next
|
||||||
|
|
||||||
|
select.done: ; preds = %select.body, %select.next, %entry
|
||||||
|
ret void
|
||||||
|
|
||||||
|
select.next: ; preds = %entry
|
||||||
|
%3 = icmp eq i32 %1, 1
|
||||||
|
br i1 %3, label %select.body, label %select.done
|
||||||
|
|
||||||
|
select.body: ; preds = %select.next
|
||||||
|
br label %select.done
|
||||||
|
}
|
||||||
|
|
||||||
|
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { argmemonly nounwind willreturn }
|
||||||
Vendored
+22
-11
@@ -1,16 +1,18 @@
|
|||||||
; ModuleID = 'float.go'
|
; ModuleID = 'float.go'
|
||||||
source_filename = "float.go"
|
source_filename = "float.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -21,22 +23,26 @@ entry:
|
|||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret float 0x41F0000000000000
|
ret float 0x41F0000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 -1
|
ret i32 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -45,7 +51,8 @@ entry:
|
|||||||
ret i32 %1
|
ret i32 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -57,7 +64,8 @@ entry:
|
|||||||
ret float %1
|
ret float %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -68,7 +76,8 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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
|
||||||
@@ -80,3 +89,5 @@ entry:
|
|||||||
%0 = select i1 %inbounds, i8 %normal, i8 %remapped
|
%0 = select i1 %inbounds, i8 %normal, i8 %remapped
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
Vendored
+15
-9
@@ -1,33 +1,35 @@
|
|||||||
; ModuleID = 'func.go'
|
; ModuleID = 'func.go'
|
||||||
source_filename = "func.go"
|
source_filename = "func.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||||
|
|
||||||
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||||
@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
|
%0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) #0
|
||||||
%1 = icmp eq i32 %0, 0
|
%1 = icmp eq i32 %0, 0
|
||||||
br i1 %1, label %fpcall.throw, label %fpcall.next
|
br i1 %1, label %fpcall.throw, label %fpcall.next
|
||||||
|
|
||||||
fpcall.throw: ; preds = %entry
|
fpcall.throw: ; preds = %entry
|
||||||
call void @runtime.nilPanic(i8* undef, i8* null)
|
call void @runtime.nilPanic(i8* undef, i8* null) #0
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
fpcall.next: ; preds = %entry
|
fpcall.next: ; preds = %entry
|
||||||
%2 = inttoptr i32 %0 to void (i32, i8*, i8*)*
|
%2 = inttoptr i32 %0 to void (i32, i8*, i8*)*
|
||||||
call void %2(i32 3, i8* %callback.context, i8* undef)
|
call void %2(i32 3, i8* %callback.context, i8* undef) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,13 +37,17 @@ declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i
|
|||||||
|
|
||||||
declare void @runtime.nilPanic(i8*, i8*)
|
declare void @runtime.nilPanic(i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @main.foo(i8* undef, i32 ptrtoint (%runtime.funcValueWithSignature* @"main.someFunc$withSignature" to i32), i8* undef, i8* undef)
|
call void @main.foo(i8* undef, i32 ptrtoint (%runtime.funcValueWithSignature* @"main.someFunc$withSignature" to i32), i8* undef, i8* undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
Vendored
+72
@@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
var (
|
||||||
|
scalar1 *byte
|
||||||
|
scalar2 *int32
|
||||||
|
scalar3 *int64
|
||||||
|
scalar4 *float32
|
||||||
|
|
||||||
|
array1 *[3]byte
|
||||||
|
array2 *[71]byte
|
||||||
|
array3 *[3]*byte
|
||||||
|
|
||||||
|
struct1 *struct{}
|
||||||
|
struct2 *struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
struct3 *struct {
|
||||||
|
x *byte
|
||||||
|
y [60]uintptr
|
||||||
|
z *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
slice1 []byte
|
||||||
|
slice2 []*int
|
||||||
|
slice3 [][]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func newScalar() {
|
||||||
|
scalar1 = new(byte)
|
||||||
|
scalar2 = new(int32)
|
||||||
|
scalar3 = new(int64)
|
||||||
|
scalar4 = new(float32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArray() {
|
||||||
|
array1 = new([3]byte)
|
||||||
|
array2 = new([71]byte)
|
||||||
|
array3 = new([3]*byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStruct() {
|
||||||
|
struct1 = new(struct{})
|
||||||
|
struct2 = new(struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
})
|
||||||
|
struct3 = new(struct {
|
||||||
|
x *byte
|
||||||
|
y [60]uintptr
|
||||||
|
z *byte
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncValue() *func() {
|
||||||
|
// On some platforms that use runtime.funcValue ("switch" style) function
|
||||||
|
// values, a func value is allocated as having two pointer words while the
|
||||||
|
// struct looks like {unsafe.Pointer; uintptr}. This is so that the interp
|
||||||
|
// package won't get confused, see getPointerBitmap in compiler/llvm.go for
|
||||||
|
// details.
|
||||||
|
return new(func())
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSlice() {
|
||||||
|
slice1 = make([]byte, 5)
|
||||||
|
slice2 = make([]*int, 5)
|
||||||
|
slice3 = make([][]byte, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInterface(v complex128) interface{} {
|
||||||
|
return v // always stored in an allocation
|
||||||
|
}
|
||||||
Vendored
+113
@@ -0,0 +1,113 @@
|
|||||||
|
; ModuleID = 'gc.go'
|
||||||
|
source_filename = "gc.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
%runtime.funcValue = type { i8*, i32 }
|
||||||
|
%runtime._interface = type { i32, i8* }
|
||||||
|
|
||||||
|
@main.scalar1 = hidden global i8* null, align 4
|
||||||
|
@main.scalar2 = hidden global i32* null, align 4
|
||||||
|
@main.scalar3 = hidden global i64* null, align 4
|
||||||
|
@main.scalar4 = hidden global float* null, align 4
|
||||||
|
@main.array1 = hidden global [3 x i8]* null, align 4
|
||||||
|
@main.array2 = hidden global [71 x i8]* null, align 4
|
||||||
|
@main.array3 = hidden global [3 x i8*]* null, align 4
|
||||||
|
@main.struct1 = hidden global {}* null, align 4
|
||||||
|
@main.struct2 = hidden global { i32, i32 }* null, align 4
|
||||||
|
@main.struct3 = hidden global { i8*, [60 x i32], i8* }* null, align 4
|
||||||
|
@main.slice1 = hidden global { i8*, i32, i32 } zeroinitializer, align 8
|
||||||
|
@main.slice2 = hidden global { i32**, i32, i32 } zeroinitializer, align 8
|
||||||
|
@main.slice3 = hidden global { { i8*, i32, i32 }*, i32, i32 } zeroinitializer, align 8
|
||||||
|
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c" \00\00\00\00\00\00\01" }
|
||||||
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 }
|
||||||
|
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:complex128", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.newScalar(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new, i8** @main.scalar1, align 4
|
||||||
|
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
|
||||||
|
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
|
||||||
|
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.newArray(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
|
||||||
|
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
|
||||||
|
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.newStruct(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
|
||||||
|
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
|
||||||
|
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime.funcValue* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef, i8* null) #0
|
||||||
|
%0 = bitcast i8* %new to %runtime.funcValue*
|
||||||
|
ret %runtime.funcValue* %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.makeSlice(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
|
||||||
|
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 1), align 4
|
||||||
|
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8
|
||||||
|
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
|
||||||
|
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 1), align 4
|
||||||
|
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8
|
||||||
|
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef, i8* null) #0
|
||||||
|
store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
|
||||||
|
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 1), align 4
|
||||||
|
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0
|
||||||
|
%.repack = bitcast i8* %0 to double*
|
||||||
|
store double %v.r, double* %.repack, align 8
|
||||||
|
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%1 = bitcast i8* %.repack1 to double*
|
||||||
|
store double %v.i, double* %1, align 8
|
||||||
|
%2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
|
||||||
|
ret %runtime._interface %2
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
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
+147
@@ -0,0 +1,147 @@
|
|||||||
|
; ModuleID = 'go1.17.go'
|
||||||
|
source_filename = "go1.17.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = getelementptr i8, i8* %p, i32 %len
|
||||||
|
ret i8* %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = trunc i64 %len to i32
|
||||||
|
%1 = getelementptr i8, i8* %p, i32 %0
|
||||||
|
ret i8* %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = icmp ult i32 %s.len, 4
|
||||||
|
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
call void @runtime.sliceToArrayPointerPanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slicetoarray.next: ; preds = %entry
|
||||||
|
%1 = bitcast i32* %s.data to [4 x i32]*
|
||||||
|
ret [4 x i32]* %1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.sliceToArrayPointerPanic(i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slicetoarray.next: ; preds = %entry
|
||||||
|
%0 = bitcast i8* %makeslice to [4 x i32]*
|
||||||
|
ret [4 x i32]* %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
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.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
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
|
||||||
|
ret { i32*, i32, i32 } %7
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.unsafeSlicePanic(i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
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.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
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
|
||||||
|
ret { i8*, i32, i32 } %6
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
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.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
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
|
||||||
|
ret { i32*, i32, i32 } %8
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
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.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
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
|
||||||
|
ret { i32*, i32, i32 } %8
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
+97
-38
@@ -1,7 +1,7 @@
|
|||||||
; ModuleID = 'goroutine.go'
|
; ModuleID = 'goroutine.go'
|
||||||
source_filename = "goroutine.go"
|
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 = "armv7m-none-eabi"
|
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||||
|
|
||||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
@@ -9,26 +9,31 @@ target triple = "armv7m-none-eabi"
|
|||||||
%"internal/task.state" = type { i32, i32* }
|
%"internal/task.state" = type { i32, i32* }
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef, i8* undef)
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef, i8* undef) #0
|
||||||
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, i8* null)
|
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, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, i8*, i8*)
|
declare void @main.regularFunction(i32, i8*, i8*)
|
||||||
|
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #0 {
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint i8* %0 to i32
|
%unpack.int = ptrtoint i8* %0 to i32
|
||||||
call void @main.regularFunction(i32 %unpack.int, i8* undef, i8* undef)
|
call void @main.regularFunction(i32 %unpack.int, i8* undef, i8* undef) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,51 +41,57 @@ declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*, i8*)
|
|||||||
|
|
||||||
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef)
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef) #0
|
||||||
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, i8* null)
|
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, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #1 {
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.int = ptrtoint i8* %0 to i32
|
%unpack.int = ptrtoint i8* %0 to i32
|
||||||
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef, i8* undef)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef, i8* undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%n = call i8* @runtime.alloc(i32 4, i8* undef, i8* null)
|
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
%0 = bitcast i8* %n to i32*
|
%0 = bitcast i8* %n to i32*
|
||||||
store i32 3, i32* %0, align 4
|
store i32 3, i32* %0, align 4
|
||||||
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0
|
||||||
%2 = bitcast i8* %1 to i32*
|
%2 = bitcast i8* %1 to i32*
|
||||||
store i32 5, i32* %2, align 4
|
store i32 5, i32* %2, align 4
|
||||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = bitcast i8* %3 to i8**
|
||||||
store i8* %n, i8** %4, align 4
|
store i8* %n, i8** %4, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef)
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef, i8* undef) #0
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef, i8* null)
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef, i8* null) #0
|
||||||
%5 = load i32, i32* %0, align 4
|
%5 = load i32, i32* %0, align 4
|
||||||
call void @runtime.printint32(i32 %5, i8* undef, i8* null)
|
call void @runtime.printint32(i32 %5, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.ptr = bitcast i8* %context to i32*
|
%unpack.ptr = bitcast i8* %context to i32*
|
||||||
store i32 7, i32* %unpack.ptr, align 4
|
store i32 7, i32* %unpack.ptr, align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = bitcast i8* %0 to i32*
|
||||||
%2 = load i32, i32* %1, align 4
|
%2 = load i32, i32* %1, align 4
|
||||||
@@ -93,23 +104,25 @@ entry:
|
|||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*, i8*)
|
declare void @runtime.printint32(i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.funcGoroutine(i8* %fn.context, void (i32, i8*, i8*)* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 12, i8* undef, i8* null)
|
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef, i8* null) #0
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = bitcast i8* %0 to i32*
|
||||||
store i32 5, i32* %1, align 4
|
store i32 5, i32* %1, align 4
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
%3 = bitcast i8* %2 to i8**
|
%3 = bitcast i8* %2 to i8**
|
||||||
store i8* %fn.context, i8** %3, align 4
|
store i8* %fn.context, i8** %3, align 4
|
||||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
%5 = bitcast i8* %4 to void (i32, i8*, i8*)**
|
%5 = bitcast i8* %4 to void ()**
|
||||||
store void (i32, i8*, i8*)* %fn.funcptr, void (i32, i8*, i8*)** %5, align 4
|
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, i8* undef)
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef, i8* undef) #0
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef, i8* null)
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #3 {
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i32*
|
%1 = bitcast i8* %0 to i32*
|
||||||
%2 = load i32, i32* %1, align 4
|
%2 = load i32, i32* %1, align 4
|
||||||
@@ -119,32 +132,78 @@ entry:
|
|||||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
%7 = bitcast i8* %6 to void (i32, i8*, i8*)**
|
%7 = bitcast i8* %6 to void (i32, i8*, i8*)**
|
||||||
%8 = load void (i32, i8*, i8*)*, void (i32, i8*, i8*)** %7, align 4
|
%8 = load void (i32, i8*, i8*)*, void (i32, i8*, i8*)** %7, align 4
|
||||||
call void %8(i32 %2, i8* %5, i8* undef)
|
call void %8(i32 %2, i8* %5, i8* undef) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
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, i8* %parentHandle) unnamed_addr {
|
; 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, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null)
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null)
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||||
|
|
||||||
attributes #0 = { "tinygo-gowrapper"="main.regularFunction" }
|
; Function Attrs: nounwind
|
||||||
attributes #1 = { "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
attributes #2 = { "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
entry:
|
||||||
attributes #3 = { "tinygo-gowrapper" }
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0
|
||||||
|
%1 = bitcast i8* %0 to i8**
|
||||||
|
store i8* %itf.value, i8** %1, align 4
|
||||||
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%.repack = bitcast i8* %2 to i8**
|
||||||
|
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
|
||||||
|
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%3 = bitcast i8* %.repack1 to i32*
|
||||||
|
store i32 4, i32* %3, align 4
|
||||||
|
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
|
%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, i8* undef) #0
|
||||||
|
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, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #5
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
||||||
|
entry:
|
||||||
|
%1 = bitcast i8* %0 to i8**
|
||||||
|
%2 = load i8*, i8** %1, align 4
|
||||||
|
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%4 = bitcast i8* %3 to i8**
|
||||||
|
%5 = load i8*, i8** %4, align 4
|
||||||
|
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%7 = bitcast i8* %6 to i32*
|
||||||
|
%8 = load i32, i32* %7, align 4
|
||||||
|
%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, i8* undef) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
||||||
|
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
|
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
|
attributes #4 = { nounwind "tinygo-gowrapper" }
|
||||||
|
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
|
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
|
|||||||
Vendored
+60
-25
@@ -1,7 +1,7 @@
|
|||||||
; ModuleID = 'goroutine.go'
|
; ModuleID = 'goroutine.go'
|
||||||
source_filename = "goroutine.go"
|
source_filename = "goroutine.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
@@ -10,21 +10,24 @@ target triple = "wasm32--wasi"
|
|||||||
%"internal/task.state" = type { i8* }
|
%"internal/task.state" = type { i8* }
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
@"main.regularFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
@"main$pack" = internal unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||||
@"main.inlineFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
@"main$pack.1" = internal unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||||
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||||
@"main.closureFunctionGoroutine$1$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
@"main.closureFunctionGoroutine$1$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||||
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @main.regularFunction to i32), i8* bitcast ({ i32, i8* }* @"main.regularFunctionGoroutine$pack" to i8*), i32 undef, i8* undef, i8* null)
|
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @main.regularFunction to i32), i8* bitcast ({ i32, i8* }* @"main$pack" to i8*), i32 undef, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,35 +35,39 @@ declare void @main.regularFunction(i32, i8*, i8*)
|
|||||||
|
|
||||||
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.inlineFunctionGoroutine$1" to i32), i8* bitcast ({ i32, i8* }* @"main.inlineFunctionGoroutine$pack" to i8*), i32 undef, i8* undef, i8* null)
|
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.inlineFunctionGoroutine$1" to i32), i8* bitcast ({ i32, i8* }* @"main$pack.1" to i8*), i32 undef, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%n = call i8* @runtime.alloc(i32 4, i8* undef, i8* null)
|
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
%0 = bitcast i8* %n to i32*
|
%0 = bitcast i8* %n to i32*
|
||||||
store i32 3, i32* %0, align 4
|
store i32 3, i32* %0, align 4
|
||||||
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0
|
||||||
%2 = bitcast i8* %1 to i32*
|
%2 = bitcast i8* %1 to i32*
|
||||||
store i32 5, i32* %2, align 4
|
store i32 5, i32* %2, align 4
|
||||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = bitcast i8* %3 to i8**
|
||||||
store i8* %n, i8** %4, align 4
|
store i8* %n, i8** %4, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* nonnull %1, i32 undef, i8* undef, i8* null)
|
call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* nonnull %1, i32 undef, i8* undef, i8* null) #0
|
||||||
%5 = load i32, i32* %0, align 4
|
%5 = load i32, i32* %0, align 4
|
||||||
call void @runtime.printint32(i32 %5, i8* undef, i8* null)
|
call void @runtime.printint32(i32 %5, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%unpack.ptr = bitcast i8* %context to i32*
|
%unpack.ptr = bitcast i8* %context to i32*
|
||||||
store i32 7, i32* %unpack.ptr, align 4
|
store i32 7, i32* %unpack.ptr, align 4
|
||||||
@@ -69,38 +76,66 @@ entry:
|
|||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*, i8*)
|
declare void @runtime.printint32(i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.funcGoroutine(i8* %fn.context, i32 %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.funcGoroutine(i8* %fn.context, i32 %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @runtime.getFuncPtr(i8* %fn.context, i32 %fn.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
|
%0 = call i32 @runtime.getFuncPtr(i8* %fn.context, i32 %fn.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) #0
|
||||||
%1 = call i8* @runtime.alloc(i32 8, i8* undef, i8* null)
|
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0
|
||||||
%2 = bitcast i8* %1 to i32*
|
%2 = bitcast i8* %1 to i32*
|
||||||
store i32 5, i32* %2, align 4
|
store i32 5, i32* %2, align 4
|
||||||
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
%3 = getelementptr inbounds i8, i8* %1, i32 4
|
||||||
%4 = bitcast i8* %3 to i8**
|
%4 = bitcast i8* %3 to i8**
|
||||||
store i8* %fn.context, i8** %4, align 4
|
store i8* %fn.context, i8** %4, align 4
|
||||||
call void @"internal/task.start"(i32 %0, i8* nonnull %1, i32 undef, i8* undef, i8* null)
|
call void @"internal/task.start"(i32 %0, i8* nonnull %1, i32 undef, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
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, i8* %parentHandle) unnamed_addr {
|
; 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, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null)
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null)
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null) #0
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0
|
||||||
|
%1 = bitcast i8* %0 to i8**
|
||||||
|
store i8* %itf.value, i8** %1, align 4
|
||||||
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
|
%.repack = bitcast i8* %2 to i8**
|
||||||
|
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
|
||||||
|
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
|
%3 = bitcast i8* %.repack1 to i32*
|
||||||
|
store i32 4, i32* %3, align 4
|
||||||
|
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
|
%5 = bitcast i8* %4 to i32*
|
||||||
|
store i32 %itf.typecode, i32* %5, align 4
|
||||||
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*, i8*, i32, i32, i8*, i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke" to i32), i8* nonnull %0, i32 undef, i8* undef, i8* null) #0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #1
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
|
|||||||
Vendored
+8
@@ -41,3 +41,11 @@ func closeBuiltinGoroutine(ch chan int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func regularFunction(x int)
|
func regularFunction(x int)
|
||||||
|
|
||||||
|
type simpleInterface interface {
|
||||||
|
Print(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startInterfaceMethod(itf simpleInterface) {
|
||||||
|
go itf.Print("test")
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+9
@@ -49,6 +49,15 @@ func isStringer(itf interface{}) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fooInterface interface {
|
||||||
|
String() string
|
||||||
|
foo(int) byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func callFooMethod(itf fooInterface) uint8 {
|
||||||
|
return itf.foo(3)
|
||||||
|
}
|
||||||
|
|
||||||
func callErrorMethod(itf error) string {
|
func callErrorMethod(itf error) string {
|
||||||
return itf.Error()
|
return itf.Error()
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+53
-30
@@ -1,58 +1,67 @@
|
|||||||
; ModuleID = 'interface.go'
|
; ModuleID = 'interface.go'
|
||||||
source_filename = "interface.go"
|
source_filename = "interface.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
%runtime._interface = type { i32, i8* }
|
%runtime._interface = type { i32, i8* }
|
||||||
%runtime._string = type { i8*, i32 }
|
%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" }
|
@"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:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
@"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:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
@"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: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" }
|
@"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: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}}" }
|
@"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/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
||||||
@"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 }
|
@"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: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 }
|
@"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}}" }
|
@"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/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.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
|
||||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._interface @main.simpleType(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._interface @main.simpleType(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._interface @main.pointerType(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._interface @main.pointerType(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._interface @main.interfaceType(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._interface @main.interfaceType(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr {
|
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null)
|
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null) #0
|
||||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
@@ -64,9 +73,10 @@ typeassert.next: ; preds = %typeassert.ok, %ent
|
|||||||
|
|
||||||
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||||
|
|
||||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* undef, i8* null)
|
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
@@ -76,11 +86,10 @@ typeassert.next: ; preds = %typeassert.ok, %ent
|
|||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.interfaceImplements(i32, i8** dereferenceable_or_null(4), i8*, i8*)
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"reflect/types.interface:interface{String() string}$interface", i32 0, i32 0), i8* undef, i8* null)
|
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
@@ -90,12 +99,26 @@ typeassert.next: ; preds = %typeassert.ok, %ent
|
|||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Error() string", i8* undef, i8* null)
|
%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, i8* undef) #0
|
||||||
%invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)*
|
ret i8 %0
|
||||||
%0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef)
|
}
|
||||||
|
|
||||||
|
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*, i8*) #3
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef, i8* undef) #0
|
||||||
ret %runtime._string %0
|
ret %runtime._string %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.interfaceMethod(i32, i8** dereferenceable_or_null(4), i8* dereferenceable_or_null(1), i8*, i8*)
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*, i8*) #4
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
||||||
|
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
|
||||||
|
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
; 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*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call double @math.Sqrt(double %x, i8* undef, i8* undef) #0
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare double @math.Sqrt(double, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call double @math.Trunc(double %x, i8* undef, i8* undef) #0
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare double @math.Trunc(double, i8*, i8*)
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
; ModuleID = 'intrinsics.go'
|
||||||
|
source_filename = "intrinsics.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call double @llvm.sqrt.f64(double %x)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone speculatable willreturn
|
||||||
|
declare double @llvm.sqrt.f64(double) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call double @llvm.trunc.f64(double %x)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone speculatable willreturn
|
||||||
|
declare double @llvm.trunc.f64(double) #1
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { nounwind readnone speculatable willreturn }
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
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
+20
-10
@@ -1,51 +1,61 @@
|
|||||||
; ModuleID = 'pointer.go'
|
; ModuleID = 'pointer.go'
|
||||||
source_filename = "pointer.go"
|
source_filename = "pointer.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret [0 x i32] zeroinitializer
|
ret [0 x i32] zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i8* %x to i32*
|
%0 = bitcast i8* %x to i32*
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %x to i8*
|
%0 = bitcast i32* %x to i8*
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret i8* %x
|
ret i8* %x
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
Vendored
+21
-15
@@ -1,7 +1,7 @@
|
|||||||
; ModuleID = 'pragma.go'
|
; ModuleID = 'pragma.go'
|
||||||
source_filename = "pragma.go"
|
source_filename = "pragma.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
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
|
||||||
@@ -10,50 +10,56 @@ target triple = "wasm32--wasi"
|
|||||||
@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*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define void @extern_func() #0 {
|
; Function Attrs: nounwind
|
||||||
|
define void @extern_func() #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @somepkg.someFunction1(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @somepkg.someFunction1(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @somepkg.someFunction2(i8*, i8*)
|
declare void @somepkg.someFunction2(i8*, i8*)
|
||||||
|
|
||||||
; Function Attrs: inlinehint
|
; Function Attrs: inlinehint nounwind
|
||||||
define hidden void @main.inlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #1 {
|
define hidden void @main.inlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.noinlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #2 {
|
define hidden void @main.noinlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden void @main.functionInSection(i8* %context, i8* %parentHandle) unnamed_addr section ".special_function_section" {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.functionInSection(i8* %context, i8* %parentHandle) unnamed_addr #0 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define void @exportedFunctionInSection() #3 section ".special_function_section" {
|
; Function Attrs: nounwind
|
||||||
|
define void @exportedFunctionInSection() #4 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.undefinedFunctionNotInSection(i8*, i8*)
|
declare void @main.undefinedFunctionNotInSection(i8*, i8*)
|
||||||
|
|
||||||
attributes #0 = { "wasm-export-name"="extern_func" }
|
attributes #0 = { nounwind }
|
||||||
attributes #1 = { inlinehint }
|
attributes #1 = { nounwind "wasm-export-name"="extern_func" }
|
||||||
attributes #2 = { noinline }
|
attributes #2 = { inlinehint nounwind }
|
||||||
attributes #3 = { "wasm-export-name"="exportedFunctionInSection" }
|
attributes #3 = { noinline nounwind }
|
||||||
|
attributes #4 = { nounwind "wasm-export-name"="exportedFunctionInSection" }
|
||||||
|
|||||||
Vendored
+18
@@ -23,3 +23,21 @@ func sliceAppendSlice(ints, added []int) []int {
|
|||||||
func sliceCopy(dst, src []int) int {
|
func sliceCopy(dst, src []int) int {
|
||||||
return copy(dst, src)
|
return copy(dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test bounds checking in *ssa.MakeSlice instruction.
|
||||||
|
|
||||||
|
func makeByteSlice(len int) []byte {
|
||||||
|
return make([]byte, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInt16Slice(len int) []int16 {
|
||||||
|
return make([]int16, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeArraySlice(len int) [][3]byte {
|
||||||
|
return make([][3]byte, len) // slice with element size of 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInt32Slice(len int) []int32 {
|
||||||
|
return make([]int32, len)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+103
-14
@@ -1,32 +1,36 @@
|
|||||||
; ModuleID = 'slice.go'
|
; ModuleID = 'slice.go'
|
||||||
source_filename = "slice.go"
|
source_filename = "slice.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.len
|
ret i32 %ints.len
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.cap
|
ret i32 %ints.cap
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
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.throw: ; preds = %entry
|
lookup.throw: ; preds = %entry
|
||||||
call void @runtime.lookupPanic(i8* undef, i8* null)
|
call void @runtime.lookupPanic(i8* undef, i8* null) #0
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
lookup.next: ; preds = %entry
|
lookup.next: ; preds = %entry
|
||||||
@@ -37,9 +41,10 @@ lookup.next: ; preds = %entry
|
|||||||
|
|
||||||
declare void @runtime.lookupPanic(i8*, i8*)
|
declare void @runtime.lookupPanic(i8*, i8*)
|
||||||
|
|
||||||
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%varargs = call i8* @runtime.alloc(i32 12, i8* undef, i8* null)
|
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
%0 = bitcast i8* %varargs to i32*
|
%0 = bitcast i8* %varargs to i32*
|
||||||
store i32 1, i32* %0, align 4
|
store i32 1, i32* %0, align 4
|
||||||
%1 = getelementptr inbounds i8, i8* %varargs, i32 4
|
%1 = getelementptr inbounds i8, i8* %varargs, i32 4
|
||||||
@@ -49,7 +54,7 @@ entry:
|
|||||||
%4 = bitcast i8* %3 to i32*
|
%4 = bitcast i8* %3 to i32*
|
||||||
store i32 3, i32* %4, align 4
|
store i32 3, i32* %4, align 4
|
||||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||||
%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, i8* null)
|
%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, i8* null) #0
|
||||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||||
@@ -62,11 +67,12 @@ entry:
|
|||||||
|
|
||||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*, i8*)
|
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
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, i8* %parentHandle) unnamed_addr {
|
; 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, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||||
%append.srcPtr1 = bitcast i32* %added.data to i8*
|
%append.srcPtr1 = bitcast i32* %added.data to i8*
|
||||||
%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, i8* null)
|
%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, i8* null) #0
|
||||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||||
@@ -77,12 +83,95 @@ entry:
|
|||||||
ret { i32*, i32, i32 } %2
|
ret { i32*, i32, i32 } %2
|
||||||
}
|
}
|
||||||
|
|
||||||
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, i8* %parentHandle) unnamed_addr {
|
; 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, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%copy.dstPtr = bitcast i32* %dst.data to i8*
|
%copy.dstPtr = bitcast i32* %dst.data to i8*
|
||||||
%copy.srcPtr = bitcast i32* %src.data to i8*
|
%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, i8* null)
|
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef, i8* null) #0
|
||||||
ret i32 %copy.n
|
ret i32 %copy.n
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
||||||
|
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { i8*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.slicePanic(i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.cap = shl i32 %len, 1
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
||||||
|
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
||||||
|
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { i16*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.cap = mul i32 %len, 3
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
||||||
|
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
||||||
|
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { [3 x i8]*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null) #0
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.cap = shl i32 %len, 2
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
|
||||||
|
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
||||||
|
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
||||||
|
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { i32*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
Vendored
+26
-16
@@ -1,41 +1,46 @@
|
|||||||
; ModuleID = 'string.go'
|
; ModuleID = 'string.go'
|
||||||
source_filename = "string.go"
|
source_filename = "string.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime._string = type { i8*, i32 }
|
%runtime._string = type { i8*, i32 }
|
||||||
|
|
||||||
@"main.someString$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
@"main$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._string @main.someString(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.someString(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main.someString$string", i32 0, i32 0), i32 3 }
|
ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main$string", i32 0, i32 0), i32 3 }
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden %runtime._string @main.zeroLengthString(i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.zeroLengthString(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._string zeroinitializer
|
ret %runtime._string zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %s.len
|
ret i32 %s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %s.len
|
%.not = icmp ult i32 %index, %s.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
lookup.throw: ; preds = %entry
|
||||||
call void @runtime.lookupPanic(i8* undef, i8* null)
|
call void @runtime.lookupPanic(i8* undef, i8* null) #0
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
lookup.next: ; preds = %entry
|
lookup.next: ; preds = %entry
|
||||||
@@ -46,26 +51,31 @@ lookup.next: ; preds = %entry
|
|||||||
|
|
||||||
declare void @runtime.lookupPanic(i8*, i8*)
|
declare void @runtime.lookupPanic(i8*, i8*)
|
||||||
|
|
||||||
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null) #0
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*, i8*)
|
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null) #0
|
||||||
%1 = xor i1 %0, true
|
%1 = xor i1 %0, true
|
||||||
ret i1 %1
|
ret i1 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringLess(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
%0 = call i1 @runtime.stringLess(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null) #0
|
||||||
%1 = xor i1 %0, true
|
%1 = xor i1 %0, true
|
||||||
ret i1 %1
|
ret i1 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
|
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
|
||||||
|
|
||||||
|
attributes #0 = { nounwind }
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
module github.com/tinygo-org/tinygo
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
go 1.13
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d // indirect
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||||
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693
|
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693
|
||||||
github.com/chromedp/chromedp v0.6.4
|
github.com/chromedp/chromedp v0.6.4
|
||||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
go.bug.st/serial v1.1.2
|
go.bug.st/serial v1.1.3
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d h1:JeuI5/546naK5hpOIX+Lq5xE8rvt7uwiTp6iL+pLQgk=
|
||||||
|
github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||||
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y=
|
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y=
|
||||||
@@ -6,8 +8,8 @@ github.com/chromedp/chromedp v0.6.4 h1:Gx7ZkRyrSVmbbDDja/ieNgNGJIvElroPOyeqYQGVD
|
|||||||
github.com/chromedp/chromedp v0.6.4/go.mod h1:vodUdJf5dF/b8n0UBJv6NeM/QK28RjP3j+eM7fq4+84=
|
github.com/chromedp/chromedp v0.6.4/go.mod h1:vodUdJf5dF/b8n0UBJv6NeM/QK28RjP3j+eM7fq4+84=
|
||||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||||
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
|
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||||
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||||
@@ -30,31 +32,40 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
||||||
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
|
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
|
||||||
go.bug.st/serial v1.1.2/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY=
|
go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||||
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||||
|
|||||||
+5
-5
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.20.0-dev"
|
const Version = "0.21.0-dev"
|
||||||
|
|
||||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||||
// If the goroot cannot be determined, (0, 0) is returned.
|
// If the goroot cannot be determined, (0, 0) is returned.
|
||||||
@@ -48,7 +48,10 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
|
|||||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||||
// can have some variations (for beta releases, for example).
|
// can have some variations (for beta releases, for example).
|
||||||
func GorootVersionString(goroot string) (string, error) {
|
func GorootVersionString(goroot string) (string, error) {
|
||||||
if data, err := ioutil.ReadFile(filepath.Join(
|
if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||||
|
return string(data), nil
|
||||||
|
|
||||||
|
} else if data, err := ioutil.ReadFile(filepath.Join(
|
||||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||||
|
|
||||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||||
@@ -59,9 +62,6 @@ func GorootVersionString(goroot string) (string, error) {
|
|||||||
|
|
||||||
return string(matches[1]), nil
|
return string(matches[1]), nil
|
||||||
|
|
||||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
|
||||||
return string(data), nil
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-12
@@ -3,7 +3,6 @@ package interp
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -18,6 +17,7 @@ func TestInterp(t *testing.T) {
|
|||||||
"consteval",
|
"consteval",
|
||||||
"interface",
|
"interface",
|
||||||
"revert",
|
"revert",
|
||||||
|
"alloc",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make tc local to this closure
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
@@ -88,8 +88,6 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var alignRegexp = regexp.MustCompile(", align [0-9]+$")
|
|
||||||
|
|
||||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||||
// equal. That means, only relevant lines are compared (excluding comments
|
// equal. That means, only relevant lines are compared (excluding comments
|
||||||
// etc.).
|
// etc.).
|
||||||
@@ -101,15 +99,6 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
|||||||
}
|
}
|
||||||
for i, line1 := range lines1 {
|
for i, line1 := range lines1 {
|
||||||
line2 := lines2[i]
|
line2 := lines2[i]
|
||||||
match1 := alignRegexp.MatchString(line1)
|
|
||||||
match2 := alignRegexp.MatchString(line2)
|
|
||||||
if match1 != match2 {
|
|
||||||
// Only one of the lines has the align keyword. Remove it.
|
|
||||||
// This is a change to make the test work in both LLVM 10 and LLVM
|
|
||||||
// 11 (LLVM 11 appears to automatically add alignment everywhere).
|
|
||||||
line1 = alignRegexp.ReplaceAllString(line1, "")
|
|
||||||
line2 = alignRegexp.ReplaceAllString(line2, "")
|
|
||||||
}
|
|
||||||
if line1 != line2 {
|
if line1 != line2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-26
@@ -234,11 +234,15 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// Get the requested memory size to be allocated.
|
// Get the requested memory size to be allocated.
|
||||||
size := operands[1].Uint()
|
size := operands[1].Uint()
|
||||||
|
|
||||||
|
// Get the object layout, if it is available.
|
||||||
|
llvmLayoutType := r.getLLVMTypeFromLayout(operands[2])
|
||||||
|
|
||||||
// Create the object.
|
// Create the object.
|
||||||
alloc := object{
|
alloc := object{
|
||||||
globalName: r.pkgName + "$alloc",
|
globalName: r.pkgName + "$alloc",
|
||||||
buffer: newRawValue(uint32(size)),
|
llvmLayoutType: llvmLayoutType,
|
||||||
size: uint32(size),
|
buffer: newRawValue(uint32(size)),
|
||||||
|
size: uint32(size),
|
||||||
}
|
}
|
||||||
index := len(r.objects)
|
index := len(r.objects)
|
||||||
r.objects = append(r.objects, alloc)
|
r.objects = append(r.objects, alloc)
|
||||||
@@ -378,7 +382,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
} else {
|
} else {
|
||||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||||
}
|
}
|
||||||
case callFn.name == "runtime.interfaceImplements":
|
case strings.HasSuffix(callFn.name, ".$typeassert"):
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||||
}
|
}
|
||||||
@@ -393,11 +397,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
||||||
interfaceMethodSetPtr, err := operands[2].asPointer(r)
|
llvmFn := inst.llvmInst.CalledValue()
|
||||||
if err != nil {
|
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||||
return nil, mem, r.errorAt(inst, err)
|
methodSetString := methodSetAttr.GetStringValue()
|
||||||
}
|
|
||||||
interfaceMethodSet := mem.get(interfaceMethodSetPtr.index()).llvmGlobal.Initializer()
|
|
||||||
|
|
||||||
// Make a set of all the methods on the concrete type, for
|
// Make a set of all the methods on the concrete type, for
|
||||||
// easier checking in the next step.
|
// easier checking in the next step.
|
||||||
@@ -412,8 +414,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// of defined methods calculated above. This is the interface
|
// of defined methods calculated above. This is the interface
|
||||||
// assert itself.
|
// assert itself.
|
||||||
assertOk := uint8(1) // i1 true
|
assertOk := uint8(1) // i1 true
|
||||||
for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
|
for _, name := range strings.Split(methodSetString, "; ") {
|
||||||
name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
|
|
||||||
if _, ok := concreteTypeMethods[name]; !ok {
|
if _, ok := concreteTypeMethods[name]; !ok {
|
||||||
// There is a method on the interface that is not
|
// There is a method on the interface that is not
|
||||||
// implemented by the type. The assertion will fail.
|
// implemented by the type. The assertion will fail.
|
||||||
@@ -423,20 +424,20 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
// If assertOk is still 1, the assertion succeeded.
|
// If assertOk is still 1, the assertion succeeded.
|
||||||
locals[inst.localIndex] = literalValue{assertOk}
|
locals[inst.localIndex] = literalValue{assertOk}
|
||||||
case callFn.name == "runtime.interfaceMethod":
|
case strings.HasSuffix(callFn.name, "$invoke"):
|
||||||
// This builtin returns the function (which may be a thunk) to
|
// This thunk is the interface method dispatcher: it is called
|
||||||
// invoke a method on an interface. It does not call the method.
|
// with all regular parameters and a type code. It will then
|
||||||
|
// call the concrete method for it.
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"interface method:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"invoke method:", operands[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the first param, which is the type code (ptrtoint of the
|
// Load the type code of the interface value.
|
||||||
// type code global).
|
typecodeIDBitCast, err := operands[len(operands)-3].toLLVMValue(inst.llvmInst.Operand(len(operands)-4).Type(), &mem)
|
||||||
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
typecodeID := typecodeIDPtrToInt.Operand(0).Initializer()
|
typecodeID := typecodeIDBitCast.Operand(0).Initializer()
|
||||||
|
|
||||||
// Load the method set, which is part of the typecodeID object.
|
// Load the method set, which is part of the typecodeID object.
|
||||||
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
||||||
@@ -444,7 +445,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// We don't need to load the interface method set.
|
// We don't need to load the interface method set.
|
||||||
|
|
||||||
// Load the signature of the to-be-called function.
|
// Load the signature of the to-be-called function.
|
||||||
signature := inst.llvmInst.Operand(2)
|
llvmFn := inst.llvmInst.CalledValue()
|
||||||
|
invokeAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||||
|
invokeName := invokeAttr.GetStringValue()
|
||||||
|
signature := r.mod.NamedGlobal(invokeName)
|
||||||
|
|
||||||
// Iterate through all methods, looking for the one method that
|
// Iterate through all methods, looking for the one method that
|
||||||
// should be returned.
|
// should be returned.
|
||||||
@@ -457,9 +461,13 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if method.IsNil() {
|
if method.IsNil() {
|
||||||
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
|
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+invokeName))
|
||||||
}
|
}
|
||||||
locals[inst.localIndex] = r.getValue(method)
|
|
||||||
|
// Change the to-be-called function to the underlying method to
|
||||||
|
// be called and fall through to the default case.
|
||||||
|
callFn = r.getFunction(method)
|
||||||
|
fallthrough
|
||||||
default:
|
default:
|
||||||
if len(callFn.blocks) == 0 {
|
if len(callFn.blocks) == 0 {
|
||||||
// Call to a function declaration without a definition
|
// Call to a function declaration without a definition
|
||||||
@@ -590,18 +598,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// GetElementPtr does pointer arithmetic, changing the offset of the
|
// GetElementPtr does pointer arithmetic, changing the offset of the
|
||||||
// pointer into the underlying object.
|
// pointer into the underlying object.
|
||||||
var offset uint64
|
var offset uint64
|
||||||
var gepOperands []uint64
|
|
||||||
for i := 2; i < len(operands); i += 2 {
|
for i := 2; i < len(operands); i += 2 {
|
||||||
index := operands[i].Uint()
|
index := operands[i].Uint()
|
||||||
elementSize := operands[i+1].Uint()
|
elementSize := operands[i+1].Uint()
|
||||||
if int64(elementSize) < 0 {
|
if int64(elementSize) < 0 {
|
||||||
// This is a struct field.
|
// This is a struct field.
|
||||||
// The field number is encoded by flipping all the bits.
|
|
||||||
gepOperands = append(gepOperands, ^elementSize)
|
|
||||||
offset += index
|
offset += index
|
||||||
} else {
|
} else {
|
||||||
// This is a normal GEP, probably an array index.
|
// This is a normal GEP, probably an array index.
|
||||||
gepOperands = append(gepOperands, index)
|
|
||||||
offset += elementSize * index
|
offset += elementSize * index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+165
-89
@@ -18,6 +18,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -27,17 +28,20 @@ import (
|
|||||||
// An object is a memory buffer that may be an already existing global or a
|
// An object is a memory buffer that may be an already existing global or a
|
||||||
// global created with runtime.alloc or the alloca instruction. If llvmGlobal is
|
// global created with runtime.alloc or the alloca instruction. If llvmGlobal is
|
||||||
// set, that's the global for this object, otherwise it needs to be created (if
|
// set, that's the global for this object, otherwise it needs to be created (if
|
||||||
// it is still reachable when the package initializer returns).
|
// it is still reachable when the package initializer returns). The
|
||||||
|
// llvmLayoutType is not necessarily a complete type: it may need to be
|
||||||
|
// repeated (for example, for a slice value).
|
||||||
//
|
//
|
||||||
// Objects are copied in a memory view when they are stored to, to provide the
|
// Objects are copied in a memory view when they are stored to, to provide the
|
||||||
// ability to roll back interpreting a function.
|
// ability to roll back interpreting a function.
|
||||||
type object struct {
|
type object struct {
|
||||||
llvmGlobal llvm.Value
|
llvmGlobal llvm.Value
|
||||||
llvmType llvm.Type // must match llvmGlobal.Type() if both are set, may be unset if llvmGlobal is set
|
llvmType llvm.Type // must match llvmGlobal.Type() if both are set, may be unset if llvmGlobal is set
|
||||||
globalName string // name, if not yet created (not guaranteed to be the final name)
|
llvmLayoutType llvm.Type // LLVM type based on runtime.alloc layout parameter, if available
|
||||||
buffer value // buffer with value as given by interp, nil if external
|
globalName string // name, if not yet created (not guaranteed to be the final name)
|
||||||
size uint32 // must match buffer.len(), if available
|
buffer value // buffer with value as given by interp, nil if external
|
||||||
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
|
size uint32 // must match buffer.len(), if available
|
||||||
|
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone() returns a cloned version of this object, for when an object needs to
|
// clone() returns a cloned version of this object, for when an object needs to
|
||||||
@@ -522,6 +526,18 @@ func (v pointerValue) llvmValue(mem *memoryView) llvm.Value {
|
|||||||
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
|
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
|
||||||
// be different than expected.
|
// be different than expected.
|
||||||
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||||
|
// If a particular LLVM type is requested, cast to it.
|
||||||
|
if !llvmType.IsNil() && llvmType.TypeKind() != llvm.PointerTypeKind {
|
||||||
|
// The LLVM value has (or should have) the same bytes once compiled, but
|
||||||
|
// does not have the right LLVM type. This can happen for example when
|
||||||
|
// storing to a struct with a single pointer field: this pointer may
|
||||||
|
// then become the value even though the pointer should be wrapped in a
|
||||||
|
// struct.
|
||||||
|
// This can be worked around by simply converting to a raw value,
|
||||||
|
// rawValue knows how to create such structs.
|
||||||
|
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
||||||
|
}
|
||||||
|
|
||||||
// Obtain the llvmValue, creating it if it doesn't exist yet.
|
// Obtain the llvmValue, creating it if it doesn't exist yet.
|
||||||
llvmValue := v.llvmValue(mem)
|
llvmValue := v.llvmValue(mem)
|
||||||
if llvmValue.IsNil() {
|
if llvmValue.IsNil() {
|
||||||
@@ -529,7 +545,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
// runtime.alloc.
|
// runtime.alloc.
|
||||||
// First allocate a new global for this object.
|
// First allocate a new global for this object.
|
||||||
obj := mem.get(v.index())
|
obj := mem.get(v.index())
|
||||||
if obj.llvmType.IsNil() {
|
if obj.llvmType.IsNil() && obj.llvmLayoutType.IsNil() {
|
||||||
// Create an initializer without knowing the global type.
|
// Create an initializer without knowing the global type.
|
||||||
// This is probably the result of a runtime.alloc call.
|
// This is probably the result of a runtime.alloc call.
|
||||||
initializer, err := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
|
initializer, err := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
|
||||||
@@ -543,7 +559,23 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
obj.llvmGlobal = llvmValue
|
obj.llvmGlobal = llvmValue
|
||||||
mem.put(v.index(), obj)
|
mem.put(v.index(), obj)
|
||||||
} else {
|
} else {
|
||||||
globalType := obj.llvmType.ElementType()
|
// The global type is known, or at least its structure.
|
||||||
|
var globalType llvm.Type
|
||||||
|
if !obj.llvmType.IsNil() {
|
||||||
|
// The exact type is known.
|
||||||
|
globalType = obj.llvmType.ElementType()
|
||||||
|
} else { // !obj.llvmLayoutType.IsNil()
|
||||||
|
// The exact type isn't known, but the object layout is known.
|
||||||
|
globalType = obj.llvmLayoutType
|
||||||
|
// The layout may not span the full size of the global because
|
||||||
|
// of repetition. One example would be make([]string, 5) which
|
||||||
|
// would be 10 words in size but the layout would only be two
|
||||||
|
// words (for the string type).
|
||||||
|
typeSize := mem.r.targetData.TypeAllocSize(globalType)
|
||||||
|
if typeSize != uint64(obj.size) {
|
||||||
|
globalType = llvm.ArrayType(globalType, int(uint64(obj.size)/typeSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
if checks && mem.r.targetData.TypeAllocSize(globalType) != uint64(obj.size) {
|
if checks && mem.r.targetData.TypeAllocSize(globalType) != uint64(obj.size) {
|
||||||
panic("size of the globalType isn't the same as the object size")
|
panic("size of the globalType isn't the same as the object size")
|
||||||
}
|
}
|
||||||
@@ -562,6 +594,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
|
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
|
||||||
}
|
}
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
|
if obj.llvmType.IsNil() {
|
||||||
|
// The exact type isn't known (only the layout), so use the
|
||||||
|
// alignment that would normally be expected from runtime.alloc.
|
||||||
|
llvmValue.SetAlignment(mem.r.maxAlign)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// It should be included in r.globals because otherwise markExternal
|
// It should be included in r.globals because otherwise markExternal
|
||||||
@@ -571,87 +608,24 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
llvmValue.SetLinkage(llvm.InternalLinkage)
|
llvmValue.SetLinkage(llvm.InternalLinkage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.IsNil() {
|
if v.offset() != 0 {
|
||||||
if v.offset() != 0 {
|
// If there is an offset, make sure to use a GEP to index into the
|
||||||
// If there is an offset, make sure to use a GEP to index into the
|
// pointer.
|
||||||
// pointer. Because there is no expected type, we use whatever is
|
// Cast to an i8* first (if needed) for easy indexing.
|
||||||
// most convenient: an *i8 type. It is trivial to index byte-wise.
|
if llvmValue.Type() != mem.r.i8ptrType {
|
||||||
if llvmValue.Type() != mem.r.i8ptrType {
|
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
||||||
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
|
||||||
}
|
|
||||||
llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
|
|
||||||
llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return llvmValue, nil
|
llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.TypeKind() != llvm.PointerTypeKind {
|
// If a particular LLVM pointer type is requested, cast to it.
|
||||||
// The LLVM value has (or should have) the same bytes once compiled, but
|
if !llvmType.IsNil() && llvmType != llvmValue.Type() {
|
||||||
// does not have the right LLVM type. This can happen for example when
|
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
||||||
// storing to a struct with a single pointer field: this pointer may
|
|
||||||
// then become the value even though the pointer should be wrapped in a
|
|
||||||
// struct.
|
|
||||||
// This can be worked around by simply converting to a raw value,
|
|
||||||
// rawValue knows how to create such structs.
|
|
||||||
if v.offset() != 0 {
|
|
||||||
return llvm.Value{}, errors.New("interp: offset set without known pointer type")
|
|
||||||
}
|
|
||||||
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedType := llvmType
|
return llvmValue, nil
|
||||||
objectElementType := llvmValue.Type()
|
|
||||||
if requestedType == objectElementType {
|
|
||||||
if v.offset() != 0 {
|
|
||||||
// This should never happen, if offset is non-zero, the types
|
|
||||||
// shouldn't match.
|
|
||||||
return llvm.Value{}, errors.New("interp: offset set while there is no way to convert the type")
|
|
||||||
}
|
|
||||||
return llvmValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.offset() == 0 {
|
|
||||||
// Offset is zero, so we can just bitcast to get a correct pointer.
|
|
||||||
return llvm.ConstBitCast(llvmValue, llvmType), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to make a constant GEP for pointer arithmetic.
|
|
||||||
int32Type := llvmType.Context().Int32Type()
|
|
||||||
indices := []llvm.Value{llvm.ConstInt(int32Type, 0, false)}
|
|
||||||
requestedType = requestedType.ElementType()
|
|
||||||
objectElementType = objectElementType.ElementType()
|
|
||||||
offset := int64(v.offset())
|
|
||||||
for offset > 0 {
|
|
||||||
switch objectElementType.TypeKind() {
|
|
||||||
case llvm.ArrayTypeKind:
|
|
||||||
elementType := objectElementType.ElementType()
|
|
||||||
elementSize := mem.r.targetData.TypeAllocSize(elementType)
|
|
||||||
elementIndex := uint64(offset) / elementSize
|
|
||||||
indices = append(indices, llvm.ConstInt(int32Type, elementIndex, false))
|
|
||||||
offset -= int64(elementIndex * elementSize)
|
|
||||||
objectElementType = elementType
|
|
||||||
case llvm.StructTypeKind:
|
|
||||||
element := mem.r.targetData.ElementContainingOffset(objectElementType, uint64(offset))
|
|
||||||
indices = append(indices, llvm.ConstInt(int32Type, uint64(element), false))
|
|
||||||
offset -= int64(mem.r.targetData.ElementOffset(objectElementType, element))
|
|
||||||
objectElementType = objectElementType.StructElementTypes()[element]
|
|
||||||
default:
|
|
||||||
return llvm.Value{}, errors.New("interp: pointer index with something other than a struct or array?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if offset < 0 {
|
|
||||||
return llvm.Value{}, errors.New("interp: offset has somehow gone negative, this should be impossible")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally do the gep, using the above computed indices.
|
|
||||||
// If it still doesn't match te requested type, it's possible to bitcast (as
|
|
||||||
// the bits of the pointer are now correct, just not the type).
|
|
||||||
gep := llvm.ConstInBoundsGEP(llvmValue, indices)
|
|
||||||
if gep.Type() != llvmType {
|
|
||||||
return llvm.ConstBitCast(gep, llvmType), nil
|
|
||||||
}
|
|
||||||
return gep, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawValue is a raw memory buffer that can store either pointers or regular
|
// rawValue is a raw memory buffer that can store either pointers or regular
|
||||||
@@ -985,12 +959,9 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
|||||||
case llvm.GetElementPtr:
|
case llvm.GetElementPtr:
|
||||||
ptr := llvmValue.Operand(0)
|
ptr := llvmValue.Operand(0)
|
||||||
index := llvmValue.Operand(1)
|
index := llvmValue.Operand(1)
|
||||||
if checks && index.IsAConstantInt().IsNil() || index.ZExtValue() != 0 {
|
|
||||||
panic("expected first index of const gep to be i32 0")
|
|
||||||
}
|
|
||||||
numOperands := llvmValue.OperandsCount()
|
numOperands := llvmValue.OperandsCount()
|
||||||
elementType := ptr.Type().ElementType()
|
elementType := ptr.Type().ElementType()
|
||||||
totalOffset := uint64(0)
|
totalOffset := r.targetData.TypeAllocSize(elementType) * index.ZExtValue()
|
||||||
for i := 2; i < numOperands; i++ {
|
for i := 2; i < numOperands; i++ {
|
||||||
indexValue := llvmValue.Operand(i)
|
indexValue := llvmValue.Operand(i)
|
||||||
if checks && indexValue.IsAConstantInt().IsNil() {
|
if checks && indexValue.IsAConstantInt().IsNil() {
|
||||||
@@ -1209,3 +1180,108 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
|
|||||||
panic("unknown value")
|
panic("unknown value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readObjectLayout reads the object layout as it is stored by the compiler. It
|
||||||
|
// returns the size in the number of words and the bitmap.
|
||||||
|
func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
|
||||||
|
pointerSize := layoutValue.len(r)
|
||||||
|
if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.i8ptrType) {
|
||||||
|
panic("inconsistent pointer size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object layout can be stored in a global variable, directly as an
|
||||||
|
// integer value, or can be nil.
|
||||||
|
ptr, err := layoutValue.asPointer(r)
|
||||||
|
if err == errIntegerAsPointer {
|
||||||
|
// It's an integer, which means it's a small object or unknown.
|
||||||
|
layout := layoutValue.Uint()
|
||||||
|
if layout == 0 {
|
||||||
|
// Nil pointer, which means the layout is unknown.
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if layout%2 != 1 {
|
||||||
|
// Sanity check: the least significant bit must be set. This is how
|
||||||
|
// the runtime can separate pointers from integers.
|
||||||
|
panic("unexpected layout")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine format of bitfields in the integer.
|
||||||
|
pointerBits := uint64(pointerSize * 8)
|
||||||
|
var sizeFieldBits uint64
|
||||||
|
switch pointerBits {
|
||||||
|
case 16:
|
||||||
|
sizeFieldBits = 4
|
||||||
|
case 32:
|
||||||
|
sizeFieldBits = 5
|
||||||
|
case 64:
|
||||||
|
sizeFieldBits = 6
|
||||||
|
default:
|
||||||
|
panic("unknown pointer size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract fields.
|
||||||
|
objectSizeWords := (layout >> 1) & (1<<sizeFieldBits - 1)
|
||||||
|
bitmap := new(big.Int).SetUint64(layout >> (1 + sizeFieldBits))
|
||||||
|
return objectSizeWords, bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the object size in words and the bitmap from the global.
|
||||||
|
buf := r.objects[ptr.index()].buffer.(rawValue)
|
||||||
|
objectSizeWords := rawValue{buf: buf.buf[:r.pointerSize]}.Uint()
|
||||||
|
rawByteValues := buf.buf[r.pointerSize:]
|
||||||
|
rawBytes := make([]byte, len(rawByteValues))
|
||||||
|
for i, v := range rawByteValues {
|
||||||
|
if uint64(byte(v)) != v {
|
||||||
|
panic("found pointer in data array?") // sanity check
|
||||||
|
}
|
||||||
|
rawBytes[i] = byte(v)
|
||||||
|
}
|
||||||
|
bitmap := new(big.Int).SetBytes(rawBytes)
|
||||||
|
return objectSizeWords, bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLLVMTypeFromLayout returns the 'layout type', which is an approximation of
|
||||||
|
// the real type. Pointers are in the correct location but the actual object may
|
||||||
|
// have some additional repetition, for example in the buffer of a slice.
|
||||||
|
func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type {
|
||||||
|
objectSizeWords, bitmap := r.readObjectLayout(layoutValue)
|
||||||
|
if bitmap == nil {
|
||||||
|
// No information available.
|
||||||
|
return llvm.Type{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bitmap.BitLen() == 0 {
|
||||||
|
// There are no pointers in this object, so treat this as a raw byte
|
||||||
|
// buffer. This is important because objects without pointers may have
|
||||||
|
// lower alignment.
|
||||||
|
return r.mod.Context().Int8Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the LLVM type.
|
||||||
|
pointerSize := layoutValue.len(r)
|
||||||
|
pointerAlignment := r.targetData.PrefTypeAlignment(r.i8ptrType)
|
||||||
|
var fields []llvm.Type
|
||||||
|
for i := 0; i < int(objectSizeWords); {
|
||||||
|
if bitmap.Bit(i) != 0 {
|
||||||
|
// Pointer field.
|
||||||
|
fields = append(fields, r.i8ptrType)
|
||||||
|
i += int(pointerSize / uint32(pointerAlignment))
|
||||||
|
} else {
|
||||||
|
// Byte/word field.
|
||||||
|
fields = append(fields, r.mod.Context().IntType(pointerAlignment*8))
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var llvmLayoutType llvm.Type
|
||||||
|
if len(fields) == 1 {
|
||||||
|
llvmLayoutType = fields[0]
|
||||||
|
} else {
|
||||||
|
llvmLayoutType = r.mod.Context().StructType(fields, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
objectSizeBytes := objectSizeWords * uint64(pointerAlignment)
|
||||||
|
if checks && r.targetData.TypeAllocSize(llvmLayoutType) != objectSizeBytes {
|
||||||
|
panic("unexpected size") // sanity check
|
||||||
|
}
|
||||||
|
return llvmLayoutType
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+53
@@ -0,0 +1,53 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
@"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" }
|
||||||
|
@pointerFree12 = global i8* null
|
||||||
|
@pointerFree7 = global i8* null
|
||||||
|
@pointerFree3 = global i8* null
|
||||||
|
@pointerFree0 = global i8* null
|
||||||
|
@layout1 = global i8* null
|
||||||
|
@layout2 = global i8* null
|
||||||
|
@layout3 = global i8* null
|
||||||
|
@layout4 = global i8* null
|
||||||
|
@bigobj1 = global i8* null
|
||||||
|
|
||||||
|
declare i8* @runtime.alloc(i32, i8*) unnamed_addr
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
call void @main.init()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @main.init() unnamed_addr {
|
||||||
|
; Object that's word-aligned.
|
||||||
|
%pointerFree12 = call i8* @runtime.alloc(i32 12, i8* inttoptr (i32 3 to i8*))
|
||||||
|
store i8* %pointerFree12, i8** @pointerFree12
|
||||||
|
; Object larger than a word but not word-aligned.
|
||||||
|
%pointerFree7 = call i8* @runtime.alloc(i32 7, i8* inttoptr (i32 3 to i8*))
|
||||||
|
store i8* %pointerFree7, i8** @pointerFree7
|
||||||
|
; Object smaller than a word (and of course not word-aligned).
|
||||||
|
%pointerFree3 = call i8* @runtime.alloc(i32 3, i8* inttoptr (i32 3 to i8*))
|
||||||
|
store i8* %pointerFree3, i8** @pointerFree3
|
||||||
|
; Zero-sized object.
|
||||||
|
%pointerFree0 = call i8* @runtime.alloc(i32 0, i8* inttoptr (i32 3 to i8*))
|
||||||
|
store i8* %pointerFree0, i8** @pointerFree0
|
||||||
|
|
||||||
|
; Object made out of 3 pointers.
|
||||||
|
%layout1 = call i8* @runtime.alloc(i32 12, i8* inttoptr (i32 67 to i8*))
|
||||||
|
store i8* %layout1, i8** @layout1
|
||||||
|
; Array (or slice) of 5 slices.
|
||||||
|
%layout2 = call i8* @runtime.alloc(i32 60, i8* inttoptr (i32 71 to i8*))
|
||||||
|
store i8* %layout2, i8** @layout2
|
||||||
|
; Oddly shaped object, using all bits in the layout integer.
|
||||||
|
%layout3 = call i8* @runtime.alloc(i32 104, i8* inttoptr (i32 2467830261 to i8*))
|
||||||
|
store i8* %layout3, i8** @layout3
|
||||||
|
; ...repeated.
|
||||||
|
%layout4 = call i8* @runtime.alloc(i32 312, i8* inttoptr (i32 2467830261 to i8*))
|
||||||
|
store i8* %layout4, i8** @layout4
|
||||||
|
|
||||||
|
; Large object that needs to be stored in a separate global.
|
||||||
|
%bigobj1 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*))
|
||||||
|
store i8* %bigobj1, i8** @bigobj1
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Vendored
+25
@@ -0,0 +1,25 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
@pointerFree12 = local_unnamed_addr global i8* getelementptr inbounds ([12 x i8], [12 x i8]* @"main$alloc", i32 0, i32 0)
|
||||||
|
@pointerFree7 = local_unnamed_addr global i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"main$alloc.1", i32 0, i32 0)
|
||||||
|
@pointerFree3 = local_unnamed_addr global i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main$alloc.2", i32 0, i32 0)
|
||||||
|
@pointerFree0 = local_unnamed_addr global i8* getelementptr inbounds ([0 x i8], [0 x i8]* @"main$alloc.3", i32 0, i32 0)
|
||||||
|
@layout1 = local_unnamed_addr global i8* bitcast ([3 x i8*]* @"main$alloc.4" to i8*)
|
||||||
|
@layout2 = local_unnamed_addr global i8* bitcast ([5 x { i8*, i32, i32 }]* @"main$alloc.5" to i8*)
|
||||||
|
@layout3 = local_unnamed_addr global i8* bitcast ({ i8*, i8*, i8*, i32, i32, i8*, i8*, i32, i32, i32, i32, i32, i32, i8*, i8*, i32, i32, i32, i8*, i8*, i32, i32, i8*, i32, i32, i8* }* @"main$alloc.6" to i8*)
|
||||||
|
@layout4 = local_unnamed_addr global i8* bitcast ([3 x { i8*, i8*, i8*, i32, i32, i8*, i8*, i32, i32, i32, i32, i32, i32, i8*, i8*, i32, i32, i32, i8*, i8*, i32, i32, i8*, i32, i32, i8* }]* @"main$alloc.7" to i8*)
|
||||||
|
@bigobj1 = local_unnamed_addr global i8* bitcast ({ i8*, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i8* }* @"main$alloc.8" to i8*)
|
||||||
|
@"main$alloc" = internal global [12 x i8] zeroinitializer, align 4
|
||||||
|
@"main$alloc.1" = internal global [7 x i8] zeroinitializer, align 4
|
||||||
|
@"main$alloc.2" = internal global [3 x i8] zeroinitializer, align 4
|
||||||
|
@"main$alloc.3" = internal global [0 x i8] zeroinitializer, align 4
|
||||||
|
@"main$alloc.4" = internal global [3 x i8*] zeroinitializer, align 4
|
||||||
|
@"main$alloc.5" = internal global [5 x { i8*, i32, i32 }] zeroinitializer, align 4
|
||||||
|
@"main$alloc.6" = internal global { i8*, i8*, i8*, i32, i32, i8*, i8*, i32, i32, i32, i32, i32, i32, i8*, i8*, i32, i32, i32, i8*, i8*, i32, i32, i8*, i32, i32, i8* } zeroinitializer, align 4
|
||||||
|
@"main$alloc.7" = internal global [3 x { i8*, i8*, i8*, i32, i32, i8*, i8*, i32, i32, i32, i32, i32, i32, i8*, i8*, i32, i32, i32, i8*, i8*, i32, i32, i8*, i32, i32, i8* }] zeroinitializer, align 4
|
||||||
|
@"main$alloc.8" = internal global { i8*, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i8* } zeroinitializer, align 4
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Vendored
+8
-8
@@ -18,14 +18,14 @@ entry:
|
|||||||
call void @runtime.printint64(i64 5)
|
call void @runtime.printint64(i64 5)
|
||||||
call void @runtime.printnl()
|
call void @runtime.printnl()
|
||||||
%value1 = call i64 @someValue()
|
%value1 = call i64 @someValue()
|
||||||
store i64 %value1, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
|
store i64 %value1, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0), align 8
|
||||||
%value2 = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
|
%value2 = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0), align 8
|
||||||
store i64 %value2, i64* @main.nonConst2
|
store i64 %value2, i64* @main.nonConst2, align 8
|
||||||
call void @modifyExternal(i32* getelementptr inbounds ([8 x { i16, i32 }], [8 x { i16, i32 }]* @main.someArray, i32 0, i32 3, i32 1))
|
call void @modifyExternal(i32* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ([8 x { i16, i32 }]* @main.someArray to i8*), i32 28) to i32*))
|
||||||
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
||||||
store i16 5, i16* @main.exposedValue1
|
store i16 5, i16* @main.exposedValue1, align 2
|
||||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||||
store i16 7, i16* @main.exposedValue2
|
store i16 7, i16* @main.exposedValue2, align 2
|
||||||
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
|
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
|
||||||
call void @runtime.printint64(i64 6)
|
call void @runtime.printint64(i64 6)
|
||||||
call void @runtime.printint64(i64 -1)
|
call void @runtime.printint64(i64 -1)
|
||||||
@@ -39,7 +39,7 @@ entry:
|
|||||||
%agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0
|
%agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0
|
||||||
%agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1
|
%agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1
|
||||||
%agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2
|
%agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2
|
||||||
store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue
|
store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue, align 8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ declare void @modifyExternal(i32*) local_unnamed_addr
|
|||||||
|
|
||||||
define void @willModifyGlobal() {
|
define void @willModifyGlobal() {
|
||||||
entry:
|
entry:
|
||||||
store i16 8, i16* @main.exposedValue2
|
store i16 8, i16* @main.exposedValue2, align 2
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+8
@@ -3,6 +3,8 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
@intToPtrResult = global i8 0
|
@intToPtrResult = global i8 0
|
||||||
@ptrToIntResult = global i8 0
|
@ptrToIntResult = global i8 0
|
||||||
|
@someArray = internal global {i16, i8, i8} zeroinitializer
|
||||||
|
@someArrayPointer = global i8* zeroinitializer
|
||||||
|
|
||||||
define void @runtime.initAll() {
|
define void @runtime.initAll() {
|
||||||
call void @main.init()
|
call void @main.init()
|
||||||
@@ -12,6 +14,7 @@ define void @runtime.initAll() {
|
|||||||
define internal void @main.init() {
|
define internal void @main.init() {
|
||||||
call void @testIntToPtr()
|
call void @testIntToPtr()
|
||||||
call void @testPtrToInt()
|
call void @testPtrToInt()
|
||||||
|
call void @testConstGEP()
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,3 +43,8 @@ b:
|
|||||||
store i8 2, i8* @ptrToIntResult
|
store i8 2, i8* @ptrToIntResult
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define internal void @testConstGEP() {
|
||||||
|
store i8* getelementptr inbounds (i8, i8* bitcast ({i16, i8, i8}* @someArray to i8*), i32 2), i8** @someArrayPointer
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -3,6 +3,8 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
@intToPtrResult = local_unnamed_addr global i8 2
|
@intToPtrResult = local_unnamed_addr global i8 2
|
||||||
@ptrToIntResult = local_unnamed_addr global i8 2
|
@ptrToIntResult = local_unnamed_addr global i8 2
|
||||||
|
@someArray = internal global { i16, i8, i8 } zeroinitializer
|
||||||
|
@someArrayPointer = local_unnamed_addr global i8* getelementptr inbounds ({ i16, i8, i8 }, { i16, i8, i8 }* @someArray, i64 0, i32 1)
|
||||||
|
|
||||||
define void @runtime.initAll() local_unnamed_addr {
|
define void @runtime.initAll() local_unnamed_addr {
|
||||||
ret void
|
ret void
|
||||||
|
|||||||
Vendored
+3
-3
@@ -10,7 +10,7 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
declare i64 @runtime.sliceCopy(i8* %dst, i8* %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
declare i64 @runtime.sliceCopy(i8* %dst, i8* %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
||||||
|
|
||||||
declare i8* @runtime.alloc(i64) unnamed_addr
|
declare i8* @runtime.alloc(i64, i8*) unnamed_addr
|
||||||
|
|
||||||
declare void @runtime.printuint8(i8)
|
declare void @runtime.printuint8(i8)
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ entry:
|
|||||||
; uint8SliceDst = make([]uint8, len(uint8SliceSrc))
|
; uint8SliceDst = make([]uint8, len(uint8SliceSrc))
|
||||||
%uint8SliceSrc = load { i8*, i64, i64 }, { i8*, i64, i64 }* @main.uint8SliceSrc
|
%uint8SliceSrc = load { i8*, i64, i64 }, { i8*, i64, i64 }* @main.uint8SliceSrc
|
||||||
%uint8SliceSrc.len = extractvalue { i8*, i64, i64 } %uint8SliceSrc, 1
|
%uint8SliceSrc.len = extractvalue { i8*, i64, i64 } %uint8SliceSrc, 1
|
||||||
%uint8SliceDst.buf = call i8* @runtime.alloc(i64 %uint8SliceSrc.len)
|
%uint8SliceDst.buf = call i8* @runtime.alloc(i64 %uint8SliceSrc.len, i8* null)
|
||||||
%0 = insertvalue { i8*, i64, i64 } undef, i8* %uint8SliceDst.buf, 0
|
%0 = insertvalue { i8*, i64, i64 } undef, i8* %uint8SliceDst.buf, 0
|
||||||
%1 = insertvalue { i8*, i64, i64 } %0, i64 %uint8SliceSrc.len, 1
|
%1 = insertvalue { i8*, i64, i64 } %0, i64 %uint8SliceSrc.len, 1
|
||||||
%2 = insertvalue { i8*, i64, i64 } %1, i64 %uint8SliceSrc.len, 2
|
%2 = insertvalue { i8*, i64, i64 } %1, i64 %uint8SliceSrc.len, 2
|
||||||
@@ -68,7 +68,7 @@ entry:
|
|||||||
%int16SliceSrc = load { i16*, i64, i64 }, { i16*, i64, i64 }* @main.int16SliceSrc
|
%int16SliceSrc = load { i16*, i64, i64 }, { i16*, i64, i64 }* @main.int16SliceSrc
|
||||||
%int16SliceSrc.len = extractvalue { i16*, i64, i64 } %int16SliceSrc, 1
|
%int16SliceSrc.len = extractvalue { i16*, i64, i64 } %int16SliceSrc, 1
|
||||||
%int16SliceSrc.len.bytes = mul i64 %int16SliceSrc.len, 2
|
%int16SliceSrc.len.bytes = mul i64 %int16SliceSrc.len, 2
|
||||||
%int16SliceDst.buf.raw = call i8* @runtime.alloc(i64 %int16SliceSrc.len.bytes)
|
%int16SliceDst.buf.raw = call i8* @runtime.alloc(i64 %int16SliceSrc.len.bytes, i8* null)
|
||||||
%int16SliceDst.buf = bitcast i8* %int16SliceDst.buf.raw to i16*
|
%int16SliceDst.buf = bitcast i8* %int16SliceDst.buf.raw to i16*
|
||||||
%3 = insertvalue { i16*, i64, i64 } undef, i16* %int16SliceDst.buf, 0
|
%3 = insertvalue { i16*, i64, i64 } undef, i16* %int16SliceDst.buf, 0
|
||||||
%4 = insertvalue { i16*, i64, i64 } %3, i64 %int16SliceSrc.len, 1
|
%4 = insertvalue { i16*, i64, i64 } %3, i64 %int16SliceSrc.len, 1
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
target triple = "x86_64--linux"
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04"
|
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04", align 8
|
||||||
|
|
||||||
declare void @runtime.printuint8(i8) local_unnamed_addr
|
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ entry:
|
|||||||
call void @runtime.printuint8(i8 3)
|
call void @runtime.printuint8(i8 3)
|
||||||
call void @runtime.printuint8(i8 3)
|
call void @runtime.printuint8(i8 3)
|
||||||
call void @runtime.printint16(i16 5)
|
call void @runtime.printint16(i16 5)
|
||||||
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*)
|
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*), align 2
|
||||||
call void @runtime.printint16(i16 %int16SliceDst.val)
|
call void @runtime.printint16(i16 %int16SliceDst.val)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
Submodule lib/cmsis-svd updated: 9c35b6d9df...df75ff974c
Submodule
+1
Submodule lib/musl added at 040c1d16b4
+1
-1
Submodule lib/picolibc updated: 80528c684b...f68b8204f7
@@ -0,0 +1,18 @@
|
|||||||
|
// This file is included in the picolibc build.
|
||||||
|
// It makes stdio functions available to the C library.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
// Defined in the runtime package. Writes to the default console (usually, the
|
||||||
|
// first UART or an USB-CDC device).
|
||||||
|
int runtime_putchar(char, FILE*);
|
||||||
|
|
||||||
|
// Define stdin, stdout, and stderr as a single object.
|
||||||
|
// This object must not reside in ROM.
|
||||||
|
static FILE __stdio = FDEV_SETUP_STREAM(runtime_putchar, NULL, NULL, _FDEV_SETUP_WRITE);
|
||||||
|
|
||||||
|
// Define the underlying structs for stdin, stdout, and stderr.
|
||||||
|
FILE *const stdin = &__stdio;
|
||||||
|
__strong_reference(stdin, stdout);
|
||||||
|
__strong_reference(stdin, stderr);
|
||||||
+1
-1
Submodule lib/wasi-libc updated: 215adc8ac9...ad5133410f
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user