mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-08 21:13:39 +00:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 907d90105a | |||
| 7b1e5f6f99 | |||
| 70c52ef1b4 | |||
| 5078ce382d | |||
| 408855da14 | |||
| 58072a5167 | |||
| 5026047cde | |||
| 67aea275c5 | |||
| 99bd4d2c7c | |||
| a4b22bd125 | |||
| 2ed7523001 | |||
| 4aec3d04f9 | |||
| 4f729c323d | |||
| 1f43f32aae | |||
| 6d1cbe6fb9 | |||
| 7cd2890434 | |||
| 13311da7e9 | |||
| 3047d8f321 | |||
| 13ed58950f | |||
| 5271bd8cfa | |||
| 72137d663b | |||
| 740134197e | |||
| 610e7fd16a | |||
| 69a6718b38 | |||
| 7d31d98f0f | |||
| 0a93347e1c | |||
| 411333327e | |||
| 159f7ebbc3 | |||
| 153ff09cc5 | |||
| 57cddf5657 | |||
| 6a35719594 | |||
| 20a46e1b28 | |||
| 926c02b6ff | |||
| f370cd18fc | |||
| a1d7cab080 | |||
| 15a9e2313a | |||
| 58f2533f15 | |||
| 3c2d2a93d3 | |||
| ea36fea5a9 | |||
| 5fdb894760 | |||
| 8fed063820 | |||
| 2f843af286 | |||
| 7afc47d67a | |||
| 0bc7c2a61f | |||
| d434058aef | |||
| 56780c2691 | |||
| 1d99b1ed84 | |||
| e1405640da | |||
| 5996e113ad | |||
| f1e6997018 | |||
| 335a7ad0b7 | |||
| 17deac116f | |||
| b112477e95 | |||
| 2fa24ef752 | |||
| fcefcb191c | |||
| ff7c71c99c | |||
| 7eaad62568 | |||
| 401bd89664 | |||
| 24b1bfcecd | |||
| 27162ebe32 | |||
| b347aea450 | |||
| 1766746c60 | |||
| 07049b87b6 | |||
| f38603530d | |||
| b65447c7d5 | |||
| 0abc909739 | |||
| d984b55311 | |||
| 446fe1f5b3 | |||
| 873412b43a | |||
| 8a5ab5ab12 | |||
| 77cf60ef30 | |||
| a4e2e09457 | |||
| 4262f0ff1f | |||
| 1ceb63d14c | |||
| e1052f921c | |||
| 6dff85c756 | |||
| afae6b3795 | |||
| 2825b4fe74 | |||
| b5c5d95b68 | |||
| 633fe95187 | |||
| 64d7f1e436 | |||
| 9af535bf98 | |||
| 159f0051bb | |||
| 49e22fe678 | |||
| c119721e3b | |||
| 9294141d70 | |||
| bcf58c0840 | |||
| 7a61cb1bc3 | |||
| 11e1b2148f | |||
| a4599ba539 | |||
| 4c64784724 | |||
| 8d6b210c09 | |||
| 79ba6a50c3 | |||
| 2fb5174910 | |||
| b31281a5b6 | |||
| 8754f64f3b | |||
| caf405b01d | |||
| bb65c5ce2b | |||
| 283fed16a5 | |||
| 76bba13963 | |||
| ada11090a2 | |||
| a07287d3c6 | |||
| 1b2e764835 | |||
| 2c93a4085c | |||
| 906757603d | |||
| 5d16811199 | |||
| f2e576decf | |||
| 2d61972475 | |||
| 5c488e3145 | |||
| e45ff9c0e8 | |||
| 39805bca45 | |||
| 97842b367c | |||
| 9246899b30 | |||
| 04ace4de5f | |||
| 87a4676137 | |||
| fd20f63ee3 | |||
| 9dd249a431 | |||
| 777d3f3ea5 | |||
| ea3b5dc689 | |||
| 3dd502a928 | |||
| 48242ba8d6 | |||
| 7ea9eff406 | |||
| 80d94115dc | |||
| 9de76fb42e | |||
| 52c61de19f | |||
| f308d7d28c | |||
| 7729a36782 | |||
| 5404c81ffd | |||
| b52310fed2 | |||
| 046070074d | |||
| c23a5b65ef | |||
| b8e433821a | |||
| dd1a836903 | |||
| 4ed0936c0e | |||
| 7a5d4c9537 | |||
| af6bbaf532 | |||
| 4c7449efe5 | |||
| 995e815b63 | |||
| 109b5298c4 | |||
| a94e03eff2 | |||
| eb3d6261b4 | |||
| 15724848f9 | |||
| 8f40b107d9 | |||
| fa673f0827 | |||
| e3fe6d8f37 | |||
| 5c23f6fb6c | |||
| 5afb63df60 | |||
| 1d2c39c3b9 | |||
| 270a2f51fd | |||
| e87cd23e87 | |||
| db389ba443 | |||
| d1dfa1155c |
+37
-22
@@ -22,12 +22,12 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-14-v1
|
- llvm-source-14-v3
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-14-v1
|
key: llvm-source-14-v3
|
||||||
paths:
|
paths:
|
||||||
- llvm-project/clang/lib/Headers
|
- llvm-project/clang/lib/Headers
|
||||||
- llvm-project/clang/include
|
- llvm-project/clang/include
|
||||||
@@ -45,29 +45,32 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- binaryen-linux-v1
|
- binaryen-linux-v2
|
||||||
- run:
|
- run:
|
||||||
name: "Build Binaryen"
|
name: "Build Binaryen"
|
||||||
command: |
|
command: |
|
||||||
make binaryen
|
make binaryen
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: binaryen-linux-v1
|
key: binaryen-linux-v2
|
||||||
paths:
|
paths:
|
||||||
- build/wasm-opt
|
- build/wasm-opt
|
||||||
test-linux:
|
test-linux:
|
||||||
parameters:
|
parameters:
|
||||||
llvm:
|
llvm:
|
||||||
type: string
|
type: string
|
||||||
|
fmt-check:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- submodules
|
- submodules
|
||||||
- run:
|
- run:
|
||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
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' > /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 | apt-key add -
|
||||||
sudo apt-get update
|
apt-get update
|
||||||
sudo apt-get install --no-install-recommends \
|
apt-get install --no-install-recommends -y \
|
||||||
llvm-<<parameters.llvm>>-dev \
|
llvm-<<parameters.llvm>>-dev \
|
||||||
clang-<<parameters.llvm>> \
|
clang-<<parameters.llvm>> \
|
||||||
libclang-<<parameters.llvm>>-dev \
|
libclang-<<parameters.llvm>>-dev \
|
||||||
@@ -80,43 +83,55 @@ commands:
|
|||||||
- build-binaryen-linux
|
- build-binaryen-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}
|
- go-cache-v3-{{ checksum "go.mod" }}
|
||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-systemclang-v5
|
- wasi-libc-sysroot-systemclang-v6
|
||||||
- run: make wasi-libc
|
- run: make wasi-libc
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: wasi-libc-sysroot-systemclang-v5
|
key: wasi-libc-sysroot-systemclang-v6
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
|
- when:
|
||||||
|
condition: <<parameters.fmt-check>>
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
# Do this before gen-device so that it doesn't check the
|
||||||
|
# formatting of generated files.
|
||||||
|
name: Check Go code formatting
|
||||||
|
command: make fmt-check
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest XTENSA=0
|
- run: make smoketest XTENSA=0
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache/go-build
|
- ~/.cache/go-build
|
||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
- run: make fmt-check
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm11-go115:
|
test-llvm13-go116:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.15-buster
|
- image: golang:1.16-buster
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "11"
|
llvm: "13"
|
||||||
test-llvm12-go117:
|
test-llvm14-go119:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.17-buster
|
- image: golang:1.19beta1-buster
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "12"
|
llvm: "14"
|
||||||
|
fmt-check: false
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
test-all:
|
test-all:
|
||||||
jobs:
|
jobs:
|
||||||
- test-llvm11-go115
|
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||||
- test-llvm12-go117
|
# least the smoke tests still pass.
|
||||||
|
- test-llvm13-go116
|
||||||
|
# This tests a beta version of Go. It should be removed once regular
|
||||||
|
# release builds are built using this version.
|
||||||
|
- test-llvm14-go119
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
- release
|
- release
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-macos:
|
build-macos:
|
||||||
name: build-macos
|
name: build-macos
|
||||||
@@ -67,7 +71,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v3
|
key: wasi-libc-sysroot-v4
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ dev, fix-docker-llvm-build ]
|
branches: [ dev, fix-docker-llvm-build ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push_to_registry:
|
push_to_registry:
|
||||||
name: build-push-dev
|
name: build-push-dev
|
||||||
@@ -51,18 +55,20 @@ jobs:
|
|||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
- name: Trigger Drivers repo build on CircleCI
|
- name: Trigger Drivers repo build on Github Actions
|
||||||
run: |
|
run: |
|
||||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/drivers/pipeline' \
|
curl -X POST \
|
||||||
--header 'Content-Type: application/json' \
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
-d '{"branch": "dev"}' \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
|
||||||
- name: Trigger Bluetooth repo build on CircleCI
|
-d '{"ref": "dev"}'
|
||||||
|
- name: Trigger Bluetooth repo build on Github Actions
|
||||||
run: |
|
run: |
|
||||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/bluetooth/pipeline' \
|
curl -X POST \
|
||||||
--header 'Content-Type: application/json' \
|
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
|
||||||
-d '{"branch": "dev"}' \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
|
||||||
|
-d '{"ref": "dev"}'
|
||||||
- name: Trigger TinyFS repo build on CircleCI
|
- name: Trigger TinyFS repo build on CircleCI
|
||||||
run: |
|
run: |
|
||||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
|
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
|
||||||
|
|||||||
+140
-21
@@ -7,22 +7,32 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
- release
|
- release
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
# Build Linux binaries, ready for release.
|
# Build Linux binaries, ready for release.
|
||||||
# This intentionally uses an older Linux image, so that we compile against
|
# This runs inside an Alpine Linux container so we can more easily create a
|
||||||
# an older glibc version and therefore are compatible with a wide range of
|
# statically linked binary.
|
||||||
# Linux distributions.
|
runs-on: ubuntu-latest
|
||||||
runs-on: ubuntu-18.04
|
container:
|
||||||
|
image: alpine:3.16
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install apk dependencies
|
||||||
|
# tar: needed for actions/cache@v2
|
||||||
|
# git+openssh: needed for checkout (I think?)
|
||||||
|
# gcompat: needed for go binary
|
||||||
|
# ruby: needed to install fpm
|
||||||
|
run: apk add tar git openssh gcompat make g++ ruby
|
||||||
|
- name: Work around CVE-2022-24765
|
||||||
|
# We're not on a multi-user machine, so this is safe.
|
||||||
|
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get install --no-install-recommends \
|
|
||||||
ninja-build
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
@@ -30,7 +40,7 @@ jobs:
|
|||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
@@ -38,7 +48,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-v1
|
key: llvm-source-14-linux-alpine-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -52,7 +62,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-linux-v1
|
key: llvm-build-14-linux-alpine-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -60,6 +70,8 @@ jobs:
|
|||||||
# fetch LLVM source
|
# fetch LLVM source
|
||||||
rm -rf llvm-project
|
rm -rf llvm-project
|
||||||
make llvm-source
|
make llvm-source
|
||||||
|
# install dependencies
|
||||||
|
apk add cmake samurai python3
|
||||||
# build!
|
# build!
|
||||||
make llvm-build
|
make llvm-build
|
||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
@@ -68,26 +80,29 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-v1
|
key: binaryen-linux-alpine-v1
|
||||||
path: build/wasm-opt
|
path: build/wasm-opt
|
||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
run: make binaryen
|
run: |
|
||||||
|
apk add cmake samurai python3
|
||||||
|
make binaryen STATIC=1
|
||||||
- name: Cache wasi-libc
|
- name: Cache wasi-libc
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-asserts-v4
|
key: wasi-libc-sysroot-linux-alpine-v1
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
run: make wasi-libc
|
run: make wasi-libc
|
||||||
- name: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
sudo gem install --no-document fpm
|
gem install --version 2.7.6 dotenv
|
||||||
|
gem install --no-document fpm
|
||||||
- name: Build TinyGo release
|
- name: Build TinyGo release
|
||||||
run: |
|
run: |
|
||||||
make release deb -j3
|
make release deb -j3 STATIC=1
|
||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
@@ -163,7 +178,7 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '12'
|
node-version: '14'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||||
@@ -179,7 +194,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-asserts-v1
|
key: llvm-source-14-linux-asserts-v2
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -218,7 +233,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-asserts-v4
|
key: wasi-libc-sysroot-linux-asserts-v5
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
@@ -257,6 +272,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends \
|
sudo apt-get install --no-install-recommends \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
g++-arm-linux-gnueabihf \
|
g++-arm-linux-gnueabihf \
|
||||||
@@ -264,7 +280,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.17'
|
go-version: '1.18.1'
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
@@ -276,7 +292,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-linux-v1
|
key: llvm-source-14-linux-v2
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -318,6 +334,7 @@ jobs:
|
|||||||
make CROSS=arm-linux-gnueabihf binaryen
|
make CROSS=arm-linux-gnueabihf binaryen
|
||||||
- name: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
|
sudo gem install --version 2.7.6 dotenv
|
||||||
sudo gem install --no-document fpm
|
sudo gem install --no-document fpm
|
||||||
- name: Build TinyGo binary
|
- name: Build TinyGo binary
|
||||||
run: |
|
run: |
|
||||||
@@ -346,3 +363,105 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
/tmp/tinygo.linux-arm.tar.gz
|
/tmp/tinygo.linux-arm.tar.gz
|
||||||
/tmp/tinygo_armhf.deb
|
/tmp/tinygo_armhf.deb
|
||||||
|
build-linux-arm64:
|
||||||
|
# Build ARM64 Linux binaries, ready for release.
|
||||||
|
# It is set to "needs: build-linux" because it modifies the release created
|
||||||
|
# in that process to avoid doing lots of duplicate work and to avoid
|
||||||
|
# complications around precompiled libraries such as compiler-rt shipped as
|
||||||
|
# part of the release tarball.
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
needs: build-linux
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install apt dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --no-install-recommends \
|
||||||
|
qemu-user \
|
||||||
|
g++-aarch64-linux-gnu \
|
||||||
|
libc6-dev-arm64-cross \
|
||||||
|
ninja-build
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.18.1'
|
||||||
|
- name: Cache Go
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
- name: Cache LLVM source
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-source
|
||||||
|
with:
|
||||||
|
key: llvm-source-14-linux-v1
|
||||||
|
path: |
|
||||||
|
llvm-project/clang/lib/Headers
|
||||||
|
llvm-project/clang/include
|
||||||
|
llvm-project/compiler-rt
|
||||||
|
llvm-project/lld/include
|
||||||
|
llvm-project/llvm/include
|
||||||
|
- name: Download LLVM source
|
||||||
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
|
run: make llvm-source
|
||||||
|
- name: Cache LLVM build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-llvm-build
|
||||||
|
with:
|
||||||
|
key: llvm-build-14-linux-arm64-v1
|
||||||
|
path: llvm-build
|
||||||
|
- name: Build LLVM
|
||||||
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
# fetch LLVM source
|
||||||
|
rm -rf llvm-project
|
||||||
|
make llvm-source
|
||||||
|
# build!
|
||||||
|
make llvm-build CROSS=aarch64-linux-gnu
|
||||||
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
|
- name: Cache Binaryen
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache-binaryen
|
||||||
|
with:
|
||||||
|
key: binaryen-linux-arm64-v1
|
||||||
|
path: build/wasm-opt
|
||||||
|
- name: Build Binaryen
|
||||||
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
git submodule update --init lib/binaryen
|
||||||
|
make CROSS=aarch64-linux-gnu binaryen
|
||||||
|
- name: Install fpm
|
||||||
|
run: |
|
||||||
|
sudo gem install --version 2.7.6 dotenv
|
||||||
|
sudo gem install --no-document fpm
|
||||||
|
- name: Build TinyGo binary
|
||||||
|
run: |
|
||||||
|
make CROSS=aarch64-linux-gnu
|
||||||
|
- name: Download amd64 release
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: linux-amd64-double-zipped
|
||||||
|
- name: Extract amd64 release
|
||||||
|
run: |
|
||||||
|
mkdir -p build/release
|
||||||
|
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||||
|
- name: Modify release
|
||||||
|
run: |
|
||||||
|
cp -p build/tinygo build/release/tinygo/bin
|
||||||
|
cp -p build/wasm-opt build/release/tinygo/bin
|
||||||
|
- name: Create arm64 release
|
||||||
|
run: |
|
||||||
|
make release deb RELEASEONLY=1 DEB_ARCH=arm64
|
||||||
|
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
||||||
|
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
||||||
|
- name: Publish release artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: linux-arm64-double-zipped
|
||||||
|
path: |
|
||||||
|
/tmp/tinygo.linux-arm64.tar.gz
|
||||||
|
/tmp/tinygo_arm64.deb
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
- release
|
- release
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
@@ -16,6 +20,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: '1.18.1'
|
go-version: '1.18.1'
|
||||||
- uses: brechtm/setup-scoop@v2
|
- uses: brechtm/setup-scoop@v2
|
||||||
|
with:
|
||||||
|
scoop_update: 'false'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -35,7 +41,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-14-windows-v1
|
key: llvm-source-14-windows-v2
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -49,7 +55,7 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-14-windows-v1
|
key: llvm-build-14-windows-v2
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -59,14 +65,14 @@ jobs:
|
|||||||
rm -rf llvm-project
|
rm -rf llvm-project
|
||||||
make llvm-source
|
make llvm-source
|
||||||
# build!
|
# build!
|
||||||
make llvm-build
|
make llvm-build CCACHE=OFF
|
||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Cache wasi-libc sysroot
|
- name: Cache wasi-libc sysroot
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v3
|
key: wasi-libc-sysroot-v4
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
@@ -78,6 +84,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-v -short"
|
run: make test GOTESTFLAGS="-v -short"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
|
shell: bash
|
||||||
run: make build/release -j4
|
run: make build/release -j4
|
||||||
- name: Make release artifact
|
- name: Make release artifact
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
+2
-1
@@ -23,6 +23,7 @@ llvm-project
|
|||||||
build/*
|
build/*
|
||||||
|
|
||||||
# Ignore files generated by smoketest
|
# Ignore files generated by smoketest
|
||||||
|
test
|
||||||
test.bin
|
test.bin
|
||||||
test.elf
|
test.elf
|
||||||
test.exe
|
test.exe
|
||||||
@@ -30,4 +31,4 @@ test.gba
|
|||||||
test.hex
|
test.hex
|
||||||
test.nro
|
test.nro
|
||||||
test.wasm
|
test.wasm
|
||||||
wasm.wasm
|
wasm.wasm
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
|
|||||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||||
|
|
||||||
* Go (1.15+)
|
* Go (1.16+)
|
||||||
* Standard build tools (gcc/clang)
|
* Standard build tools (gcc/clang)
|
||||||
* git
|
* git
|
||||||
* CMake
|
* CMake
|
||||||
|
|||||||
@@ -1,3 +1,55 @@
|
|||||||
|
0.24.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **command line**
|
||||||
|
- remove support for go 1.15
|
||||||
|
- remove support for LLVM 11 and LLVM 12
|
||||||
|
- add initial Go 1.19 beta support
|
||||||
|
- `test`: fix package/... syntax
|
||||||
|
* **compiler**
|
||||||
|
- add support for the embed package
|
||||||
|
- `builder`: improve error message for "command not found"
|
||||||
|
- `builder`: add support for ThinLTO on MacOS and Windows
|
||||||
|
- `builder`: free LLVM objects after use, to reduce memory leaking
|
||||||
|
- `builder`: improve `-no-debug` error messages
|
||||||
|
- `cgo`: be more strict: CGo now requires every Go file to import the headers it needs
|
||||||
|
- `compiler`: alignof(func) is 1 pointer, not 2
|
||||||
|
- `compiler`: add support for type parameters (aka generics)
|
||||||
|
- `compiler`: implement `recover()` built-in function
|
||||||
|
- `compiler`: support atomic, volatile, and LLVM memcpy-like functions in defer
|
||||||
|
- `compiler`: drop support for macos syscalls via inline assembly
|
||||||
|
- `interp`: do not try to interpret past task.Pause()
|
||||||
|
- `interp`: fix some buggy localValue handling
|
||||||
|
- `interp`: do not unroll loops
|
||||||
|
- `transform`: fix MakeGCStackSlots that caused a possible GC bug on WebAssembly
|
||||||
|
* **standard library**
|
||||||
|
- `os`: enable os.Stdin for baremetal target
|
||||||
|
- `reflect`: add `Value.UnsafePointer` method
|
||||||
|
- `runtime`: scan GC globals conservatively on Windows, MacOS, Linux and Nintendo Switch
|
||||||
|
- `runtime`: add per-map hash seeds
|
||||||
|
- `runtime`: handle nil map write panics
|
||||||
|
- `runtime`: add stronger hash functions
|
||||||
|
- `syscall`: implement `Getpagesize`
|
||||||
|
* **targets**
|
||||||
|
- `atmega2560`: support UART1-3 + example for uart
|
||||||
|
- `avr`: use compiler-rt for improved float64 support
|
||||||
|
- `avr`: simplify timer-based time
|
||||||
|
- `avr`: fix race condition in stack write
|
||||||
|
- `darwin`: add support for `GOARCH=arm64` (aka Apple Silicon)
|
||||||
|
- `darwin`: support `-size=short` and `-size=full` flag
|
||||||
|
- `rp2040`: replace sleep 'busy loop' with timer alarm
|
||||||
|
- `rp2040`: align api for `PortMaskSet`, `PortMaskClear`
|
||||||
|
- `rp2040`: fix GPIO interrupts
|
||||||
|
- `samd21`, `samd51`, `nrf52840`: add support for USBHID (keyboard / mouse)
|
||||||
|
- `wasm`: update wasi-libc version
|
||||||
|
- `wasm`: use newer WebAssembly features
|
||||||
|
* **boards**
|
||||||
|
- add Badger 2040
|
||||||
|
- `matrixportal-m4`: attach USB DP to the correct pin
|
||||||
|
- `teensy40`: add I2C support
|
||||||
|
- `wioterminal`: fix I2C definition
|
||||||
|
|
||||||
|
|
||||||
0.23.0
|
0.23.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,16 @@ MD5SUM = md5sum
|
|||||||
# tinygo binary for tests
|
# tinygo binary for tests
|
||||||
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||||
|
|
||||||
# Use CCACHE for LLVM if possible
|
# Check for ccache if the user hasn't set it to on or off.
|
||||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
ifeq (, $(CCACHE))
|
||||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
|
# Use CCACHE for LLVM if possible
|
||||||
|
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||||
|
CCACHE := ON
|
||||||
|
else
|
||||||
|
CCACHE := OFF
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||||
|
|
||||||
# Allow enabling LLVM assertions
|
# Allow enabling LLVM assertions
|
||||||
ifeq (1, $(ASSERT))
|
ifeq (1, $(ASSERT))
|
||||||
@@ -50,6 +56,23 @@ else
|
|||||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq (1, $(STATIC))
|
||||||
|
# Build TinyGo as a fully statically linked binary (no dynamically loaded
|
||||||
|
# libraries such as a libc). This is not supported with glibc which is used
|
||||||
|
# on most major Linux distributions. However, it is supported in Alpine
|
||||||
|
# Linux with musl.
|
||||||
|
CGO_LDFLAGS += -static
|
||||||
|
# Also set the thread stack size to 1MB. This is necessary on musl as the
|
||||||
|
# default stack size is 128kB and LLVM uses more than that.
|
||||||
|
# For more information, see:
|
||||||
|
# https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size
|
||||||
|
CGO_LDFLAGS += -Wl,-z,stack-size=1048576
|
||||||
|
# Build wasm-opt with static linking.
|
||||||
|
# For details, see:
|
||||||
|
# https://github.com/WebAssembly/binaryen/blob/version_102/.github/workflows/ci.yml#L181
|
||||||
|
BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static"
|
||||||
|
endif
|
||||||
|
|
||||||
# Cross compiling support.
|
# Cross compiling support.
|
||||||
ifneq ($(CROSS),)
|
ifneq ($(CROSS),)
|
||||||
CC = $(CROSS)-gcc
|
CC = $(CROSS)-gcc
|
||||||
@@ -68,6 +91,15 @@ ifneq ($(CROSS),)
|
|||||||
-DLLVM_TARGET_ARCH=ARM
|
-DLLVM_TARGET_ARCH=ARM
|
||||||
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||||
|
else ifeq ($(CROSS), aarch64-linux-gnu)
|
||||||
|
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||||
|
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
|
||||||
|
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||||
|
LLVM_OPTION += \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DLLVM_TARGET_ARCH=AArch64
|
||||||
|
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||||
|
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||||
else
|
else
|
||||||
$(error Unknown cross compilation target: $(CROSS))
|
$(error Unknown cross compilation target: $(CROSS))
|
||||||
endif
|
endif
|
||||||
@@ -141,7 +173,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/testing src/internal/reflectlite transform
|
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform
|
||||||
fmt:
|
fmt:
|
||||||
@gofmt -l -w $(FMT_PATHS)
|
@gofmt -l -w $(FMT_PATHS)
|
||||||
fmt-check:
|
fmt-check:
|
||||||
@@ -203,7 +235,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
|
|||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
$(LLVM_BUILDDIR)/build.ninja:
|
||||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
@@ -225,15 +257,15 @@ endif
|
|||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG" MALLOC_IMPL=none WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||||
|
|
||||||
|
|
||||||
# Build the Go compiler.
|
# Build the Go compiler.
|
||||||
tinygo:
|
tinygo:
|
||||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
||||||
test: wasi-libc
|
test: wasi-libc
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
|
||||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||||
TEST_PACKAGES_SLOW = \
|
TEST_PACKAGES_SLOW = \
|
||||||
@@ -256,6 +288,7 @@ TEST_PACKAGES_FAST = \
|
|||||||
crypto/sha256 \
|
crypto/sha256 \
|
||||||
crypto/sha512 \
|
crypto/sha512 \
|
||||||
debug/macho \
|
debug/macho \
|
||||||
|
embed/internal/embedtest \
|
||||||
encoding \
|
encoding \
|
||||||
encoding/ascii85 \
|
encoding/ascii85 \
|
||||||
encoding/base32 \
|
encoding/base32 \
|
||||||
@@ -297,10 +330,14 @@ TEST_PACKAGES_LINUX := \
|
|||||||
archive/zip \
|
archive/zip \
|
||||||
compress/flate \
|
compress/flate \
|
||||||
compress/lzw \
|
compress/lzw \
|
||||||
|
crypto/hmac \
|
||||||
debug/dwarf \
|
debug/dwarf \
|
||||||
debug/plan9obj \
|
debug/plan9obj \
|
||||||
io/fs \
|
io/fs \
|
||||||
testing/fstest
|
io/ioutil \
|
||||||
|
strconv \
|
||||||
|
testing/fstest \
|
||||||
|
text/template/parse
|
||||||
|
|
||||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||||
|
|
||||||
@@ -366,6 +403,8 @@ tinygo-baremetal:
|
|||||||
.PHONY: smoketest
|
.PHONY: smoketest
|
||||||
smoketest:
|
smoketest:
|
||||||
$(TINYGO) version
|
$(TINYGO) version
|
||||||
|
# regression test for #2892
|
||||||
|
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||||
# compile-only platform-independent examples
|
# compile-only platform-independent examples
|
||||||
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
||||||
# regression test for #2563
|
# regression test for #2563
|
||||||
@@ -385,6 +424,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||||
@@ -401,6 +442,10 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(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
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
# test simulated boards on play.tinygo.org
|
# test simulated boards on play.tinygo.org
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||||
@@ -409,14 +454,14 @@ ifneq ($(WASM), 0)
|
|||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -size short -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 -size short -o test.wasm -tags=pca10040 examples/blinky2
|
$(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink
|
||||||
@$(MD5SUM) test.wasm
|
|
||||||
$(TINYGO) build -size short -o test.wasm -tags=pca10056 examples/blinky2
|
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -size short -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
|
||||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
|
$(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/serial
|
||||||
|
@$(MD5SUM) test.wasm
|
||||||
endif
|
endif
|
||||||
# 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
|
||||||
@@ -535,6 +580,14 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
# test pwm
|
# test pwm
|
||||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -542,6 +595,13 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
# test usb
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(STM32), 0)
|
ifneq ($(STM32), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -605,6 +665,8 @@ ifneq ($(XTENSA), 0)
|
|||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target m5stack examples/serial
|
$(TINYGO) build -size short -o test.bin -target m5stack examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
|
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
endif
|
endif
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
@@ -633,7 +695,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
|
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
|
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||||
# not garbage collected.
|
# not garbage collected.
|
||||||
@@ -709,12 +772,12 @@ endif
|
|||||||
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||||
@cp -rp src build/release/tinygo/src
|
@cp -rp src build/release/tinygo/src
|
||||||
@cp -rp targets build/release/tinygo/targets
|
@cp -rp targets build/release/tinygo/targets
|
||||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||||
|
|
||||||
release:
|
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 85 microcontroller boards are currently supported:
|
The following 88 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)
|
||||||
@@ -89,11 +89,13 @@ The following 85 microcontroller boards are currently supported:
|
|||||||
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
||||||
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
||||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||||
|
* [iLabs Challenger RP2040 LoRa](https://ilabs.se/product/challenger-rp2040-lora/)
|
||||||
* [M5Stack](https://docs.m5stack.com/en/core/basic)
|
* [M5Stack](https://docs.m5stack.com/en/core/basic)
|
||||||
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
|
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
|
||||||
* [M5Stamp C3](https://docs.m5stack.com/en/core/stamp_c3)
|
* [M5Stamp C3](https://docs.m5stack.com/en/core/stamp_c3)
|
||||||
* [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/)
|
||||||
|
* [MCH2022 badge](https://badge.team/docs/badges/mch2022/)
|
||||||
* [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)
|
||||||
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
|
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
|
||||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||||
@@ -105,6 +107,7 @@ The following 85 microcontroller boards are currently supported:
|
|||||||
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
|
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
|
||||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||||
|
* [Pimoroni Badger2040](https://shop.pimoroni.com/products/badger-2040)
|
||||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||||
@@ -114,6 +117,7 @@ The following 85 microcontroller boards are currently supported:
|
|||||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||||
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
||||||
|
* [Seeed XIAO RP2040](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html)
|
||||||
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
||||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||||
|
|||||||
+265
-96
@@ -4,6 +4,7 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
@@ -80,6 +81,7 @@ type packageAction struct {
|
|||||||
Config *compiler.Config
|
Config *compiler.Config
|
||||||
CFlags []string
|
CFlags []string
|
||||||
FileHashes map[string]string // hash of every file that's part of the package
|
FileHashes map[string]string // hash of every file that's part of the package
|
||||||
|
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
|
||||||
Imports map[string]string // map from imported package to action ID hash
|
Imports map[string]string // map from imported package to action ID hash
|
||||||
OptLevel int // LLVM optimization level (0-3)
|
OptLevel int // LLVM optimization level (0-3)
|
||||||
SizeLevel int // LLVM optimization for size level (0-2)
|
SizeLevel int // LLVM optimization for size level (0-2)
|
||||||
@@ -188,9 +190,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer machine.Dispose()
|
||||||
|
|
||||||
// Load entire program AST into memory.
|
// Load entire program AST into memory.
|
||||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||||
Sizes: compiler.Sizes(machine),
|
Sizes: compiler.Sizes(machine),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -208,8 +211,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Add jobs to compile each package.
|
// Add jobs to compile each package.
|
||||||
// Packages that have a cache hit will not be compiled again.
|
// Packages that have a cache hit will not be compiled again.
|
||||||
var packageJobs []*compileJob
|
var packageJobs []*compileJob
|
||||||
packageBitcodePaths := make(map[string]string)
|
packageActionIDJobs := make(map[string]*compileJob)
|
||||||
packageActionIDs := make(map[string]string)
|
|
||||||
|
|
||||||
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||||
version := goenv.Version
|
version := goenv.Version
|
||||||
@@ -225,6 +227,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var embedFileObjects []*compileJob
|
||||||
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
|
||||||
|
|
||||||
@@ -234,52 +237,114 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
}
|
}
|
||||||
sort.Strings(undefinedGlobals)
|
sort.Strings(undefinedGlobals)
|
||||||
|
|
||||||
// Create a cache key: a hash from the action ID below that contains all
|
// Make compile jobs to load files to be embedded in the output binary.
|
||||||
// the parameters for the build.
|
var actionIDDependencies []*compileJob
|
||||||
actionID := packageAction{
|
allFiles := map[string][]*loader.EmbedFile{}
|
||||||
ImportPath: pkg.ImportPath,
|
for _, files := range pkg.EmbedGlobals {
|
||||||
CompilerBuildID: string(compilerBuildID),
|
for _, file := range files {
|
||||||
TinyGoVersion: goenv.Version,
|
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||||
LLVMVersion: llvm.Version,
|
}
|
||||||
Config: compilerConfig,
|
|
||||||
CFlags: pkg.CFlags,
|
|
||||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
|
||||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
|
||||||
OptLevel: optLevel,
|
|
||||||
SizeLevel: sizeLevel,
|
|
||||||
UndefinedGlobals: undefinedGlobals,
|
|
||||||
}
|
}
|
||||||
for filePath, hash := range pkg.FileHashes {
|
for name, files := range allFiles {
|
||||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
name := name
|
||||||
|
files := files
|
||||||
|
job := &compileJob{
|
||||||
|
description: "make object file for " + name,
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
// Read the file contents in memory.
|
||||||
|
path := filepath.Join(pkg.Dir, name)
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the file.
|
||||||
|
sum := sha256.Sum256(data)
|
||||||
|
hexSum := hex.EncodeToString(sum[:16])
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
file.Size = uint64(len(data))
|
||||||
|
file.Hash = hexSum
|
||||||
|
if file.NeedsData {
|
||||||
|
file.Data = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actionIDDependencies = append(actionIDDependencies, job)
|
||||||
|
embedFileObjects = append(embedFileObjects, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Action ID jobs need to know the action ID of all the jobs the package
|
||||||
|
// imports.
|
||||||
|
var importedPackages []*compileJob
|
||||||
for _, imported := range pkg.Pkg.Imports() {
|
for _, imported := range pkg.Pkg.Imports() {
|
||||||
hash, ok := packageActionIDs[imported.Path()]
|
job, ok := packageActionIDJobs[imported.Path()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||||
}
|
}
|
||||||
actionID.Imports[imported.Path()] = hash
|
importedPackages = append(importedPackages, job)
|
||||||
|
actionIDDependencies = append(actionIDDependencies, job)
|
||||||
}
|
}
|
||||||
buf, err := json.Marshal(actionID)
|
|
||||||
if err != nil {
|
// Create a job that will calculate the action ID for a package compile
|
||||||
panic(err) // shouldn't happen
|
// job. The action ID is the cache key that is used for caching this
|
||||||
|
// package.
|
||||||
|
packageActionIDJob := &compileJob{
|
||||||
|
description: "calculate cache key for package " + pkg.ImportPath,
|
||||||
|
dependencies: actionIDDependencies,
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
// Create a cache key: a hash from the action ID below that contains all
|
||||||
|
// the parameters for the build.
|
||||||
|
actionID := packageAction{
|
||||||
|
ImportPath: pkg.ImportPath,
|
||||||
|
CompilerBuildID: string(compilerBuildID),
|
||||||
|
TinyGoVersion: goenv.Version,
|
||||||
|
LLVMVersion: llvm.Version,
|
||||||
|
Config: compilerConfig,
|
||||||
|
CFlags: pkg.CFlags,
|
||||||
|
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||||
|
EmbeddedFiles: make(map[string]string, len(allFiles)),
|
||||||
|
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||||
|
OptLevel: optLevel,
|
||||||
|
SizeLevel: sizeLevel,
|
||||||
|
UndefinedGlobals: undefinedGlobals,
|
||||||
|
}
|
||||||
|
for filePath, hash := range pkg.FileHashes {
|
||||||
|
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||||
|
}
|
||||||
|
for name, files := range allFiles {
|
||||||
|
actionID.EmbeddedFiles[name] = files[0].Hash
|
||||||
|
}
|
||||||
|
for i, imported := range pkg.Pkg.Imports() {
|
||||||
|
actionID.Imports[imported.Path()] = importedPackages[i].result
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(actionID)
|
||||||
|
if err != nil {
|
||||||
|
return err // shouldn't happen
|
||||||
|
}
|
||||||
|
hash := sha512.Sum512_224(buf)
|
||||||
|
job.result = hex.EncodeToString(hash[:])
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
hash := sha512.Sum512_224(buf)
|
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
||||||
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
|
||||||
|
|
||||||
// Determine the path of the bitcode file (which is a serialized version
|
// Now create the job to actually build the package. It will exit early
|
||||||
// of a LLVM module).
|
// if the package is already compiled.
|
||||||
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
|
||||||
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
|
||||||
|
|
||||||
// The package has not yet been compiled, so create a job to do so.
|
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile package " + pkg.ImportPath,
|
description: "compile package " + pkg.ImportPath,
|
||||||
run: func(*compileJob) error {
|
dependencies: []*compileJob{packageActionIDJob},
|
||||||
|
run: func(job *compileJob) error {
|
||||||
|
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
|
||||||
// Acquire a lock (if supported).
|
// Acquire a lock (if supported).
|
||||||
unlock := lock(bitcodePath + ".lock")
|
unlock := lock(job.result + ".lock")
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
if _, err := os.Stat(bitcodePath); err == nil {
|
if _, err := os.Stat(job.result); err == nil {
|
||||||
// Already cached, don't recreate this package.
|
// Already cached, don't recreate this package.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -287,6 +352,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Compile AST to IR. The compiler.CompilePackage function will
|
// Compile AST to IR. The compiler.CompilePackage function will
|
||||||
// build the SSA as needed.
|
// build the SSA as needed.
|
||||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||||
|
defer mod.Context().Dispose()
|
||||||
|
defer mod.Dispose()
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
return newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
@@ -372,33 +439,13 @@ 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())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run function passes for each function in the module.
|
transform.OptimizePackage(mod, config)
|
||||||
// These passes are intended to be run on each function right
|
|
||||||
// after they're created to reduce IR size (and maybe also for
|
|
||||||
// cache locality to improve performance), but for now they're
|
|
||||||
// run here for each function in turn. Maybe this can be
|
|
||||||
// improved in the future.
|
|
||||||
builder := llvm.NewPassManagerBuilder()
|
|
||||||
defer builder.Dispose()
|
|
||||||
builder.SetOptLevel(optLevel)
|
|
||||||
builder.SetSizeLevel(sizeLevel)
|
|
||||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
|
||||||
defer funcPasses.Dispose()
|
|
||||||
builder.PopulateFunc(funcPasses)
|
|
||||||
funcPasses.InitializeFunc()
|
|
||||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
|
||||||
if fn.IsDeclaration() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
funcPasses.RunFunc(fn)
|
|
||||||
}
|
|
||||||
funcPasses.FinalizeFunc()
|
|
||||||
|
|
||||||
// Serialize the LLVM module as a bitcode file.
|
// Serialize the LLVM module as a bitcode file.
|
||||||
// Write to a temporary path that is renamed to the destination
|
// Write to a temporary path that is renamed to the destination
|
||||||
// file to avoid race conditions with other TinyGo invocatiosn
|
// file to avoid race conditions with other TinyGo invocatiosn
|
||||||
// that might also be compiling this package at the same time.
|
// that might also be compiling this package at the same time.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -418,13 +465,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||||
// own, so create a somewhat useful error message here.
|
// own, so create a somewhat useful error message here.
|
||||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
|
||||||
}
|
}
|
||||||
err = f.Close()
|
err = f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.Rename(f.Name(), bitcodePath)
|
return os.Rename(f.Name(), job.result)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
packageJobs = append(packageJobs, job)
|
packageJobs = append(packageJobs, job)
|
||||||
@@ -432,6 +479,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
|
|
||||||
// Add job that links and optimizes all packages together.
|
// Add job that links and optimizes all packages together.
|
||||||
var mod llvm.Module
|
var mod llvm.Module
|
||||||
|
defer func() {
|
||||||
|
if !mod.IsNil() {
|
||||||
|
ctx := mod.Context()
|
||||||
|
mod.Dispose()
|
||||||
|
ctx.Dispose()
|
||||||
|
}
|
||||||
|
}()
|
||||||
var stackSizeLoads []string
|
var stackSizeLoads []string
|
||||||
programJob := &compileJob{
|
programJob := &compileJob{
|
||||||
description: "link+optimize packages (LTO)",
|
description: "link+optimize packages (LTO)",
|
||||||
@@ -441,8 +495,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// anything, it only links the bitcode files together.
|
// anything, it only links the bitcode files together.
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
mod = ctx.NewModule("main")
|
mod = ctx.NewModule("main")
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkgJob := range packageJobs {
|
||||||
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||||
}
|
}
|
||||||
@@ -534,6 +588,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer llvmBuf.Dispose()
|
||||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||||
case ".bc":
|
case ".bc":
|
||||||
var buf llvm.MemoryBuffer
|
var buf llvm.MemoryBuffer
|
||||||
@@ -642,6 +697,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// Add libc dependencies, if they exist.
|
// Add libc dependencies, if they exist.
|
||||||
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
||||||
|
|
||||||
|
// Add embedded files.
|
||||||
|
linkerDependencies = append(linkerDependencies, embedFileObjects...)
|
||||||
|
|
||||||
// Strip debug information with -no-debug.
|
// Strip debug information with -no-debug.
|
||||||
if !config.Debug() {
|
if !config.Debug() {
|
||||||
for _, tag := range config.BuildTags() {
|
for _, tag := range config.BuildTags() {
|
||||||
@@ -651,6 +709,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if config.GOOS() == "darwin" {
|
||||||
|
// Debug information isn't stored in the binary itself on MacOS but
|
||||||
|
// is left in the object files by default. The binary does store the
|
||||||
|
// path to these object files though.
|
||||||
|
return errors.New("cannot remove debug information: MacOS doesn't store debug info in the executable by default")
|
||||||
|
}
|
||||||
if config.Target.Linker == "wasm-ld" {
|
if config.Target.Linker == "wasm-ld" {
|
||||||
// Don't just strip debug information, also compress relocations
|
// Don't just strip debug information, also compress relocations
|
||||||
// while we're at it. Relocations can only be compressed when debug
|
// while we're at it. Relocations can only be compressed when debug
|
||||||
@@ -660,21 +724,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
// ld.lld is also used on Linux.
|
// ld.lld is also used on Linux.
|
||||||
ldflags = append(ldflags, "--strip-debug")
|
ldflags = append(ldflags, "--strip-debug")
|
||||||
} else {
|
} else {
|
||||||
switch config.GOOS() {
|
// Other linkers may have different flags.
|
||||||
case "linux":
|
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
|
||||||
// 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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,11 +745,24 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||||
}
|
}
|
||||||
if config.UseThinLTO() {
|
if config.UseThinLTO() {
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
if config.GOOS() == "windows" {
|
||||||
"-plugin-opt=mcpu="+config.CPU(),
|
// Options for the MinGW wrapper for the lld COFF linker.
|
||||||
"-plugin-opt=O"+strconv.Itoa(optLevel),
|
ldflags = append(ldflags,
|
||||||
"-plugin-opt=thinlto")
|
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
||||||
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
|
} else if config.GOOS() == "darwin" {
|
||||||
|
// Options for the ld64-compatible lld linker.
|
||||||
|
ldflags = append(ldflags,
|
||||||
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
|
} else {
|
||||||
|
// Options for the ELF linker.
|
||||||
|
ldflags = append(ldflags,
|
||||||
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
|
)
|
||||||
|
}
|
||||||
if config.CodeModel() != "default" {
|
if config.CodeModel() != "default" {
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"-mllvm", "-code-model="+config.CodeModel())
|
"-mllvm", "-code-model="+config.CodeModel())
|
||||||
@@ -881,6 +945,112 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createEmbedObjectFile creates a new object file with the given contents, for
|
||||||
|
// the embed package.
|
||||||
|
func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
|
||||||
|
// TODO: this works for small files, but can be a problem for larger files.
|
||||||
|
// For larger files, it seems more appropriate to generate the object file
|
||||||
|
// manually without going through LLVM.
|
||||||
|
// On the other hand, generating DWARF like we do here can be difficult
|
||||||
|
// without assistance from LLVM.
|
||||||
|
|
||||||
|
// Create new LLVM module just for this file.
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
defer ctx.Dispose()
|
||||||
|
mod := ctx.NewModule("data")
|
||||||
|
defer mod.Dispose()
|
||||||
|
|
||||||
|
// Create data global.
|
||||||
|
value := ctx.ConstString(data, false)
|
||||||
|
globalName := "embed/file_" + hexSum
|
||||||
|
global := llvm.AddGlobal(mod, value.Type(), globalName)
|
||||||
|
global.SetInitializer(value)
|
||||||
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
|
global.SetGlobalConstant(true)
|
||||||
|
global.SetUnnamedAddr(true)
|
||||||
|
global.SetAlignment(1)
|
||||||
|
if compilerConfig.GOOS != "darwin" {
|
||||||
|
// MachO doesn't support COMDATs, while COFF requires it (to avoid
|
||||||
|
// "duplicate symbol" errors). ELF works either way.
|
||||||
|
// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
|
||||||
|
global.SetComdat(mod.Comdat(globalName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add DWARF debug information to this global, so that it is
|
||||||
|
// correctly counted when compiling with the -size= flag.
|
||||||
|
dibuilder := llvm.NewDIBuilder(mod)
|
||||||
|
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||||
|
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||||
|
File: sourceFile,
|
||||||
|
Dir: sourceDir,
|
||||||
|
Producer: "TinyGo",
|
||||||
|
Optimized: false,
|
||||||
|
})
|
||||||
|
ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||||
|
SizeInBits: uint64(len(data)) * 8,
|
||||||
|
AlignInBits: 8,
|
||||||
|
ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||||
|
Name: "byte",
|
||||||
|
SizeInBits: 8,
|
||||||
|
Encoding: llvm.DW_ATE_unsigned_char,
|
||||||
|
}),
|
||||||
|
Subscripts: []llvm.DISubrange{
|
||||||
|
{
|
||||||
|
Lo: 0,
|
||||||
|
Count: int64(len(data)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
difile := dibuilder.CreateFile(sourceFile, sourceDir)
|
||||||
|
diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
|
||||||
|
Name: globalName,
|
||||||
|
File: difile,
|
||||||
|
Line: 1,
|
||||||
|
Type: ditype,
|
||||||
|
Expr: dibuilder.CreateExpression(nil),
|
||||||
|
AlignInBits: 8,
|
||||||
|
})
|
||||||
|
global.AddMetadata(0, diglobalexpr)
|
||||||
|
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
ctx.MDNode([]llvm.Metadata{
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
|
||||||
|
ctx.MDString("Debug Info Version"),
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
ctx.MDNode([]llvm.Metadata{
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
|
||||||
|
ctx.MDString("Dwarf Version"),
|
||||||
|
llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
dibuilder.Finalize()
|
||||||
|
dibuilder.Destroy()
|
||||||
|
|
||||||
|
// Write this LLVM module out as an object file.
|
||||||
|
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer machine.Dispose()
|
||||||
|
outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer outfile.Close()
|
||||||
|
buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer buf.Dispose()
|
||||||
|
_, err = outfile.Write(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return outfile.Name(), outfile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// optimizeProgram runs a series of optimizations and transformations that are
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
@@ -935,17 +1105,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
return errors.New("verification failure after LLVM optimization passes")
|
return errors.New("verification failure after LLVM optimization passes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
|
||||||
// disabled) unless it is explicitly disabled with a function attribute.
|
|
||||||
// This is a problem, as it tries to emit them and prints an error when it
|
|
||||||
// can't with this feature disabled.
|
|
||||||
// Because as of september 2020 tail calls are not yet widely supported,
|
|
||||||
// they need to be disabled until they are widely supported (at which point
|
|
||||||
// the +tail-call target feautre can be set).
|
|
||||||
if strings.HasPrefix(config.Triple(), "wasm") {
|
|
||||||
transform.DisableTailCalls(mod)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1069,10 +1228,15 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
|||||||
// Goroutines need to be started and finished and take up some stack space
|
// Goroutines need to be started and finished and take up some stack space
|
||||||
// that way. This can be measured by measuing the stack size of
|
// that way. This can be measured by measuing the stack size of
|
||||||
// tinygo_startTask.
|
// tinygo_startTask.
|
||||||
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
var baseStackSize uint64
|
||||||
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
var baseStackSizeType stacksize.SizeType
|
||||||
|
var baseStackSizeFailedAt *stacksize.CallNode
|
||||||
|
if len(gowrappers) != 0 {
|
||||||
|
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
||||||
|
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
||||||
|
}
|
||||||
|
baseStackSize, baseStackSizeType, baseStackSizeFailedAt = functions["tinygo_startTask"][0].StackSize()
|
||||||
}
|
}
|
||||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
|
||||||
|
|
||||||
sizes := make(map[string]functionStackSize)
|
sizes := make(map[string]functionStackSize)
|
||||||
|
|
||||||
@@ -1144,6 +1308,11 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if data == nil {
|
||||||
|
// The .tinygo_stacksizes section doesn't exist, so assume this
|
||||||
|
// modification isn't needed.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if len(stackSizeLoads)*4 != len(data) {
|
if len(stackSizeLoads)*4 != len(data) {
|
||||||
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
|
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
"cortex-m0",
|
"cortex-m0",
|
||||||
"cortex-m0plus",
|
"cortex-m0plus",
|
||||||
"cortex-m3",
|
"cortex-m3",
|
||||||
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
"cortex-m33",
|
||||||
"cortex-m4",
|
"cortex-m4",
|
||||||
"cortex-m7",
|
"cortex-m7",
|
||||||
"esp32c3",
|
"esp32c3",
|
||||||
|
|||||||
+8
-2
@@ -40,7 +40,6 @@ var genericBuiltins = []string{
|
|||||||
"divdf3.c",
|
"divdf3.c",
|
||||||
"divdi3.c",
|
"divdi3.c",
|
||||||
"divmoddi4.c",
|
"divmoddi4.c",
|
||||||
"divmodsi4.c",
|
|
||||||
"divsc3.c",
|
"divsc3.c",
|
||||||
"divsf3.c",
|
"divsf3.c",
|
||||||
"divsi3.c",
|
"divsi3.c",
|
||||||
@@ -127,7 +126,6 @@ var genericBuiltins = []string{
|
|||||||
"ucmpti2.c",
|
"ucmpti2.c",
|
||||||
"udivdi3.c",
|
"udivdi3.c",
|
||||||
"udivmoddi4.c",
|
"udivmoddi4.c",
|
||||||
"udivmodsi4.c",
|
|
||||||
"udivmodti4.c",
|
"udivmodti4.c",
|
||||||
"udivsi3.c",
|
"udivsi3.c",
|
||||||
"udivti3.c",
|
"udivti3.c",
|
||||||
@@ -154,6 +152,14 @@ var aeabiBuiltins = []string{
|
|||||||
"arm/aeabi_memset.S",
|
"arm/aeabi_memset.S",
|
||||||
"arm/aeabi_uidivmod.S",
|
"arm/aeabi_uidivmod.S",
|
||||||
"arm/aeabi_uldivmod.S",
|
"arm/aeabi_uldivmod.S",
|
||||||
|
|
||||||
|
// These two are not technically EABI builtins but are used by them and only
|
||||||
|
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||||
|
// other architectures.
|
||||||
|
// Most importantly, they have a different calling convention on AVR so
|
||||||
|
// should not be used on AVR.
|
||||||
|
"divmodsi4.c",
|
||||||
|
"udivmodsi4.c",
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
|
|||||||
}
|
}
|
||||||
return cmdName, nil
|
return cmdName, nil
|
||||||
}
|
}
|
||||||
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func execCommand(name string, args ...string) error {
|
func execCommand(name string, args ...string) error {
|
||||||
|
|||||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||||
}
|
}
|
||||||
if major != 1 || minor < 15 || minor > 18 {
|
if major != 1 || minor < 16 || minor > 19 {
|
||||||
return nil, fmt.Errorf("requires go version 1.15 through 1.18, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.16 through 1.19, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a job that builds a Darwin libSystem.dylib stub library. This library
|
// Create a job that builds a Darwin libSystem.dylib stub library. This library
|
||||||
@@ -39,12 +38,8 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
|||||||
|
|
||||||
// Link object file to dynamic library.
|
// Link object file to dynamic library.
|
||||||
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
||||||
flavor := "darwin"
|
|
||||||
if strings.Split(llvm.Version, ".")[0] < "13" {
|
|
||||||
flavor = "darwinnew" // needed on LLVM 12 and below
|
|
||||||
}
|
|
||||||
flags = []string{
|
flags = []string{
|
||||||
"-flavor", flavor,
|
"-flavor", "darwin",
|
||||||
"-demangle",
|
"-demangle",
|
||||||
"-dynamic",
|
"-dynamic",
|
||||||
"-dylib",
|
"-dylib",
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ func getElfSectionData(executable string, sectionName string) ([]byte, elf.FileH
|
|||||||
|
|
||||||
section := elfFile.Section(sectionName)
|
section := elfFile.Section(sectionName)
|
||||||
if section == nil {
|
if section == nil {
|
||||||
return nil, elf.FileHeader{}, fmt.Errorf("could not find %s section", sectionName)
|
return nil, elf.FileHeader{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := section.Data()
|
data, err := section.Data()
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
// However, ARM has not done this.
|
// However, ARM has not done this.
|
||||||
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||||
args = append(args, "-march="+cpu)
|
args = append(args, "-march="+cpu)
|
||||||
|
} else if strings.HasPrefix(target, "avr") {
|
||||||
|
args = append(args, "-mmcu="+cpu)
|
||||||
} else {
|
} else {
|
||||||
args = append(args, "-mcpu="+cpu)
|
args = append(args, "-mcpu="+cpu)
|
||||||
}
|
}
|
||||||
@@ -155,6 +157,13 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(target, "avr") {
|
||||||
|
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||||
|
// from what most code (and certainly compiler-rt) expects. So we need
|
||||||
|
// to force the compiler to use 64-bit floating point numbers for
|
||||||
|
// double.
|
||||||
|
args = append(args, "-mdouble=64")
|
||||||
|
}
|
||||||
if strings.HasPrefix(target, "riscv32-") {
|
if strings.HasPrefix(target, "riscv32-") {
|
||||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ var Musl = Library{
|
|||||||
"internal/libc.c",
|
"internal/libc.c",
|
||||||
"internal/syscall_ret.c",
|
"internal/syscall_ret.c",
|
||||||
"internal/vdso.c",
|
"internal/vdso.c",
|
||||||
|
"legacy/*.c",
|
||||||
"malloc/*.c",
|
"malloc/*.c",
|
||||||
"mman/*.c",
|
"mman/*.c",
|
||||||
"signal/*.c",
|
"signal/*.c",
|
||||||
|
|||||||
+193
-7
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"debug/macho"
|
||||||
"debug/pe"
|
"debug/pe"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -116,7 +117,7 @@ var (
|
|||||||
// alloc: heap allocations during init interpretation
|
// alloc: heap allocations during init interpretation
|
||||||
// pack: data created when storing a constant in an interface for example
|
// pack: data created when storing a constant in an interface for example
|
||||||
// string: buffer behind strings
|
// string: buffer behind strings
|
||||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
||||||
|
|
||||||
// Reflect sidetables. Created by the reflect lowering pass.
|
// Reflect sidetables. Created by the reflect lowering pass.
|
||||||
// See src/reflect/sidetables.go.
|
// See src/reflect/sidetables.go.
|
||||||
@@ -126,7 +127,7 @@ var (
|
|||||||
// readProgramSizeFromDWARF reads the source location for each line of code and
|
// 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
|
// each variable in the program, as far as this is stored in the DWARF debug
|
||||||
// information.
|
// information.
|
||||||
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
|
func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone bool) ([]addressLine, error) {
|
||||||
r := data.Reader()
|
r := data.Reader()
|
||||||
var lines []*dwarf.LineFile
|
var lines []*dwarf.LineFile
|
||||||
var addresses []addressLine
|
var addresses []addressLine
|
||||||
@@ -168,7 +169,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevLineEntry.EndSequence && lineEntry.Address == 0 {
|
if prevLineEntry.EndSequence && lineEntry.Address == 0 && skipTombstone {
|
||||||
// Tombstone value. This symbol has been removed, for
|
// Tombstone value. This symbol has been removed, for
|
||||||
// example by the --gc-sections linker flag. It is still
|
// example by the --gc-sections linker flag. It is still
|
||||||
// here in the debug information because the linker can't
|
// here in the debug information because the linker can't
|
||||||
@@ -177,6 +178,10 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
|||||||
// skipped.
|
// skipped.
|
||||||
// For more details, see (among others):
|
// For more details, see (among others):
|
||||||
// https://reviews.llvm.org/D84825
|
// https://reviews.llvm.org/D84825
|
||||||
|
// The value 0 can however really occur in object files,
|
||||||
|
// that typically start at address 0. So don't skip
|
||||||
|
// tombstone values in object files (like when parsing MachO
|
||||||
|
// files).
|
||||||
for {
|
for {
|
||||||
err := lr.Next(&lineEntry)
|
err := lr.Next(&lineEntry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -255,6 +260,65 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLin
|
|||||||
return addresses, nil
|
return addresses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read a MachO object file and return a line table.
|
||||||
|
// Also return an index from symbol name to start address in the line table.
|
||||||
|
func readMachOSymbolAddresses(path string) (map[string]int, []addressLine, error) {
|
||||||
|
// Some constants from mach-o/nlist.h
|
||||||
|
// See: https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
|
||||||
|
const (
|
||||||
|
N_STAB = 0xe0
|
||||||
|
N_TYPE = 0x0e // bitmask for N_TYPE field
|
||||||
|
N_SECT = 0xe // one of the possible type in the N_TYPE field
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read DWARF from the given object file.
|
||||||
|
file, err := macho.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
dwarf, err := file.DWARF()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
lines, err := readProgramSizeFromDWARF(dwarf, 0, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a map from start addresses to indices in the line table (because the
|
||||||
|
// line table is a slice, not a map).
|
||||||
|
addressToLine := make(map[uint64]int, len(lines))
|
||||||
|
for i, line := range lines {
|
||||||
|
if _, ok := addressToLine[line.Address]; ok {
|
||||||
|
addressToLine[line.Address] = -1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addressToLine[line.Address] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a map that for each symbol gives the start index in the line table.
|
||||||
|
addresses := make(map[string]int, len(addressToLine))
|
||||||
|
for _, symbol := range file.Symtab.Syms {
|
||||||
|
if symbol.Type&N_STAB != 0 {
|
||||||
|
continue // STABS entry, ignore
|
||||||
|
}
|
||||||
|
if symbol.Type&0x0e != N_SECT {
|
||||||
|
continue // undefined symbol
|
||||||
|
}
|
||||||
|
if index, ok := addressToLine[symbol.Value]; ok && index >= 0 {
|
||||||
|
if _, ok := addresses[symbol.Name]; ok {
|
||||||
|
// There is a duplicate. Mark it as unavailable.
|
||||||
|
addresses[symbol.Name] = -1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addresses[symbol.Name] = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses, lines, nil
|
||||||
|
}
|
||||||
|
|
||||||
// loadProgramSize calculate a program/data size breakdown of each package for a
|
// loadProgramSize calculate a program/data size breakdown of each package for a
|
||||||
// given ELF file.
|
// given ELF file.
|
||||||
// If the file doesn't contain DWARF debug information, the returned program
|
// If the file doesn't contain DWARF debug information, the returned program
|
||||||
@@ -277,7 +341,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
// Read DWARF information. The error is intentionally ignored.
|
// Read DWARF information. The error is intentionally ignored.
|
||||||
data, _ := file.DWARF()
|
data, _ := file.DWARF()
|
||||||
if data != nil {
|
if data != nil {
|
||||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
addresses, err = readProgramSizeFromDWARF(data, 0, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// However, _do_ report an error here. Something must have gone
|
// However, _do_ report an error here. Something must have gone
|
||||||
// wrong while trying to parse DWARF data.
|
// wrong while trying to parse DWARF data.
|
||||||
@@ -368,11 +432,133 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if file, err := macho.NewFile(f); err == nil {
|
||||||
|
// Read segments, for use while reading through sections.
|
||||||
|
segments := map[string]*macho.Segment{}
|
||||||
|
for _, load := range file.Loads {
|
||||||
|
switch load := load.(type) {
|
||||||
|
case *macho.Segment:
|
||||||
|
segments[load.Name] = load
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read MachO sections.
|
||||||
|
for _, section := range file.Sections {
|
||||||
|
sectionType := section.Flags & 0xff
|
||||||
|
sectionFlags := section.Flags >> 8
|
||||||
|
segment := segments[section.Seg]
|
||||||
|
// For the constants used here, see:
|
||||||
|
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
|
||||||
|
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
|
||||||
|
// Section containing only instructions.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryCode,
|
||||||
|
})
|
||||||
|
} else if sectionType == 1 { // S_ZEROFILL
|
||||||
|
// Section filled with zeroes on demand.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryBSS,
|
||||||
|
})
|
||||||
|
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
|
||||||
|
// Protection doesn't allow writes, so mark this section read-only.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryROData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// The rest is assumed to be regular data.
|
||||||
|
sections = append(sections, memorySection{
|
||||||
|
Address: section.Addr,
|
||||||
|
Size: uint64(section.Size),
|
||||||
|
Type: memoryData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read DWARF information.
|
||||||
|
// The data isn't stored directly in the binary as in most executable
|
||||||
|
// formats. Instead, it is left in the object files that were used as a
|
||||||
|
// basis for linking. The executable does however contain STABS debug
|
||||||
|
// information that points to the source object file and is used by
|
||||||
|
// debuggers.
|
||||||
|
// For more information:
|
||||||
|
// http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme
|
||||||
|
var objSymbolNames map[string]int
|
||||||
|
var objAddresses []addressLine
|
||||||
|
var previousSymbol macho.Symbol
|
||||||
|
for _, symbol := range file.Symtab.Syms {
|
||||||
|
// STABS constants, from mach-o/stab.h:
|
||||||
|
// https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/stab.h.auto.html
|
||||||
|
const (
|
||||||
|
N_GSYM = 0x20
|
||||||
|
N_FUN = 0x24
|
||||||
|
N_STSYM = 0x26
|
||||||
|
N_SO = 0x64
|
||||||
|
N_OSO = 0x66
|
||||||
|
)
|
||||||
|
if symbol.Type == N_OSO {
|
||||||
|
// Found an object file. Now try to parse it.
|
||||||
|
objSymbolNames, objAddresses, err = readMachOSymbolAddresses(symbol.Name)
|
||||||
|
if err != nil && sizesDebug {
|
||||||
|
// Errors are normally ignored. If there is an error, it's
|
||||||
|
// simply treated as that the DWARF is not available.
|
||||||
|
fmt.Fprintf(os.Stderr, "could not read DWARF from file %s: %s\n", symbol.Name, err)
|
||||||
|
}
|
||||||
|
} else if symbol.Type == N_FUN {
|
||||||
|
// Found a function.
|
||||||
|
// The way this is encoded is a bit weird. MachO symbols don't
|
||||||
|
// have a length. What I've found is that the length is encoded
|
||||||
|
// by first having a N_FUN symbol as usual, and then having a
|
||||||
|
// symbol with a zero-length name that has the value not set to
|
||||||
|
// the address of the symbol but to the length. So in order to
|
||||||
|
// get both the address and the length, we look for a symbol
|
||||||
|
// with a name followed by a symbol without a name.
|
||||||
|
if symbol.Name == "" && previousSymbol.Type == N_FUN && previousSymbol.Name != "" {
|
||||||
|
// Functions are encoded as many small chunks in the line
|
||||||
|
// table (one or a few instructions per source line). But
|
||||||
|
// the symbol length covers the whole symbols, over many
|
||||||
|
// lines and possibly including inlined functions. So we
|
||||||
|
// continue to iterate through the objAddresses slice until
|
||||||
|
// we've found all the source lines that are part of this
|
||||||
|
// symbol.
|
||||||
|
address := previousSymbol.Value
|
||||||
|
length := symbol.Value
|
||||||
|
if index, ok := objSymbolNames[previousSymbol.Name]; ok && index >= 0 {
|
||||||
|
for length > 0 {
|
||||||
|
line := objAddresses[index]
|
||||||
|
line.Address = address
|
||||||
|
if line.Length > length {
|
||||||
|
// Line extends beyond the end of te symbol?
|
||||||
|
// Weird, shouldn't happen.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
addresses = append(addresses, line)
|
||||||
|
index++
|
||||||
|
length -= line.Length
|
||||||
|
address += line.Length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if symbol.Type == N_GSYM || symbol.Type == N_STSYM {
|
||||||
|
// Global variables.
|
||||||
|
if index, ok := objSymbolNames[symbol.Name]; ok {
|
||||||
|
address := objAddresses[index]
|
||||||
|
address.Address = symbol.Value
|
||||||
|
addresses = append(addresses, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousSymbol = symbol
|
||||||
|
}
|
||||||
} else if file, err := pe.NewFile(f); err == nil {
|
} else if file, err := pe.NewFile(f); err == nil {
|
||||||
// Read DWARF information. The error is intentionally ignored.
|
// Read DWARF information. The error is intentionally ignored.
|
||||||
data, _ := file.DWARF()
|
data, _ := file.DWARF()
|
||||||
if data != nil {
|
if data != nil {
|
||||||
addresses, err = readProgramSizeFromDWARF(data, 0)
|
addresses, err = readProgramSizeFromDWARF(data, 0, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// However, _do_ report an error here. Something must have gone
|
// However, _do_ report an error here. Something must have gone
|
||||||
// wrong while trying to parse DWARF data.
|
// wrong while trying to parse DWARF data.
|
||||||
@@ -442,9 +628,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
const codeOffset = 0x8000_0000_0000_0000
|
const codeOffset = 0x8000_0000_0000_0000
|
||||||
|
|
||||||
// Read DWARF information. The error is intentionally ignored.
|
// Read DWARF information. The error is intentionally ignored.
|
||||||
data, err := file.DWARF()
|
data, _ := file.DWARF()
|
||||||
if data != nil {
|
if data != nil {
|
||||||
addresses, err = readProgramSizeFromDWARF(data, codeOffset)
|
addresses, err = readProgramSizeFromDWARF(data, codeOffset, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// However, _do_ report an error here. Something must have gone
|
// However, _do_ report an error here. Something must have gone
|
||||||
// wrong while trying to parse DWARF data.
|
// wrong while trying to parse DWARF data.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func RunTool(tool string, args ...string) error {
|
|||||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||||
case "ld.lld":
|
case "ld.lld":
|
||||||
switch linker {
|
switch linker {
|
||||||
case "darwin", "darwinnew": // darwinnew is only needed for LLVM 12 and below
|
case "darwin":
|
||||||
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
||||||
case "elf":
|
case "elf":
|
||||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||||
|
|||||||
+365
-531
File diff suppressed because it is too large
Load Diff
+396
-199
@@ -13,10 +13,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||||
|
#include <llvm/Config/llvm-config.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -72,7 +75,21 @@ var diagnosticSeverity = [...]string{
|
|||||||
C.CXDiagnostic_Fatal: "fatal",
|
C.CXDiagnostic_Fatal: "fatal",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
|
||||||
|
// theory decoupled from Clang) can also use this type.
|
||||||
|
type clangCursor = C.GoCXCursor
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Check that we haven't messed up LLVM versioning.
|
||||||
|
// This can happen when llvm_config_*.go files in either this or the
|
||||||
|
// tinygo.org/x/go-llvm packages is incorrect. It should not ever happen
|
||||||
|
// with byollvm.
|
||||||
|
if C.LLVM_VERSION_STRING != llvm.Version {
|
||||||
|
panic("incorrect build: using LLVM version " + llvm.Version + " in the tinygo.org/x/llvm package, and version " + C.LLVM_VERSION_STRING + " in the ./cgo package")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
|
||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
@@ -119,8 +136,8 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
|||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||||
pos := p.getClangLocationPosition(location, unit)
|
pos := f.getClangLocationPosition(location, unit)
|
||||||
p.addError(pos, severity+": "+spelling)
|
f.addError(pos, severity+": "+spelling)
|
||||||
}
|
}
|
||||||
for i := 0; i < numDiagnostics; i++ {
|
for i := 0; i < numDiagnostics; i++ {
|
||||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||||
@@ -135,7 +152,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract information required by CGo.
|
// Extract information required by CGo.
|
||||||
ref := storedRefs.Put(p)
|
ref := storedRefs.Put(f)
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
@@ -155,35 +172,64 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st
|
|||||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||||
|
|
||||||
// Hash the contents if it isn't hashed yet.
|
// Hash the contents if it isn't hashed yet.
|
||||||
if _, ok := p.visitedFiles[path]; !ok {
|
if _, ok := f.visitedFiles[path]; !ok {
|
||||||
// already stored
|
// already stored
|
||||||
sum := sha512.Sum512_224(data)
|
sum := sha512.Sum512_224(data)
|
||||||
p.visitedFiles[path] = sum[:]
|
f.visitedFiles[path] = sum[:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||||
defer storedRefs.Remove(inclusionCallbackRef)
|
defer storedRefs.Remove(inclusionCallbackRef)
|
||||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||||
|
|
||||||
|
// Do all the C AST operations inside a callback. This makes sure that
|
||||||
|
// libclang related memory is only freed after it is not necessary anymore.
|
||||||
|
callback(f.names)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
// it.
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
pos := p.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||||
fn := &functionInfo{
|
obj := &ast.Object{
|
||||||
pos: pos,
|
Kind: ast.Fun,
|
||||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
args := make([]*ast.Field, numArgs)
|
||||||
|
decl := &ast.FuncDecl{
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//export " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.FuncType{
|
||||||
|
Func: pos,
|
||||||
|
Params: &ast.FieldList{
|
||||||
|
Opening: pos,
|
||||||
|
List: args,
|
||||||
|
Closing: pos,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||||
|
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//go:variadic",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
p.functions[name] = fn
|
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
@@ -191,50 +237,108 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
if argName == "" {
|
if argName == "" {
|
||||||
argName = "$" + strconv.Itoa(i)
|
argName = "$" + strconv.Itoa(i)
|
||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
args[i] = &ast.Field{
|
||||||
name: argName,
|
Names: []*ast.Ident{
|
||||||
typeExpr: p.makeDecayingASTType(argType, pos),
|
{
|
||||||
})
|
NamePos: pos,
|
||||||
|
Name: argName,
|
||||||
|
Obj: &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: argName,
|
||||||
|
Decl: decl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: f.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{
|
decl.Type.Results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
{
|
{
|
||||||
Type: p.makeASTType(resultType, pos),
|
Type: f.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case C.CXCursor_StructDecl:
|
obj.Decl = decl
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
return decl, nil
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
typ := f.makeASTRecordType(c, pos)
|
||||||
return C.CXChildVisit_Continue
|
typeName := "C." + name
|
||||||
|
typeExpr := typ.typeExpr
|
||||||
|
if typ.unionSize != 0 {
|
||||||
|
// Convert to a single-field struct type.
|
||||||
|
typeExpr = f.makeUnionField(typ)
|
||||||
}
|
}
|
||||||
p.makeASTType(typ, pos)
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: typ.pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, typ
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typeName := "C." + name
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
obj := &ast.Object{
|
||||||
return C.CXChildVisit_Continue
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
}
|
}
|
||||||
p.makeASTType(typedefType, pos)
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: f.makeASTType(underlyingType, pos),
|
||||||
|
}
|
||||||
|
if underlyingType.kind != C.CXType_Enum {
|
||||||
|
typeSpec.Assign = pos
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, nil
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
p.globals[name] = globalInfo{
|
typeExpr := f.makeASTType(cursorType, pos)
|
||||||
typeExpr: p.makeASTType(cursorType, pos),
|
gen := &ast.GenDecl{
|
||||||
pos: pos,
|
TokPos: pos,
|
||||||
|
Tok: token.VAR,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
Doc: &ast.CommentGroup{
|
||||||
|
List: []*ast.Comment{
|
||||||
|
{
|
||||||
|
Slash: pos - 1,
|
||||||
|
Text: "//go:extern " + name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Type: typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
case C.CXCursor_MacroDefinition:
|
case C.CXCursor_MacroDefinition:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||||
start := C.clang_getRangeStart(sourceRange)
|
start := C.clang_getRangeStart(sourceRange)
|
||||||
end := C.clang_getRangeEnd(sourceRange)
|
end := C.clang_getRangeEnd(sourceRange)
|
||||||
@@ -242,17 +346,17 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
var startOffset, endOffset C.unsigned
|
var startOffset, endOffset C.unsigned
|
||||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||||
if file == nil {
|
if file == nil {
|
||||||
p.addError(pos, "internal error: could not find file where macro is defined")
|
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||||
if file != endFile {
|
if file != endFile {
|
||||||
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
if startOffset > endOffset {
|
if startOffset > endOffset {
|
||||||
p.addError(pos, "internal error: start offset of macro is after end offset")
|
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// read file contents and extract the relevant byte range
|
// read file contents and extract the relevant byte range
|
||||||
@@ -260,31 +364,94 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
var size C.size_t
|
var size C.size_t
|
||||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
if endOffset >= C.uint(size) {
|
if endOffset >= C.uint(size) {
|
||||||
p.addError(pos, "internal error: end offset of macro lies after end of file")
|
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||||
if !strings.HasPrefix(source, name) {
|
if !strings.HasPrefix(source, name) {
|
||||||
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||||
break
|
return nil, nil
|
||||||
}
|
}
|
||||||
value := source[len(name):]
|
value := source[len(name):]
|
||||||
// Try to convert this #define into a Go constant expression.
|
// Try to convert this #define into a Go constant expression.
|
||||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||||
if scannerError != nil {
|
if scannerError != nil {
|
||||||
p.errors = append(p.errors, *scannerError)
|
f.errors = append(f.errors, *scannerError)
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
if expr != nil {
|
|
||||||
// Parsing was successful.
|
gen := &ast.GenDecl{
|
||||||
p.constants[name] = constantInfo{expr, pos}
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
}
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
case C.CXCursor_EnumDecl:
|
case C.CXCursor_EnumDecl:
|
||||||
// Visit all enums, because the fields may be used even when the enum
|
obj := &ast.Object{
|
||||||
// type itself is not.
|
Kind: ast.Typ,
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
Name: "C." + name,
|
||||||
p.makeASTType(typ, pos)
|
}
|
||||||
|
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||||
|
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||||
|
// instead of types such as C.int, which are used here.
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Assign: pos,
|
||||||
|
Type: f.makeASTType(underlying, pos),
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
return typeSpec, nil
|
||||||
|
case C.CXCursor_EnumConstantDecl:
|
||||||
|
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||||
|
expr := &ast.BasicLit{
|
||||||
|
ValuePos: pos,
|
||||||
|
Kind: token.INT,
|
||||||
|
Value: strconv.FormatInt(int64(value), 10),
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
return gen, nil
|
||||||
|
default:
|
||||||
|
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getString(clangString C.CXString) (s string) {
|
func getString(clangString C.CXString) (s string) {
|
||||||
@@ -294,6 +461,49 @@ func getString(clangString C.CXString) (s string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export tinygo_clang_globals_visitor
|
||||||
|
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
|
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
|
||||||
|
switch C.tinygo_clang_getCursorKind(c) {
|
||||||
|
case C.CXCursor_FunctionDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
f.names["struct_"+name] = c
|
||||||
|
}
|
||||||
|
case C.CXCursor_UnionDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
f.names["union_"+name] = c
|
||||||
|
}
|
||||||
|
case C.CXCursor_TypedefDecl:
|
||||||
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_VarDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_MacroDefinition:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
case C.CXCursor_EnumDecl:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if name != "" {
|
||||||
|
// Named enum, which can be referenced from Go using C.enum_foo.
|
||||||
|
f.names["enum_"+name] = c
|
||||||
|
}
|
||||||
|
// The enum fields are in global scope, so recurse to visit them.
|
||||||
|
return C.CXChildVisit_Recurse
|
||||||
|
case C.CXCursor_EnumConstantDecl:
|
||||||
|
// We arrive here because of the "Recurse" above.
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
f.names[name] = c
|
||||||
|
}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
||||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||||
@@ -391,7 +601,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
|||||||
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||||
// types (arrays in function parameters, etc). It is otherwise identical to
|
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||||
// makeASTType.
|
// makeASTType.
|
||||||
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
// Strip typedefs, if any.
|
// Strip typedefs, if any.
|
||||||
underlyingType := typ
|
underlyingType := typ
|
||||||
if underlyingType.kind == C.CXType_Typedef {
|
if underlyingType.kind == C.CXType_Typedef {
|
||||||
@@ -417,15 +627,15 @@ func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
pointeeType := C.clang_getElementType(underlyingType)
|
pointeeType := C.clang_getElementType(underlyingType)
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: f.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.makeASTType(typ, pos)
|
return f.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 (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
var typeName string
|
var typeName string
|
||||||
switch typ.kind {
|
switch typ.kind {
|
||||||
case C.CXType_Char_S, C.CXType_Char_U:
|
case C.CXType_Char_S, C.CXType_Char_U:
|
||||||
@@ -486,7 +696,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: f.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
case C.CXType_ConstantArray:
|
case C.CXType_ConstantArray:
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
@@ -496,7 +706,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||||
},
|
},
|
||||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
|
||||||
}
|
}
|
||||||
case C.CXType_FunctionProto:
|
case C.CXType_FunctionProto:
|
||||||
// Be compatible with gc, which uses the *[0]byte type for function
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
@@ -517,71 +727,21 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
case C.CXType_Typedef:
|
case C.CXType_Typedef:
|
||||||
name := getString(C.clang_getTypedefName(typ))
|
name := getString(C.clang_getTypedefName(typ))
|
||||||
if _, ok := p.typedefs[name]; !ok {
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
p.typedefs[name] = nil // don't recurse
|
|
||||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
|
||||||
expr := p.makeASTType(underlyingType, pos)
|
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
|
||||||
expr := expr.(*ast.Ident)
|
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
|
||||||
switch expr.Name {
|
|
||||||
case "C.char":
|
|
||||||
if typeSize != 1 {
|
|
||||||
// This happens for some very special purpose architectures
|
|
||||||
// (DSPs etc.) that are not currently targeted.
|
|
||||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
|
||||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
|
||||||
}
|
|
||||||
switch underlyingType.kind {
|
|
||||||
case C.CXType_Char_S:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case C.CXType_Char_U:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
}
|
|
||||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "int16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "int32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "int64"
|
|
||||||
}
|
|
||||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "uint16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "uint32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "uint64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.typedefs[name] = &typedefInfo{
|
|
||||||
typeExpr: expr,
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C." + name,
|
Name: f.getASTDeclName(name, c, false),
|
||||||
}
|
}
|
||||||
case C.CXType_Elaborated:
|
case C.CXType_Elaborated:
|
||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
return p.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
return p.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
default:
|
default:
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||||
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||||
typeName = "<unknown>"
|
typeName = "<unknown>"
|
||||||
}
|
}
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
@@ -599,63 +759,46 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// Anonymous record, probably inside a typedef.
|
// Anonymous record, probably inside a typedef.
|
||||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
var file C.CXFile
|
||||||
// This record is a union or is a struct with bitfields, so we
|
var line C.unsigned
|
||||||
// have to declare it as a named type (for getters/setters to
|
var column C.unsigned
|
||||||
// work).
|
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||||
p.anonStructNum++
|
location := token.Position{
|
||||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
Filename: getString(C.clang_getFileName(file)),
|
||||||
p.elaboratedTypes[cgoName] = typeInfo
|
Line: int(line),
|
||||||
return &ast.Ident{
|
Column: int(column),
|
||||||
NamePos: pos,
|
|
||||||
Name: "C." + cgoName,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return typeInfo.typeExpr
|
if location.Filename == "" || location.Line == 0 {
|
||||||
|
// Not sure when this would happen, but protect from it anyway.
|
||||||
|
f.addError(pos, "could not find file/line information")
|
||||||
|
}
|
||||||
|
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||||
} else {
|
} else {
|
||||||
cgoName := cgoRecordPrefix + name
|
name = cgoRecordPrefix + name
|
||||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
}
|
||||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
return &ast.Ident{
|
||||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
NamePos: pos,
|
||||||
}
|
Name: f.getASTDeclName(name, cursor, false),
|
||||||
return &ast.Ident{
|
|
||||||
NamePos: pos,
|
|
||||||
Name: "C." + cgoName,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// anonymous enum
|
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
|
||||||
ref := storedRefs.Put(p)
|
|
||||||
defer storedRefs.Remove(ref)
|
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
|
||||||
return p.makeASTType(underlying, pos)
|
|
||||||
} else {
|
} else {
|
||||||
// named enum
|
name = "enum_" + name
|
||||||
if _, ok := p.enums[name]; !ok {
|
}
|
||||||
ref := storedRefs.Put(p)
|
return &ast.Ident{
|
||||||
defer storedRefs.Remove(ref)
|
NamePos: pos,
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
Name: f.getASTDeclName(name, cursor, false),
|
||||||
p.enums[name] = enumInfo{
|
|
||||||
typeExpr: p.makeASTType(underlying, pos),
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ast.Ident{
|
|
||||||
NamePos: pos,
|
|
||||||
Name: "C.enum_" + name,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if typeName == "" {
|
if typeName == "" {
|
||||||
// Report this as an error.
|
// Report this as an error.
|
||||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||||
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||||
typeName = "C.<unknown>"
|
typeName = "C.<unknown>"
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
@@ -664,9 +807,80 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getIntegerType returns an AST node that defines types such as C.int.
|
||||||
|
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
|
||||||
|
pos := p.getCursorPosition(cursor)
|
||||||
|
|
||||||
|
// Find a Go type that matches the size and signedness of the given C type.
|
||||||
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
|
||||||
|
var goName string
|
||||||
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
|
switch name {
|
||||||
|
case "C.char":
|
||||||
|
if typeSize != 1 {
|
||||||
|
// This happens for some very special purpose architectures
|
||||||
|
// (DSPs etc.) that are not currently targeted.
|
||||||
|
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||||
|
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||||
|
}
|
||||||
|
switch underlyingType.kind {
|
||||||
|
case C.CXType_Char_S:
|
||||||
|
goName = "int8"
|
||||||
|
case C.CXType_Char_U:
|
||||||
|
goName = "uint8"
|
||||||
|
}
|
||||||
|
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
goName = "int8"
|
||||||
|
case 2:
|
||||||
|
goName = "int16"
|
||||||
|
case 4:
|
||||||
|
goName = "int32"
|
||||||
|
case 8:
|
||||||
|
goName = "int64"
|
||||||
|
}
|
||||||
|
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
goName = "uint8"
|
||||||
|
case 2:
|
||||||
|
goName = "uint16"
|
||||||
|
case 4:
|
||||||
|
goName = "uint32"
|
||||||
|
case 8:
|
||||||
|
goName = "uint64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if goName == "" { // should not happen
|
||||||
|
p.addError(pos, "internal error: did not find Go type for C type "+name)
|
||||||
|
goName = "int"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct an *ast.TypeSpec for this type.
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
spec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: goName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = spec
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||||
// a Go struct type.
|
// a Go struct type.
|
||||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||||
fieldList := &ast.FieldList{
|
fieldList := &ast.FieldList{
|
||||||
Opening: pos,
|
Opening: pos,
|
||||||
Closing: pos,
|
Closing: pos,
|
||||||
@@ -676,11 +890,11 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
bitfieldNum := 0
|
bitfieldNum := 0
|
||||||
ref := storedRefs.Put(struct {
|
ref := storedRefs.Put(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
file *cgoFile
|
||||||
inBitfield *bool
|
inBitfield *bool
|
||||||
bitfieldNum *int
|
bitfieldNum *int
|
||||||
bitfieldList *[]bitfieldInfo
|
bitfieldList *[]bitfieldInfo
|
||||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||||
defer storedRefs.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
renameFieldKeywords(fieldList)
|
renameFieldKeywords(fieldList)
|
||||||
@@ -709,13 +923,13 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
}
|
}
|
||||||
if bitfieldList != nil {
|
if bitfieldList != nil {
|
||||||
// This is valid C... but please don't do this.
|
// This is valid C... but please don't do this.
|
||||||
p.addError(pos, "bitfield in a union is not supported")
|
f.addError(pos, "bitfield in a union is not supported")
|
||||||
}
|
}
|
||||||
typ := C.tinygo_clang_getCursorType(cursor)
|
typ := C.tinygo_clang_getCursorType(cursor)
|
||||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||||
if sizeInBytes == 0 {
|
if sizeInBytes == 0 {
|
||||||
p.addError(pos, "zero-length union is not supported")
|
f.addError(pos, "zero-length union is not supported")
|
||||||
}
|
}
|
||||||
typeInfo.unionSize = sizeInBytes
|
typeInfo.unionSize = sizeInBytes
|
||||||
typeInfo.unionAlign = alignInBytes
|
typeInfo.unionAlign = alignInBytes
|
||||||
@@ -723,7 +937,7 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
default:
|
default:
|
||||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||||
return &elaboratedTypeInfo{
|
return &elaboratedTypeInfo{
|
||||||
typeExpr: &ast.StructType{
|
typeExpr: &ast.StructType{
|
||||||
Struct: pos,
|
Struct: pos,
|
||||||
@@ -737,17 +951,17 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab
|
|||||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
file *cgoFile
|
||||||
inBitfield *bool
|
inBitfield *bool
|
||||||
bitfieldNum *int
|
bitfieldNum *int
|
||||||
bitfieldList *[]bitfieldInfo
|
bitfieldList *[]bitfieldInfo
|
||||||
})
|
})
|
||||||
fieldList := passed.fieldList
|
fieldList := passed.fieldList
|
||||||
p := passed.pkg
|
f := passed.file
|
||||||
inBitfield := passed.inBitfield
|
inBitfield := passed.inBitfield
|
||||||
bitfieldNum := passed.bitfieldNum
|
bitfieldNum := passed.bitfieldNum
|
||||||
bitfieldList := passed.bitfieldList
|
bitfieldList := passed.bitfieldList
|
||||||
pos := p.getCursorPosition(c)
|
pos := f.getCursorPosition(c)
|
||||||
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
||||||
case C.CXCursor_FieldDecl:
|
case C.CXCursor_FieldDecl:
|
||||||
// Expected. This is a regular field.
|
// Expected. This is a regular field.
|
||||||
@@ -756,7 +970,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
default:
|
default:
|
||||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||||
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
@@ -767,14 +981,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
}
|
}
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
Type: f.makeASTType(typ, f.getCursorPosition(c)),
|
||||||
}
|
}
|
||||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||||
bitfieldOffset := offsetof % alignOf
|
bitfieldOffset := offsetof % alignOf
|
||||||
if bitfieldOffset != 0 {
|
if bitfieldOffset != 0 {
|
||||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||||
p.addError(pos, "expected a bitfield")
|
f.addError(pos, "expected a bitfield")
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
if !*inBitfield {
|
if !*inBitfield {
|
||||||
@@ -821,23 +1035,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_enum_visitor
|
|
||||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
pos := p.getCursorPosition(c)
|
|
||||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
|
||||||
p.constants[name] = constantInfo{
|
|
||||||
expr: &ast.BasicLit{
|
|
||||||
ValuePos: pos,
|
|
||||||
Kind: token.INT,
|
|
||||||
Value: strconv.FormatInt(int64(value), 10),
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//export tinygo_clang_inclusion_visitor
|
//export tinygo_clang_inclusion_visitor
|
||||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build !byollvm && llvm11
|
|
||||||
// +build !byollvm,llvm11
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
|
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
|
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
|
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build !byollvm && llvm12
|
|
||||||
// +build !byollvm,llvm12
|
|
||||||
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
|
|
||||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
|
|
||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
|
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
|
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
|
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
|
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
|
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !byollvm && !llvm11 && !llvm12 && !llvm14
|
//go:build !byollvm && llvm13
|
||||||
// +build !byollvm,!llvm11,!llvm12,!llvm14
|
// +build !byollvm,llvm13
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !byollvm && llvm14
|
//go:build !byollvm && !llvm13
|
||||||
// +build !byollvm,llvm14
|
// +build !byollvm,!llvm13
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
Vendored
+13
-20
@@ -24,23 +24,16 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.int16_t = int16
|
type (
|
||||||
type C.int32_t = int32
|
C.char uint8
|
||||||
type C.int64_t = int64
|
C.schar int8
|
||||||
type C.int8_t = int8
|
C.uchar uint8
|
||||||
type C.uint16_t = uint16
|
C.short int16
|
||||||
type C.uint32_t = uint32
|
C.ushort uint16
|
||||||
type C.uint64_t = uint64
|
C.int int32
|
||||||
type C.uint8_t = uint8
|
C.uint uint32
|
||||||
type C.uintptr_t = uintptr
|
C.long int32
|
||||||
type C.char uint8
|
C.ulong uint32
|
||||||
type C.int int32
|
C.longlong int64
|
||||||
type C.long int32
|
C.ulonglong uint64
|
||||||
type C.longlong int64
|
)
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+15
-22
@@ -24,26 +24,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.bar = C.foo
|
type (
|
||||||
const C.foo = 3
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
type C.int16_t = int16
|
const C.foo = 3
|
||||||
type C.int32_t = int32
|
const C.bar = C.foo
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ import "C"
|
|||||||
//line errors.go:100
|
//line errors.go:100
|
||||||
var (
|
var (
|
||||||
// constant too large
|
// constant too large
|
||||||
_ C.uint8_t = 2 << 10
|
_ C.char = 2 << 10
|
||||||
|
|
||||||
// z member does not exist
|
// z member does not exist
|
||||||
_ C.point_t = C.point_t{z: 3}
|
_ C.point_t = C.point_t{z: 3}
|
||||||
|
|||||||
Vendored
+18
-24
@@ -6,7 +6,7 @@
|
|||||||
// testdata/errors.go:19:26: 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 C.char value in variable declaration (overflows)
|
||||||
// testdata/errors.go:105: unknown field z in struct literal
|
// testdata/errors.go:105: unknown field z in struct literal
|
||||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
// testdata/errors.go:108: 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)
|
||||||
@@ -38,29 +38,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.SOME_CONST_3 = 1234
|
type (
|
||||||
|
C.char uint8
|
||||||
type C.int16_t = int16
|
C.schar int8
|
||||||
type C.int32_t = int32
|
C.uchar uint8
|
||||||
type C.int64_t = int64
|
C.short int16
|
||||||
type C.int8_t = int8
|
C.ushort uint16
|
||||||
type C.uint16_t = uint16
|
C.int int32
|
||||||
type C.uint32_t = uint32
|
C.uint uint32
|
||||||
type C.uint64_t = uint64
|
C.long int32
|
||||||
type C.uint8_t = uint8
|
C.ulong uint32
|
||||||
type C.uintptr_t = uintptr
|
C.longlong int64
|
||||||
type C.char uint8
|
C.ulonglong uint64
|
||||||
type C.int int32
|
)
|
||||||
type C.long int32
|
type C._Ctype_struct___0 struct {
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
type C.point_t = struct {
|
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
|
type C.point_t = C._Ctype_struct___0
|
||||||
|
|
||||||
|
const C.SOME_CONST_3 = 1234
|
||||||
|
|||||||
Vendored
+14
-21
@@ -29,26 +29,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
const C.BAR = 3
|
const C.BAR = 3
|
||||||
const C.FOO_H = 1
|
const C.FOO_H = 1
|
||||||
|
|
||||||
type C.int16_t = int16
|
|
||||||
type C.int32_t = int32
|
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+18
-23
@@ -24,41 +24,36 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
C.char uint8
|
||||||
|
C.schar int8
|
||||||
|
C.uchar uint8
|
||||||
|
C.short int16
|
||||||
|
C.ushort uint16
|
||||||
|
C.int int32
|
||||||
|
C.uint uint32
|
||||||
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
|
C.longlong int64
|
||||||
|
C.ulonglong uint64
|
||||||
|
)
|
||||||
|
|
||||||
//export foo
|
//export foo
|
||||||
func C.foo(a C.int, b C.int) C.int
|
func C.foo(a C.int, b C.int) C.int
|
||||||
|
|
||||||
|
var C.foo$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//export variadic0
|
//export variadic0
|
||||||
//go:variadic
|
//go:variadic
|
||||||
func C.variadic0()
|
func C.variadic0()
|
||||||
|
|
||||||
|
var C.variadic0$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//export variadic2
|
//export variadic2
|
||||||
//go:variadic
|
//go:variadic
|
||||||
func C.variadic2(x C.int, y C.int)
|
func C.variadic2(x C.int, y C.int)
|
||||||
|
|
||||||
var C.foo$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic0$funcaddr unsafe.Pointer
|
|
||||||
var C.variadic2$funcaddr unsafe.Pointer
|
var C.variadic2$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
//go:extern someValue
|
//go:extern someValue
|
||||||
var C.someValue C.int
|
var C.someValue C.int
|
||||||
|
|
||||||
type C.int16_t = int16
|
|
||||||
type C.int32_t = int32
|
|
||||||
type C.int64_t = int64
|
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
|
|||||||
Vendored
+98
-104
@@ -24,132 +24,126 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
return C.__GoBytes(ptr, uintptr(length))
|
return C.__GoBytes(ptr, uintptr(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
const C.option2A = 20
|
type (
|
||||||
const C.optionA = 0
|
C.char uint8
|
||||||
const C.optionB = 1
|
C.schar int8
|
||||||
const C.optionC = -5
|
C.uchar uint8
|
||||||
const C.optionD = -4
|
C.short int16
|
||||||
const C.optionE = 10
|
C.ushort uint16
|
||||||
const C.optionF = 11
|
C.int int32
|
||||||
const C.optionG = 12
|
C.uint uint32
|
||||||
const C.unused1 = 5
|
C.long int32
|
||||||
|
C.ulong uint32
|
||||||
type C.int16_t = int16
|
C.longlong int64
|
||||||
type C.int32_t = int32
|
C.ulonglong uint64
|
||||||
type C.int64_t = int64
|
)
|
||||||
type C.int8_t = int8
|
|
||||||
type C.uint16_t = uint16
|
|
||||||
type C.uint32_t = uint32
|
|
||||||
type C.uint64_t = uint64
|
|
||||||
type C.uint8_t = uint8
|
|
||||||
type C.uintptr_t = uintptr
|
|
||||||
type C.char uint8
|
|
||||||
type C.int int32
|
|
||||||
type C.long int32
|
|
||||||
type C.longlong int64
|
|
||||||
type C.schar int8
|
|
||||||
type C.short int16
|
|
||||||
type C.uchar uint8
|
|
||||||
type C.uint uint32
|
|
||||||
type C.ulong uint32
|
|
||||||
type C.ulonglong uint64
|
|
||||||
type C.ushort uint16
|
|
||||||
type C.bitfield_t = C.struct_4
|
|
||||||
type C.myIntArray = [10]C.int
|
|
||||||
type C.myint = C.int
|
type C.myint = C.int
|
||||||
type C.option2_t = C.uint
|
type C._Ctype_struct___0 struct {
|
||||||
type C.option_t = C.enum_option
|
|
||||||
type C.point2d_t = struct {
|
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
type C.point3d_t = C.struct_point3d
|
type C.point2d_t = C._Ctype_struct___0
|
||||||
type C.struct_nested_t = struct {
|
|
||||||
begin C.point2d_t
|
|
||||||
end C.point2d_t
|
|
||||||
tag C.int
|
|
||||||
|
|
||||||
coord C.union_2
|
|
||||||
}
|
|
||||||
type C.types_t = struct {
|
|
||||||
f float32
|
|
||||||
d float64
|
|
||||||
ptr *C.int
|
|
||||||
}
|
|
||||||
type C.union1_t = struct{ i C.int }
|
|
||||||
type C.union2d_t = C.union_union2d
|
|
||||||
type C.union3_t = C.union_1
|
|
||||||
type C.union_nested_t = C.union_3
|
|
||||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
|
||||||
|
|
||||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
|
||||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
|
||||||
return s.__bitfield_1 >> 5 & 0x1
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
|
||||||
return s.__bitfield_1 >> 6
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
|
||||||
|
|
||||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
|
||||||
|
|
||||||
type C.struct_4 struct {
|
|
||||||
start C.uchar
|
|
||||||
__bitfield_1 C.uchar
|
|
||||||
|
|
||||||
d C.uchar
|
|
||||||
e C.uchar
|
|
||||||
}
|
|
||||||
type C.struct_point3d struct {
|
type C.struct_point3d struct {
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
z C.int
|
z C.int
|
||||||
}
|
}
|
||||||
|
type C.point3d_t = C.struct_point3d
|
||||||
type C.struct_type1 struct {
|
type C.struct_type1 struct {
|
||||||
_type C.int
|
_type C.int
|
||||||
__type C.int
|
__type C.int
|
||||||
___type C.int
|
___type C.int
|
||||||
}
|
}
|
||||||
type C.struct_type2 struct{ _type C.int }
|
type C.struct_type2 struct{ _type C.int }
|
||||||
|
type C._Ctype_union___1 struct{ i C.int }
|
||||||
|
type C.union1_t = C._Ctype_union___1
|
||||||
|
type C._Ctype_union___2 struct{ $union uint64 }
|
||||||
|
|
||||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
||||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
|
||||||
|
|
||||||
type C.union_1 struct{ $union uint64 }
|
|
||||||
|
|
||||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
|
||||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
}
|
||||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
return (*float64)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
||||||
|
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_2 struct{ $union [3]uint32 }
|
type C.union3_t = C._Ctype_union___2
|
||||||
|
type C.union_union2d struct{ $union [2]uint64 }
|
||||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
|
||||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
|
||||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
|
|
||||||
type C.union_3 struct{ $union [2]uint64 }
|
|
||||||
|
|
||||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_union2d struct{ $union [2]uint64 }
|
type C.union2d_t = C.union_union2d
|
||||||
type C.enum_option C.int
|
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
||||||
type C.enum_unused C.uint
|
type C.unionarray_t = C._Ctype_union___3
|
||||||
|
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
||||||
|
|
||||||
|
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
||||||
|
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
||||||
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C._Ctype_struct___4 struct {
|
||||||
|
begin C.point2d_t
|
||||||
|
end C.point2d_t
|
||||||
|
tag C.int
|
||||||
|
|
||||||
|
coord C._Ctype_union___5
|
||||||
|
}
|
||||||
|
type C.struct_nested_t = C._Ctype_struct___4
|
||||||
|
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
||||||
|
|
||||||
|
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
||||||
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
||||||
|
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
||||||
|
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C.union_nested_t = C._Ctype_union___6
|
||||||
|
type C.enum_option = C.int
|
||||||
|
type C.option_t = C.enum_option
|
||||||
|
type C._Ctype_enum___7 = C.uint
|
||||||
|
type C.option2_t = C._Ctype_enum___7
|
||||||
|
type C._Ctype_struct___8 struct {
|
||||||
|
f float32
|
||||||
|
d float64
|
||||||
|
ptr *C.int
|
||||||
|
}
|
||||||
|
type C.types_t = C._Ctype_struct___8
|
||||||
|
type C.myIntArray = [10]C.int
|
||||||
|
type C._Ctype_struct___9 struct {
|
||||||
|
start C.uchar
|
||||||
|
__bitfield_1 C.uchar
|
||||||
|
|
||||||
|
d C.uchar
|
||||||
|
e C.uchar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
||||||
|
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
||||||
|
return s.__bitfield_1 >> 5 & 0x1
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
||||||
|
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
||||||
|
return s.__bitfield_1 >> 6
|
||||||
|
}
|
||||||
|
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
||||||
|
|
||||||
|
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||||
|
|
||||||
|
type C.bitfield_t = C._Ctype_struct___9
|
||||||
|
|||||||
@@ -192,15 +192,6 @@ func (c *Config) UseThinLTO() bool {
|
|||||||
// through a plugin, but it's too much hassle to set up.
|
// through a plugin, but it's too much hassle to set up.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(parts) >= 2 && strings.HasPrefix(parts[2], "macos") {
|
|
||||||
// We use an external linker here at the moment.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(parts) >= 2 && parts[2] == "windows" {
|
|
||||||
// Linker error (undefined runtime.trackedGlobalsBitmap) when linking
|
|
||||||
// for Windows. Disable it for now until that's figured out and fixed.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Other architectures support ThinLTO.
|
// Other architectures support ThinLTO.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-14
@@ -12,11 +12,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Target specification for a given target. Used for bare metal targets.
|
// Target specification for a given target. Used for bare metal targets.
|
||||||
@@ -266,12 +264,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.Libc = "darwin-libSystem"
|
spec.Libc = "darwin-libSystem"
|
||||||
arch := strings.Split(triple, "-")[0]
|
arch := strings.Split(triple, "-")[0]
|
||||||
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
||||||
flavor := "darwin"
|
|
||||||
if strings.Split(llvm.Version, ".")[0] < "13" {
|
|
||||||
flavor = "darwinnew" // needed on LLVM 12 and below
|
|
||||||
}
|
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-flavor", flavor,
|
"-flavor", "darwin",
|
||||||
"-dead_strip",
|
"-dead_strip",
|
||||||
"-arch", arch,
|
"-arch", arch,
|
||||||
"-platform_version", "macos", platformVersion, platformVersion,
|
"-platform_version", "macos", platformVersion, platformVersion,
|
||||||
@@ -296,13 +290,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
"--image-base", "0x400000",
|
"--image-base", "0x400000",
|
||||||
"--gc-sections",
|
"--gc-sections",
|
||||||
"--no-insert-timestamp",
|
"--no-insert-timestamp",
|
||||||
|
"--no-dynamicbase",
|
||||||
)
|
)
|
||||||
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
|
||||||
if llvmMajor >= 12 {
|
|
||||||
// This flag was added in LLVM 12. At the same time, LLVM 12
|
|
||||||
// switched the default from --dynamicbase to --no-dynamicbase.
|
|
||||||
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
|
|
||||||
}
|
|
||||||
} else {
|
} 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
|
||||||
}
|
}
|
||||||
@@ -313,7 +302,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
// systems so we need separate assembly files.
|
// systems so we need separate assembly files.
|
||||||
suffix = "_windows"
|
suffix = "_windows"
|
||||||
}
|
}
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||||
}
|
}
|
||||||
if goarch != runtime.GOARCH {
|
if goarch != runtime.GOARCH {
|
||||||
|
|||||||
+1
-1
@@ -86,7 +86,7 @@ func (b *builder) createAlias(alias llvm.Value) {
|
|||||||
pos := b.program.Fset.Position(b.fn.Pos())
|
pos := b.program.Fset.Position(b.fn.Pos())
|
||||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||||
}
|
}
|
||||||
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
entryBlock := b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||||
b.SetInsertPointAtEnd(entryBlock)
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
if b.llvmFn.Type() != alias.Type() {
|
if b.llvmFn.Type() != alias.Type() {
|
||||||
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||||
|
|||||||
+3
-1
@@ -240,8 +240,10 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put the fault block at the end of the function and the next block at the
|
||||||
|
// current insert position.
|
||||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
|
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
|
|
||||||
// Now branch to the out-of-bounds or the regular block.
|
// Now branch to the out-of-bounds or the regular block.
|
||||||
|
|||||||
+25
-48
@@ -4,19 +4,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
|
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic
|
||||||
// operation. It returns the result of the operation and true if the call could
|
// operation. It returns the result of the operation, or a zero llvm.Value if
|
||||||
// be lowered inline, and false otherwise.
|
// the result is void.
|
||||||
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
func (b *builder) createAtomicOp(name string) llvm.Value {
|
||||||
name := call.Value.(*ssa.Function).Name()
|
|
||||||
switch name {
|
switch name {
|
||||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||||
ptr := b.getValue(call.Args[0])
|
ptr := b.getValue(b.fn.Params[0])
|
||||||
val := b.getValue(call.Args[1])
|
val := b.getValue(b.fn.Params[1])
|
||||||
if strings.HasPrefix(b.Triple, "avr") {
|
if strings.HasPrefix(b.Triple, "avr") {
|
||||||
// AtomicRMW does not work on AVR as intended:
|
// AtomicRMW does not work on AVR as intended:
|
||||||
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
|
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
|
||||||
@@ -29,17 +27,18 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
|||||||
}
|
}
|
||||||
oldVal := b.createCall(fn, []llvm.Value{ptr, val}, "")
|
oldVal := b.createCall(fn, []llvm.Value{ptr, val}, "")
|
||||||
// Return the new value, not the original value returned.
|
// Return the new value, not the original value returned.
|
||||||
return b.CreateAdd(oldVal, val, ""), true
|
return b.CreateAdd(oldVal, val, "")
|
||||||
}
|
}
|
||||||
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||||
// Return the new value, not the original value returned by atomicrmw.
|
// Return the new value, not the original value returned by atomicrmw.
|
||||||
return b.CreateAdd(oldVal, val, ""), true
|
return b.CreateAdd(oldVal, val, "")
|
||||||
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
||||||
ptr := b.getValue(call.Args[0])
|
ptr := b.getValue(b.fn.Params[0])
|
||||||
val := b.getValue(call.Args[1])
|
val := b.getValue(b.fn.Params[1])
|
||||||
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
|
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
|
||||||
if isPointer {
|
if isPointer {
|
||||||
// atomicrmw only supports integers, so cast to an integer.
|
// atomicrmw only supports integers, so cast to an integer.
|
||||||
|
// TODO: this is fixed in LLVM 15.
|
||||||
val = b.CreatePtrToInt(val, b.uintptrType, "")
|
val = b.CreatePtrToInt(val, b.uintptrType, "")
|
||||||
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
|
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
|
||||||
}
|
}
|
||||||
@@ -47,47 +46,23 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
|||||||
if isPointer {
|
if isPointer {
|
||||||
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
||||||
}
|
}
|
||||||
return oldVal, true
|
return oldVal
|
||||||
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
||||||
ptr := b.getValue(call.Args[0])
|
ptr := b.getValue(b.fn.Params[0])
|
||||||
old := b.getValue(call.Args[1])
|
old := b.getValue(b.fn.Params[1])
|
||||||
newVal := b.getValue(call.Args[2])
|
newVal := b.getValue(b.fn.Params[2])
|
||||||
if strings.HasSuffix(name, "64") {
|
|
||||||
if strings.HasPrefix(b.Triple, "thumb") {
|
|
||||||
// Work around a bug in LLVM, at least LLVM 11:
|
|
||||||
// https://reviews.llvm.org/D95891
|
|
||||||
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
|
||||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
|
||||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
|
||||||
if compareAndSwap.IsNil() {
|
|
||||||
// Declare the function if it isn't already declared.
|
|
||||||
i64Type := b.ctx.Int64Type()
|
|
||||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
|
||||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
|
||||||
}
|
|
||||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
|
||||||
// The __sync_val_compare_and_swap_8 function returns the old
|
|
||||||
// value. However, we shouldn't return the old value, we should
|
|
||||||
// return whether the compare/exchange was successful. This is
|
|
||||||
// easily done by comparing the returned (actual) old value with
|
|
||||||
// the expected old value passed to
|
|
||||||
// __sync_val_compare_and_swap_8.
|
|
||||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
|
||||||
return swapped, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||||
return swapped, true
|
return swapped
|
||||||
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
|
||||||
ptr := b.getValue(call.Args[0])
|
ptr := b.getValue(b.fn.Params[0])
|
||||||
val := b.CreateLoad(ptr, "")
|
val := b.CreateLoad(ptr, "")
|
||||||
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||||
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||||
return val, true
|
return val
|
||||||
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
||||||
ptr := b.getValue(call.Args[0])
|
ptr := b.getValue(b.fn.Params[0])
|
||||||
val := b.getValue(call.Args[1])
|
val := b.getValue(b.fn.Params[1])
|
||||||
if strings.HasPrefix(b.Triple, "avr") {
|
if strings.HasPrefix(b.Triple, "avr") {
|
||||||
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
|
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
|
||||||
vType := val.Type()
|
vType := val.Type()
|
||||||
@@ -103,13 +78,15 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
|||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
|
||||||
}
|
}
|
||||||
return b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, ""), true
|
b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
|
||||||
|
return llvm.Value{}
|
||||||
}
|
}
|
||||||
store := b.CreateStore(val, ptr)
|
store := b.CreateStore(val, ptr)
|
||||||
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||||
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||||
return store, true
|
return llvm.Value{}
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, false
|
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name())
|
||||||
|
return llvm.Value{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-2
@@ -33,17 +33,36 @@ const (
|
|||||||
paramIsDeferenceableOrNull = 1 << iota
|
paramIsDeferenceableOrNull = 1 << iota
|
||||||
)
|
)
|
||||||
|
|
||||||
// createCall creates a new call to runtime.<fnName> with the given arguments.
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
// createRuntimeInvoke instead.
|
||||||
|
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||||
llvmFn := b.getFunction(fn)
|
llvmFn := b.getFunction(fn)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||||
}
|
}
|
||||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||||
|
if isInvoke {
|
||||||
|
return b.createInvoke(llvmFn, args, name)
|
||||||
|
}
|
||||||
return b.createCall(llvmFn, args, name)
|
return b.createCall(llvmFn, args, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createRuntimeCall creates a new call to runtime.<fnName> with the given
|
||||||
|
// arguments.
|
||||||
|
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||||
|
return b.createRuntimeCallCommon(fnName, args, name, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRuntimeInvoke creates a new call to runtime.<fnName> with the given
|
||||||
|
// arguments. If the runtime call panics, control flow is diverted to the
|
||||||
|
// landing pad block.
|
||||||
|
// Note that "invoke" here is meant in the LLVM sense (a call that can
|
||||||
|
// panic/throw), not in the Go sense (an interface method call).
|
||||||
|
func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||||
|
return b.createRuntimeCallCommon(fnName, args, name, true)
|
||||||
|
}
|
||||||
|
|
||||||
// createCall creates a call to the given function with the arguments possibly
|
// createCall creates a call to the given function with the arguments possibly
|
||||||
// expanded.
|
// expanded.
|
||||||
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||||
@@ -55,6 +74,15 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
|
|||||||
return b.CreateCall(fn, expanded, name)
|
return b.CreateCall(fn, expanded, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createInvoke is like createCall but continues execution at the landing pad if
|
||||||
|
// the call resulted in a panic.
|
||||||
|
func (b *builder) createInvoke(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
|
||||||
|
if b.hasDeferFrame() {
|
||||||
|
b.createInvokeCheckpoint()
|
||||||
|
}
|
||||||
|
return b.createCall(fn, args, name)
|
||||||
|
}
|
||||||
|
|
||||||
// Expand an argument type to a list that can be used in a function call
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// parameter list.
|
// parameter list.
|
||||||
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
|
||||||
|
|||||||
+281
-66
@@ -9,6 +9,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -17,9 +18,14 @@ import (
|
|||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
llvm.InitializeAllTargetMCs()
|
llvm.InitializeAllTargetMCs()
|
||||||
@@ -65,7 +71,7 @@ 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
|
llvmTypes typeutil.Map
|
||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
@@ -76,6 +82,7 @@ type compilerContext struct {
|
|||||||
program *ssa.Program
|
program *ssa.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
astComments map[string]*ast.CommentGroup
|
astComments map[string]*ast.CommentGroup
|
||||||
|
embedGlobals map[string][]*loader.EmbedFile
|
||||||
pkg *types.Package
|
pkg *types.Package
|
||||||
packageDir string // directory for this package
|
packageDir string // directory for this package
|
||||||
runtimePkg *types.Package
|
runtimePkg *types.Package
|
||||||
@@ -89,7 +96,6 @@ 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{},
|
||||||
@@ -137,6 +143,8 @@ type builder struct {
|
|||||||
currentBlock *ssa.BasicBlock
|
currentBlock *ssa.BasicBlock
|
||||||
phis []phiNode
|
phis []phiNode
|
||||||
deferPtr llvm.Value
|
deferPtr llvm.Value
|
||||||
|
deferFrame llvm.Value
|
||||||
|
landingpad llvm.BasicBlock
|
||||||
difunc llvm.Metadata
|
difunc llvm.Metadata
|
||||||
dilocals map[*types.Var]llvm.Metadata
|
dilocals map[*types.Var]llvm.Metadata
|
||||||
initInlinedAt llvm.Metadata // fake inlinedAt position
|
initInlinedAt llvm.Metadata // fake inlinedAt position
|
||||||
@@ -250,6 +258,7 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
|||||||
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.packageDir = pkg.OriginalDir()
|
c.packageDir = pkg.OriginalDir()
|
||||||
|
c.embedGlobals = pkg.EmbedGlobals
|
||||||
c.pkg = pkg.Pkg
|
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
|
||||||
@@ -301,6 +310,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
|
c.dibuilder.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.mod, c.diagnostics
|
return c.mod, c.diagnostics
|
||||||
@@ -319,18 +329,23 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
|||||||
// important for named struct types (which should only be created once).
|
// 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.
|
// Try to load the LLVM type from the cache.
|
||||||
if t, ok := c.llvmTypes[goType]; ok {
|
// Note: *types.Named isn't unique when working with generics.
|
||||||
return t
|
// See https://github.com/golang/go/issues/53914
|
||||||
|
// This is the reason for using typeutil.Map to lookup LLVM types for Go types.
|
||||||
|
ival := c.llvmTypes.At(goType)
|
||||||
|
if ival != nil {
|
||||||
|
return ival.(llvm.Type)
|
||||||
}
|
}
|
||||||
// Not already created, so adding this type to the cache.
|
// Not already created, so adding this type to the cache.
|
||||||
llvmType := c.makeLLVMType(goType)
|
llvmType := c.makeLLVMType(goType)
|
||||||
c.llvmTypes[goType] = llvmType
|
c.llvmTypes.Set(goType, llvmType)
|
||||||
return llvmType
|
return llvmType
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||||
// getLLVMType instead.
|
// getLLVMType instead.
|
||||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||||
|
goType = typeParamUnderlyingType(goType)
|
||||||
switch typ := goType.(type) {
|
switch typ := goType.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
@@ -378,9 +393,9 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
// in LLVM IR, named structs are implemented as named structs in
|
// in LLVM IR, named structs are implemented as named structs in
|
||||||
// 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.String()
|
||||||
llvmType := c.ctx.StructCreateNamed(llvmName)
|
llvmType := c.ctx.StructCreateNamed(llvmName)
|
||||||
c.llvmTypes[goType] = llvmType // avoid infinite recursion
|
c.llvmTypes.Set(goType, llvmType) // avoid infinite recursion
|
||||||
underlying := c.getLLVMType(st)
|
underlying := c.getLLVMType(st)
|
||||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||||
return llvmType
|
return llvmType
|
||||||
@@ -440,6 +455,7 @@ func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
|
|||||||
// createDIType creates a new DWARF type. Don't call this function directly,
|
// createDIType creates a new DWARF type. Don't call this function directly,
|
||||||
// call getDIType instead.
|
// call getDIType instead.
|
||||||
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
||||||
|
typ = typeParamUnderlyingType(typ)
|
||||||
llvmType := c.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
@@ -770,10 +786,19 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
member := pkg.Members[name]
|
member := pkg.Members[name]
|
||||||
switch member := member.(type) {
|
switch member := member.(type) {
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
|
if member.Synthetic == "generic function" {
|
||||||
|
// Do not try to build generic (non-instantiated) functions.
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Create the function definition.
|
// Create the function definition.
|
||||||
b := newBuilder(c, irbuilder, member)
|
b := newBuilder(c, irbuilder, member)
|
||||||
if member.Blocks == nil {
|
if member.Blocks == nil {
|
||||||
continue // external function
|
// Try to define this as an intrinsic function.
|
||||||
|
b.defineIntrinsicFunction()
|
||||||
|
// It might not be an intrinsic function but simply an external
|
||||||
|
// function (defined via //go:linkname). Leave it undefined in
|
||||||
|
// that case.
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
b.createFunction()
|
b.createFunction()
|
||||||
case *ssa.Type:
|
case *ssa.Type:
|
||||||
@@ -790,6 +815,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
// Parse this method.
|
// Parse this method.
|
||||||
fn := pkg.Prog.MethodValue(method)
|
fn := pkg.Prog.MethodValue(method)
|
||||||
|
if fn == nil {
|
||||||
|
continue // probably a generic method
|
||||||
|
}
|
||||||
if fn.Blocks == nil {
|
if fn.Blocks == nil {
|
||||||
continue // external function
|
continue // external function
|
||||||
}
|
}
|
||||||
@@ -814,7 +842,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
// Global variable.
|
// Global variable.
|
||||||
info := c.getGlobalInfo(member)
|
info := c.getGlobalInfo(member)
|
||||||
global := c.getGlobal(member)
|
global := c.getGlobal(member)
|
||||||
if !info.extern {
|
if files, ok := c.embedGlobals[member.Name()]; ok {
|
||||||
|
c.createEmbedGlobal(member, global, files)
|
||||||
|
} else if !info.extern {
|
||||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
if info.section != "" {
|
if info.section != "" {
|
||||||
@@ -848,10 +878,155 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createFunction builds the LLVM IR implementation for this function. The
|
// createEmbedGlobal creates an initializer for a //go:embed global variable.
|
||||||
// function must not yet be defined, otherwise this function will create a
|
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
|
||||||
// diagnostic.
|
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
|
||||||
func (b *builder) createFunction() {
|
case *types.Basic:
|
||||||
|
// String type.
|
||||||
|
if typ.Kind() != types.String {
|
||||||
|
// This is checked at the AST level, so should be unreachable.
|
||||||
|
panic("expected a string type")
|
||||||
|
}
|
||||||
|
if len(files) != 1 {
|
||||||
|
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strObj := c.getEmbedFileString(files[0])
|
||||||
|
global.SetInitializer(strObj)
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
|
||||||
|
case *types.Slice:
|
||||||
|
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
||||||
|
// This is checked at the AST level, so should be unreachable.
|
||||||
|
panic("expected a byte slice")
|
||||||
|
}
|
||||||
|
if len(files) != 1 {
|
||||||
|
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := files[0]
|
||||||
|
bufferValue := c.ctx.ConstString(string(file.Data), false)
|
||||||
|
bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
|
||||||
|
bufferGlobal.SetInitializer(bufferValue)
|
||||||
|
bufferGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
bufferGlobal.SetAlignment(1)
|
||||||
|
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
})
|
||||||
|
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||||
|
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||||
|
global.SetInitializer(sliceObj)
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
|
||||||
|
case *types.Struct:
|
||||||
|
// Assume this is an embed.FS struct:
|
||||||
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
|
||||||
|
// It looks like this:
|
||||||
|
// type FS struct {
|
||||||
|
// files *file
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Make a slice of the files, as they will appear in the binary. They
|
||||||
|
// are sorted in a special way to allow for binary searches, see
|
||||||
|
// src/embed/embed.go for details.
|
||||||
|
dirset := map[string]struct{}{}
|
||||||
|
var allFiles []*loader.EmbedFile
|
||||||
|
for _, file := range files {
|
||||||
|
allFiles = append(allFiles, file)
|
||||||
|
dirname := file.Name
|
||||||
|
for {
|
||||||
|
dirname, _ = path.Split(path.Clean(dirname))
|
||||||
|
if dirname == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, ok := dirset[dirname]; ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dirset[dirname] = struct{}{}
|
||||||
|
allFiles = append(allFiles, &loader.EmbedFile{
|
||||||
|
Name: dirname,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(allFiles, func(i, j int) bool {
|
||||||
|
dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
|
||||||
|
dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
|
||||||
|
if dir1 != dir2 {
|
||||||
|
return dir1 < dir2
|
||||||
|
}
|
||||||
|
return name1 < name2
|
||||||
|
})
|
||||||
|
|
||||||
|
// Make the backing array for the []files slice. This is a LLVM global.
|
||||||
|
embedFileStructType := c.getLLVMType(typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem())
|
||||||
|
var fileStructs []llvm.Value
|
||||||
|
for _, file := range allFiles {
|
||||||
|
fileStruct := llvm.ConstNull(embedFileStructType)
|
||||||
|
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
|
||||||
|
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
|
||||||
|
if file.Hash != "" {
|
||||||
|
data := c.getEmbedFileString(file)
|
||||||
|
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
|
||||||
|
}
|
||||||
|
fileStructs = append(fileStructs, fileStruct)
|
||||||
|
}
|
||||||
|
sliceDataInitializer := llvm.ConstArray(embedFileStructType, fileStructs)
|
||||||
|
sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
|
||||||
|
sliceDataGlobal.SetInitializer(sliceDataInitializer)
|
||||||
|
sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
sliceDataGlobal.SetGlobalConstant(true)
|
||||||
|
sliceDataGlobal.SetUnnamedAddr(true)
|
||||||
|
sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
|
||||||
|
|
||||||
|
// Create the slice object itself.
|
||||||
|
// Because embed.FS refers to it as *[]embed.file instead of a plain
|
||||||
|
// []embed.file, we have to store this as a global.
|
||||||
|
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
})
|
||||||
|
sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
|
||||||
|
sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||||
|
sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
|
||||||
|
sliceGlobal.SetInitializer(sliceInitializer)
|
||||||
|
sliceGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
|
sliceGlobal.SetGlobalConstant(true)
|
||||||
|
sliceGlobal.SetUnnamedAddr(true)
|
||||||
|
sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
|
||||||
|
|
||||||
|
// Define the embed.FS struct. It has only one field: the files (as a
|
||||||
|
// *[]embed.file).
|
||||||
|
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||||
|
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
|
||||||
|
global.SetInitializer(globalInitializer)
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEmbedFileString returns the (constant) string object with the contents of
|
||||||
|
// the given file. This is a llvm.Value of a regular Go string.
|
||||||
|
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
|
||||||
|
dataGlobalName := "embed/file_" + file.Hash
|
||||||
|
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
|
||||||
|
if dataGlobal.IsNil() {
|
||||||
|
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
|
||||||
|
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
|
||||||
|
}
|
||||||
|
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
|
})
|
||||||
|
strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||||
|
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start defining a function so that it can be filled with instructions: load
|
||||||
|
// parameters, create basic blocks, and set up debug information.
|
||||||
|
// This is separated out from createFunction() so that it is also usable to
|
||||||
|
// define compiler intrinsics like the atomic operations in sync/atomic.
|
||||||
|
func (b *builder) createFunctionStart() {
|
||||||
if b.DumpSSA {
|
if b.DumpSSA {
|
||||||
fmt.Printf("\nfunc %s:\n", b.fn)
|
fmt.Printf("\nfunc %s:\n", b.fn)
|
||||||
}
|
}
|
||||||
@@ -921,7 +1096,16 @@ func (b *builder) createFunction() {
|
|||||||
b.blockEntries[block] = llvmBlock
|
b.blockEntries[block] = llvmBlock
|
||||||
b.blockExits[block] = llvmBlock
|
b.blockExits[block] = llvmBlock
|
||||||
}
|
}
|
||||||
entryBlock := b.blockEntries[b.fn.Blocks[0]]
|
var entryBlock llvm.BasicBlock
|
||||||
|
if len(b.fn.Blocks) != 0 {
|
||||||
|
// Normal functions have an entry block.
|
||||||
|
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
||||||
|
} else {
|
||||||
|
// This function isn't defined in Go SSA. It is probably a compiler
|
||||||
|
// intrinsic (like an atomic operation). Create the entry block
|
||||||
|
// manually.
|
||||||
|
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
|
||||||
|
}
|
||||||
b.SetInsertPointAtEnd(entryBlock)
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
|
|
||||||
if b.fn.Synthetic == "package initializer" {
|
if b.fn.Synthetic == "package initializer" {
|
||||||
@@ -996,6 +1180,13 @@ func (b *builder) createFunction() {
|
|||||||
// them.
|
// them.
|
||||||
b.deferInitFunc()
|
b.deferInitFunc()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
|
// function must not yet be defined, otherwise this function will create a
|
||||||
|
// diagnostic.
|
||||||
|
func (b *builder) createFunction() {
|
||||||
|
b.createFunctionStart()
|
||||||
|
|
||||||
// Fill blocks with instructions.
|
// Fill blocks with instructions.
|
||||||
for _, block := range b.fn.DomPreorder() {
|
for _, block := range b.fn.DomPreorder() {
|
||||||
@@ -1043,6 +1234,12 @@ func (b *builder) createFunction() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.hasDeferFrame() {
|
||||||
|
// Create the landing pad block, where execution continues after a
|
||||||
|
// panic.
|
||||||
|
b.createLandingPad()
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve phi nodes
|
// Resolve phi nodes
|
||||||
for _, phi := range b.phis {
|
for _, phi := range b.phis {
|
||||||
block := phi.ssa.Block()
|
block := phi.ssa.Block()
|
||||||
@@ -1170,9 +1367,12 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
|||||||
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
||||||
case *ssa.Panic:
|
case *ssa.Panic:
|
||||||
value := b.getValue(instr.X)
|
value := b.getValue(instr.X)
|
||||||
b.createRuntimeCall("_panic", []llvm.Value{value}, "")
|
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
|
||||||
b.CreateUnreachable()
|
b.CreateUnreachable()
|
||||||
case *ssa.Return:
|
case *ssa.Return:
|
||||||
|
if b.hasDeferFrame() {
|
||||||
|
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
|
||||||
|
}
|
||||||
if len(instr.Results) == 0 {
|
if len(instr.Results) == 0 {
|
||||||
b.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
} else if len(instr.Results) == 1 {
|
} else if len(instr.Results) == 1 {
|
||||||
@@ -1349,6 +1549,12 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
ptrValue := b.CreatePtrToInt(value, b.uintptrType, "")
|
ptrValue := b.CreatePtrToInt(value, b.uintptrType, "")
|
||||||
b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
|
b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
|
||||||
|
case *types.Slice:
|
||||||
|
bufptr := b.CreateExtractValue(value, 0, "")
|
||||||
|
buflen := b.CreateExtractValue(value, 1, "")
|
||||||
|
bufcap := b.CreateExtractValue(value, 2, "")
|
||||||
|
ptrValue := b.CreatePtrToInt(bufptr, b.uintptrType, "")
|
||||||
|
b.createRuntimeCall("printslice", []llvm.Value{ptrValue, buflen, bufcap}, "")
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(pos, "unknown arg type: "+typ.String())
|
return llvm.Value{}, b.makeError(pos, "unknown arg type: "+typ.String())
|
||||||
}
|
}
|
||||||
@@ -1361,7 +1567,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
cplx := argValues[0]
|
cplx := argValues[0]
|
||||||
return b.CreateExtractValue(cplx, 0, "real"), nil
|
return b.CreateExtractValue(cplx, 0, "real"), nil
|
||||||
case "recover":
|
case "recover":
|
||||||
return b.createRuntimeCall("_recover", nil, ""), nil
|
useParentFrame := uint64(0)
|
||||||
|
if b.hasDeferFrame() {
|
||||||
|
// recover() should return the panic value of the parent function,
|
||||||
|
// not of the current function.
|
||||||
|
useParentFrame = 1
|
||||||
|
}
|
||||||
|
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
|
||||||
case "ssa:wrapnilchk":
|
case "ssa:wrapnilchk":
|
||||||
// TODO: do an actual nil check?
|
// TODO: do an actual nil check?
|
||||||
return argValues[0], nil
|
return argValues[0], nil
|
||||||
@@ -1373,6 +1585,20 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
ptr := argValues[0]
|
ptr := argValues[0]
|
||||||
len := argValues[1]
|
len := argValues[1]
|
||||||
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
||||||
|
case "Alignof": // unsafe.Alignof
|
||||||
|
align := b.targetData.ABITypeAlignment(argValues[0].Type())
|
||||||
|
return llvm.ConstInt(b.uintptrType, uint64(align), false), nil
|
||||||
|
case "Offsetof": // unsafe.Offsetof
|
||||||
|
// This builtin is a bit harder to implement and may need a bit of
|
||||||
|
// refactoring to work (it may be easier to implement if we have access
|
||||||
|
// to the underlying Go SSA instruction). It is also rarely used: it
|
||||||
|
// only applies in generic code and unsafe.Offsetof isn't very commonly
|
||||||
|
// used anyway.
|
||||||
|
// In other words, postpone it to some other day.
|
||||||
|
return llvm.Value{}, b.makeError(pos, "todo: unsafe.Offsetof")
|
||||||
|
case "Sizeof": // unsafe.Sizeof
|
||||||
|
size := b.targetData.TypeAllocSize(argValues[0].Type())
|
||||||
|
return llvm.ConstInt(b.uintptrType, size, false), nil
|
||||||
case "Slice": // unsafe.Slice
|
case "Slice": // unsafe.Slice
|
||||||
// This creates a slice from a pointer and a length.
|
// This creates a slice from a pointer and a length.
|
||||||
// Note that the exception mentioned in the documentation (if the
|
// Note that the exception mentioned in the documentation (if the
|
||||||
@@ -1421,10 +1647,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
// applied) function call. If it is anonymous, it may be a closure.
|
// applied) function call. If it is anonymous, it may be a closure.
|
||||||
name := fn.RelString(nil)
|
name := fn.RelString(nil)
|
||||||
switch {
|
switch {
|
||||||
case name == "runtime.memcpy" || name == "runtime.memmove" || name == "reflect.memcpy":
|
|
||||||
return b.createMemoryCopyCall(fn, instr.Args)
|
|
||||||
case name == "runtime.memzero":
|
|
||||||
return b.createMemoryZeroCall(instr.Args)
|
|
||||||
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
||||||
result, ok := b.createMathOp(instr)
|
result, ok := b.createMathOp(instr)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -1444,18 +1666,12 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return b.createSyscall(instr)
|
return b.createSyscall(instr)
|
||||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
||||||
return b.createRawSyscallNoError(instr)
|
return b.createRawSyscallNoError(instr)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
case name == "runtime.supportsRecover":
|
||||||
return b.createVolatileLoad(instr)
|
supportsRecover := uint64(0)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
if b.supportsRecover() {
|
||||||
return b.createVolatileStore(instr)
|
supportsRecover = 1
|
||||||
case strings.HasPrefix(name, "sync/atomic."):
|
|
||||||
val, ok := b.createAtomicOp(instr)
|
|
||||||
if ok {
|
|
||||||
// This call could be lowered as an atomic operation.
|
|
||||||
return val, nil
|
|
||||||
}
|
}
|
||||||
// This call couldn't be lowered as an atomic operation, it's
|
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
|
||||||
// probably something else. Continue as usual.
|
|
||||||
case name == "runtime/interrupt.New":
|
case name == "runtime/interrupt.New":
|
||||||
return b.createInterruptGlobal(instr)
|
return b.createInterruptGlobal(instr)
|
||||||
}
|
}
|
||||||
@@ -1518,7 +1734,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
params = append(params, context)
|
params = append(params, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.createCall(callee, params, ""), nil
|
return b.createInvoke(callee, params, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getValue returns the LLVM value of a constant, function value, global, or
|
// getValue returns the LLVM value of a constant, function value, global, or
|
||||||
@@ -2469,42 +2685,41 @@ 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(expr *ssa.Const) llvm.Value {
|
func (c *compilerContext) 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 := c.getLLVMType(typ)
|
||||||
if typ.Info()&types.IsBoolean != 0 {
|
if typ.Info()&types.IsBoolean != 0 {
|
||||||
b := constant.BoolVal(expr.Value)
|
|
||||||
n := uint64(0)
|
n := uint64(0)
|
||||||
if b {
|
if constant.BoolVal(expr.Value) {
|
||||||
n = 1
|
n = 1
|
||||||
}
|
}
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
} else if typ.Info()&types.IsString != 0 {
|
} else if typ.Info()&types.IsString != 0 {
|
||||||
str := constant.StringVal(expr.Value)
|
str := constant.StringVal(expr.Value)
|
||||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
|
||||||
var strPtr llvm.Value
|
var strPtr llvm.Value
|
||||||
if str != "" {
|
if str != "" {
|
||||||
objname := b.pkg.Path() + "$string"
|
objname := c.pkg.Path() + "$string"
|
||||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
||||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
global.SetInitializer(c.ctx.ConstString(str, false))
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
global.SetAlignment(1)
|
global.SetAlignment(1)
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
||||||
} else {
|
} else {
|
||||||
strPtr = llvm.ConstNull(b.i8ptrType)
|
strPtr = llvm.ConstNull(c.i8ptrType)
|
||||||
}
|
}
|
||||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
return strObj
|
return strObj
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
if !expr.IsNil() {
|
if !expr.IsNil() {
|
||||||
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(b.i8ptrType)
|
return llvm.ConstNull(c.i8ptrType)
|
||||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||||
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
@@ -2515,18 +2730,18 @@ func (b *builder) createConst(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(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||||
return cplx
|
return cplx
|
||||||
} else if typ.Kind() == types.Complex128 {
|
} else if typ.Kind() == types.Complex128 {
|
||||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||||
return cplx
|
return cplx
|
||||||
} else {
|
} else {
|
||||||
panic("unknown constant of basic type: " + expr.String())
|
panic("unknown constant of basic type: " + expr.String())
|
||||||
@@ -2535,35 +2750,35 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil chan constant")
|
panic("expected nil chan constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil signature constant")
|
panic("expected nil signature constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil interface constant")
|
panic("expected nil interface constant")
|
||||||
}
|
}
|
||||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||||
fields := []llvm.Value{
|
fields := []llvm.Value{
|
||||||
llvm.ConstInt(b.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstPointerNull(b.i8ptrType),
|
llvm.ConstPointerNull(c.i8ptrType),
|
||||||
}
|
}
|
||||||
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
|
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil pointer constant")
|
panic("expected nil pointer constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstPointerNull(b.getLLVMType(typ))
|
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil slice constant")
|
panic("expected nil slice constant")
|
||||||
}
|
}
|
||||||
elemType := b.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
||||||
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
|
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
||||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvmPtr, // backing array
|
llvmPtr, // backing array
|
||||||
llvmLen, // len
|
llvmLen, // len
|
||||||
llvmLen, // cap
|
llvmLen, // cap
|
||||||
@@ -2574,7 +2789,7 @@ func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
|||||||
// I believe this is not allowed by the Go spec.
|
// I believe this is not allowed by the Go spec.
|
||||||
panic("non-nil map constant")
|
panic("non-nil map constant")
|
||||||
}
|
}
|
||||||
llvmType := b.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
return llvm.ConstNull(llvmType)
|
return llvm.ConstNull(llvmType)
|
||||||
default:
|
default:
|
||||||
panic("unknown constant: " + expr.String())
|
panic("unknown constant: " + expr.String())
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||||
|
// support.
|
||||||
|
|
||||||
|
import "go/types"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||||
|
if t, ok := t.(*types.TypeParam); ok {
|
||||||
|
return t.Underlying()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,18 +28,11 @@ type testCase struct {
|
|||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Check LLVM version.
|
// Determine LLVM version.
|
||||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||||
}
|
}
|
||||||
if llvmMajor < 11 {
|
|
||||||
// It is likely this version needs to be bumped in the future.
|
|
||||||
// The goal is to at least test the LLVM version that's used by default
|
|
||||||
// in TinyGo and (if possible without too many workarounds) also some
|
|
||||||
// previous versions.
|
|
||||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
@@ -56,6 +49,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"float.go", "", ""},
|
{"float.go", "", ""},
|
||||||
{"interface.go", "", ""},
|
{"interface.go", "", ""},
|
||||||
{"func.go", "", ""},
|
{"func.go", "", ""},
|
||||||
|
{"defer.go", "cortex-m-qemu", ""},
|
||||||
{"pragma.go", "", ""},
|
{"pragma.go", "", ""},
|
||||||
{"goroutine.go", "wasm", "asyncify"},
|
{"goroutine.go", "wasm", "asyncify"},
|
||||||
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||||
@@ -71,6 +65,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
if goMinor >= 17 {
|
if goMinor >= 17 {
|
||||||
tests = append(tests, testCase{"go1.17.go", "", ""})
|
tests = append(tests, testCase{"go1.17.go", "", ""})
|
||||||
}
|
}
|
||||||
|
if goMinor >= 18 {
|
||||||
|
tests = append(tests, testCase{"generics.go", "", ""})
|
||||||
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
name := tc.file
|
name := tc.file
|
||||||
@@ -100,6 +97,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
compilerConfig := &Config{
|
compilerConfig := &Config{
|
||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
|
Features: config.Features(),
|
||||||
GOOS: config.GOOS(),
|
GOOS: config.GOOS(),
|
||||||
GOARCH: config.GOARCH(),
|
GOARCH: config.GOARCH(),
|
||||||
CodeModel: config.CodeModel(),
|
CodeModel: config.CodeModel(),
|
||||||
@@ -113,9 +111,10 @@ func TestCompiler(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to create target machine:", err)
|
t.Fatal("failed to create target machine:", err)
|
||||||
}
|
}
|
||||||
|
defer machine.Dispose()
|
||||||
|
|
||||||
// Load entire program AST into memory.
|
// Load entire program AST into memory.
|
||||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{
|
||||||
Sizes: Sizes(machine),
|
Sizes: Sizes(machine),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -221,11 +220,6 @@ func filterIrrelevantIRLines(lines []string) []string {
|
|||||||
if strings.HasPrefix(line, "source_filename = ") {
|
if strings.HasPrefix(line, "source_filename = ") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
|
|
||||||
// Ignore attribute groups. These may change between LLVM versions.
|
|
||||||
// Right now test outputs are for LLVM 12 and higher.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
||||||
// The datalayout string may vary betewen LLVM versions.
|
// The datalayout string may vary betewen LLVM versions.
|
||||||
// Right now test outputs are for LLVM 14 and higher.
|
// Right now test outputs are for LLVM 14 and higher.
|
||||||
|
|||||||
+201
-28
@@ -15,12 +15,39 @@ package compiler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// supportsRecover returns whether the compiler supports the recover() builtin
|
||||||
|
// for the current architecture.
|
||||||
|
func (b *builder) supportsRecover() bool {
|
||||||
|
switch b.archFamily() {
|
||||||
|
case "wasm32":
|
||||||
|
// Probably needs to be implemented using the exception handling
|
||||||
|
// proposal of WebAssembly:
|
||||||
|
// https://github.com/WebAssembly/exception-handling
|
||||||
|
return false
|
||||||
|
case "riscv64", "xtensa":
|
||||||
|
// TODO: add support for these architectures
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasDeferFrame returns whether the current function needs to catch panics and
|
||||||
|
// run defers.
|
||||||
|
func (b *builder) hasDeferFrame() bool {
|
||||||
|
if b.fn.Recover == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return b.supportsRecover()
|
||||||
|
}
|
||||||
|
|
||||||
// deferInitFunc sets up this function for future deferred calls. It must be
|
// deferInitFunc sets up this function for future deferred calls. It must be
|
||||||
// called from within the entry block when this function contains deferred
|
// called from within the entry block when this function contains deferred
|
||||||
// calls.
|
// calls.
|
||||||
@@ -36,6 +63,151 @@ func (b *builder) deferInitFunc() {
|
|||||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||||
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
|
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
|
||||||
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
|
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
|
||||||
|
|
||||||
|
if b.hasDeferFrame() {
|
||||||
|
// Set up the defer frame with the current stack pointer.
|
||||||
|
// This assumes that the stack pointer doesn't move outside of the
|
||||||
|
// function prologue/epilogue (an invariant maintained by TinyGo but
|
||||||
|
// possibly broken by the C alloca function).
|
||||||
|
// The frame pointer is _not_ saved, because it is marked as clobbered
|
||||||
|
// in the setjmp-like inline assembly.
|
||||||
|
deferFrameType := b.getLLVMRuntimeType("deferFrame")
|
||||||
|
b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
|
||||||
|
stackPointer := b.readStackPointer()
|
||||||
|
b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
|
||||||
|
|
||||||
|
// Create the landing pad block, which is where control transfers after
|
||||||
|
// a panic.
|
||||||
|
b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createLandingPad fills in the landing pad block. This block runs the deferred
|
||||||
|
// functions and returns (by jumping to the recover block). If the function is
|
||||||
|
// still panicking after the defers are run, the panic will be re-raised in
|
||||||
|
// destroyDeferFrame.
|
||||||
|
func (b *builder) createLandingPad() {
|
||||||
|
b.SetInsertPointAtEnd(b.landingpad)
|
||||||
|
|
||||||
|
// Add debug info, if needed.
|
||||||
|
// The location used is the closing bracket of the function.
|
||||||
|
if b.Debug {
|
||||||
|
pos := b.program.Fset.Position(b.fn.Syntax().End())
|
||||||
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||||
|
}
|
||||||
|
|
||||||
|
b.createRunDefers()
|
||||||
|
|
||||||
|
// Continue at the 'recover' block, which returns to the parent in an
|
||||||
|
// appropriate way.
|
||||||
|
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||||
|
}
|
||||||
|
|
||||||
|
// createInvokeCheckpoint saves the function state at the given point, to
|
||||||
|
// continue at the landing pad if a panic happened. This is implemented using a
|
||||||
|
// setjmp-like construct.
|
||||||
|
func (b *builder) createInvokeCheckpoint() {
|
||||||
|
// Construct inline assembly equivalents of setjmp.
|
||||||
|
// The assembly works as follows:
|
||||||
|
// * All registers (both callee-saved and caller saved) are clobbered
|
||||||
|
// after the inline assembly returns.
|
||||||
|
// * The assembly stores the address just past the end of the assembly
|
||||||
|
// into the jump buffer.
|
||||||
|
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||||
|
// assembly but set to an unspecified non-zero value when jumping using
|
||||||
|
// a longjmp.
|
||||||
|
var asmString, constraints string
|
||||||
|
resultType := b.uintptrType
|
||||||
|
switch b.archFamily() {
|
||||||
|
case "i386":
|
||||||
|
asmString = `
|
||||||
|
xorl %eax, %eax
|
||||||
|
movl $$1f, 4(%ebx)
|
||||||
|
1:`
|
||||||
|
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||||
|
// This doesn't include the floating point stack because TinyGo uses
|
||||||
|
// newer floating point instructions.
|
||||||
|
case "x86_64":
|
||||||
|
asmString = `
|
||||||
|
leaq 1f(%rip), %rax
|
||||||
|
movq %rax, 8(%rbx)
|
||||||
|
xorq %rax, %rax
|
||||||
|
1:`
|
||||||
|
constraints = "={rax},{rbx},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||||
|
// This list doesn't include AVX/AVX512 registers because TinyGo
|
||||||
|
// doesn't currently enable support for AVX instructions.
|
||||||
|
case "arm":
|
||||||
|
// Note: the following assembly takes into account that the PC is
|
||||||
|
// always 4 bytes ahead on ARM. The PC that is stored always points
|
||||||
|
// to the instruction just after the assembly fragment so that
|
||||||
|
// tinygo_longjmp lands at the correct instruction.
|
||||||
|
if b.isThumb() {
|
||||||
|
// Instructions are 2 bytes in size.
|
||||||
|
asmString = `
|
||||||
|
movs r0, #0
|
||||||
|
mov r2, pc
|
||||||
|
str r2, [r1, #4]`
|
||||||
|
} else {
|
||||||
|
// Instructions are 4 bytes in size.
|
||||||
|
asmString = `
|
||||||
|
str pc, [r1, #4]
|
||||||
|
movs r0, #0`
|
||||||
|
}
|
||||||
|
constraints = "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"
|
||||||
|
case "aarch64":
|
||||||
|
asmString = `
|
||||||
|
adr x2, 1f
|
||||||
|
str x2, [x1, #8]
|
||||||
|
mov x0, #0
|
||||||
|
1:
|
||||||
|
`
|
||||||
|
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
||||||
|
if b.GOOS != "darwin" {
|
||||||
|
// These registers cause the following warning when compiling for
|
||||||
|
// MacOS:
|
||||||
|
// warning: inline asm clobber list contains reserved registers:
|
||||||
|
// X18, FP
|
||||||
|
// Reserved registers on the clobber list may not be preserved
|
||||||
|
// across the asm statement, and clobbering them may lead to
|
||||||
|
// undefined behaviour.
|
||||||
|
constraints += ",~{x18},~{fp}"
|
||||||
|
}
|
||||||
|
// TODO: SVE registers, which we don't use in TinyGo at the moment.
|
||||||
|
case "avr":
|
||||||
|
// Note: the Y register (R28:R29) is a fixed register and therefore
|
||||||
|
// needs to be saved manually. TODO: do this only once per function with
|
||||||
|
// a defer frame, not for every call.
|
||||||
|
resultType = b.ctx.Int8Type()
|
||||||
|
asmString = `
|
||||||
|
ldi r24, pm_lo8(1f)
|
||||||
|
ldi r25, pm_hi8(1f)
|
||||||
|
std z+2, r24
|
||||||
|
std z+3, r25
|
||||||
|
std z+4, r28
|
||||||
|
std z+5, r29
|
||||||
|
ldi r24, 0
|
||||||
|
1:`
|
||||||
|
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
||||||
|
case "riscv32":
|
||||||
|
asmString = `
|
||||||
|
la a2, 1f
|
||||||
|
sw a2, 4(a1)
|
||||||
|
li a0, 0
|
||||||
|
1:`
|
||||||
|
constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
|
||||||
|
default:
|
||||||
|
// This case should have been handled by b.supportsRecover().
|
||||||
|
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
||||||
|
}
|
||||||
|
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||||
|
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||||
|
result := b.CreateCall(asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||||
|
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||||
|
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||||
|
continueBB := b.insertBasicBlock("")
|
||||||
|
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||||
|
b.SetInsertPointAtEnd(continueBB)
|
||||||
|
b.blockExits[b.currentBlock] = continueBB
|
||||||
}
|
}
|
||||||
|
|
||||||
// isInLoop checks if there is a path from a basic block to itself.
|
// isInLoop checks if there is a path from a basic block to itself.
|
||||||
@@ -201,30 +373,31 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a struct out of the collected values to put in the defer frame.
|
// Make a struct out of the collected values to put in the deferred call
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
// struct.
|
||||||
deferFrame := llvm.ConstNull(deferFrameType)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
|
deferredCall := llvm.ConstNull(deferredCallType)
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
deferFrame = b.CreateInsertValue(deferFrame, value, i, "")
|
deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put this struct in an allocation.
|
// Put this struct in an allocation.
|
||||||
var alloca llvm.Value
|
var alloca llvm.Value
|
||||||
if !isInLoop(instr.Block()) {
|
if !isInLoop(instr.Block()) {
|
||||||
// This can safely use a stack allocation.
|
// This can safely use a stack allocation.
|
||||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca")
|
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||||
} else {
|
} else {
|
||||||
// This may be hit a variable number of times, so use a heap allocation.
|
// This may be hit a variable number of times, so use a heap allocation.
|
||||||
size := b.targetData.TypeAllocSize(deferFrameType)
|
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
nilPtr := llvm.ConstNull(b.i8ptrType)
|
nilPtr := llvm.ConstNull(b.i8ptrType)
|
||||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
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(deferredCallType, 0), "defer.alloc")
|
||||||
}
|
}
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(alloca)
|
b.trackPointer(alloca)
|
||||||
}
|
}
|
||||||
b.CreateStore(deferFrame, alloca)
|
b.CreateStore(deferredCall, alloca)
|
||||||
|
|
||||||
// Push it on top of the linked list by replacing deferPtr.
|
// Push it on top of the linked list by replacing deferPtr.
|
||||||
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||||
@@ -248,11 +421,11 @@ func (b *builder) createRunDefers() {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Create loop.
|
// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end.
|
||||||
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
|
end := b.insertBasicBlock("rundefers.end")
|
||||||
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
|
unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default")
|
||||||
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
|
loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop")
|
||||||
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
|
loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead")
|
||||||
b.CreateBr(loophead)
|
b.CreateBr(loophead)
|
||||||
|
|
||||||
// Create loop head:
|
// Create loop head:
|
||||||
@@ -284,7 +457,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// Create switch case, for example:
|
// Create switch case, for example:
|
||||||
// case 0:
|
// case 0:
|
||||||
// // run first deferred call
|
// // run first deferred call
|
||||||
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
|
block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i))
|
||||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
|
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
|
||||||
b.SetInsertPointAtEnd(block)
|
b.SetInsertPointAtEnd(block)
|
||||||
switch callback := callback.(type) {
|
switch callback := callback.(type) {
|
||||||
@@ -295,7 +468,7 @@ func (b *builder) createRunDefers() {
|
|||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||||
|
|
||||||
if !callback.IsInvoke() {
|
if !callback.IsInvoke() {
|
||||||
//Expect funcValue to be passed through the defer frame.
|
//Expect funcValue to be passed through the deferred call.
|
||||||
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||||
} else {
|
} else {
|
||||||
//Expect typecode
|
//Expect typecode
|
||||||
@@ -306,14 +479,14 @@ func (b *builder) createRunDefers() {
|
|||||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||||
|
|
||||||
// Extract the params from the struct (including receiver).
|
// Extract the params from the struct (including receiver).
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -353,14 +526,14 @@ func (b *builder) createRunDefers() {
|
|||||||
for _, param := range getParams(callback.Signature) {
|
for _, param := range getParams(callback.Signature) {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||||
}
|
}
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := range getParams(callback.Signature) {
|
for i := range getParams(callback.Signature) {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -374,7 +547,7 @@ func (b *builder) createRunDefers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call real function.
|
// Call real function.
|
||||||
b.createCall(b.getFunction(callback), forwardParams, "")
|
b.createInvoke(b.getFunction(callback), forwardParams, "")
|
||||||
|
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
@@ -385,14 +558,14 @@ func (b *builder) createRunDefers() {
|
|||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||||
}
|
}
|
||||||
valueTypes = append(valueTypes, b.i8ptrType) // closure
|
valueTypes = append(valueTypes, b.i8ptrType) // closure
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -411,14 +584,14 @@ func (b *builder) createRunDefers() {
|
|||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
var argValues []llvm.Value
|
var argValues []llvm.Value
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(gep, "param")
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
argValues = append(argValues, forwardParam)
|
argValues = append(argValues, forwardParam)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -389,8 +389,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
// value.
|
// value.
|
||||||
|
|
||||||
prevBlock := b.GetInsertBlock()
|
prevBlock := b.GetInsertBlock()
|
||||||
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
|
okBlock := b.insertBasicBlock("typeassert.ok")
|
||||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next")
|
nextBlock := b.insertBasicBlock("typeassert.next")
|
||||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||||
|
|
||||||
|
|||||||
+42
-10
@@ -3,36 +3,68 @@ package compiler
|
|||||||
// This file contains helper functions to create calls to LLVM intrinsics.
|
// This file contains helper functions to create calls to LLVM intrinsics.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/token"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove
|
// Define unimplemented intrinsic functions.
|
||||||
|
//
|
||||||
|
// Some functions are either normally implemented in Go assembly (like
|
||||||
|
// sync/atomic functions) or intentionally left undefined to be implemented
|
||||||
|
// directly in the compiler (like runtime/volatile functions). Either way, look
|
||||||
|
// for these and implement them if this is the case.
|
||||||
|
func (b *builder) defineIntrinsicFunction() {
|
||||||
|
name := b.fn.RelString(nil)
|
||||||
|
switch {
|
||||||
|
case name == "runtime.memcpy" || name == "runtime.memmove":
|
||||||
|
b.createMemoryCopyImpl()
|
||||||
|
case name == "runtime.memzero":
|
||||||
|
b.createMemoryZeroImpl()
|
||||||
|
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||||
|
b.createVolatileLoad()
|
||||||
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
|
b.createVolatileStore()
|
||||||
|
case strings.HasPrefix(name, "sync/atomic.") && token.IsExported(b.fn.Name()):
|
||||||
|
b.createFunctionStart()
|
||||||
|
returnValue := b.createAtomicOp(b.fn.Name())
|
||||||
|
if !returnValue.IsNil() {
|
||||||
|
b.CreateRet(returnValue)
|
||||||
|
} else {
|
||||||
|
b.CreateRetVoid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
|
||||||
// function, declaring this function if needed. These calls are treated
|
// function, declaring this function if needed. These calls are treated
|
||||||
// specially by optimization passes possibly resulting in better generated code,
|
// specially by optimization passes possibly resulting in better generated code,
|
||||||
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
// and will otherwise be lowered to regular libc memcpy/memmove calls.
|
||||||
func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) {
|
func (b *builder) createMemoryCopyImpl() {
|
||||||
fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
b.createFunctionStart()
|
||||||
|
fnName := "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
for _, param := range args {
|
for _, param := range b.fn.Params {
|
||||||
params = append(params, b.getValue(param))
|
params = append(params, b.getValue(param))
|
||||||
}
|
}
|
||||||
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
|
||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFn, params, "")
|
||||||
return llvm.Value{}, nil
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of
|
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
|
||||||
// memory, declaring the function if needed. These calls will be lowered to
|
// memory, declaring the function if needed. These calls will be lowered to
|
||||||
// regular libc memset calls if they aren't optimized out in a different way.
|
// regular libc memset calls if they aren't optimized out in a different way.
|
||||||
func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
func (b *builder) createMemoryZeroImpl() {
|
||||||
|
b.createFunctionStart()
|
||||||
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
@@ -40,13 +72,13 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
|||||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
params := []llvm.Value{
|
params := []llvm.Value{
|
||||||
b.getValue(args[0]),
|
b.getValue(b.fn.Params[0]),
|
||||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||||
b.getValue(args[1]),
|
b.getValue(b.fn.Params[1]),
|
||||||
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
|
||||||
}
|
}
|
||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFn, params, "")
|
||||||
return llvm.Value{}, nil
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
var mathToLLVMMapping = map[string]string{
|
var mathToLLVMMapping = map[string]string{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -23,6 +24,23 @@ func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitca
|
|||||||
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insertBasicBlock inserts a new basic block after the current basic block.
|
||||||
|
// This is useful when inserting new basic blocks while converting a
|
||||||
|
// *ssa.BasicBlock to a llvm.BasicBlock and the LLVM basic block needs some
|
||||||
|
// extra blocks.
|
||||||
|
// It does not update b.blockExits, this must be done by the caller.
|
||||||
|
func (b *builder) insertBasicBlock(name string) llvm.BasicBlock {
|
||||||
|
currentBB := b.Builder.GetInsertBlock()
|
||||||
|
nextBB := llvm.NextBasicBlock(currentBB)
|
||||||
|
if nextBB.IsNil() {
|
||||||
|
// Last basic block in the function, so add one to the end.
|
||||||
|
return b.ctx.AddBasicBlock(b.llvmFn, name)
|
||||||
|
}
|
||||||
|
// Insert a basic block before the next basic block - that is, at the
|
||||||
|
// current insert location.
|
||||||
|
return b.ctx.InsertBasicBlock(nextBB, name)
|
||||||
|
}
|
||||||
|
|
||||||
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||||
// createTemporaryAlloca.
|
// createTemporaryAlloca.
|
||||||
@@ -253,3 +271,46 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
|
|||||||
panic("unknown LLVM type")
|
panic("unknown LLVM type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// archFamily returns the archtecture from the LLVM triple but with some
|
||||||
|
// architecture names ("armv6", "thumbv7m", etc) merged into a single
|
||||||
|
// architecture name ("arm").
|
||||||
|
func (c *compilerContext) archFamily() string {
|
||||||
|
arch := strings.Split(c.Triple, "-")[0]
|
||||||
|
if strings.HasPrefix(arch, "arm64") {
|
||||||
|
return "aarch64"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||||
|
return "arm"
|
||||||
|
}
|
||||||
|
return arch
|
||||||
|
}
|
||||||
|
|
||||||
|
// isThumb returns whether we're in ARM or in Thumb mode. It panics if the
|
||||||
|
// features string is not one for an ARM architecture.
|
||||||
|
func (c *compilerContext) isThumb() bool {
|
||||||
|
var isThumb, isNotThumb bool
|
||||||
|
for _, feature := range strings.Split(c.Features, ",") {
|
||||||
|
if feature == "+thumb-mode" {
|
||||||
|
isThumb = true
|
||||||
|
}
|
||||||
|
if feature == "-thumb-mode" {
|
||||||
|
isNotThumb = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isThumb == isNotThumb {
|
||||||
|
panic("unexpected feature flags")
|
||||||
|
}
|
||||||
|
return isThumb
|
||||||
|
}
|
||||||
|
|
||||||
|
// readStackPointer emits a LLVM intrinsic call that returns the current stack
|
||||||
|
// pointer as an *i8.
|
||||||
|
func (b *builder) readStackPointer() llvm.Value {
|
||||||
|
stacksave := b.mod.NamedFunction("llvm.stacksave")
|
||||||
|
if stacksave.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
|
||||||
|
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
||||||
|
}
|
||||||
|
return b.CreateCall(stacksave, nil, "")
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
|||||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||||
@@ -46,6 +47,7 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
|||||||
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
|
|
||||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ import (
|
|||||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, 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())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||||
|
|
||||||
valueTypes := make([]llvm.Type, len(values))
|
valueTypes := make([]llvm.Type, len(values))
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
@@ -127,8 +128,9 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs
|
|||||||
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||||
|
|
||||||
packedType := ctx.StructType(valueTypes, false)
|
packedType := ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
|||||||
if t.Info()&types.IsString != 0 {
|
if t.Info()&types.IsString != 0 {
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
case *types.Signature:
|
||||||
|
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
|
||||||
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
|
||||||
a := s.Sizeof(T) // may be 0
|
a := s.Sizeof(T) // may be 0
|
||||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||||
if a < 1 {
|
if a < 1 {
|
||||||
|
|||||||
+1
-1
@@ -191,7 +191,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
|||||||
// should be created right away.
|
// should be created right away.
|
||||||
// The exception is the package initializer, which does appear in the
|
// The exception is the package initializer, which does appear in the
|
||||||
// *ssa.Package members and so shouldn't be created here.
|
// *ssa.Package members and so shouldn't be created here.
|
||||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
|
||||||
irbuilder := c.ctx.NewBuilder()
|
irbuilder := c.ctx.NewBuilder()
|
||||||
b := newBuilder(c, irbuilder, fn)
|
b := newBuilder(c, irbuilder, fn)
|
||||||
b.createFunction()
|
b.createFunction()
|
||||||
|
|||||||
+1
-34
@@ -16,20 +16,7 @@ import (
|
|||||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
num := b.getValue(call.Args[0])
|
num := b.getValue(call.Args[0])
|
||||||
switch {
|
switch {
|
||||||
case b.GOARCH == "amd64":
|
case b.GOARCH == "amd64" && b.GOOS == "linux":
|
||||||
if b.GOOS == "darwin" {
|
|
||||||
// Darwin adds this magic number to system call numbers:
|
|
||||||
//
|
|
||||||
// > Syscall classes for 64-bit system call entry.
|
|
||||||
// > For 64-bit users, the 32-bit syscall number is partitioned
|
|
||||||
// > with the high-order bits representing the class and low-order
|
|
||||||
// > bits being the syscall number within that class.
|
|
||||||
// > The high-order 32-bits of the 64-bit syscall number are unused.
|
|
||||||
// > All system classes enter the kernel via the syscall instruction.
|
|
||||||
//
|
|
||||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
|
||||||
num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "")
|
|
||||||
}
|
|
||||||
// Sources:
|
// Sources:
|
||||||
// https://stackoverflow.com/a/2538212
|
// https://stackoverflow.com/a/2538212
|
||||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||||
@@ -179,26 +166,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
case "darwin":
|
|
||||||
syscallResult, err := b.createRawSyscall(call)
|
|
||||||
if err != nil {
|
|
||||||
return syscallResult, err
|
|
||||||
}
|
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
|
||||||
// Pseudocode:
|
|
||||||
// var err uintptr
|
|
||||||
// if syscallResult != 0 {
|
|
||||||
// err = syscallResult
|
|
||||||
// }
|
|
||||||
// return syscallResult, 0, err
|
|
||||||
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
|
||||||
hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
|
|
||||||
errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
|
||||||
retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
|
||||||
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
|
||||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
|
||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
|
||||||
return retval, nil
|
|
||||||
case "windows":
|
case "windows":
|
||||||
// On Windows, syscall.Syscall* is basically just a function pointer
|
// On Windows, syscall.Syscall* is basically just a function pointer
|
||||||
// call. This is complicated in gc because of stack switching and the
|
// call. This is complicated in gc because of stack switching and the
|
||||||
|
|||||||
Vendored
+42
-40
@@ -6,40 +6,36 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
%main.kv = type { float }
|
%main.kv = type { float }
|
||||||
%main.kv.0 = type { i8 }
|
%main.kv.0 = type { i8 }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = add i32 %x, %y
|
%0 = add i32 %x, %y
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %x, %y
|
%0 = icmp eq i32 %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
divbyzero.next: ; preds = %entry
|
divbyzero.next: ; preds = %entry
|
||||||
%1 = icmp eq i32 %y, -1
|
%1 = icmp eq i32 %y, -1
|
||||||
%2 = icmp eq i32 %x, -2147483648
|
%2 = icmp eq i32 %x, -2147483648
|
||||||
@@ -47,35 +43,35 @@ divbyzero.next: ; preds = %entry
|
|||||||
%4 = select i1 %3, i32 1, i32 %y
|
%4 = select i1 %3, i32 1, i32 %y
|
||||||
%5 = sdiv i32 %x, %4
|
%5 = sdiv i32 %x, %4
|
||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.divideByZeroPanic(i8*)
|
declare void @runtime.divideByZeroPanic(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
divbyzero.next: ; preds = %entry
|
divbyzero.next: ; preds = %entry
|
||||||
%1 = udiv i32 %x, %y
|
%1 = udiv i32 %x, %y
|
||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
divbyzero.next: ; preds = %entry
|
divbyzero.next: ; preds = %entry
|
||||||
%1 = icmp eq i32 %y, -1
|
%1 = icmp eq i32 %y, -1
|
||||||
%2 = icmp eq i32 %x, -2147483648
|
%2 = icmp eq i32 %x, -2147483648
|
||||||
@@ -83,79 +79,83 @@ divbyzero.next: ; preds = %entry
|
|||||||
%4 = select i1 %3, i32 1, i32 %y
|
%4 = select i1 %3, i32 1, i32 %y
|
||||||
%5 = srem i32 %x, %4
|
%5 = srem i32 %x, %4
|
||||||
ret i32 %5
|
ret i32 %5
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i32 %y, 0
|
%0 = icmp eq i32 %y, 0
|
||||||
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
br i1 %0, label %divbyzero.throw, label %divbyzero.next
|
||||||
|
|
||||||
divbyzero.throw: ; preds = %entry
|
|
||||||
call void @runtime.divideByZeroPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
divbyzero.next: ; preds = %entry
|
divbyzero.next: ; preds = %entry
|
||||||
%1 = urem i32 %x, %y
|
%1 = urem i32 %x, %y
|
||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
|
divbyzero.throw: ; preds = %entry
|
||||||
|
call void @runtime.divideByZeroPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oeq float %x, %y
|
%0 = fcmp oeq float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatNE(float %x, float %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.floatNE(float %x, float %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp une float %x, %y
|
%0 = fcmp une float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatLower(float %x, float %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.floatLower(float %x, float %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp olt float %x, %y
|
%0 = fcmp olt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ole float %x, %y
|
%0 = fcmp ole float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp ogt float %x, %y
|
%0 = fcmp ogt float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fcmp oge float %x, %y
|
%0 = fcmp oge float %x, %y
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #0 {
|
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.r
|
ret float %x.r
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context) unnamed_addr #0 {
|
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float %x.i
|
ret float %x.i
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
|
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fadd float %x.r, %y.r
|
%0 = fadd float %x.r, %y.r
|
||||||
%1 = fadd float %x.i, %y.i
|
%1 = fadd float %x.i, %y.i
|
||||||
@@ -165,7 +165,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
|
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fsub float %x.r, %y.r
|
%0 = fsub float %x.r, %y.r
|
||||||
%1 = fsub float %x.i, %y.i
|
%1 = fsub float %x.i, %y.i
|
||||||
@@ -175,7 +175,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = fmul float %x.r, %y.r
|
%0 = fmul float %x.r, %y.r
|
||||||
%1 = fmul float %x.i, %y.i
|
%1 = fmul float %x.i, %y.i
|
||||||
@@ -189,16 +189,18 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #0 {
|
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"main.foo$1"(%main.kv.0* null, i8* undef)
|
call void @"main.foo$1"(%main.kv.0* null, i8* undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #0 {
|
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
Vendored
+24
-22
@@ -5,24 +5,24 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
%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 } }
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
||||||
%"internal/task.gcData" = type { i8* }
|
%"internal/task.gcData" = type { i8* }
|
||||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||||
%"internal/task.stackState" = type { i32, i32 }
|
%"internal/task.stackState" = type { i32, i32 }
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
@@ -31,22 +31,22 @@ entry:
|
|||||||
store i32 3, i32* %chan.value, align 4
|
store i32 3, i32* %chan.value, align 4
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
%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 @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) #0
|
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
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)
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
||||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
|
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2
|
||||||
|
|
||||||
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*)
|
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
|
||||||
|
|
||||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
||||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
|
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
@@ -54,41 +54,41 @@ entry:
|
|||||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
%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 @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) #0
|
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
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)
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*)
|
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca {}, align 8
|
%complit = alloca {}, align 8
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%0 = bitcast {}* %complit to i8*
|
%0 = bitcast {}* %complit to i8*
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #3
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
%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 @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) #0
|
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
%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 @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) #0
|
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #3
|
||||||
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context) unnamed_addr #0 {
|
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||||
%select.send.value = alloca i32, align 4
|
%select.send.value = alloca i32, align 4
|
||||||
@@ -105,7 +105,7 @@ entry:
|
|||||||
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
%.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
|
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.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) #0
|
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef) #3
|
||||||
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||||
%2 = icmp eq i32 %1, 0
|
%2 = icmp eq i32 %1, 0
|
||||||
@@ -122,7 +122,9 @@ select.body: ; preds = %select.next
|
|||||||
br label %select.done
|
br label %select.done
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*)
|
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*) #0
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { argmemonly nofree nosync nounwind willreturn }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { argmemonly nofree nosync nounwind willreturn }
|
||||||
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
+266
@@ -0,0 +1,266 @@
|
|||||||
|
; ModuleID = 'defer.go'
|
||||||
|
source_filename = "defer.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"
|
||||||
|
|
||||||
|
%runtime._defer = type { i32, %runtime._defer* }
|
||||||
|
%runtime.deferFrame = type { i8*, i8*, [0 x i8*], %runtime.deferFrame*, i1, %runtime._interface }
|
||||||
|
%runtime._interface = type { i32, i8* }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @main.external(i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.deferSimple(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%defer.alloca = alloca { i32, %runtime._defer* }, align 4
|
||||||
|
%deferPtr = alloca %runtime._defer*, align 4
|
||||||
|
store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
|
||||||
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%0 = call i8* @llvm.stacksave()
|
||||||
|
call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
|
||||||
|
%defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
|
||||||
|
store i32 0, i32* %defer.alloca.repack, align 4
|
||||||
|
%defer.alloca.repack16 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
|
||||||
|
store %runtime._defer* null, %runtime._defer** %defer.alloca.repack16, align 4
|
||||||
|
%1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
||||||
|
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, align 4
|
||||||
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||||
|
br i1 %setjmp.result, label %2, label %lpad
|
||||||
|
|
||||||
|
2: ; preds = %entry
|
||||||
|
call void @main.external(i8* undef) #3
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.loophead: ; preds = %4, %2
|
||||||
|
%3 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||||
|
%stackIsNil = icmp eq %runtime._defer* %3, null
|
||||||
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
|
%stack.next.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %3, i32 0, i32 1
|
||||||
|
%stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
|
||||||
|
store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
|
||||||
|
%callback.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %3, i32 0, i32 0
|
||||||
|
%callback = load i32, i32* %callback.gep, align 4
|
||||||
|
switch i32 %callback, label %rundefers.default [
|
||||||
|
i32 0, label %rundefers.callback0
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
|
%setjmp1 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result2 = icmp eq i32 %setjmp1, 0
|
||||||
|
br i1 %setjmp.result2, label %4, label %lpad
|
||||||
|
|
||||||
|
4: ; preds = %rundefers.callback0
|
||||||
|
call void @"main.deferSimple$1"(i8* undef)
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
|
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
|
||||||
|
recover: ; preds = %rundefers.end3
|
||||||
|
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
|
||||||
|
lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry
|
||||||
|
br label %rundefers.loophead6
|
||||||
|
|
||||||
|
rundefers.loophead6: ; preds = %6, %lpad
|
||||||
|
%5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||||
|
%stackIsNil7 = icmp eq %runtime._defer* %5, null
|
||||||
|
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
||||||
|
|
||||||
|
rundefers.loop5: ; preds = %rundefers.loophead6
|
||||||
|
%stack.next.gep8 = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 1
|
||||||
|
%stack.next9 = load %runtime._defer*, %runtime._defer** %stack.next.gep8, align 4
|
||||||
|
store %runtime._defer* %stack.next9, %runtime._defer** %deferPtr, align 4
|
||||||
|
%callback.gep10 = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 0
|
||||||
|
%callback11 = load i32, i32* %callback.gep10, align 4
|
||||||
|
switch i32 %callback11, label %rundefers.default4 [
|
||||||
|
i32 0, label %rundefers.callback012
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback012: ; preds = %rundefers.loop5
|
||||||
|
%setjmp14 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result15 = icmp eq i32 %setjmp14, 0
|
||||||
|
br i1 %setjmp.result15, label %6, label %lpad
|
||||||
|
|
||||||
|
6: ; preds = %rundefers.callback012
|
||||||
|
call void @"main.deferSimple$1"(i8* undef)
|
||||||
|
br label %rundefers.loophead6
|
||||||
|
|
||||||
|
rundefers.default4: ; preds = %rundefers.loop5
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end3: ; preds = %rundefers.loophead6
|
||||||
|
br label %recover
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nofree nosync nounwind willreturn
|
||||||
|
declare i8* @llvm.stacksave() #2
|
||||||
|
|
||||||
|
declare void @runtime.setupDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*, i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.deferSimple$1"(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
call void @runtime.printint32(i32 3, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.destroyDeferFrame(%runtime.deferFrame* dereferenceable_or_null(24), i8*) #0
|
||||||
|
|
||||||
|
declare void @runtime.printint32(i32, i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.deferMultiple(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%defer.alloca2 = alloca { i32, %runtime._defer* }, align 4
|
||||||
|
%defer.alloca = alloca { i32, %runtime._defer* }, align 4
|
||||||
|
%deferPtr = alloca %runtime._defer*, align 4
|
||||||
|
store %runtime._defer* null, %runtime._defer** %deferPtr, align 4
|
||||||
|
%deferframe.buf = alloca %runtime.deferFrame, align 4
|
||||||
|
%0 = call i8* @llvm.stacksave()
|
||||||
|
call void @runtime.setupDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* %0, i8* undef) #3
|
||||||
|
%defer.alloca.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 0
|
||||||
|
store i32 0, i32* %defer.alloca.repack, align 4
|
||||||
|
%defer.alloca.repack26 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca, i32 0, i32 1
|
||||||
|
store %runtime._defer* null, %runtime._defer** %defer.alloca.repack26, align 4
|
||||||
|
%1 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
||||||
|
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %1, align 4
|
||||||
|
%defer.alloca2.repack = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 0
|
||||||
|
store i32 1, i32* %defer.alloca2.repack, align 4
|
||||||
|
%defer.alloca2.repack27 = getelementptr inbounds { i32, %runtime._defer* }, { i32, %runtime._defer* }* %defer.alloca2, i32 0, i32 1
|
||||||
|
%2 = bitcast %runtime._defer** %defer.alloca2.repack27 to { i32, %runtime._defer* }**
|
||||||
|
store { i32, %runtime._defer* }* %defer.alloca, { i32, %runtime._defer* }** %2, align 4
|
||||||
|
%3 = bitcast %runtime._defer** %deferPtr to { i32, %runtime._defer* }**
|
||||||
|
store { i32, %runtime._defer* }* %defer.alloca2, { i32, %runtime._defer* }** %3, align 4
|
||||||
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result = icmp eq i32 %setjmp, 0
|
||||||
|
br i1 %setjmp.result, label %4, label %lpad
|
||||||
|
|
||||||
|
4: ; preds = %entry
|
||||||
|
call void @main.external(i8* undef) #3
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.loophead: ; preds = %7, %6, %4
|
||||||
|
%5 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||||
|
%stackIsNil = icmp eq %runtime._defer* %5, null
|
||||||
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
|
%stack.next.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 1
|
||||||
|
%stack.next = load %runtime._defer*, %runtime._defer** %stack.next.gep, align 4
|
||||||
|
store %runtime._defer* %stack.next, %runtime._defer** %deferPtr, align 4
|
||||||
|
%callback.gep = getelementptr inbounds %runtime._defer, %runtime._defer* %5, i32 0, i32 0
|
||||||
|
%callback = load i32, i32* %callback.gep, align 4
|
||||||
|
switch i32 %callback, label %rundefers.default [
|
||||||
|
i32 0, label %rundefers.callback0
|
||||||
|
i32 1, label %rundefers.callback1
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback0: ; preds = %rundefers.loop
|
||||||
|
%setjmp4 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result5 = icmp eq i32 %setjmp4, 0
|
||||||
|
br i1 %setjmp.result5, label %6, label %lpad
|
||||||
|
|
||||||
|
6: ; preds = %rundefers.callback0
|
||||||
|
call void @"main.deferMultiple$1"(i8* undef)
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.callback1: ; preds = %rundefers.loop
|
||||||
|
%setjmp7 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result8 = icmp eq i32 %setjmp7, 0
|
||||||
|
br i1 %setjmp.result8, label %7, label %lpad
|
||||||
|
|
||||||
|
7: ; preds = %rundefers.callback1
|
||||||
|
call void @"main.deferMultiple$2"(i8* undef)
|
||||||
|
br label %rundefers.loophead
|
||||||
|
|
||||||
|
rundefers.default: ; preds = %rundefers.loop
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end: ; preds = %rundefers.loophead
|
||||||
|
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
|
||||||
|
recover: ; preds = %rundefers.end9
|
||||||
|
call void @runtime.destroyDeferFrame(%runtime.deferFrame* nonnull %deferframe.buf, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
|
||||||
|
lpad: ; preds = %rundefers.callback122, %rundefers.callback018, %rundefers.callback1, %rundefers.callback0, %entry
|
||||||
|
br label %rundefers.loophead12
|
||||||
|
|
||||||
|
rundefers.loophead12: ; preds = %10, %9, %lpad
|
||||||
|
%8 = load %runtime._defer*, %runtime._defer** %deferPtr, align 4
|
||||||
|
%stackIsNil13 = icmp eq %runtime._defer* %8, null
|
||||||
|
br i1 %stackIsNil13, label %rundefers.end9, label %rundefers.loop11
|
||||||
|
|
||||||
|
rundefers.loop11: ; preds = %rundefers.loophead12
|
||||||
|
%stack.next.gep14 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 1
|
||||||
|
%stack.next15 = load %runtime._defer*, %runtime._defer** %stack.next.gep14, align 4
|
||||||
|
store %runtime._defer* %stack.next15, %runtime._defer** %deferPtr, align 4
|
||||||
|
%callback.gep16 = getelementptr inbounds %runtime._defer, %runtime._defer* %8, i32 0, i32 0
|
||||||
|
%callback17 = load i32, i32* %callback.gep16, align 4
|
||||||
|
switch i32 %callback17, label %rundefers.default10 [
|
||||||
|
i32 0, label %rundefers.callback018
|
||||||
|
i32 1, label %rundefers.callback122
|
||||||
|
]
|
||||||
|
|
||||||
|
rundefers.callback018: ; preds = %rundefers.loop11
|
||||||
|
%setjmp20 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result21 = icmp eq i32 %setjmp20, 0
|
||||||
|
br i1 %setjmp.result21, label %9, label %lpad
|
||||||
|
|
||||||
|
9: ; preds = %rundefers.callback018
|
||||||
|
call void @"main.deferMultiple$1"(i8* undef)
|
||||||
|
br label %rundefers.loophead12
|
||||||
|
|
||||||
|
rundefers.callback122: ; preds = %rundefers.loop11
|
||||||
|
%setjmp24 = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(%runtime.deferFrame* nonnull %deferframe.buf) #4
|
||||||
|
%setjmp.result25 = icmp eq i32 %setjmp24, 0
|
||||||
|
br i1 %setjmp.result25, label %10, label %lpad
|
||||||
|
|
||||||
|
10: ; preds = %rundefers.callback122
|
||||||
|
call void @"main.deferMultiple$2"(i8* undef)
|
||||||
|
br label %rundefers.loophead12
|
||||||
|
|
||||||
|
rundefers.default10: ; preds = %rundefers.loop11
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
rundefers.end9: ; preds = %rundefers.loophead12
|
||||||
|
br label %recover
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.deferMultiple$1"(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
call void @runtime.printint32(i32 3, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @"main.deferMultiple$2"(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
call void @runtime.printint32(i32 5, i8* undef) #3
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
attributes #2 = { nofree nosync nounwind willreturn }
|
||||||
|
attributes #3 = { nounwind }
|
||||||
|
attributes #4 = { nounwind returns_twice }
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func external()
|
||||||
|
|
||||||
|
func deferSimple() {
|
||||||
|
defer func() {
|
||||||
|
print(3)
|
||||||
|
}()
|
||||||
|
external()
|
||||||
|
}
|
||||||
|
|
||||||
|
func deferMultiple() {
|
||||||
|
defer func() {
|
||||||
|
print(3)
|
||||||
|
}()
|
||||||
|
defer func() {
|
||||||
|
print(5)
|
||||||
|
}()
|
||||||
|
external()
|
||||||
|
}
|
||||||
Vendored
+13
-12
@@ -3,18 +3,18 @@ source_filename = "float.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -26,25 +26,25 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.maxu32f(i8* %context) unnamed_addr #0 {
|
define hidden float @main.maxu32f(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret float 0x41F0000000000000
|
ret float 0x41F0000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 -1
|
ret i32 -1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context) unnamed_addr #0 {
|
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = uitofp i32 %v to float
|
%0 = uitofp i32 %v to float
|
||||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||||
@@ -54,7 +54,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #0 {
|
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||||
@@ -67,7 +67,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #0 {
|
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%positive = fcmp oge float %v, 0.000000e+00
|
%positive = fcmp oge float %v, 0.000000e+00
|
||||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||||
@@ -79,7 +79,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #0 {
|
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||||
@@ -92,4 +92,5 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
|||||||
Vendored
+15
-13
@@ -3,45 +3,47 @@ source_filename = "func.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #0 {
|
define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq void ()* %callback.funcptr, null
|
%0 = icmp eq void ()* %callback.funcptr, null
|
||||||
br i1 %0, label %fpcall.throw, label %fpcall.next
|
br i1 %0, label %fpcall.throw, label %fpcall.next
|
||||||
|
|
||||||
fpcall.throw: ; preds = %entry
|
|
||||||
call void @runtime.nilPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
fpcall.next: ; preds = %entry
|
fpcall.next: ; preds = %entry
|
||||||
%1 = bitcast void ()* %callback.funcptr to void (i32, i8*)*
|
%1 = bitcast void ()* %callback.funcptr to void (i32, i8*)*
|
||||||
call void %1(i32 3, i8* %callback.context) #0
|
call void %1(i32 3, i8* %callback.context) #2
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
|
fpcall.throw: ; preds = %entry
|
||||||
|
call void @runtime.nilPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.nilPanic(i8*)
|
declare void @runtime.nilPanic(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.bar(i8* %context) unnamed_addr #0 {
|
define hidden void @main.bar(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @main.foo(i8* undef, void ()* bitcast (void (i32, i8*)* @main.someFunc to void ()*), i8* undef)
|
call void @main.foo(i8* undef, void ()* bitcast (void (i32, i8*)* @main.someFunc to void ()*), i8* undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.someFunc(i32 %arg0, i8* %context) unnamed_addr #0 {
|
define hidden void @main.someFunc(i32 %arg0, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
Vendored
+45
-43
@@ -26,91 +26,91 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 }
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant %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 }
|
@"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*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newScalar(i8* %context) unnamed_addr #0 {
|
define hidden void @main.newScalar(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||||
store i8* %new, i8** @main.scalar1, align 4
|
store i8* %new, i8** @main.scalar1, align 4
|
||||||
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
||||||
store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4
|
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) #0
|
%new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
||||||
store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4
|
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) #0
|
%new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
|
||||||
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
|
store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newArray(i8* %context) unnamed_addr #0 {
|
define hidden void @main.newArray(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||||
store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4
|
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) #0
|
%new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
||||||
store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4
|
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) #0
|
%new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
||||||
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
|
store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.newStruct(i8* %context) unnamed_addr #0 {
|
define hidden void @main.newStruct(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||||
store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4
|
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) #0
|
%new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2
|
||||||
store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4
|
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) #0
|
%new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2
|
||||||
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
|
store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4
|
||||||
%new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #0
|
%new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2
|
||||||
store i8* %new3, i8** bitcast ({ i8*, [61 x i32] }** @main.struct4 to i8**), align 4
|
store i8* %new3, i8** bitcast ({ i8*, [61 x i32] }** @main.struct4 to i8**), align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #0 {
|
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #0
|
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #2
|
||||||
%0 = bitcast i8* %new to { i8*, void ()* }*
|
%0 = bitcast i8* %new to { i8*, void ()* }*
|
||||||
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2
|
||||||
ret { i8*, void ()* }* %0
|
ret { i8*, void ()* }* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.makeSlice(i8* %context) unnamed_addr #0 {
|
define hidden void @main.makeSlice(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
|
||||||
store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8
|
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 1), align 4
|
||||||
store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8
|
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) #0
|
%makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #2
|
||||||
store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8
|
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 1), align 4
|
||||||
store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8
|
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) #0
|
%makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #2
|
||||||
store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8
|
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 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
|
store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8
|
||||||
@@ -118,18 +118,20 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #0 {
|
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
%.repack = bitcast i8* %0 to double*
|
%.repack = bitcast i8* %0 to double*
|
||||||
store double %v.r, double* %.repack, align 8
|
store double %v.r, double* %.repack, align 8
|
||||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
%1 = bitcast i8* %.repack1 to double*
|
%1 = bitcast i8* %.repack1 to double*
|
||||||
store double %v.i, double* %1, align 8
|
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
|
%2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
ret %runtime._interface %2
|
ret %runtime._interface %2
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
Vendored
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type Coord interface {
|
||||||
|
int | float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Point[T Coord] struct {
|
||||||
|
X, Y T
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add[T Coord](a, b Point[T]) Point[T] {
|
||||||
|
checkSize(unsafe.Alignof(a))
|
||||||
|
checkSize(unsafe.Sizeof(a))
|
||||||
|
return Point[T]{
|
||||||
|
X: a.X + b.X,
|
||||||
|
Y: a.Y + b.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var af, bf Point[float32]
|
||||||
|
Add(af, bf)
|
||||||
|
|
||||||
|
var ai, bi Point[int]
|
||||||
|
Add(ai, bi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSize(uintptr)
|
||||||
Vendored
+259
@@ -0,0 +1,259 @@
|
|||||||
|
; ModuleID = 'generics.go'
|
||||||
|
source_filename = "generics.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%"main.Point[int]" = type { i32, i32 }
|
||||||
|
%"main.Point[float32]" = type { float, float }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.main(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%bi = alloca %"main.Point[int]", align 8
|
||||||
|
%ai = alloca %"main.Point[int]", align 8
|
||||||
|
%bf = alloca %"main.Point[float32]", align 8
|
||||||
|
%af = alloca %"main.Point[float32]", align 8
|
||||||
|
%af.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
||||||
|
store float 0.000000e+00, float* %af.repack, align 8
|
||||||
|
%af.repack1 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
||||||
|
store float 0.000000e+00, float* %af.repack1, align 4
|
||||||
|
%0 = bitcast %"main.Point[float32]"* %af to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
|
%bf.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
||||||
|
store float 0.000000e+00, float* %bf.repack, align 8
|
||||||
|
%bf.repack2 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
||||||
|
store float 0.000000e+00, float* %bf.repack2, align 4
|
||||||
|
%1 = bitcast %"main.Point[float32]"* %bf to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
||||||
|
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 0
|
||||||
|
%.unpack = load float, float* %.elt, align 8
|
||||||
|
%.elt3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %af, i32 0, i32 1
|
||||||
|
%.unpack4 = load float, float* %.elt3, align 4
|
||||||
|
%.elt5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 0
|
||||||
|
%.unpack6 = load float, float* %.elt5, align 8
|
||||||
|
%.elt7 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %bf, i32 0, i32 1
|
||||||
|
%.unpack8 = load float, float* %.elt7, align 4
|
||||||
|
%2 = call %"main.Point[float32]" @"main.Add[float32]"(float %.unpack, float %.unpack4, float %.unpack6, float %.unpack8, i8* undef)
|
||||||
|
%ai.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
||||||
|
store i32 0, i32* %ai.repack, align 8
|
||||||
|
%ai.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
||||||
|
store i32 0, i32* %ai.repack9, align 4
|
||||||
|
%3 = bitcast %"main.Point[int]"* %ai to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %3, i8* undef) #2
|
||||||
|
%bi.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
||||||
|
store i32 0, i32* %bi.repack, align 8
|
||||||
|
%bi.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
||||||
|
store i32 0, i32* %bi.repack10, align 4
|
||||||
|
%4 = bitcast %"main.Point[int]"* %bi to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %4, i8* undef) #2
|
||||||
|
%.elt11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 0
|
||||||
|
%.unpack12 = load i32, i32* %.elt11, align 8
|
||||||
|
%.elt13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %ai, i32 0, i32 1
|
||||||
|
%.unpack14 = load i32, i32* %.elt13, align 4
|
||||||
|
%.elt15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 0
|
||||||
|
%.unpack16 = load i32, i32* %.elt15, align 8
|
||||||
|
%.elt17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %bi, i32 0, i32 1
|
||||||
|
%.unpack18 = load i32, i32* %.elt17, align 4
|
||||||
|
%5 = call %"main.Point[int]" @"main.Add[int]"(i32 %.unpack12, i32 %.unpack14, i32 %.unpack16, i32 %.unpack18, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%complit = alloca %"main.Point[float32]", align 8
|
||||||
|
%b = alloca %"main.Point[float32]", align 8
|
||||||
|
%a = alloca %"main.Point[float32]", align 8
|
||||||
|
%a.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
||||||
|
store float 0.000000e+00, float* %a.repack, align 8
|
||||||
|
%a.repack9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
||||||
|
store float 0.000000e+00, float* %a.repack9, align 4
|
||||||
|
%0 = bitcast %"main.Point[float32]"* %a to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
|
%a.repack10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
||||||
|
store float %a.X, float* %a.repack10, align 8
|
||||||
|
%a.repack11 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
||||||
|
store float %a.Y, float* %a.repack11, align 4
|
||||||
|
%b.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
||||||
|
store float 0.000000e+00, float* %b.repack, align 8
|
||||||
|
%b.repack13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
||||||
|
store float 0.000000e+00, float* %b.repack13, align 4
|
||||||
|
%1 = bitcast %"main.Point[float32]"* %b to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
||||||
|
%b.repack14 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
||||||
|
store float %b.X, float* %b.repack14, align 8
|
||||||
|
%b.repack15 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
||||||
|
store float %b.Y, float* %b.repack15, align 4
|
||||||
|
call void @main.checkSize(i32 4, i8* undef) #2
|
||||||
|
call void @main.checkSize(i32 8, i8* undef) #2
|
||||||
|
%complit.repack = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
||||||
|
store float 0.000000e+00, float* %complit.repack, align 8
|
||||||
|
%complit.repack17 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
||||||
|
store float 0.000000e+00, float* %complit.repack17, align 4
|
||||||
|
%2 = bitcast %"main.Point[float32]"* %complit to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
||||||
|
%3 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
||||||
|
br i1 false, label %deref.throw, label %deref.next
|
||||||
|
|
||||||
|
deref.next: ; preds = %entry
|
||||||
|
br i1 false, label %deref.throw1, label %deref.next2
|
||||||
|
|
||||||
|
deref.next2: ; preds = %deref.next
|
||||||
|
%4 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 0
|
||||||
|
%5 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 0
|
||||||
|
%6 = load float, float* %5, align 8
|
||||||
|
%7 = load float, float* %4, align 8
|
||||||
|
%8 = fadd float %6, %7
|
||||||
|
br i1 false, label %deref.throw3, label %deref.next4
|
||||||
|
|
||||||
|
deref.next4: ; preds = %deref.next2
|
||||||
|
br i1 false, label %deref.throw5, label %deref.next6
|
||||||
|
|
||||||
|
deref.next6: ; preds = %deref.next4
|
||||||
|
%9 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %b, i32 0, i32 1
|
||||||
|
%10 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %a, i32 0, i32 1
|
||||||
|
%11 = load float, float* %10, align 4
|
||||||
|
%12 = load float, float* %9, align 4
|
||||||
|
br i1 false, label %store.throw, label %store.next
|
||||||
|
|
||||||
|
store.next: ; preds = %deref.next6
|
||||||
|
store float %8, float* %3, align 8
|
||||||
|
br i1 false, label %store.throw7, label %store.next8
|
||||||
|
|
||||||
|
store.next8: ; preds = %store.next
|
||||||
|
%13 = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 1
|
||||||
|
%14 = fadd float %11, %12
|
||||||
|
store float %14, float* %13, align 4
|
||||||
|
%.elt = getelementptr inbounds %"main.Point[float32]", %"main.Point[float32]"* %complit, i32 0, i32 0
|
||||||
|
%.unpack = load float, float* %.elt, align 8
|
||||||
|
%15 = insertvalue %"main.Point[float32]" undef, float %.unpack, 0
|
||||||
|
%16 = insertvalue %"main.Point[float32]" %15, float %14, 1
|
||||||
|
ret %"main.Point[float32]" %16
|
||||||
|
|
||||||
|
deref.throw: ; preds = %entry
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
deref.throw1: ; preds = %deref.next
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
deref.throw3: ; preds = %deref.next2
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
deref.throw5: ; preds = %deref.next4
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
store.throw: ; preds = %deref.next6
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
store.throw7: ; preds = %store.next
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @main.checkSize(i32, i8*) #0
|
||||||
|
|
||||||
|
declare void @runtime.nilPanic(i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%complit = alloca %"main.Point[int]", align 8
|
||||||
|
%b = alloca %"main.Point[int]", align 8
|
||||||
|
%a = alloca %"main.Point[int]", align 8
|
||||||
|
%a.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
||||||
|
store i32 0, i32* %a.repack, align 8
|
||||||
|
%a.repack9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
||||||
|
store i32 0, i32* %a.repack9, align 4
|
||||||
|
%0 = bitcast %"main.Point[int]"* %a to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
|
%a.repack10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
||||||
|
store i32 %a.X, i32* %a.repack10, align 8
|
||||||
|
%a.repack11 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
||||||
|
store i32 %a.Y, i32* %a.repack11, align 4
|
||||||
|
%b.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
||||||
|
store i32 0, i32* %b.repack, align 8
|
||||||
|
%b.repack13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
||||||
|
store i32 0, i32* %b.repack13, align 4
|
||||||
|
%1 = bitcast %"main.Point[int]"* %b to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #2
|
||||||
|
%b.repack14 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
||||||
|
store i32 %b.X, i32* %b.repack14, align 8
|
||||||
|
%b.repack15 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
||||||
|
store i32 %b.Y, i32* %b.repack15, align 4
|
||||||
|
call void @main.checkSize(i32 4, i8* undef) #2
|
||||||
|
call void @main.checkSize(i32 8, i8* undef) #2
|
||||||
|
%complit.repack = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
||||||
|
store i32 0, i32* %complit.repack, align 8
|
||||||
|
%complit.repack17 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
||||||
|
store i32 0, i32* %complit.repack17, align 4
|
||||||
|
%2 = bitcast %"main.Point[int]"* %complit to i8*
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %2, i8* undef) #2
|
||||||
|
%3 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
||||||
|
br i1 false, label %deref.throw, label %deref.next
|
||||||
|
|
||||||
|
deref.next: ; preds = %entry
|
||||||
|
br i1 false, label %deref.throw1, label %deref.next2
|
||||||
|
|
||||||
|
deref.next2: ; preds = %deref.next
|
||||||
|
%4 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 0
|
||||||
|
%5 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 0
|
||||||
|
%6 = load i32, i32* %5, align 8
|
||||||
|
%7 = load i32, i32* %4, align 8
|
||||||
|
%8 = add i32 %6, %7
|
||||||
|
br i1 false, label %deref.throw3, label %deref.next4
|
||||||
|
|
||||||
|
deref.next4: ; preds = %deref.next2
|
||||||
|
br i1 false, label %deref.throw5, label %deref.next6
|
||||||
|
|
||||||
|
deref.next6: ; preds = %deref.next4
|
||||||
|
%9 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %b, i32 0, i32 1
|
||||||
|
%10 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %a, i32 0, i32 1
|
||||||
|
%11 = load i32, i32* %10, align 4
|
||||||
|
%12 = load i32, i32* %9, align 4
|
||||||
|
br i1 false, label %store.throw, label %store.next
|
||||||
|
|
||||||
|
store.next: ; preds = %deref.next6
|
||||||
|
store i32 %8, i32* %3, align 8
|
||||||
|
br i1 false, label %store.throw7, label %store.next8
|
||||||
|
|
||||||
|
store.next8: ; preds = %store.next
|
||||||
|
%13 = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 1
|
||||||
|
%14 = add i32 %11, %12
|
||||||
|
store i32 %14, i32* %13, align 4
|
||||||
|
%.elt = getelementptr inbounds %"main.Point[int]", %"main.Point[int]"* %complit, i32 0, i32 0
|
||||||
|
%.unpack = load i32, i32* %.elt, align 8
|
||||||
|
%15 = insertvalue %"main.Point[int]" undef, i32 %.unpack, 0
|
||||||
|
%16 = insertvalue %"main.Point[int]" %15, i32 %14, 1
|
||||||
|
ret %"main.Point[int]" %16
|
||||||
|
|
||||||
|
deref.throw: ; preds = %entry
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
deref.throw1: ; preds = %deref.next
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
deref.throw3: ; preds = %deref.next2
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
deref.throw5: ; preds = %deref.next4
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
store.throw: ; preds = %deref.next6
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
store.throw7: ; preds = %store.next
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
Vendored
+50
-48
@@ -3,67 +3,67 @@ source_filename = "go1.17.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #0 {
|
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = getelementptr i8, i8* %p, i32 %len
|
%0 = getelementptr i8, i8* %p, i32 %len
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #0 {
|
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = trunc i64 %len to i32
|
%0 = trunc i64 %len to i32
|
||||||
%1 = getelementptr i8, i8* %p, i32 %0
|
%1 = getelementptr i8, i8* %p, i32 %0
|
||||||
call void @runtime.trackPointer(i8* %1, i8* undef) #0
|
call void @runtime.trackPointer(i8* %1, i8* undef) #2
|
||||||
ret i8* %1
|
ret i8* %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #0 {
|
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp ult i32 %s.len, 4
|
%0 = icmp ult i32 %s.len, 4
|
||||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
slicetoarray.throw: ; preds = %entry
|
|
||||||
call void @runtime.sliceToArrayPointerPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
slicetoarray.next: ; preds = %entry
|
slicetoarray.next: ; preds = %entry
|
||||||
%1 = bitcast i32* %s.data to [4 x i32]*
|
%1 = bitcast i32* %s.data to [4 x i32]*
|
||||||
ret [4 x i32]* %1
|
ret [4 x i32]* %1
|
||||||
}
|
|
||||||
|
|
||||||
declare void @runtime.sliceToArrayPointerPanic(i8*)
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #0 {
|
|
||||||
entry:
|
|
||||||
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
|
|
||||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
|
||||||
|
|
||||||
slicetoarray.throw: ; preds = %entry
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
call void @runtime.sliceToArrayPointerPanic(i8* undef) #2
|
||||||
unreachable
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.sliceToArrayPointerPanic(i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
|
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
|
||||||
|
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
slicetoarray.next: ; preds = %entry
|
slicetoarray.next: ; preds = %entry
|
||||||
%0 = bitcast i8* %makeslice to [4 x i32]*
|
%0 = bitcast i8* %makeslice to [4 x i32]*
|
||||||
ret [4 x i32]* %0
|
ret [4 x i32]* %0
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp ugt i32 %len, 1073741823
|
%0 = icmp ugt i32 %len, 1073741823
|
||||||
%1 = icmp eq i32* %ptr, null
|
%1 = icmp eq i32* %ptr, null
|
||||||
@@ -72,44 +72,44 @@ entry:
|
|||||||
%4 = or i1 %3, %0
|
%4 = or i1 %3, %0
|
||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
unsafe.Slice.next: ; preds = %entry
|
||||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
||||||
%8 = bitcast i32* %ptr to i8*
|
%8 = bitcast i32* %ptr to i8*
|
||||||
call void @runtime.trackPointer(i8* %8, i8* undef) #0
|
call void @runtime.trackPointer(i8* %8, i8* undef) #2
|
||||||
ret { i32*, i32, i32 } %7
|
ret { i32*, i32, i32 } %7
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.unsafeSlicePanic(i8*)
|
declare void @runtime.unsafeSlicePanic(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp eq i8* %ptr, null
|
%0 = icmp eq i8* %ptr, null
|
||||||
%1 = icmp ne i16 %len, 0
|
%1 = icmp ne i16 %len, 0
|
||||||
%2 = and i1 %0, %1
|
%2 = and i1 %0, %1
|
||||||
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
unsafe.Slice.next: ; preds = %entry
|
||||||
%3 = zext i16 %len to i32
|
%3 = zext i16 %len to i32
|
||||||
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
||||||
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
||||||
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
||||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
|
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
||||||
ret { i8*, i32, i32 } %6
|
ret { i8*, i32, i32 } %6
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp ugt i64 %len, 1073741823
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
%1 = icmp eq i32* %ptr, null
|
%1 = icmp eq i32* %ptr, null
|
||||||
@@ -118,22 +118,22 @@ entry:
|
|||||||
%4 = or i1 %3, %0
|
%4 = or i1 %3, %0
|
||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
unsafe.Slice.next: ; preds = %entry
|
||||||
%5 = trunc i64 %len to i32
|
%5 = trunc i64 %len to i32
|
||||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||||
%9 = bitcast i32* %ptr to i8*
|
%9 = bitcast i32* %ptr to i8*
|
||||||
call void @runtime.trackPointer(i8* %9, i8* undef) #0
|
call void @runtime.trackPointer(i8* %9, i8* undef) #2
|
||||||
ret { i32*, i32, i32 } %8
|
ret { i32*, i32, i32 } %8
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = icmp ugt i64 %len, 1073741823
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
%1 = icmp eq i32* %ptr, null
|
%1 = icmp eq i32* %ptr, null
|
||||||
@@ -142,18 +142,20 @@ entry:
|
|||||||
%4 = or i1 %3, %0
|
%4 = or i1 %3, %0
|
||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
unsafe.Slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.unsafeSlicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
unsafe.Slice.next: ; preds = %entry
|
||||||
%5 = trunc i64 %len to i32
|
%5 = trunc i64 %len to i32
|
||||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||||
%9 = bitcast i32* %ptr to i8*
|
%9 = bitcast i32* %ptr to i8*
|
||||||
call void @runtime.trackPointer(i8* %9, i8* undef) #0
|
call void @runtime.trackPointer(i8* %9, i8* undef) #2
|
||||||
ret { i32*, i32, i32 } %8
|
ret { i32*, i32, i32 } %8
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
+54
-52
@@ -5,59 +5,59 @@ 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 } }
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
||||||
%"internal/task.gcData" = type {}
|
%"internal/task.gcData" = type {}
|
||||||
%"internal/task.state" = type { i32, i32* }
|
%"internal/task.state" = type { i32, i32* }
|
||||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #0
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #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) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, i8*)
|
declare void @main.regularFunction(i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
|
define linkonce_odr void @"main.regularFunction$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.regularFunction(i32 %unpack.int, i8* undef) #0
|
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*)
|
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*) #0
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, i8*, i32, i8*)
|
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #0
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 %stacksize, i8* undef) #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) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||||
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)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
|
||||||
@@ -65,26 +65,26 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
|
||||||
%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* null, i8* undef) #0
|
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
|
||||||
%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) #0
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, 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) #8
|
||||||
%5 = load i32, i32* %0, align 4
|
%5 = load i32, i32* %0, align 4
|
||||||
call void @runtime.printint32(i32 %5, i8* undef) #0
|
call void @runtime.printint32(i32 %5, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||||
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
|
||||||
@@ -92,7 +92,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$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
|
||||||
@@ -103,12 +103,12 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*)
|
declare void @runtime.printint32(i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #0 {
|
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
|
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
|
||||||
%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
|
||||||
@@ -117,13 +117,13 @@ entry:
|
|||||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
%5 = bitcast i8* %4 to void ()**
|
%5 = bitcast i8* %4 to void ()**
|
||||||
store void ()* %fn.funcptr, void ()** %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) #0
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #0
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
|
||||||
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
|
||||||
@@ -133,38 +133,38 @@ 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*)**
|
%7 = bitcast i8* %6 to void (i32, i8*)**
|
||||||
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
||||||
call void %8(i32 %2, i8* %5) #0
|
call void %8(i32 %2, i8* %5) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
|
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #0
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
|
||||||
%1 = bitcast i8* %0 to i8**
|
%1 = bitcast i8* %0 to i8**
|
||||||
store i8* %itf.value, i8** %1, align 4
|
store i8* %itf.value, i8** %1, align 4
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
@@ -176,15 +176,15 @@ entry:
|
|||||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
%5 = bitcast i8* %4 to i32*
|
%5 = bitcast i8* %4 to i32*
|
||||||
store i32 %itf.typecode, i32* %5, align 4
|
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) #0
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* undef) #8
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #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) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i8**
|
%1 = bitcast i8* %0 to i8**
|
||||||
%2 = load i8*, i8** %1, align 4
|
%2 = load i8*, i8** %1, align 4
|
||||||
@@ -197,14 +197,16 @@ entry:
|
|||||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
%10 = bitcast i8* %9 to i32*
|
%10 = bitcast i8* %9 to i32*
|
||||||
%11 = load i32, i32* %10, align 4
|
%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) #0
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #2 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #4 = { nounwind "tinygo-gowrapper" }
|
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||||
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #6 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
|
attributes #7 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
|
attributes #8 = { nounwind }
|
||||||
|
|||||||
+61
-59
@@ -5,7 +5,7 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
%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 } }
|
||||||
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state", i8* }
|
||||||
%"internal/task.gcData" = type { i8* }
|
%"internal/task.gcData" = type { i8* }
|
||||||
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
|
||||||
%"internal/task.stackState" = type { i32, i32 }
|
%"internal/task.stackState" = type { i32, i32 }
|
||||||
@@ -13,84 +13,84 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #0
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.regularFunction(i32, i8*)
|
declare void @main.regularFunction(i32, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.deadlock(i8*)
|
declare void @runtime.deadlock(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
|
define linkonce_odr void @"main.regularFunction$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.regularFunction(i32 %unpack.int, i8* undef) #0
|
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8
|
||||||
call void @runtime.deadlock(i8* undef) #0
|
call void @runtime.deadlock(i8* undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"internal/task.start"(i32, i8*, i32, i8*)
|
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #0
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
|
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
||||||
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)
|
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
|
||||||
call void @runtime.deadlock(i8* undef) #0
|
call void @runtime.deadlock(i8* undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8
|
||||||
%0 = bitcast i8* %n to i32*
|
%0 = bitcast i8* %n to i32*
|
||||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
|
||||||
store i32 3, i32* %0, align 4
|
store i32 3, i32* %0, align 4
|
||||||
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8
|
||||||
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #0
|
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #8
|
||||||
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
|
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8
|
||||||
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #8
|
||||||
%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 (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #0
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #8
|
||||||
%5 = load i32, i32* %0, align 4
|
%5 = load i32, i32* %0, align 4
|
||||||
call void @runtime.printint32(i32 %5, i8* undef) #0
|
call void @runtime.printint32(i32 %5, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
|
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 {
|
||||||
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
|
||||||
@@ -98,7 +98,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$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
|
||||||
@@ -106,17 +106,17 @@ entry:
|
|||||||
%4 = bitcast i8* %3 to i8**
|
%4 = bitcast i8* %3 to i8**
|
||||||
%5 = load i8*, i8** %4, align 4
|
%5 = load i8*, i8** %4, align 4
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
|
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5)
|
||||||
call void @runtime.deadlock(i8* undef) #0
|
call void @runtime.deadlock(i8* undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, i8*)
|
declare void @runtime.printint32(i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #0 {
|
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
|
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
|
||||||
%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
|
||||||
@@ -125,12 +125,12 @@ entry:
|
|||||||
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
%4 = getelementptr inbounds i8, i8* %0, i32 8
|
||||||
%5 = bitcast i8* %4 to void ()**
|
%5 = bitcast i8* %4 to void ()**
|
||||||
store void ()* %fn.funcptr, void ()** %5, align 4
|
store void ()* %fn.funcptr, void ()** %5, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #0
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 {
|
||||||
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
|
||||||
@@ -140,40 +140,40 @@ 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*)**
|
%7 = bitcast i8* %6 to void (i32, i8*)**
|
||||||
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
%8 = load void (i32, i8*)*, void (i32, i8*)** %7, align 4
|
||||||
call void %8(i32 %2, i8* %5) #0
|
call void %8(i32 %2, i8* %5) #8
|
||||||
call void @runtime.deadlock(i8* undef) #0
|
call void @runtime.deadlock(i8* undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
|
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
|
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #0
|
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
|
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
|
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
|
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
|
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8
|
||||||
%1 = bitcast i8* %0 to i8**
|
%1 = bitcast i8* %0 to i8**
|
||||||
store i8* %itf.value, i8** %1, align 4
|
store i8* %itf.value, i8** %1, align 4
|
||||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||||
@@ -185,14 +185,14 @@ entry:
|
|||||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
%5 = bitcast i8* %4 to i32*
|
%5 = bitcast i8* %4 to i32*
|
||||||
store i32 %itf.typecode, i32* %5, align 4
|
store i32 %itf.typecode, i32* %5, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 16384, i8* undef) #0
|
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 16384, i8* undef) #8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
|
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 {
|
||||||
entry:
|
entry:
|
||||||
%1 = bitcast i8* %0 to i8**
|
%1 = bitcast i8* %0 to i8**
|
||||||
%2 = load i8*, i8** %1, align 4
|
%2 = load i8*, i8** %1, align 4
|
||||||
@@ -205,15 +205,17 @@ entry:
|
|||||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
||||||
%10 = bitcast i8* %9 to i32*
|
%10 = bitcast i8* %9 to i32*
|
||||||
%11 = load i32, i32* %10, align 4
|
%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) #0
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #8
|
||||||
call void @runtime.deadlock(i8* undef) #0
|
call void @runtime.deadlock(i8* undef) #8
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #4 = { nounwind "tinygo-gowrapper" }
|
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
|
||||||
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
|
attributes #7 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
|
attributes #8 = { nounwind }
|
||||||
|
|||||||
Vendored
+51
-49
@@ -22,111 +22,113 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"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
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #0 {
|
define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||||
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #0 {
|
define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||||
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #0 {
|
define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||||
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #1
|
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #0 {
|
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* null, i8* undef) #0
|
call void @runtime.trackPointer(i8* null, i8* undef) #6
|
||||||
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #2
|
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #0
|
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #6
|
||||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
|
||||||
br label %typeassert.next
|
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
ret i1 %typecode
|
ret i1 %typecode
|
||||||
}
|
|
||||||
|
|
||||||
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*)
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
|
||||||
entry:
|
|
||||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
|
||||||
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
|
||||||
entry:
|
|
||||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
|
||||||
ret i1 %0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #0
|
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
|
||||||
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
|
ret i1 %0
|
||||||
|
|
||||||
|
typeassert.ok: ; preds = %entry
|
||||||
|
br label %typeassert.next
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #6
|
||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #3
|
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #4
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
|
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #0
|
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #6
|
||||||
%1 = extractvalue %runtime._string %0, 0
|
%1 = extractvalue %runtime._string %0, 0
|
||||||
call void @runtime.trackPointer(i8* %1, i8* undef) #0
|
call void @runtime.trackPointer(i8* %1, i8* undef) #6
|
||||||
ret %runtime._string %0
|
ret %runtime._string %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #4
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #5
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
attributes #2 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
||||||
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #4 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||||
|
attributes #5 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
attributes #6 = { nounwind }
|
||||||
|
|||||||
+11
-9
@@ -3,30 +3,32 @@ source_filename = "intrinsics.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
|
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call double @math.Sqrt(double %x, i8* undef) #0
|
%0 = call double @math.Sqrt(double %x, i8* undef) #2
|
||||||
ret double %0
|
ret double %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare double @math.Sqrt(double, i8*)
|
declare double @math.Sqrt(double, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
|
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call double @math.Trunc(double %x, i8* undef) #0
|
%0 = call double @math.Trunc(double %x, i8* undef) #2
|
||||||
ret double %0
|
ret double %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare double @math.Trunc(double, i8*)
|
declare double @math.Trunc(double, i8*) #0
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
Vendored
+10
-9
@@ -3,35 +3,36 @@ source_filename = "intrinsics.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
|
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call double @llvm.sqrt.f64(double %x)
|
%0 = call double @llvm.sqrt.f64(double %x)
|
||||||
ret double %0
|
ret double %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||||
declare double @llvm.sqrt.f64(double) #1
|
declare double @llvm.sqrt.f64(double) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
|
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call double @llvm.trunc.f64(double %x)
|
%0 = call double @llvm.trunc.f64(double %x)
|
||||||
ret double %0
|
ret double %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||||
declare double @llvm.trunc.f64(double) #1
|
declare double @llvm.trunc.f64(double) #2
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nofree nosync nounwind readnone speculatable willreturn }
|
||||||
|
|||||||
Vendored
+25
-23
@@ -3,76 +3,78 @@ source_filename = "pointer.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #0 {
|
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret [0 x i32] zeroinitializer
|
ret [0 x i32] zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #0 {
|
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i8* %x to i32*
|
%0 = bitcast i8* %x to i32*
|
||||||
call void @runtime.trackPointer(i8* %x, i8* undef) #0
|
call void @runtime.trackPointer(i8* %x, i8* undef) #2
|
||||||
ret i32* %0
|
ret i32* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #0 {
|
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %x to i8*
|
%0 = bitcast i32* %x to i8*
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #0 {
|
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* %x, i8* undef) #0
|
call void @runtime.trackPointer(i8* %x, i8* undef) #2
|
||||||
ret i8* %x
|
ret i8* %x
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #0 {
|
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
|
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #0 {
|
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
|
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
|
||||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||||
ret i8* %0
|
ret i8* %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #0 {
|
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = bitcast i32* %ptr to i8*
|
%0 = bitcast i32* %ptr to i8*
|
||||||
call void @runtime.trackPointer(i8* %0, i8* undef) #0
|
call void @runtime.trackPointer(i8* %0, i8* undef) #2
|
||||||
%1 = getelementptr i32, i32* %ptr, i32 %offset
|
%1 = getelementptr i32, i32* %ptr, i32 %offset
|
||||||
%2 = bitcast i32* %1 to i8*
|
%2 = bitcast i32* %1 to i8*
|
||||||
call void @runtime.trackPointer(i8* %2, i8* undef) #0
|
call void @runtime.trackPointer(i8* %2, i8* undef) #2
|
||||||
%3 = bitcast i32* %1 to i8*
|
%3 = bitcast i32* %1 to i8*
|
||||||
call void @runtime.trackPointer(i8* %3, i8* undef) #0
|
call void @runtime.trackPointer(i8* %3, i8* undef) #2
|
||||||
ret i32* %1
|
ret i32* %1
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
Vendored
+17
-16
@@ -10,58 +10,59 @@ target triple = "wasm32-unknown-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*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define void @extern_func() #1 {
|
define void @extern_func() #2 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #0 {
|
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @somepkg.someFunction2(i8*)
|
declare void @somepkg.someFunction2(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: inlinehint nounwind
|
; Function Attrs: inlinehint nounwind
|
||||||
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #2 {
|
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #3 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #3 {
|
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.functionInSection(i8* %context) unnamed_addr #0 section ".special_function_section" {
|
define hidden void @main.functionInSection(i8* %context) unnamed_addr #1 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define void @exportedFunctionInSection() #4 section ".special_function_section" {
|
define void @exportedFunctionInSection() #5 section ".special_function_section" {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @main.undefinedFunctionNotInSection(i8*)
|
declare void @main.undefinedFunctionNotInSection(i8*) #0
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { nounwind "wasm-export-name"="extern_func" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { inlinehint nounwind }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
|
||||||
attributes #3 = { noinline nounwind }
|
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #4 = { nounwind "wasm-export-name"="exportedFunctionInSection" }
|
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
|
||||||
|
|||||||
Vendored
+55
-53
@@ -3,51 +3,51 @@ source_filename = "slice.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.len
|
ret i32 %ints.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %ints.cap
|
ret i32 %ints.cap
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%.not = icmp ult i32 %index, %ints.len
|
%.not = icmp ult i32 %index, %ints.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
|
||||||
call void @runtime.lookupPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
lookup.next: ; preds = %entry
|
lookup.next: ; preds = %entry
|
||||||
%0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
|
%0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
|
||||||
%1 = load i32, i32* %0, align 4
|
%1 = load i32, i32* %0, align 4
|
||||||
ret i32 %1
|
ret i32 %1
|
||||||
|
|
||||||
|
lookup.throw: ; preds = %entry
|
||||||
|
call void @runtime.lookupPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.lookupPanic(i8*)
|
declare void @runtime.lookupPanic(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
|
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #2
|
||||||
%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
|
||||||
@@ -57,7 +57,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) #0
|
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, i8* undef) #2
|
||||||
%append.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
|
||||||
@@ -65,18 +65,18 @@ entry:
|
|||||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
%5 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %append.newLen, 1
|
%6 = insertvalue { i32*, i32, i32 } %5, i32 %append.newLen, 1
|
||||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %append.newCap, 2
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %append.newCap, 2
|
||||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #0
|
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
|
||||||
ret { i32*, i32, i32 } %7
|
ret { i32*, i32, i32 } %7
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*)
|
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context) unnamed_addr #0 {
|
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context) unnamed_addr #1 {
|
||||||
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) #0
|
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef) #2
|
||||||
%append.newPtr = extractvalue { 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
|
||||||
@@ -84,103 +84,105 @@ entry:
|
|||||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
%0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
|
%1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
|
||||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
|
%2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
|
||||||
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #0
|
call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #2
|
||||||
ret { i32*, i32, i32 } %2
|
ret { i32*, i32, i32 } %2
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 {
|
||||||
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) #0
|
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef) #2
|
||||||
ret i32 %copy.n
|
ret i32 %copy.n
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%slice.maxcap = icmp slt i32 %len, 0
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.slicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
||||||
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||||
ret { i8*, i32, i32 } %2
|
ret { i8*, i32, i32 } %2
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.slicePanic(i8*)
|
declare void @runtime.slicePanic(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%slice.maxcap = icmp slt i32 %len, 0
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.slicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = shl i32 %len, 1
|
%makeslice.cap = shl i32 %len, 1
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
||||||
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
||||||
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||||
ret { i16*, i32, i32 } %2
|
ret { i16*, i32, i32 } %2
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #0 {
|
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.slicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = mul i32 %len, 3
|
%makeslice.cap = mul i32 %len, 3
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
||||||
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
%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
|
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||||
ret { [3 x i8]*, i32, i32 } %2
|
ret { [3 x i8]*, i32, i32 } %2
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #0 {
|
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
slice.throw: ; preds = %entry
|
|
||||||
call void @runtime.slicePanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
slice.next: ; preds = %entry
|
slice.next: ; preds = %entry
|
||||||
%makeslice.cap = shl i32 %len, 2
|
%makeslice.cap = shl i32 %len, 2
|
||||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
|
||||||
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
||||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
||||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
||||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
||||||
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
|
call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #2
|
||||||
ret { i32*, i32, i32 } %2
|
ret { i32*, i32, i32 } %2
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
Vendored
+28
-26
@@ -7,93 +7,95 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
|
|
||||||
@"main$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*) #0
|
||||||
|
|
||||||
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
|
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.init(i8* %context) unnamed_addr #0 {
|
define hidden void @main.init(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #0 {
|
define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main$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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.zeroLengthString(i8* %context) unnamed_addr #0 {
|
define hidden %runtime._string @main.zeroLengthString(i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._string zeroinitializer
|
ret %runtime._string zeroinitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context) unnamed_addr #0 {
|
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 %s.len
|
ret i32 %s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context) unnamed_addr #0 {
|
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context) unnamed_addr #1 {
|
||||||
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
|
|
||||||
call void @runtime.lookupPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
lookup.next: ; preds = %entry
|
lookup.next: ; preds = %entry
|
||||||
%0 = getelementptr inbounds i8, i8* %s.data, i32 %index
|
%0 = getelementptr inbounds i8, i8* %s.data, i32 %index
|
||||||
%1 = load i8, i8* %0, align 1
|
%1 = load i8, i8* %0, align 1
|
||||||
ret i8 %1
|
ret i8 %1
|
||||||
|
|
||||||
|
lookup.throw: ; preds = %entry
|
||||||
|
call void @runtime.lookupPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.lookupPanic(i8*)
|
declare void @runtime.lookupPanic(i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #0
|
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*)
|
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #0
|
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2
|
||||||
%1 = xor i1 %0, true
|
%1 = xor i1 %0, true
|
||||||
ret i1 %1
|
ret i1 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
|
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #0
|
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #2
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*)
|
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #0 {
|
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = zext i8 %x to i32
|
%0 = zext i8 %x to i32
|
||||||
%.not = icmp ult i32 %0, %s.len
|
%.not = icmp ult i32 %0, %s.len
|
||||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||||
|
|
||||||
lookup.throw: ; preds = %entry
|
|
||||||
call void @runtime.lookupPanic(i8* undef) #0
|
|
||||||
unreachable
|
|
||||||
|
|
||||||
lookup.next: ; preds = %entry
|
lookup.next: ; preds = %entry
|
||||||
%1 = getelementptr inbounds i8, i8* %s.data, i32 %0
|
%1 = getelementptr inbounds i8, i8* %s.data, i32 %0
|
||||||
%2 = load i8, i8* %1, align 1
|
%2 = load i8, i8* %1, align 1
|
||||||
ret i8 %2
|
ret i8 %2
|
||||||
|
|
||||||
|
lookup.throw: ; preds = %entry
|
||||||
|
call void @runtime.lookupPanic(i8* undef) #2
|
||||||
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
|||||||
+11
-14
@@ -3,28 +3,25 @@ package compiler
|
|||||||
// This file implements volatile loads/stores in runtime/volatile.LoadT and
|
// This file implements volatile loads/stores in runtime/volatile.LoadT and
|
||||||
// runtime/volatile.StoreT as compiler builtins.
|
// runtime/volatile.StoreT as compiler builtins.
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/tools/go/ssa"
|
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// createVolatileLoad is the implementation of the intrinsic function
|
// createVolatileLoad is the implementation of the intrinsic function
|
||||||
// runtime/volatile.LoadT().
|
// runtime/volatile.LoadT().
|
||||||
func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createVolatileLoad() {
|
||||||
addr := b.getValue(instr.Args[0])
|
b.createFunctionStart()
|
||||||
b.createNilCheck(instr.Args[0], addr, "deref")
|
addr := b.getValue(b.fn.Params[0])
|
||||||
|
b.createNilCheck(b.fn.Params[0], addr, "deref")
|
||||||
val := b.CreateLoad(addr, "")
|
val := b.CreateLoad(addr, "")
|
||||||
val.SetVolatile(true)
|
val.SetVolatile(true)
|
||||||
return val, nil
|
b.CreateRet(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createVolatileStore is the implementation of the intrinsic function
|
// createVolatileStore is the implementation of the intrinsic function
|
||||||
// runtime/volatile.StoreT().
|
// runtime/volatile.StoreT().
|
||||||
func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createVolatileStore() {
|
||||||
addr := b.getValue(instr.Args[0])
|
b.createFunctionStart()
|
||||||
val := b.getValue(instr.Args[1])
|
addr := b.getValue(b.fn.Params[0])
|
||||||
b.createNilCheck(instr.Args[0], addr, "deref")
|
val := b.getValue(b.fn.Params[1])
|
||||||
|
b.createNilCheck(b.fn.Params[0], addr, "deref")
|
||||||
store := b.CreateStore(val, addr)
|
store := b.CreateStore(val, addr)
|
||||||
store.SetVolatile(true)
|
store.SetVolatile(true)
|
||||||
return llvm.Value{}, nil
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
module github.com/tinygo-org/tinygo
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
|
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc
|
||||||
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-20220113222801-0725d94bb6ee
|
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
|
||||||
github.com/chromedp/chromedp v0.7.6
|
github.com/chromedp/chromedp v0.7.6
|
||||||
@@ -13,7 +13,7 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
go.bug.st/serial v1.1.3
|
go.bug.st/serial v1.1.3
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
golang.org/x/tools v0.1.11
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
|
tinygo.org/x/go-llvm v0.0.0-20220626113704-45f1e2dbf887
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a h1:QPU7APo6y/6VkCDq6HU3WWIUzER8iywSac23+1UQv60=
|
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc h1:Yp49g+qqgQRPk/gcRSmAsXgnT16XPJ6Y5JM1poc6gYM=
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc/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-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
||||||
@@ -40,45 +40,44 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
|
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
|
||||||
go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk=
|
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
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/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
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/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-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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
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.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
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=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f h1:9/J+NpFTpAhYcbh1mC4dr9W/aAPnwrUh8dmq21xWdSM=
|
tinygo.org/x/go-llvm v0.0.0-20220626113704-45f1e2dbf887 h1:k+Y1DU/WoBDkTkRJGF149yk3S2K2VhNglN435DXDS5s=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20220626113704-45f1e2dbf887/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+1
-1
@@ -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.23.0"
|
const Version = "0.25.0-dev"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// This variable is set at build time using -ldflags parameters.
|
// This variable is set at build time using -ldflags parameters.
|
||||||
|
|||||||
+4
-1
@@ -18,6 +18,7 @@ var (
|
|||||||
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
||||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||||
|
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is one of the errors that can be returned from toLLVMValue when the
|
// This is one of the errors that can be returned from toLLVMValue when the
|
||||||
@@ -26,7 +27,9 @@ var (
|
|||||||
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
||||||
|
|
||||||
func isRecoverableError(err error) bool {
|
func isRecoverableError(err error) bool {
|
||||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||||
|
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||||
|
err == errLoopUnrolled
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLine is one line in a traceback. The position may be missing.
|
// ErrorLine is one line in a traceback. The position may be missing.
|
||||||
|
|||||||
+18
-3
@@ -50,10 +50,17 @@ func newRunner(mod llvm.Module, debug bool) *runner {
|
|||||||
return &r
|
return &r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispose deallocates all alloated LLVM resources.
|
||||||
|
func (r *runner) dispose() {
|
||||||
|
r.targetData.Dispose()
|
||||||
|
r.targetData = llvm.TargetData{}
|
||||||
|
}
|
||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func Run(mod llvm.Module, debug bool) error {
|
func Run(mod llvm.Module, debug bool) error {
|
||||||
r := newRunner(mod, debug)
|
r := newRunner(mod, debug)
|
||||||
|
defer r.dispose()
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
bb := initAll.EntryBasicBlock()
|
bb := initAll.EntryBasicBlock()
|
||||||
@@ -121,7 +128,10 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
|
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
|
||||||
// Make sure that any globals touched by the package
|
// Make sure that any globals touched by the package
|
||||||
// initializer, won't be accessed by later package initializers.
|
// initializer, won't be accessed by later package initializers.
|
||||||
r.markExternalLoad(fn)
|
err := r.markExternalLoad(fn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to interpret package %s: %w", r.pkgName, err)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return callErr
|
return callErr
|
||||||
@@ -193,6 +203,7 @@ func RunFunc(fn llvm.Value, debug bool) error {
|
|||||||
// Create and initialize *runner object.
|
// Create and initialize *runner object.
|
||||||
mod := fn.GlobalParent()
|
mod := fn.GlobalParent()
|
||||||
r := newRunner(mod, debug)
|
r := newRunner(mod, debug)
|
||||||
|
defer r.dispose()
|
||||||
initName := fn.Name()
|
initName := fn.Name()
|
||||||
if !strings.HasSuffix(initName, ".init") {
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
||||||
@@ -288,12 +299,16 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
|||||||
// variable. Another package initializer might read from the same global
|
// variable. Another package initializer might read from the same global
|
||||||
// variable. By marking this function as being run at runtime, that load
|
// variable. By marking this function as being run at runtime, that load
|
||||||
// instruction will need to be run at runtime instead of at compile time.
|
// instruction will need to be run at runtime instead of at compile time.
|
||||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
func (r *runner) markExternalLoad(llvmValue llvm.Value) error {
|
||||||
mem := memoryView{r: r}
|
mem := memoryView{r: r}
|
||||||
mem.markExternalLoad(llvmValue)
|
err := mem.markExternalLoad(llvmValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for index, obj := range mem.objects {
|
for index, obj := range mem.objects {
|
||||||
if obj.marked > r.objects[index].marked {
|
if obj.marked > r.objects[index].marked {
|
||||||
r.objects[index].marked = obj.marked
|
r.objects[index].marked = obj.marked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -3,6 +3,7 @@ package interp
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -10,6 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInterp(t *testing.T) {
|
func TestInterp(t *testing.T) {
|
||||||
|
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||||
|
if err != nil {
|
||||||
|
// Note: this should never happen and if it does, it will always happen
|
||||||
|
// for a particular build because llvm.Version is a constant.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
for _, name := range []string{
|
for _, name := range []string{
|
||||||
"basic",
|
"basic",
|
||||||
"phi",
|
"phi",
|
||||||
@@ -19,7 +26,10 @@ func TestInterp(t *testing.T) {
|
|||||||
"revert",
|
"revert",
|
||||||
"alloc",
|
"alloc",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make local to this closure
|
||||||
|
if name == "slice-copy" && llvmVersion < 14 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(t, "testdata/"+name)
|
runTest(t, "testdata/"+name)
|
||||||
@@ -30,6 +40,7 @@ func TestInterp(t *testing.T) {
|
|||||||
func runTest(t *testing.T, pathPrefix string) {
|
func runTest(t *testing.T, pathPrefix string) {
|
||||||
// Read the input IR.
|
// Read the input IR.
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
|
defer ctx.Dispose()
|
||||||
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
||||||
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,6 +50,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not load module:\n%v", err)
|
t.Fatalf("could not load module:\n%v", err)
|
||||||
}
|
}
|
||||||
|
defer mod.Dispose()
|
||||||
|
|
||||||
// Perform the transform.
|
// Perform the transform.
|
||||||
err = Run(mod, false)
|
err = Run(mod, false)
|
||||||
|
|||||||
+56
-4
@@ -24,15 +24,39 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
locals[i] = param
|
locals[i] = param
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track what blocks have run instructions at runtime.
|
||||||
|
// This is used to prevent unrolling.
|
||||||
|
var runtimeBlocks map[int]struct{}
|
||||||
|
|
||||||
// Start with the first basic block and the first instruction.
|
// Start with the first basic block and the first instruction.
|
||||||
// Branch instructions may modify both bb and instIndex when branching.
|
// Branch instructions may modify both bb and instIndex when branching.
|
||||||
bb := fn.blocks[0]
|
bb := fn.blocks[0]
|
||||||
currentBB := 0
|
currentBB := 0
|
||||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||||
var operands []value
|
var operands []value
|
||||||
|
startRTInsts := len(mem.instructions)
|
||||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||||
if instIndex == 0 {
|
if instIndex == 0 {
|
||||||
// This is the start of a new basic block.
|
// This is the start of a new basic block.
|
||||||
|
if len(mem.instructions) != startRTInsts {
|
||||||
|
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||||
|
// This loop has been unrolled.
|
||||||
|
// Avoid doing this, as it can result in a large amount of extra machine code.
|
||||||
|
// This currently uses the branch from the last block, as there is no available information to give a better location.
|
||||||
|
lastBBInsts := fn.blocks[lastBB].instructions
|
||||||
|
return nil, mem, r.errorAt(lastBBInsts[len(lastBBInsts)-1], errLoopUnrolled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the last block as having run stuff at runtime.
|
||||||
|
if runtimeBlocks == nil {
|
||||||
|
runtimeBlocks = make(map[int]struct{})
|
||||||
|
}
|
||||||
|
runtimeBlocks[lastBB] = struct{}{}
|
||||||
|
|
||||||
|
// Reset the block-start runtime instructions counter.
|
||||||
|
startRTInsts = len(mem.instructions)
|
||||||
|
}
|
||||||
|
|
||||||
// There may be PHI nodes that need to be resolved. Resolve all PHI
|
// There may be PHI nodes that need to be resolved. Resolve all PHI
|
||||||
// nodes before continuing with regular instructions.
|
// nodes before continuing with regular instructions.
|
||||||
// PHI nodes need to be treated specially because they can have a
|
// PHI nodes need to be treated specially because they can have a
|
||||||
@@ -81,12 +105,28 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if inst.opcode != llvm.PHI {
|
if inst.opcode != llvm.PHI {
|
||||||
for _, v := range inst.operands {
|
for _, v := range inst.operands {
|
||||||
if v, ok := v.(localValue); ok {
|
if v, ok := v.(localValue); ok {
|
||||||
if localVal := locals[fn.locals[v.value]]; localVal == nil {
|
index, ok := fn.locals[v.value]
|
||||||
|
if !ok {
|
||||||
|
// This is a localValue that is not local to the
|
||||||
|
// function. An example would be an inline assembly call
|
||||||
|
// operand.
|
||||||
|
isRuntimeInst = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
localVal := locals[index]
|
||||||
|
if localVal == nil {
|
||||||
|
// Trying to read a function-local value before it is
|
||||||
|
// set.
|
||||||
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
||||||
} else {
|
} else {
|
||||||
operands = append(operands, localVal)
|
operands = append(operands, localVal)
|
||||||
if _, ok := localVal.(localValue); ok {
|
if _, ok := localVal.(localValue); ok {
|
||||||
|
// The function-local value is still just a
|
||||||
|
// localValue (which can't be interpreted at compile
|
||||||
|
// time). Not sure whether this ever happens in
|
||||||
|
// practice.
|
||||||
isRuntimeInst = true
|
isRuntimeInst = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -219,6 +259,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, err
|
return nil, mem, err
|
||||||
}
|
}
|
||||||
|
case callFn.name == "internal/task.Pause":
|
||||||
|
// Task scheduling isn't possible at compile time.
|
||||||
|
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
|
||||||
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
||||||
// The time package contains a call to runtime.nanotime.
|
// The time package contains a call to runtime.nanotime.
|
||||||
// This appears to be to work around a limitation in Windows
|
// This appears to be to work around a limitation in Windows
|
||||||
@@ -926,12 +969,18 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
args := operands[:len(operands)-1]
|
args := operands[:len(operands)-1]
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
mem.markExternalStore(arg)
|
err := mem.markExternalStore(arg)
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
||||||
case llvm.Load:
|
case llvm.Load:
|
||||||
mem.markExternalLoad(operands[0])
|
err := mem.markExternalLoad(operands[0])
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
result = r.builder.CreateLoad(operands[0], inst.name)
|
result = r.builder.CreateLoad(operands[0], inst.name)
|
||||||
if inst.llvmInst.IsVolatile() {
|
if inst.llvmInst.IsVolatile() {
|
||||||
result.SetVolatile(true)
|
result.SetVolatile(true)
|
||||||
@@ -940,7 +989,10 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
result.SetOrdering(ordering)
|
result.SetOrdering(ordering)
|
||||||
}
|
}
|
||||||
case llvm.Store:
|
case llvm.Store:
|
||||||
mem.markExternalStore(operands[1])
|
err := mem.markExternalStore(operands[1])
|
||||||
|
if err != nil {
|
||||||
|
return r.errorAt(inst, err)
|
||||||
|
}
|
||||||
result = r.builder.CreateStore(operands[0], operands[1])
|
result = r.builder.CreateStore(operands[0], operands[1])
|
||||||
if inst.llvmInst.IsVolatile() {
|
if inst.llvmInst.IsVolatile() {
|
||||||
result.SetVolatile(true)
|
result.SetVolatile(true)
|
||||||
|
|||||||
+41
-14
@@ -17,6 +17,7 @@ package interp
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -104,28 +105,28 @@ func (mv *memoryView) revert() {
|
|||||||
// means that the interpreter can still read from it, but cannot write to it as
|
// means that the interpreter can still read from it, but cannot write to it as
|
||||||
// that would mean the external read (done at runtime) reads from a state that
|
// that would mean the external read (done at runtime) reads from a state that
|
||||||
// would not exist had the whole initialization been done at runtime.
|
// would not exist had the whole initialization been done at runtime.
|
||||||
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) {
|
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error {
|
||||||
mv.markExternal(llvmValue, 1)
|
return mv.markExternal(llvmValue, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// markExternalStore marks the given LLVM value as having an external write.
|
// markExternalStore marks the given LLVM value as having an external write.
|
||||||
// This means that the interpreter can no longer read from it or write to it, as
|
// This means that the interpreter can no longer read from it or write to it, as
|
||||||
// that would happen in a different order than if all initialization were
|
// that would happen in a different order than if all initialization were
|
||||||
// happening at runtime.
|
// happening at runtime.
|
||||||
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) {
|
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error {
|
||||||
mv.markExternal(llvmValue, 2)
|
return mv.markExternal(llvmValue, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// markExternal is a helper for markExternalLoad and markExternalStore, and
|
// markExternal is a helper for markExternalLoad and markExternalStore, and
|
||||||
// should not be called directly.
|
// should not be called directly.
|
||||||
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||||
if llvmValue.IsUndef() || llvmValue.IsNull() {
|
if llvmValue.IsUndef() || llvmValue.IsNull() {
|
||||||
// Null and undef definitely don't contain (valid) pointers.
|
// Null and undef definitely don't contain (valid) pointers.
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
|
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
|
||||||
// These are considered external by default, there is nothing to mark.
|
// These are considered external by default, there is nothing to mark.
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !llvmValue.IsAGlobalValue().IsNil() {
|
if !llvmValue.IsAGlobalValue().IsNil() {
|
||||||
@@ -144,7 +145,10 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
// Using mark '2' (which means read/write access) because
|
// Using mark '2' (which means read/write access) because
|
||||||
// even from an object that is only read from, the resulting
|
// even from an object that is only read from, the resulting
|
||||||
// loaded pointer can be written to.
|
// loaded pointer can be written to.
|
||||||
mv.markExternal(initializer, 2)
|
err := mv.markExternal(initializer, 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is a function. Go through all instructions and mark all
|
// This is a function. Go through all instructions and mark all
|
||||||
@@ -170,7 +174,10 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
for i := 0; i < numOperands; i++ {
|
for i := 0; i < numOperands; i++ {
|
||||||
// Using mark '2' (which means read/write access)
|
// Using mark '2' (which means read/write access)
|
||||||
// because this might be a store instruction.
|
// because this might be a store instruction.
|
||||||
mv.markExternal(inst.Operand(i), 2)
|
err := mv.markExternal(inst.Operand(i), 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,9 +186,22 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
||||||
switch llvmValue.Opcode() {
|
switch llvmValue.Opcode() {
|
||||||
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
|
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
|
||||||
mv.markExternal(llvmValue.Operand(0), mark)
|
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
|
||||||
|
// Integer binary operators. Mark both operands.
|
||||||
|
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = mv.markExternal(llvmValue.Operand(1), mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("interp: unknown constant expression")
|
return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()])
|
||||||
}
|
}
|
||||||
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
||||||
// Inline assembly can modify globals but only exported globals. Let's
|
// Inline assembly can modify globals but only exported globals. Let's
|
||||||
@@ -196,18 +216,25 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
|||||||
numElements := llvmType.StructElementTypesCount()
|
numElements := llvmType.StructElementTypesCount()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := 0; i < numElements; i++ {
|
||||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||||
mv.markExternal(element, mark)
|
err := mv.markExternal(element, mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
numElements := llvmType.ArrayLength()
|
numElements := llvmType.ArrayLength()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := 0; i < numElements; i++ {
|
||||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||||
mv.markExternal(element, mark)
|
err := mv.markExternal(element, mark)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("interp: unknown type kind in markExternalValue")
|
return errors.New("interp: unknown type kind in markExternalValue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasExternalLoadOrStore returns true if this object has an external load or
|
// hasExternalLoadOrStore returns true if this object has an external load or
|
||||||
|
|||||||
Vendored
+45
@@ -3,6 +3,8 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
declare void @externalCall(i64)
|
declare void @externalCall(i64)
|
||||||
|
|
||||||
|
declare i64 @ptrHash(i8* nocapture)
|
||||||
|
|
||||||
@foo.knownAtRuntime = global i64 0
|
@foo.knownAtRuntime = global i64 0
|
||||||
@bar.knownAtRuntime = global i64 0
|
@bar.knownAtRuntime = global i64 0
|
||||||
@baz.someGlobal = external global [3 x {i64, i32}]
|
@baz.someGlobal = external global [3 x {i64, i32}]
|
||||||
@@ -10,6 +12,8 @@ declare void @externalCall(i64)
|
|||||||
@x.atomicNum = global i32 0
|
@x.atomicNum = global i32 0
|
||||||
@x.volatileNum = global i32 0
|
@x.volatileNum = global i32 0
|
||||||
@y.ready = global i32 0
|
@y.ready = global i32 0
|
||||||
|
@z.bloom = global i64 0
|
||||||
|
@z.arr = global [32 x i8] zeroinitializer
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
@@ -19,6 +23,7 @@ entry:
|
|||||||
call void @main.init(i8* undef)
|
call void @main.init(i8* undef)
|
||||||
call void @x.init(i8* undef)
|
call void @x.init(i8* undef)
|
||||||
call void @y.init(i8* undef)
|
call void @y.init(i8* undef)
|
||||||
|
call void @z.init(i8* undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,3 +77,43 @@ loop:
|
|||||||
end:
|
end:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define internal void @z.init(i8* %context) unnamed_addr {
|
||||||
|
%bloom = bitcast i64* @z.bloom to i8*
|
||||||
|
|
||||||
|
; This can be safely expanded.
|
||||||
|
call void @z.setArr(i8* %bloom, i64 1, i8* %bloom)
|
||||||
|
|
||||||
|
; This call should be reverted to prevent unrolling.
|
||||||
|
call void @z.setArr(i8* bitcast ([32 x i8]* @z.arr to i8*), i64 32, i8* %bloom)
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
br label %loop
|
||||||
|
|
||||||
|
loop:
|
||||||
|
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||||
|
%idx = sub i64 %prev, 1
|
||||||
|
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||||
|
call void @z.set(i8* %elem, i8* %context)
|
||||||
|
%done = icmp eq i64 %idx, 0
|
||||||
|
br i1 %done, label %end, label %loop
|
||||||
|
|
||||||
|
end:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||||
|
; Insert the pointer into the Bloom filter.
|
||||||
|
%hash = call i64 @ptrHash(i8* %ptr)
|
||||||
|
%index = lshr i64 %hash, 58
|
||||||
|
%bit = shl i64 1, %index
|
||||||
|
%bloom = bitcast i8* %context to i64*
|
||||||
|
%old = load i64, i64* %bloom
|
||||||
|
%new = or i64 %old, %bit
|
||||||
|
store i64 %new, i64* %bloom
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+33
@@ -8,9 +8,13 @@ target triple = "x86_64--linux"
|
|||||||
@x.atomicNum = local_unnamed_addr global i32 0
|
@x.atomicNum = local_unnamed_addr global i32 0
|
||||||
@x.volatileNum = global i32 0
|
@x.volatileNum = global i32 0
|
||||||
@y.ready = local_unnamed_addr global i32 0
|
@y.ready = local_unnamed_addr global i32 0
|
||||||
|
@z.bloom = global i64 0
|
||||||
|
@z.arr = global [32 x i8] zeroinitializer
|
||||||
|
|
||||||
declare void @externalCall(i64) local_unnamed_addr
|
declare void @externalCall(i64) local_unnamed_addr
|
||||||
|
|
||||||
|
declare i64 @ptrHash(i8* nocapture) local_unnamed_addr
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
call fastcc void @baz.init(i8* undef)
|
call fastcc void @baz.init(i8* undef)
|
||||||
@@ -24,6 +28,8 @@ entry:
|
|||||||
%y = load volatile i32, i32* @x.volatileNum, align 4
|
%y = load volatile i32, i32* @x.volatileNum, align 4
|
||||||
store volatile i32 %y, i32* @x.volatileNum, align 4
|
store volatile i32 %y, i32* @x.volatileNum, align 4
|
||||||
call fastcc void @y.init(i8* undef)
|
call fastcc void @y.init(i8* undef)
|
||||||
|
call fastcc void @z.set(i8* bitcast (i64* @z.bloom to i8*), i8* bitcast (i64* @z.bloom to i8*))
|
||||||
|
call fastcc void @z.setArr(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @z.arr, i32 0, i32 0), i64 32, i8* bitcast (i64* @z.bloom to i8*))
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,3 +54,30 @@ loop: ; preds = %loop, %entry
|
|||||||
end: ; preds = %loop
|
end: ; preds = %loop
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define internal fastcc void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
br label %loop
|
||||||
|
|
||||||
|
loop: ; preds = %loop, %entry
|
||||||
|
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||||
|
%idx = sub i64 %prev, 1
|
||||||
|
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||||
|
call fastcc void @z.set(i8* %elem, i8* %context)
|
||||||
|
%done = icmp eq i64 %idx, 0
|
||||||
|
br i1 %done, label %end, label %loop
|
||||||
|
|
||||||
|
end: ; preds = %loop
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal fastcc void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||||
|
%hash = call i64 @ptrHash(i8* %ptr)
|
||||||
|
%index = lshr i64 %hash, 58
|
||||||
|
%bit = shl i64 1, %index
|
||||||
|
%bloom = bitcast i8* %context to i64*
|
||||||
|
%old = load i64, i64* %bloom, align 8
|
||||||
|
%new = or i64 %old, %bit
|
||||||
|
store i64 %new, i64* %bloom, align 8
|
||||||
|
ret void
|
||||||
|
}
|
||||||
+1
-1
Submodule lib/wasi-libc updated: ad5133410f...30094b6ed0
+283
-13
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/constant"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
@@ -15,16 +16,20 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var addInstances func(*types.Info)
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
type Program struct {
|
type Program struct {
|
||||||
config *compileopts.Config
|
config *compileopts.Config
|
||||||
@@ -61,6 +66,9 @@ type PackageJSON struct {
|
|||||||
CgoFiles []string
|
CgoFiles []string
|
||||||
CFiles []string
|
CFiles []string
|
||||||
|
|
||||||
|
// Embedded files
|
||||||
|
EmbedFiles []string
|
||||||
|
|
||||||
// Dependency information
|
// Dependency information
|
||||||
Imports []string
|
Imports []string
|
||||||
ImportMap map[string]string
|
ImportMap map[string]string
|
||||||
@@ -77,19 +85,28 @@ type PackageJSON struct {
|
|||||||
type Package struct {
|
type Package struct {
|
||||||
PackageJSON
|
PackageJSON
|
||||||
|
|
||||||
program *Program
|
program *Program
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
FileHashes map[string][]byte
|
FileHashes map[string][]byte
|
||||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||||
CGoHeaders []string // text above 'import "C"' lines
|
CGoHeaders []string // text above 'import "C"' lines
|
||||||
Pkg *types.Package
|
EmbedGlobals map[string][]*EmbedFile
|
||||||
info types.Info
|
Pkg *types.Package
|
||||||
|
info types.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmbedFile struct {
|
||||||
|
Name string
|
||||||
|
Size uint64
|
||||||
|
Hash string // hash of the file (as a hex string)
|
||||||
|
NeedsData bool // true if this file is embedded as a byte slice
|
||||||
|
Data []byte // contents of this file (only if NeedsData is set)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||||
// processing, if necessary).
|
// processing, if necessary).
|
||||||
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||||
goroot, err := GetCachedGoroot(config)
|
goroot, err := GetCachedGoroot(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -118,7 +135,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
|||||||
if config.TestConfig.CompileTestBinary {
|
if config.TestConfig.CompileTestBinary {
|
||||||
extraArgs = append(extraArgs, "-test")
|
extraArgs = append(extraArgs, "-test")
|
||||||
}
|
}
|
||||||
cmd, err := List(config, extraArgs, inputPkgs)
|
cmd, err := List(config, extraArgs, []string{inputPkg})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -137,8 +154,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
|||||||
decoder := json.NewDecoder(buf)
|
decoder := json.NewDecoder(buf)
|
||||||
for {
|
for {
|
||||||
pkg := &Package{
|
pkg := &Package{
|
||||||
program: p,
|
program: p,
|
||||||
FileHashes: make(map[string][]byte),
|
FileHashes: make(map[string][]byte),
|
||||||
|
EmbedGlobals: make(map[string][]*EmbedFile),
|
||||||
info: types.Info{
|
info: types.Info{
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
@@ -148,6 +166,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
|||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if addInstances != nil {
|
||||||
|
addInstances(&pkg.info)
|
||||||
|
}
|
||||||
err := decoder.Decode(&pkg.PackageJSON)
|
err := decoder.Decode(&pkg.PackageJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -357,15 +378,15 @@ func (p *Package) Check() error {
|
|||||||
return nil // already typechecked
|
return nil // already typechecked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare some state used during type checking.
|
||||||
var typeErrors []error
|
var typeErrors []error
|
||||||
checker := p.program.typeChecker // make a copy, because it will be modified
|
checker := p.program.typeChecker // make a copy, because it will be modified
|
||||||
checker.Error = func(err error) {
|
checker.Error = func(err error) {
|
||||||
typeErrors = append(typeErrors, err)
|
typeErrors = append(typeErrors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do typechecking of the package.
|
|
||||||
checker.Importer = p
|
checker.Importer = p
|
||||||
|
|
||||||
|
// Do typechecking of the package.
|
||||||
packageName := p.ImportPath
|
packageName := p.ImportPath
|
||||||
if p == p.program.MainPkg() {
|
if p == p.program.MainPkg() {
|
||||||
if p.Name != "main" {
|
if p.Name != "main" {
|
||||||
@@ -382,6 +403,12 @@ func (p *Package) Check() error {
|
|||||||
return Errors{p, typeErrors}
|
return Errors{p, typeErrors}
|
||||||
}
|
}
|
||||||
p.Pkg = typesPkg
|
p.Pkg = typesPkg
|
||||||
|
|
||||||
|
p.extractEmbedLines(checker.Error)
|
||||||
|
if len(typeErrors) != 0 {
|
||||||
|
return Errors{p, typeErrors}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,6 +467,249 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractEmbedLines finds all //go:embed lines in the package and matches them
|
||||||
|
// against EmbedFiles from `go list`.
|
||||||
|
func (p *Package) extractEmbedLines(addError func(error)) {
|
||||||
|
for _, file := range p.Files {
|
||||||
|
// Check for an `import "embed"` line at the start of the file.
|
||||||
|
// //go:embed lines are only valid if the given file itself imports the
|
||||||
|
// embed package. It is not valid if it is only imported in a separate
|
||||||
|
// Go file.
|
||||||
|
hasEmbed := false
|
||||||
|
for _, importSpec := range file.Imports {
|
||||||
|
if importSpec.Path.Value == `"embed"` {
|
||||||
|
hasEmbed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
if decl.Tok != token.VAR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, spec := range decl.Specs {
|
||||||
|
spec := spec.(*ast.ValueSpec)
|
||||||
|
var doc *ast.CommentGroup
|
||||||
|
if decl.Lparen == token.NoPos {
|
||||||
|
// Plain 'var' declaration, like:
|
||||||
|
// //go:embed hello.txt
|
||||||
|
// var hello string
|
||||||
|
doc = decl.Doc
|
||||||
|
} else {
|
||||||
|
// Bigger 'var' declaration like:
|
||||||
|
// var (
|
||||||
|
// //go:embed hello.txt
|
||||||
|
// hello string
|
||||||
|
// )
|
||||||
|
doc = spec.Doc
|
||||||
|
}
|
||||||
|
if doc == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for //go:embed comments.
|
||||||
|
var allPatterns []string
|
||||||
|
for _, comment := range doc.List {
|
||||||
|
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !hasEmbed {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: comment.Pos() + 2,
|
||||||
|
Msg: "//go:embed only allowed in Go files that import \"embed\"",
|
||||||
|
})
|
||||||
|
// Continue, because otherwise we might run into
|
||||||
|
// issues below.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
patterns, err := p.parseGoEmbed(comment.Text[len("//go:embed"):], comment.Slash)
|
||||||
|
if err != nil {
|
||||||
|
addError(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(patterns) == 0 {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: comment.Pos() + 2,
|
||||||
|
Msg: "usage: //go:embed pattern...",
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
// Check that the pattern is well-formed.
|
||||||
|
// It must be valid: the Go toolchain has already
|
||||||
|
// checked for invalid patterns. But let's check
|
||||||
|
// anyway to be sure.
|
||||||
|
if _, err := path.Match(pattern, ""); err != nil {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: comment.Pos(),
|
||||||
|
Msg: "invalid pattern syntax",
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allPatterns = append(allPatterns, pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allPatterns) != 0 {
|
||||||
|
// This is a //go:embed global. Do a few more checks.
|
||||||
|
if len(spec.Names) != 1 {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: spec.Names[1].NamePos,
|
||||||
|
Msg: "//go:embed cannot apply to multiple vars",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if spec.Values != nil {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: spec.Values[0].Pos(),
|
||||||
|
Msg: "//go:embed cannot apply to var with initializer",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
globalName := spec.Names[0].Name
|
||||||
|
globalType := p.Pkg.Scope().Lookup(globalName).Type()
|
||||||
|
valid, byteSlice := isValidEmbedType(globalType)
|
||||||
|
if !valid {
|
||||||
|
addError(types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: spec.Type.Pos(),
|
||||||
|
Msg: "//go:embed cannot apply to var of type " + globalType.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match all //go:embed patterns against the embed files
|
||||||
|
// provided by `go list`.
|
||||||
|
for _, name := range p.EmbedFiles {
|
||||||
|
for _, pattern := range allPatterns {
|
||||||
|
if matchPattern(pattern, name) {
|
||||||
|
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
|
||||||
|
Name: name,
|
||||||
|
NeedsData: byteSlice,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchPattern returns true if (and only if) the given pattern would match the
|
||||||
|
// filename. The pattern could also match a parent directory of name, in which
|
||||||
|
// case hidden files do not match.
|
||||||
|
func matchPattern(pattern, name string) bool {
|
||||||
|
// Match this file.
|
||||||
|
matched, _ := path.Match(pattern, name)
|
||||||
|
if matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match parent directories.
|
||||||
|
dir := name
|
||||||
|
for {
|
||||||
|
dir, _ = path.Split(dir)
|
||||||
|
if dir == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dir = path.Clean(dir)
|
||||||
|
if matched, _ := path.Match(pattern, dir); matched {
|
||||||
|
// Pattern matches the directory.
|
||||||
|
suffix := name[len(dir):]
|
||||||
|
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
|
||||||
|
// Pattern matches a hidden file.
|
||||||
|
// Hidden files are included when listed directly as a
|
||||||
|
// pattern, but not when they are part of a directory tree.
|
||||||
|
// Source:
|
||||||
|
// > If a pattern names a directory, all files in the
|
||||||
|
// > subtree rooted at that directory are embedded
|
||||||
|
// > (recursively), except that files with names beginning
|
||||||
|
// > with ‘.’ or ‘_’ are excluded.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
|
||||||
|
// regular fields and quoted fields (that may contain spaces).
|
||||||
|
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
|
||||||
|
args = strings.TrimSpace(args)
|
||||||
|
initialLen := len(args)
|
||||||
|
for args != "" {
|
||||||
|
patternPos := pos + token.Pos(initialLen-len(args))
|
||||||
|
switch args[0] {
|
||||||
|
case '`', '"', '\\':
|
||||||
|
// Parse the next pattern using the Go scanner.
|
||||||
|
// This is perhaps a bit overkill, but it does correctly implement
|
||||||
|
// parsing of the various Go strings.
|
||||||
|
var sc scanner.Scanner
|
||||||
|
fset := &token.FileSet{}
|
||||||
|
file := fset.AddFile("", 0, len(args))
|
||||||
|
sc.Init(file, []byte(args), nil, 0)
|
||||||
|
_, tok, lit := sc.Scan()
|
||||||
|
if tok != token.STRING || sc.ErrorCount != 0 {
|
||||||
|
// Calculate start of token
|
||||||
|
return nil, types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: patternPos,
|
||||||
|
Msg: "invalid quoted string in //go:embed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
|
||||||
|
patterns = append(patterns, pattern)
|
||||||
|
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
|
||||||
|
default:
|
||||||
|
// The value is just a regular value.
|
||||||
|
// Split it at the first white space.
|
||||||
|
index := strings.IndexFunc(args, unicode.IsSpace)
|
||||||
|
if index < 0 {
|
||||||
|
index = len(args)
|
||||||
|
}
|
||||||
|
pattern := args[:index]
|
||||||
|
patterns = append(patterns, pattern)
|
||||||
|
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
|
||||||
|
}
|
||||||
|
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
|
||||||
|
return nil, types.Error{
|
||||||
|
Fset: p.program.fset,
|
||||||
|
Pos: patternPos,
|
||||||
|
Msg: "invalid pattern syntax",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patterns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidEmbedType returns whether the given Go type can be used as a
|
||||||
|
// //go:embed type. This is only true for embed.FS, strings, and byte slices.
|
||||||
|
// The second return value indicates that this is a byte slice, and therefore
|
||||||
|
// the contents of the file needs to be passed to the compiler.
|
||||||
|
func isValidEmbedType(typ types.Type) (valid, byteSlice bool) {
|
||||||
|
if typ.Underlying() == types.Typ[types.String] {
|
||||||
|
// string type
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
if sliceType, ok := typ.Underlying().(*types.Slice); ok {
|
||||||
|
if elemType, ok := sliceType.Elem().Underlying().(*types.Basic); ok && elemType.Kind() == types.Byte {
|
||||||
|
// byte slice type
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if namedType, ok := typ.(*types.Named); ok && namedType.String() == "embed.FS" {
|
||||||
|
// embed.FS type
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
// Import implements types.Importer. It loads and parses packages it encounters
|
// Import implements types.Importer. It loads and parses packages it encounters
|
||||||
// along the way, if needed.
|
// along the way, if needed.
|
||||||
func (p *Package) Import(to string) (*types.Package, error) {
|
func (p *Package) Import(to string) (*types.Package, error) {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||||
|
// support.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
addInstances = func(info *types.Info) {
|
||||||
|
info.Instances = make(map[*ast.Ident]types.Instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
//
|
//
|
||||||
// The program must already be parsed and type-checked with the .Parse() method.
|
// The program must already be parsed and type-checked with the .Parse() method.
|
||||||
func (p *Program) LoadSSA() *ssa.Program {
|
func (p *Program) LoadSSA() *ssa.Program {
|
||||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
|
||||||
|
|
||||||
for _, pkg := range p.sorted {
|
for _, pkg := range p.sorted {
|
||||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -349,16 +350,14 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
// do we need port reset to put MCU into bootloader mode?
|
// do we need port reset to put MCU into bootloader mode?
|
||||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||||
port, err := getDefaultPort(port, config.Target.SerialPort)
|
port, err := getDefaultPort(port, config.Target.SerialPort)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
err = touchSerialPortAt1200bps(port)
|
||||||
|
if err != nil {
|
||||||
|
return &commandError{"failed to reset port", result.Binary, err}
|
||||||
|
}
|
||||||
|
// give the target MCU a chance to restart into bootloader
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = touchSerialPortAt1200bps(port)
|
|
||||||
if err != nil {
|
|
||||||
return &commandError{"failed to reset port", result.Binary, err}
|
|
||||||
}
|
|
||||||
// give the target MCU a chance to restart into bootloader
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this flashing method copies the binary data to a Mass Storage Device (msd)
|
// this flashing method copies the binary data to a Mass Storage Device (msd)
|
||||||
@@ -1252,6 +1251,35 @@ func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
|
|||||||
return map[string]map[string]string(globalVarValues), nil
|
return map[string]map[string]string(globalVarValues), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getListOfPackages returns a standard list of packages for a given list that might
|
||||||
|
// include wildards using `go list`.
|
||||||
|
// For example [./...] => ["pkg1", "pkg1/pkg12", "pkg2"]
|
||||||
|
func getListOfPackages(pkgs []string, options *compileopts.Options) ([]string, error) {
|
||||||
|
config, err := builder.NewConfig(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd, err := loader.List(config, nil, pkgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to run `go list`: %w", err)
|
||||||
|
}
|
||||||
|
outputBuf := bytes.NewBuffer(nil)
|
||||||
|
cmd.Stdout = outputBuf
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkgNames []string
|
||||||
|
sc := bufio.NewScanner(outputBuf)
|
||||||
|
for sc.Scan() {
|
||||||
|
pkgNames = append(pkgNames, sc.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||||
@@ -1487,14 +1515,21 @@ func main() {
|
|||||||
if len(pkgNames) == 0 {
|
if len(pkgNames) == 0 {
|
||||||
pkgNames = []string{"."}
|
pkgNames = []string{"."}
|
||||||
}
|
}
|
||||||
if outpath != "" && len(pkgNames) > 1 {
|
|
||||||
|
explicitPkgNames, err := getListOfPackages(pkgNames, options)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("cannot resolve packages: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if outpath != "" && len(explicitPkgNames) > 1 {
|
||||||
fmt.Println("cannot use -o flag with multiple packages")
|
fmt.Println("cannot use -o flag with multiple packages")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fail := make(chan struct{}, 1)
|
fail := make(chan struct{}, 1)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
bufs := make([]testOutputBuf, len(pkgNames))
|
bufs := make([]testOutputBuf, len(explicitPkgNames))
|
||||||
for i := range bufs {
|
for i := range bufs {
|
||||||
bufs[i].done = make(chan struct{})
|
bufs[i].done = make(chan struct{})
|
||||||
}
|
}
|
||||||
@@ -1520,7 +1555,7 @@ func main() {
|
|||||||
// Build and run the tests concurrently.
|
// Build and run the tests concurrently.
|
||||||
// This uses an additional semaphore to reduce the memory usage.
|
// This uses an additional semaphore to reduce the memory usage.
|
||||||
testSema := make(chan struct{}, cap(options.Semaphore))
|
testSema := make(chan struct{}, cap(options.Semaphore))
|
||||||
for i, pkgName := range pkgNames {
|
for i, pkgName := range explicitPkgNames {
|
||||||
pkgName := pkgName
|
pkgName := pkgName
|
||||||
buf := &bufs[i]
|
buf := &bufs[i]
|
||||||
testSema <- struct{}{}
|
testSema <- struct{}{}
|
||||||
|
|||||||
+83
-40
@@ -13,6 +13,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -48,6 +49,7 @@ func TestBuild(t *testing.T) {
|
|||||||
"calls.go",
|
"calls.go",
|
||||||
"cgo/",
|
"cgo/",
|
||||||
"channel.go",
|
"channel.go",
|
||||||
|
"embed/",
|
||||||
"float.go",
|
"float.go",
|
||||||
"gc.go",
|
"gc.go",
|
||||||
"goroutines.go",
|
"goroutines.go",
|
||||||
@@ -74,6 +76,7 @@ func TestBuild(t *testing.T) {
|
|||||||
tests = append(tests, "go1.17.go")
|
tests = append(tests, "go1.17.go")
|
||||||
}
|
}
|
||||||
if minor >= 18 {
|
if minor >= 18 {
|
||||||
|
tests = append(tests, "generics.go")
|
||||||
tests = append(tests, "testing_go118.go")
|
tests = append(tests, "testing_go118.go")
|
||||||
} else {
|
} else {
|
||||||
tests = append(tests, "testing.go")
|
tests = append(tests, "testing.go")
|
||||||
@@ -146,46 +149,7 @@ func TestBuild(t *testing.T) {
|
|||||||
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
|
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
|
||||||
// This bug is non-deterministic.
|
// This bug is non-deterministic.
|
||||||
t.Skip("skipped due to non-deterministic backend bugs")
|
t.Skip("skipped due to non-deterministic backend bugs")
|
||||||
|
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
||||||
var avrTests []string
|
|
||||||
for _, t := range tests {
|
|
||||||
switch t {
|
|
||||||
case "atomic.go":
|
|
||||||
// Requires GCC 11.2.0 or above for interface comparison.
|
|
||||||
// https://github.com/gcc-mirror/gcc/commit/f30dd607669212de135dec1f1d8a93b8954c327c
|
|
||||||
|
|
||||||
case "reflect.go":
|
|
||||||
// Reflect tests do not work due to type code issues.
|
|
||||||
|
|
||||||
case "gc.go":
|
|
||||||
// Does not pass due to high mark false positive rate.
|
|
||||||
|
|
||||||
case "json.go", "stdlib.go", "testing.go":
|
|
||||||
// Breaks interp.
|
|
||||||
|
|
||||||
case "map.go":
|
|
||||||
// Reflect size calculation crashes.
|
|
||||||
|
|
||||||
case "binop.go":
|
|
||||||
// Interface comparison results are inverted.
|
|
||||||
|
|
||||||
case "channel.go":
|
|
||||||
// Freezes after recv from closed channel.
|
|
||||||
|
|
||||||
case "float.go", "math.go", "print.go":
|
|
||||||
// Stuck in runtime.printfloat64.
|
|
||||||
|
|
||||||
case "interface.go":
|
|
||||||
// Several comparison tests fail.
|
|
||||||
|
|
||||||
case "cgo/":
|
|
||||||
// CGo does not work on AVR.
|
|
||||||
|
|
||||||
default:
|
|
||||||
avrTests = append(avrTests, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runPlatTests(optionsFromTarget("simavr", sema), avrTests, t)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
@@ -217,6 +181,37 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range tests {
|
for _, name := range tests {
|
||||||
|
if options.Target == "simavr" {
|
||||||
|
// Not all tests are currently supported on AVR.
|
||||||
|
// Skip the ones that aren't.
|
||||||
|
switch name {
|
||||||
|
case "reflect.go":
|
||||||
|
// Reflect tests do not work due to type code issues.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "gc.go":
|
||||||
|
// Does not pass due to high mark false positive rate.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "json.go", "stdlib.go", "testing.go", "testing_go118.go":
|
||||||
|
// Breaks interp.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "channel.go":
|
||||||
|
// Freezes after recv from closed channel.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "math.go":
|
||||||
|
// Stuck somewhere, not sure what's happening.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "cgo/":
|
||||||
|
// CGo does not work on AVR.
|
||||||
|
continue
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
name := name // redefine to avoid race condition
|
name := name // redefine to avoid race condition
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
@@ -249,6 +244,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
runTest("rand.go", options, t, nil, nil)
|
runTest("rand.go", options, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if options.Target != "wasi" && options.Target != "wasm" {
|
||||||
|
// The recover() builtin isn't supported yet on WebAssembly and Windows.
|
||||||
|
t.Run("recover.go", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runTest("recover.go", options, t, nil, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func emuCheck(t *testing.T, options compileopts.Options) {
|
func emuCheck(t *testing.T, options compileopts.Options) {
|
||||||
@@ -347,6 +349,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
||||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
||||||
actual = bytes.Replace(actual, []byte{'.', '.', '\n'}, []byte{'\n'}, -1)
|
actual = bytes.Replace(actual, []byte{'.', '.', '\n'}, []byte{'\n'}, -1)
|
||||||
|
actual = bytes.Replace(actual, []byte{'\n', '.', '\n'}, []byte{'\n', '\n'}, -1)
|
||||||
}
|
}
|
||||||
if name == "testing.go" {
|
if name == "testing.go" {
|
||||||
// Strip actual time.
|
// Strip actual time.
|
||||||
@@ -527,6 +530,46 @@ func ioLogger(t *testing.T, wg *sync.WaitGroup) io.WriteCloser {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetListOfPackages(t *testing.T) {
|
||||||
|
opts := optionsFromTarget("", sema)
|
||||||
|
tests := []struct {
|
||||||
|
pkgs []string
|
||||||
|
expectedPkgs []string
|
||||||
|
expectesError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pkgs: []string{"./tests/testing/recurse/..."},
|
||||||
|
expectedPkgs: []string{
|
||||||
|
"github.com/tinygo-org/tinygo/tests/testing/recurse",
|
||||||
|
"github.com/tinygo-org/tinygo/tests/testing/recurse/subdir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pkgs: []string{"./tests/testing/pass"},
|
||||||
|
expectedPkgs: []string{
|
||||||
|
"github.com/tinygo-org/tinygo/tests/testing/pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pkgs: []string{"./tests/testing"},
|
||||||
|
expectesError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
actualPkgs, err := getListOfPackages(test.pkgs, &opts)
|
||||||
|
if err != nil && !test.expectesError {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
} else if err == nil && test.expectesError {
|
||||||
|
t.Error("expected error, but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(test.expectedPkgs, actualPkgs) {
|
||||||
|
t.Errorf("expected two slices to be equal, expected %v got %v", test.expectedPkgs, actualPkgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||||
// to recursive tests.
|
// to recursive tests.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build darwin || tinygo.wasm
|
||||||
// +build darwin tinygo.wasm
|
// +build darwin tinygo.wasm
|
||||||
|
|
||||||
// This implementation of crypto/rand uses the arc4random_buf function
|
// This implementation of crypto/rand uses the arc4random_buf function
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build linux && !baremetal && !wasi
|
||||||
// +build linux,!baremetal,!wasi
|
// +build linux,!baremetal,!wasi
|
||||||
|
|
||||||
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to
|
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to
|
||||||
|
|||||||
@@ -193,12 +193,12 @@ func enableIcache(enable bool) {
|
|||||||
arm.Asm("dsb 0xF")
|
arm.Asm("dsb 0xF")
|
||||||
arm.Asm("isb 0xF")
|
arm.Asm("isb 0xF")
|
||||||
} else {
|
} else {
|
||||||
arm.Asm("dsb 0xF")
|
arm.Asm("dsb 0xF")
|
||||||
arm.Asm("isb 0xF")
|
arm.Asm("isb 0xF")
|
||||||
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
||||||
SystemControl.ICIALLU.Set(0)
|
SystemControl.ICIALLU.Set(0)
|
||||||
arm.Asm("dsb 0xF")
|
arm.Asm("dsb 0xF")
|
||||||
arm.Asm("isb 0xF")
|
arm.Asm("isb 0xF")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// This is a echo console running on the os.Stdin and os.Stdout.
|
||||||
|
// Stdin and os.Stdout are connected to machine.Serial in the baremetal target.
|
||||||
|
//
|
||||||
|
// Serial can be switched with the -serial option as follows
|
||||||
|
// 1. tinygo flash -target wioterminal -serial usb examples/echo2
|
||||||
|
// 2. tinygo flash -target wioterminal -serial uart examples/echo2
|
||||||
|
//
|
||||||
|
// This example will also work with standard Go.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Printf("Echo console enabled. Type something then press enter:\r\n")
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
|
||||||
|
for {
|
||||||
|
msg := ""
|
||||||
|
fmt.Scanf("%s\n", &msg)
|
||||||
|
fmt.Printf("You typed (scanf) : %s\r\n", msg)
|
||||||
|
|
||||||
|
if scanner.Scan() {
|
||||||
|
fmt.Printf("You typed (scanner) : %s\r\n", scanner.Text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"machine/usb/hid/keyboard"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
button := machine.BUTTON
|
||||||
|
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
|
kb := keyboard.New()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if !button.Get() {
|
||||||
|
kb.Write([]byte("tinygo"))
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"machine/usb/hid/mouse"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
button := machine.BUTTON
|
||||||
|
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
|
mouse := mouse.New()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if !button.Get() {
|
||||||
|
for j := 0; j < 5; j++ {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
mouse.Move(1, 0)
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
mouse.Move(0, 1)
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
mouse.Move(-1, -1)
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// This reads from UART1 and outputs to default serial, usually UART0 or USB.
|
||||||
|
// Example of how to work with UARTs other than the default.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
uart = machine.UART1
|
||||||
|
tx = machine.UART1_TX_PIN
|
||||||
|
rx = machine.UART1_RX_PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||||
|
for {
|
||||||
|
if uart.Buffered() > 0 {
|
||||||
|
data, _ := uart.ReadByte()
|
||||||
|
print(string(data))
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"machine/usb/midi"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Try it easily by opening the following site in Chrome.
|
||||||
|
// https://www.onlinemusictools.com/kb/
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
led := machine.LED
|
||||||
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
|
button := machine.BUTTON
|
||||||
|
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
|
m := midi.New()
|
||||||
|
m.SetHandler(func(b []byte) {
|
||||||
|
led.Set(!led.Get())
|
||||||
|
fmt.Printf("% X\r\n", b)
|
||||||
|
m.Write(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
prev := true
|
||||||
|
chords := []struct {
|
||||||
|
name string
|
||||||
|
keys []midi.Note
|
||||||
|
}{
|
||||||
|
{name: "C ", keys: []midi.Note{midi.C4, midi.E4, midi.G4}},
|
||||||
|
{name: "G ", keys: []midi.Note{midi.G3, midi.B3, midi.D4}},
|
||||||
|
{name: "Am", keys: []midi.Note{midi.A3, midi.C4, midi.E4}},
|
||||||
|
{name: "F ", keys: []midi.Note{midi.F3, midi.A3, midi.C4}},
|
||||||
|
}
|
||||||
|
index := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
current := button.Get()
|
||||||
|
if prev != current {
|
||||||
|
led.Set(current)
|
||||||
|
if current {
|
||||||
|
for _, c := range chords[index].keys {
|
||||||
|
m.NoteOff(0, 0, c, 0x40)
|
||||||
|
}
|
||||||
|
index = (index + 1) % len(chords)
|
||||||
|
} else {
|
||||||
|
for _, c := range chords[index].keys {
|
||||||
|
m.NoteOn(0, 0, c, 0x40)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = current
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,7 +115,7 @@ func CheckCorpus(vals []any, types []reflect.Type) error {
|
|||||||
return errors.New("not implemented")
|
return errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResetCoverage() {}
|
func ResetCoverage() {}
|
||||||
func SnapshotCoverage() {}
|
func SnapshotCoverage() {}
|
||||||
|
|
||||||
// RunFuzzWorker is called in a worker process to communicate with the
|
// RunFuzzWorker is called in a worker process to communicate with the
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ type Task struct {
|
|||||||
|
|
||||||
// state is the underlying running state of the task.
|
// state is the underlying running state of the task.
|
||||||
state state
|
state state
|
||||||
|
|
||||||
|
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
|
||||||
|
// goroutine that is used for the recover builtin.
|
||||||
|
DeferFrame unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
|
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
|
||||||
|
|||||||
@@ -99,9 +99,9 @@ func Pause() {
|
|||||||
//export tinygo_unwind
|
//export tinygo_unwind
|
||||||
func (*stackState) unwind()
|
func (*stackState) unwind()
|
||||||
|
|
||||||
// Resume the task until it pauses or completes.
|
// Switch to this task until it pauses or completes.
|
||||||
// This may only be called from the scheduler.
|
// This may only be called from the scheduler.
|
||||||
func (t *Task) Resume() {
|
func (t *Task) Switch() {
|
||||||
// The current task must be saved and restored because this can nest on WASM with JS.
|
// The current task must be saved and restored because this can nest on WASM with JS.
|
||||||
prevTask := currentTask
|
prevTask := currentTask
|
||||||
t.gcData.swap()
|
t.gcData.swap()
|
||||||
@@ -121,9 +121,3 @@ func (t *Task) Resume() {
|
|||||||
|
|
||||||
//export tinygo_rewind
|
//export tinygo_rewind
|
||||||
func (*state) rewind()
|
func (*state) rewind()
|
||||||
|
|
||||||
// OnSystemStack returns whether the caller is running on the system stack.
|
|
||||||
func OnSystemStack() bool {
|
|
||||||
// If there is not an active goroutine, then this must be running on the system stack.
|
|
||||||
return Current() == nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
//go:build scheduler.none
|
||||||
// +build scheduler.none
|
// +build scheduler.none
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
|
// There is only one goroutine so the task struct can be a global.
|
||||||
|
var mainTask Task
|
||||||
|
|
||||||
//go:linkname runtimePanic runtime.runtimePanic
|
//go:linkname runtimePanic runtime.runtimePanic
|
||||||
func runtimePanic(str string)
|
func runtimePanic(str string)
|
||||||
|
|
||||||
@@ -12,8 +16,8 @@ func Pause() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Current() *Task {
|
func Current() *Task {
|
||||||
runtimePanic("scheduler is disabled")
|
// Return a task struct, which is used for the recover builtin for example.
|
||||||
return nil
|
return &mainTask
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:noinline
|
//go:noinline
|
||||||
@@ -24,12 +28,12 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
|||||||
|
|
||||||
type state struct{}
|
type state struct{}
|
||||||
|
|
||||||
func (t *Task) Resume() {
|
func (t *Task) Switch() {
|
||||||
runtimePanic("scheduler is disabled")
|
runtimePanic("scheduler is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSystemStack returns whether the caller is running on the system stack.
|
// MainTask returns whether the caller is running in the main goroutine.
|
||||||
func OnSystemStack() bool {
|
func MainTask() bool {
|
||||||
// This scheduler does not do any stack switching.
|
// This scheduler does not do any stack switching.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user