Compare commits

..

1 Commits

Author SHA1 Message Date
BCG 277b3f71e3 Added no-op io.Reader implementation to machine.NullSerial 2022-01-29 15:19:25 -05:00
752 changed files with 8692 additions and 23834 deletions
+25 -32
View File
@@ -22,16 +22,15 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-14-v3 - llvm-source-13-v1
- 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-v3 key: llvm-source-13-v1
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project/clang/lib/Headers
- llvm-project/clang/include - llvm-project/clang/include
- llvm-project/compiler-rt
- llvm-project/lld/include - llvm-project/lld/include
- llvm-project/llvm/include - llvm-project/llvm/include
hack-ninja-jobs: hack-ninja-jobs:
@@ -45,32 +44,29 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- binaryen-linux-v2 - binaryen-linux-v1
- run: - run:
name: "Build Binaryen" name: "Build Binaryen"
command: | command: |
make binaryen make binaryen
- save_cache: - save_cache:
key: binaryen-linux-v2 key: binaryen-linux-v1
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' > /etc/apt/sources.list.d/llvm.list echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
apt-get update sudo apt-get update
apt-get install --no-install-recommends -y \ sudo apt-get install --no-install-recommends \
llvm-<<parameters.llvm>>-dev \ llvm-<<parameters.llvm>>-dev \
clang-<<parameters.llvm>> \ clang-<<parameters.llvm>> \
libclang-<<parameters.llvm>>-dev \ libclang-<<parameters.llvm>>-dev \
@@ -83,46 +79,43 @@ commands:
- build-binaryen-linux - build-binaryen-linux
- restore_cache: - restore_cache:
keys: keys:
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v3-{{ checksum "go.mod" }} - go-cache-v2-{{ 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-v6 - wasi-libc-sysroot-systemclang-v3
- run: make wasi-libc - run: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-systemclang-v6 key: wasi-libc-sysroot-systemclang-v3
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-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }} key: go-cache-v2-{{ 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-llvm14-go118: test-llvm11-go115:
docker: docker:
- image: golang:1.18-buster - image: circleci/golang:1.15-buster
steps: steps:
- test-linux: - test-linux:
llvm: "14" llvm: "11"
resource_class: large test-llvm12-go116:
docker:
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "12"
workflows: workflows:
test-all: test-all:
jobs: jobs:
# This tests our lowest supported versions of Go and LLVM, to make sure at - test-llvm11-go115
# least the smoke tests still pass. - test-llvm12-go116
- test-llvm14-go118
+11 -38
View File
@@ -7,15 +7,15 @@ 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
runs-on: macos-11 runs-on: macos-10.15
steps: steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
@@ -31,30 +31,24 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.19'
cache: true
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-macos-v1 key: llvm-source-13-macos-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Cache LLVM build - name: Cache LLVM build
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-macos-v1 key: llvm-build-13-macos-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'
@@ -69,10 +63,10 @@ jobs:
make llvm-build make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache wasi-libc sysroot - name: Cache wasi-libc sysroot
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-v4 key: wasi-libc-sysroot-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'
@@ -82,8 +76,6 @@ jobs:
run: make test GOTESTFLAGS="-v -short" run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball - name: Build TinyGo release tarball
run: make release -j3 run: make release -j3
- name: Test stdlib packages
run: make tinygo-test
- name: Make release artifact - name: Make release artifact
shell: bash shell: bash
run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz
@@ -101,22 +93,3 @@ jobs:
- name: Smoke tests - name: Smoke tests
shell: bash shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0
test-macos-homebrew:
name: homebrew-install
runs-on: macos-latest
steps:
- name: Install LLVM
shell: bash
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@14
- name: Checkout
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.19'
cache: true
- name: Build TinyGo
run: go install
- name: Check binary
run: tinygo version
+2 -32
View File
@@ -7,10 +7,6 @@ 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
@@ -55,35 +51,9 @@ 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 Github Actions - name: Trigger Drivers repo build on CircleCI
run: | run: |
curl -X POST \ curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/drivers/pipeline' \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger Bluetooth repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFS repo build on CircleCI
run: |
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
- name: Trigger TinyFont repo build on CircleCI
run: |
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfont/pipeline' \
--header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
- name: Trigger TinyDraw repo build on CircleCI
run: |
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinydraw/pipeline' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
-d '{"branch": "dev"}' \ -d '{"branch": "dev"}' \
-u "${{ secrets.CIRCLECI_API_TOKEN }}" -u "${{ secrets.CIRCLECI_API_TOKEN }}"
+48 -257
View File
@@ -7,57 +7,51 @@ 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 runs inside an Alpine Linux container so we can more easily create a # This intentionally uses an older Linux image, so that we compile against
# statically linked binary. # an older glibc version and therefore are compatible with a wide range of
runs-on: ubuntu-latest # Linux distributions.
container: runs-on: ubuntu-18.04
image: golang:1.19-alpine
steps: steps:
- name: Install apk dependencies
# tar: needed for actions/cache@v3
# git+openssh: needed for checkout (I think?)
# ruby: needed to install fpm
run: apk add tar git openssh 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: Cache Go - name: Install apt dependencies
uses: actions/cache@v3 run: |
sudo apt-get install --no-install-recommends \
ninja-build
- name: Install Go
uses: actions/setup-go@v2
with: with:
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }} go-version: '1.17'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-v1-${{ hashFiles('go.mod') }}
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-linux-alpine-v1 key: llvm-source-13-linux-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Cache LLVM build - name: Cache LLVM build
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-linux-alpine-v1 key: llvm-build-13-linux-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'
@@ -65,46 +59,41 @@ 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).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-binaryen id: cache-binaryen
with: with:
key: binaryen-linux-alpine-v1 key: binaryen-linux-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: | run: make binaryen
apk add cmake samurai python3
make binaryen STATIC=1
- name: Cache wasi-libc - name: Cache wasi-libc
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-linux-alpine-v1 key: wasi-libc-sysroot-linux-asserts-v2
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: |
gem install --version 4.0.7 public_suffix sudo apt-get install ruby ruby-dev
gem install --version 2.7.6 dotenv sudo gem install --no-document fpm
gem install --no-document fpm
- name: Build TinyGo release - name: Build TinyGo release
run: | run: |
make release deb -j3 STATIC=1 make release deb -j3
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb cp -p build/release.deb /tmp/tinygo_amd64.deb
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: linux-amd64-double-zipped name: release-double-zipped
path: | path: |
/tmp/tinygo.linux-amd64.tar.gz /tmp/tinygo.linux-amd64.tar.gz
/tmp/tinygo_amd64.deb /tmp/tinygo_amd64.deb
@@ -116,10 +105,9 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v2
with: with:
go-version: '1.19' go-version: '1.17'
cache: true
- name: Install wasmtime - name: Install wasmtime
run: | run: |
curl https://wasmtime.dev/install.sh -sSf | bash curl https://wasmtime.dev/install.sh -sSf | bash
@@ -127,7 +115,7 @@ jobs:
- name: Download release artifact - name: Download release artifact
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
name: linux-amd64-double-zipped name: release-double-zipped
- name: Extract release tarball - name: Extract release tarball
run: | run: |
mkdir -p ~/lib mkdir -p ~/lib
@@ -169,37 +157,42 @@ jobs:
simavr \ simavr \
ninja-build ninja-build
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v2
with: with:
go-version: '1.19' go-version: '1.17'
cache: true
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: '14' node-version: '12'
- name: Install wasmtime - name: Install wasmtime
run: | run: |
curl https://wasmtime.dev/install.sh -sSf | bash curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-asserts-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-linux-asserts-v2 key: llvm-source-13-linux-asserts-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Cache LLVM build - name: Cache LLVM build
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-linux-asserts-v1 key: llvm-build-13-linux-asserts-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'
@@ -212,7 +205,7 @@ jobs:
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-binaryen id: cache-binaryen
with: with:
key: binaryen-linux-asserts-v1 key: binaryen-linux-asserts-v1
@@ -221,10 +214,10 @@ jobs:
if: steps.cache-binaryen.outputs.cache-hit != 'true' if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen run: make binaryen
- name: Cache wasi-libc - name: Cache wasi-libc
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-linux-asserts-v5 key: wasi-libc-sysroot-linux-asserts-v2
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'
@@ -236,8 +229,6 @@ jobs:
run: | run: |
make ASSERT=1 make ASSERT=1
echo "$(pwd)/build" >> $GITHUB_PATH echo "$(pwd)/build" >> $GITHUB_PATH
- name: Test machine package
run: make check-machine
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test run: make tinygo-test
- name: Install Xtensa toolchain - name: Install Xtensa toolchain
@@ -248,203 +239,3 @@ jobs:
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
- run: make smoketest - run: make smoketest
- run: make wasmtest - run: make wasmtest
- run: make tinygo-baremetal
build-linux-arm:
# Build ARM Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of
# Linux distributions.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
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++-arm-linux-gnueabihf \
libc6-dev-armhf-cross
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.19'
cache: true
- name: Cache LLVM source
uses: actions/cache@v3
id: cache-llvm-source
with:
key: llvm-source-14-linux-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v3
id: cache-llvm-build
with:
key: llvm-build-14-linux-arm-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
# Install build dependencies.
sudo apt-get install --no-install-recommends ninja-build
# build!
make llvm-build CROSS=arm-linux-gnueabihf
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-arm-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
sudo apt-get install --no-install-recommends ninja-build
git submodule update --init lib/binaryen
make CROSS=arm-linux-gnueabihf binaryen
- name: Install fpm
run: |
sudo gem install --version 4.0.7 public_suffix
sudo gem install --version 2.7.6 dotenv
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=arm-linux-gnueabihf
- 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 arm release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=armhf
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: linux-arm-double-zipped
path: |
/tmp/tinygo.linux-arm.tar.gz
/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@v3
with:
go-version: '1.19'
cache: true
- name: Cache LLVM source
uses: actions/cache@v3
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@v3
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@v3
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 4.0.7 public_suffix
sudo gem install --version 2.7.6 dotenv
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=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
+18 -25
View File
@@ -7,17 +7,15 @@ 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-2019
steps: steps:
- uses: brechtm/setup-scoop@v2 - name: Install Go
uses: actions/setup-go@v2
with: with:
scoop_update: 'false' go-version: '1.17'
- uses: MinoruSekine/setup-scoop@v1
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
@@ -26,30 +24,31 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Install Go - name: Cache Go
uses: actions/setup-go@v3 uses: actions/cache@v2
with: with:
go-version: '1.19' key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
cache: true path: |
~/AppData/Local/go-build
~/go/pkg/mod
- name: Cache LLVM source - name: Cache LLVM source
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-windows-v2 key: llvm-source-13-windows-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Cache LLVM build - name: Cache LLVM build
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-windows-v2 key: llvm-build-13-windows-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'
@@ -59,26 +58,22 @@ jobs:
rm -rf llvm-project rm -rf llvm-project
make llvm-source make llvm-source
# build! # build!
make llvm-build CCACHE=OFF make llvm-build
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache wasi-libc sysroot - name: Cache wasi-libc sysroot
uses: actions/cache@v3 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-v4 key: wasi-libc-sysroot-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 wasmtime
run: |
scoop install wasmtime
- name: Test TinyGo - name: Test TinyGo
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
@@ -100,5 +95,3 @@ jobs:
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0 run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test run: make tinygo-test
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast
+1 -2
View File
@@ -23,7 +23,6 @@ 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
@@ -31,4 +30,4 @@ test.gba
test.hex test.hex
test.nro test.nro
test.wasm test.wasm
wasm.wasm wasm.wasm
+4 -3
View File
@@ -10,6 +10,10 @@
[submodule "lib/cmsis-svd"] [submodule "lib/cmsis-svd"]
path = lib/cmsis-svd path = lib/cmsis-svd
url = https://github.com/tinygo-org/cmsis-svd url = https://github.com/tinygo-org/cmsis-svd
[submodule "lib/compiler-rt"]
path = lib/compiler-rt
url = https://github.com/llvm-mirror/compiler-rt.git
branch = release_80
[submodule "lib/wasi-libc"] [submodule "lib/wasi-libc"]
path = lib/wasi-libc path = lib/wasi-libc
url = https://github.com/CraneStation/wasi-libc url = https://github.com/CraneStation/wasi-libc
@@ -28,6 +32,3 @@
[submodule "lib/mingw-w64"] [submodule "lib/mingw-w64"]
path = lib/mingw-w64 path = lib/mingw-w64
url = https://github.com/mingw-w64/mingw-w64.git url = https://github.com/mingw-w64/mingw-w64.git
[submodule "lib/macos-minimal-sdk"]
path = lib/macos-minimal-sdk
url = https://github.com/aykevl/macos-minimal-sdk.git
+1 -1
View File
@@ -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.18+) * Go (1.15+)
* Standard build tools (gcc/clang) * Standard build tools (gcc/clang)
* git * git
* CMake * CMake
-205
View File
@@ -1,208 +1,3 @@
0.25.0
---
* **command line**
- change to ignore PortReset failures
* **compiler**
- `compiler`: darwin/arm64 is aarch64, not arm
- `compiler`: don't clobber X18 and FP registers on darwin/arm64
- `compiler`: fix issue with methods on generic structs
- `compiler`: do not try to build generic functions
- `compiler`: fix type names for generic named structs
- `compiler`: fix multiple defined function issue for generic functions
- `compiler`: implement `unsafe.Alignof` and `unsafe.Sizeof` for generic code
* **standard library**
- `machine`: add DTR and RTS to Serialer interface
- `machine`: reorder pin definitions to improve pin list on tinygo.org
- `machine/usb`: add support for MIDI
- `machine/usb`: adjust buffer alignment (samd21, samd51, nrf52840)
- `machine/usb/midi`: add `NoteOn`, `NoteOff`, and `SendCC` methods
- `machine/usb/midi`: add definition of MIDI note number
- `runtime`: add benchmarks for memhash
- `runtime`: add support for printing slices via print/println
* **targets**
- `avr`: fix some apparent mistake in atmega1280/atmega2560 pin constants
- `esp32`: provide hardware pin constants
- `esp32`: fix WDT reset on the MCH2022 badge
- `esp32`: optimize SPI transmit
- `esp32c3`: provide hardware pin constants
- `esp8266`: provide hardware pin constants like `GPIO2`
- `nrf51`: define and use `P0_xx` constants
- `nrf52840`, `samd21`, `samd51`: unify bootloader entry process
- `nrf52840`, `samd21`, `samd51`: change usbSetup and sendZlp to public
- `nrf52840`, `samd21`, `samd51`: refactor handleStandardSetup and initEndpoint
- `nrf52840`, `samd21`, `samd51`: improve usb-device initialization
- `nrf52840`, `samd21`, `samd51`: move usbcdc to machine/usb/cdc
- `rp2040`: add usb serial vendor/product ID
- `rp2040`: add support for usb
- `rp2040`: change default for serial to usb
- `rp2040`: add support for `machine.EnterBootloader`
- `rp2040`: turn off pullup/down when input type is not specified
- `rp2040`: make picoprobe default openocd interface
- `samd51`: add support for `DAC1`
- `samd51`: improve TRNG
- `wasm`: stub `runtime.buffered`, `runtime.getchar`
- `wasi`: make leveldb runtime hash the default
* **boards**
- add Challenger RP2040 LoRa
- add MCH2022 badge
- add XIAO RP2040
- `clue`: remove pins `D21`..`D28`
- `feather-rp2040`, `macropad-rp2040`: fix qspi-flash settings
- `xiao-ble`: add support for flash-1200-bps-reset
- `gopherbot`, `gopherbot2`: add these aliases to simplify for newer users
0.24.0
---
* **command line**
- remove support for go 1.15
- remove support for LLVM 11 and LLVM 12
- add initial Go 1.19 beta support
- `test`: fix package/... syntax
* **compiler**
- add support for the embed package
- `builder`: improve error message for "command not found"
- `builder`: add support for ThinLTO on MacOS and Windows
- `builder`: free LLVM objects after use, to reduce memory leaking
- `builder`: improve `-no-debug` error messages
- `cgo`: be more strict: CGo now requires every Go file to import the headers it needs
- `compiler`: alignof(func) is 1 pointer, not 2
- `compiler`: add support for type parameters (aka generics)
- `compiler`: implement `recover()` built-in function
- `compiler`: support atomic, volatile, and LLVM memcpy-like functions in defer
- `compiler`: drop support for macos syscalls via inline assembly
- `interp`: do not try to interpret past task.Pause()
- `interp`: fix some buggy localValue handling
- `interp`: do not unroll loops
- `transform`: fix MakeGCStackSlots that caused a possible GC bug on WebAssembly
* **standard library**
- `os`: enable os.Stdin for baremetal target
- `reflect`: add `Value.UnsafePointer` method
- `runtime`: scan GC globals conservatively on Windows, MacOS, Linux and Nintendo Switch
- `runtime`: add per-map hash seeds
- `runtime`: handle nil map write panics
- `runtime`: add stronger hash functions
- `syscall`: implement `Getpagesize`
* **targets**
- `atmega2560`: support UART1-3 + example for uart
- `avr`: use compiler-rt for improved float64 support
- `avr`: simplify timer-based time
- `avr`: fix race condition in stack write
- `darwin`: add support for `GOARCH=arm64` (aka Apple Silicon)
- `darwin`: support `-size=short` and `-size=full` flag
- `rp2040`: replace sleep 'busy loop' with timer alarm
- `rp2040`: align api for `PortMaskSet`, `PortMaskClear`
- `rp2040`: fix GPIO interrupts
- `samd21`, `samd51`, `nrf52840`: add support for USBHID (keyboard / mouse)
- `wasm`: update wasi-libc version
- `wasm`: use newer WebAssembly features
* **boards**
- add Badger 2040
- `matrixportal-m4`: attach USB DP to the correct pin
- `teensy40`: add I2C support
- `wioterminal`: fix I2C definition
0.23.0
---
* **command line**
- add `-work` flag
- add Go 1.18 support
- add LLVM 14 support
- `run`: add support for command-line parameters
- `build`: calculate default output path if `-o` is not specified
- `build`: add JSON output
- `test`: support multiple test binaries with `-c`
- `test`: support flags like `-v` on all targets (including emulated firmware)
* **compiler**
- add support for ThinLTO
- use compiler-rt from LLVM
- `builder`: prefer GNU build ID over Go build ID for caching
- `builder`: add support for cross compiling to Darwin
- `builder`: support machine outlining pass in stacksize calculation
- `builder`: disable asynchronous unwind tables
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
- `compiler`: move allocations > 256 bytes to the heap
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
- `compiler`: accept alias for slice `cap` builtin
- `compiler`: allow slices of empty structs
- `compiler`: fix difference in aliases in interface methods
- `compiler`: make `RawSyscall` an alias for `Syscall`
- `compiler`: remove support for memory references in `AsmFull`
- `loader`: only add Clang header path for CGo
- `transform`: fix poison value in heap-to-stack transform
* **standard library**
- `internal/fuzz`: add this package as a shim
- `os`: implement readdir for darwin and linux
- `os`: add `DirFS`, which is used by many programs to access readdir.
- `os`: isWine: be compatible with older versions of wine, too
- `os`: implement `RemoveAll`
- `os`: Use a `uintptr` for `NewFile`
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
- `os`: export correct values for `DevNull` for each OS
- `os`: improve support for `Signal` by fixing various bugs
- `os`: implement `File.Fd` method
- `os`: implement `UserHomeDir`
- `os`: add `exec.ProcessState` stub
- `os`: implement `Pipe` for darwin
- `os`: define stub `ErrDeadlineExceeded`
- `reflect`: add stubs for more missing methods
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
- `reflect`: add `Value.FieldByIndexErr` stub
- `runtime`: fix various small GC bugs
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
- `runtime`: implement `memhash`
- `runtime`: implement `fastrand`
- `runtime`: add stub for `debug.ReadBuildInfo`
- `runtime`: add stub for `NumCPU`
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
- `runtime`: add `Version`
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
- `runtime`: stub {Lock,Unlock}OSThread on Windows
- `runtime`: be able to deal with a very small heap
- `syscall`: make `Environ` return a copy of the environment
- `syscall`: implement getpagesize and munmap
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
- `syscall`: darwin: more complete list of signals
- `syscall`: `wasi`: more complete list of signals
- `syscall`: stub `WaitStatus`
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
- `testing`: implement `TempDir`
- `testing`: nudge type TB closer to upstream; should be a no-op change.
- `testing`: on baremetal platforms, use simpler test matcher
* **targets**
- `atsamd`: fix usbcdc initialization when `-serial=uart`
- `atsamd51`: allow higher frequency when using SPI
- `esp`: support CGo
- `esp32c3`: add support for input pin
- `esp32c3`: add support for GPIO interrupts
- `esp32c3`: add support to receive UART data
- `rp2040`: fix PWM bug at high frequency
- `rp2040`: fix some minor I2C bugs
- `rp2040`: fix incorrect inline assembly
- `rp2040`: fix spurious i2c STOP during write+read transaction
- `rp2040`: improve ADC support
- `wasi`: remove `--export-dynamic` linker flag
- `wasm`: remove heap allocator from wasi-libc
* **boards**
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
- `m5stamp-c3`: add pin setting of UART
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
- `teensy41`: add the Teensy 4.1 board
- `teensy40`: add ADC support
- `teensy40`: add SPI support
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
- `wioterminal`: add DefaultUART
- `wioterminal`: verify written data when flashing through OpenOCD
- `xiao-ble`: add XIAO BLE nRF52840 support
0.22.0 0.22.0
--- ---
+1 -1
View File
@@ -1,5 +1,5 @@
# tinygo-llvm stage obtains the llvm source for TinyGo # tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.19 AS tinygo-llvm FROM golang:1.17 AS tinygo-llvm
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
+58 -243
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools. # Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order. # Versions are listed here in descending priority order.
LLVM_VERSIONS = 14 13 12 11 LLVM_VERSIONS = 13 12 11
errifempty = $(if $(1),$(1),$(error $(2))) errifempty = $(if $(1),$(1),$(error $(2)))
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2))) detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
toolSearchPathsVersion = $(1)-$(2) toolSearchPathsVersion = $(1)-$(2)
@@ -38,16 +38,10 @@ 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)
# Check for ccache if the user hasn't set it to on or off. # Use CCACHE for LLVM if possible
ifeq (, $(CCACHE)) ifneq (, $(shell command -v ccache 2> /dev/null))
# Use CCACHE for LLVM if possible LLVM_OPTION += '-DLLVM_CCACHE_BUILD=ON'
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))
@@ -56,55 +50,6 @@ 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.
ifneq ($(CROSS),)
CC = $(CROSS)-gcc
CXX = $(CROSS)-g++
LLVM_OPTION += \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
ifeq ($(CROSS), arm-linux-gnueabihf)
# Assume we're building on a Debian-like distro, with QEMU installed.
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
LLVM_OPTION += \
-DCMAKE_SYSTEM_NAME=Linux \
-DLLVM_TARGET_ARCH=ARM
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
else ifeq ($(CROSS), aarch64-linux-gnu)
# Assume we're building on a Debian-like distro, with QEMU installed.
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
LLVM_OPTION += \
-DCMAKE_SYSTEM_NAME=Linux \
-DLLVM_TARGET_ARCH=AArch64
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
else
$(error Unknown cross compilation target: $(CROSS))
endif
endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp .PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
@@ -128,8 +73,6 @@ ifeq ($(OS),Windows_NT)
else ifeq ($(shell uname -s),Darwin) else ifeq ($(shell uname -s),Darwin)
MD5SUM = md5 MD5SUM = md5
CGO_LDFLAGS += -lxar
USE_SYSTEM_BINARYEN ?= 1 USE_SYSTEM_BINARYEN ?= 1
else ifeq ($(shell uname -s),FreeBSD) else ifeq ($(shell uname -s),FreeBSD)
@@ -146,11 +89,11 @@ CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGe
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++ CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD. # Libraries that should be linked in for the statically linked LLD.
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO lldMinGW lldReaderWriter lldWasm lldYAML
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP) LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
# Other libraries that are needed to link TinyGo. # Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA EXTRA_LIB_NAMES = LLVMInterpreter
# All libraries to be built and linked with the tinygo binary (lib/lib*.a). # All libraries to be built and linked with the tinygo binary (lib/lib*.a).
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES) LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
@@ -165,15 +108,15 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
# For static linking. # For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","") ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14 CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA) CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif endif
clean: clean:
@rm -rf build @rm -rf build
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
fmt: fmt:
@gofmt -l -w $(FMT_PATHS) @gofmt -l -w $(FMT_PATHS)
fmt-check: fmt-check:
@@ -230,12 +173,12 @@ gen-device-rp: build/gen-device-svd
# Get LLVM sources. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR) git clone -b xtensa_release_13.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: $(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION) 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.
@@ -257,41 +200,31 @@ endif
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi @if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM) cd lib/wasi-libc && make -j4 WASM_CFLAGS="-O2 -g -DNDEBUG" WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_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 osusergo" -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)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./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 ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# Check whether the machine package matches the documentation. # Tests that take over a minute in wasi
# TODO: improve `tinygo targets` so it doesn't return these invalid targets.
CHECK_MACHINE_EXCULDE = \
cortex-m-qemu \
particle-3rd-gen \
riscv-qemu \
riscv64-qemu \
rp2040 \
$(nil)
check-machine:
$(GO) run ./tools/machinecheck $(filter-out $(CHECK_MACHINE_EXCULDE),$(shell $(TINYGO) targets))
# 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 = \
compress/bzip2 \ compress/bzip2 \
compress/flate \
crypto/dsa \ crypto/dsa \
index/suffixarray \ index/suffixarray \
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows TEST_PACKAGES_BASE = \
TEST_PACKAGES_FAST = \
compress/zlib \ compress/zlib \
container/heap \ container/heap \
container/list \ container/list \
container/ring \ container/ring \
crypto/des \ crypto/des \
crypto/elliptic/internal/fiat \
crypto/internal/subtle \ crypto/internal/subtle \
crypto/md5 \ crypto/md5 \
crypto/rc4 \ crypto/rc4 \
@@ -299,7 +232,6 @@ 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 \
@@ -315,7 +247,6 @@ TEST_PACKAGES_FAST = \
internal/profile \ internal/profile \
math \ math \
math/cmplx \ math/cmplx \
net \
net/http/internal/ascii \ net/http/internal/ascii \
net/mail \ net/mail \
os \ os \
@@ -328,99 +259,41 @@ TEST_PACKAGES_FAST = \
unicode \ unicode \
unicode/utf16 \ unicode/utf16 \
unicode/utf8 \ unicode/utf8 \
$(nil)
# Assume this will go away before Go2, so only check minor version. # Standard library packages that pass tests natively
ifeq ($(filter $(shell $(GO) env GOVERSION | cut -f 2 -d.), 16 17 18), ) TEST_PACKAGES := \
TEST_PACKAGES_FAST += crypto/internal/nistec/fiat $(TEST_PACKAGES_BASE)
else
TEST_PACKAGES_FAST += crypto/elliptic/internal/fiat # archive/zip requires ReadAt, which is not yet supported on windows
ifneq ($(OS),Windows_NT)
TEST_PACKAGES := \
$(TEST_PACKAGES) \
archive/zip
endif endif
# archive/zip requires os.ReadAt, which is not yet supported on windows # Standard library packages that pass tests on wasi
# compress/flate appears to hang on wasi TEST_PACKAGES_WASI = \
# compress/lzw appears to hang on wasi $(TEST_PACKAGES_BASE)
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
# strconv requires recover() which is not yet supported on wasi
# text/template/parse requires recover(), which is not yet supported on wasi
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
# Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \
archive/zip \
compress/flate \
compress/lzw \
crypto/hmac \
debug/dwarf \
debug/plan9obj \
io/ioutil \
strconv \
testing/fstest \
text/template/parse
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
TEST_PACKAGES_WINDOWS := \
compress/flate \
compress/lzw \
crypto/hmac \
strconv \
text/template/parse \
$(nil)
# Report platforms on which each standard library package is known to pass tests
jointmp := $(shell echo /tmp/join.$$$$)
report-stdlib-tests-pass:
@for t in $(TEST_PACKAGES_DARWIN); do echo "$$t darwin"; done | sort > $(jointmp).darwin
@for t in $(TEST_PACKAGES_LINUX); do echo "$$t linux"; done | sort > $(jointmp).linux
@for t in $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW); do echo "$$t darwin linux wasi windows"; done | sort > $(jointmp).portable
@join -a1 -a2 $(jointmp).darwin $(jointmp).linux | \
join -a1 -a2 - $(jointmp).portable
@rm $(jointmp).*
# Standard library packages that pass tests quickly on the current platform
ifeq ($(shell uname),Darwin)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
TEST_IOFS := true
endif
ifeq ($(shell uname),Linux)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
TEST_IOFS := true
endif
ifeq ($(OS),Windows_NT)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
TEST_IOFS := false
endif
# Test known-working standard library packages. # Test known-working standard library packages.
# TODO: parallelize, and only show failing tests (no implied -v flag). # TODO: parallelize, and only show failing tests (no implied -v flag).
.PHONY: tinygo-test .PHONY: tinygo-test
tinygo-test: tinygo-test:
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW) $(TINYGO) test $(TEST_PACKAGES) $(TEST_PACKAGES_SLOW)
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
@# requires a large stack-size. Hence, io/fs is only run conditionally.
@# For more details, see the comments on issue #3143.
ifeq ($(TEST_IOFS),true)
$(TINYGO) test -stack-size=6MB io/fs
endif
tinygo-test-fast: tinygo-test-fast:
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TINYGO) test $(TEST_PACKAGES)
tinygo-bench: tinygo-bench:
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW) $(TINYGO) test -bench . $(TEST_PACKAGES) $(TEST_PACKAGES_SLOW)
tinygo-bench-fast: tinygo-bench-fast:
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TINYGO) test -bench . $(TEST_PACKAGES)
# Same thing, except for wasi rather than the current platform.
tinygo-test-wasi: tinygo-test-wasi:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi $(TINYGO) test -target wasi $(TEST_PACKAGES_WASI) $(TEST_PACKAGES_SLOW)
tinygo-test-wasi-fast: tinygo-test-wasi-fast:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi $(TINYGO) test -target wasi $(TEST_PACKAGES_WASI)
tinygo-bench-wasi: tinygo-bench-wasi:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) $(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_WASI) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasi-fast: tinygo-bench-wasi-fast:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_WASI)
# Test external packages in a large corpus. # Test external packages in a large corpus.
test-corpus: test-corpus:
@@ -430,19 +303,10 @@ test-corpus-fast:
test-corpus-wasi: wasi-libc test-corpus-wasi: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasi CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasi
tinygo-baremetal:
# Regression tests that run on a baremetal target and don't fit in either main_test.go or smoketest.
# regression test for #2666: e.g. encoding/hex must pass on baremetal
$(TINYGO) test -target cortex-m-qemu encoding/hex
.PHONY: smoketest .PHONY: smoketest
smoketest: smoketest:
$(TINYGO) version $(TINYGO) version
$(TINYGO) targets > /dev/null
# regression test for #2892
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
# compile-only platform-independent examples
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
# regression test for #2563 # regression test for #2563
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
# test all examples (except pwm) # test all examples (except pwm)
@@ -460,8 +324,6 @@ 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
@@ -478,10 +340,6 @@ 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
@@ -490,14 +348,12 @@ 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=microbit examples/microbit-blink $(TINYGO) build -size short -o test.wasm -tags=pca10040 examples/blinky2
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=pca10056 examples/blinky2
@$(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
@$(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
@@ -548,8 +404,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=matrixportal-m4 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1 $(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
@@ -588,8 +442,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=qtpy examples/serial $(TINYGO) build -size short -o test.hex -target=qtpy examples/serial
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=teensy41 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
@@ -616,22 +468,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=qtpy-rp2040 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=macropad-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=tufty2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=xiao-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=challenger-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=trinkey-qt2040 examples/temp
@$(MD5SUM) test.hex
# test pwm # test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -639,13 +475,6 @@ 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
@@ -709,19 +538,15 @@ 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
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial $(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1 $(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1 $(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
@@ -741,8 +566,6 @@ 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 -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.
@@ -757,7 +580,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/bin @mkdir -p build/release/tinygo/bin
@mkdir -p build/release/tinygo/lib/clang/include @mkdir -p build/release/tinygo/lib/clang/include
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS @mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk @mkdir -p build/release/tinygo/lib/compiler-rt/lib
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@mkdir -p build/release/tinygo/lib/musl/arch @mkdir -p build/release/tinygo/lib/musl/arch
@@ -778,7 +601,9 @@ endif
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include @cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS @cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS @cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk @cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
@@ -794,7 +619,6 @@ endif
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
@@ -813,32 +637,23 @@ endif
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc @cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc @cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm @cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib @cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot @cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/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/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-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-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/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-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/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-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc ./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-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/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-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc ./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
release: release: build/release
tar -czf build/release.tar.gz -C build/release tinygo tar -czf build/release.tar.gz -C build/release tinygo
DEB_ARCH ?= native deb: build/release
deb:
@mkdir -p build/release-deb/usr/local/bin @mkdir -p build/release-deb/usr/local/bin
@mkdir -p build/release-deb/usr/local/lib @mkdir -p build/release-deb/usr/local/lib
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
ifneq ($(RELEASEONLY), 1)
release: build/release
deb: build/release
endif
+2 -15
View File
@@ -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 91 microcontroller boards are currently supported: The following 80 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)
@@ -59,16 +59,13 @@ The following 91 microcontroller boards are currently supported:
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800) * [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481) * [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
* [Adafruit MacroPad RP2040](https://www.adafruit.com/product/5100)
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745) * [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000) * [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
* [Adafruit PyBadge](https://www.adafruit.com/product/4200) * [Adafruit PyBadge](https://www.adafruit.com/product/4200)
* [Adafruit PyGamer](https://www.adafruit.com/product/4242) * [Adafruit PyGamer](https://www.adafruit.com/product/4242)
* [Adafruit PyPortal](https://www.adafruit.com/product/4116) * [Adafruit PyPortal](https://www.adafruit.com/product/4116)
* [Adafruit QT Py](https://www.adafruit.com/product/4600) * [Adafruit QT Py](https://www.adafruit.com/product/4600)
* [Adafruit QT Py RP2040](https://www.adafruit.com/product/4900)
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500) * [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
* [Adafruit Trinkey QT2040](https://adafruit.com/product/5056)
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/) * [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3) * [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi) * [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
@@ -87,17 +84,14 @@ The following 91 microcontroller boards are currently supported:
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html) * [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32) * [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32) * [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
* [ESP32-C3-12f](https://www.espressif.com/en/products/socs/esp32-c3)
* [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/)
@@ -109,24 +103,17 @@ The following 91 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)
* [Pimoroni Tufty2040](https://shop.pimoroni.com/products/tufty-2040)
* [PineTime DevKit](https://www.pine64.org/pinetime/) * [PineTime DevKit](https://www.pine64.org/pinetime/)
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html) * [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html) * [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
* [PJRC Teensy 4.1](https://www.pjrc.com/store/teensy41.html)
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) * [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/) * [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199) * [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 ESP32C3](https://www.seeedstudio.com/Seeed-XIAO-ESP32C3-p-5431.html)
* [Seeed XIAO RP2040](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html)
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html) * [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html) * [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) * [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1-rev-b) * [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html) * [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html) * [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html) * [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
+1 -1
View File
@@ -18,7 +18,7 @@ import (
// makeArchive creates an arcive for static linking from a list of object files // makeArchive creates an arcive for static linking from a list of object files
// given as a parameter. It is equivalent to the following command: // given as a parameter. It is equivalent to the following command:
// //
// ar -rcs <archivePath> <objs...> // ar -rcs <archivePath> <objs...>
func makeArchive(arfile *os.File, objs []string) error { func makeArchive(arfile *os.File, objs []string) error {
// Open the archive file. // Open the archive file.
arwriter := ar.NewWriter(arfile) arwriter := ar.NewWriter(arfile)
+119 -341
View File
@@ -4,7 +4,6 @@
package builder package builder
import ( import (
"crypto/sha256"
"crypto/sha512" "crypto/sha512"
"debug/elf" "debug/elf"
"encoding/binary" "encoding/binary"
@@ -14,7 +13,7 @@ import (
"fmt" "fmt"
"go/types" "go/types"
"hash/crc32" "hash/crc32"
"io/fs" "io/ioutil"
"math/bits" "math/bits"
"os" "os"
"os/exec" "os/exec"
@@ -38,14 +37,8 @@ import (
// BuildResult is the output of a build. This includes the binary itself and // BuildResult is the output of a build. This includes the binary itself and
// some other metadata that is obtained while building the binary. // some other metadata that is obtained while building the binary.
type BuildResult struct { type BuildResult struct {
// The executable directly from the linker, usually including debug
// information. Used for GDB for example.
Executable string
// A path to the output binary. It will be removed after Build returns, so // A path to the output binary. It will be removed after Build returns, so
// if it should be kept it must be copied or moved away. // if it should be kept it must be copied or moved away.
// It is often the same as Executable, but differs if the output format is
// .hex for example (instead of the usual ELF).
Binary string Binary string
// The directory of the main package. This is useful for testing as the test // The directory of the main package. This is useful for testing as the test
@@ -81,7 +74,6 @@ 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)
@@ -103,24 +95,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
// Create a temporary directory for intermediary files. // Create a temporary directory for intermediary files.
dir, err := os.MkdirTemp("", "tinygo") dir, err := ioutil.TempDir("", "tinygo")
if err != nil { if err != nil {
return err return err
} }
if config.Options.Work { defer os.RemoveAll(dir)
fmt.Printf("WORK=%s\n", dir)
} else {
defer os.RemoveAll(dir)
}
// Look up the build cache directory, which is used to speed up incremental
// builds.
cacheDir := goenv.Get("GOCACHE")
if cacheDir == "off" {
// Use temporary build directory instead, effectively disabling the
// build cache.
cacheDir = dir
}
// Check for a libc dependency. // Check for a libc dependency.
// As a side effect, this also creates the headers for the given libc, if // As a side effect, this also creates the headers for the given libc, if
@@ -128,9 +107,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
root := goenv.Get("TINYGOROOT") root := goenv.Get("TINYGOROOT")
var libcDependencies []*compileJob var libcDependencies []*compileJob
switch config.Target.Libc { switch config.Target.Libc {
case "darwin-libSystem":
job := makeDarwinLibSystemJob(config, dir)
libcDependencies = append(libcDependencies, job)
case "musl": case "musl":
job, unlock, err := Musl.load(config, dir) job, unlock, err := Musl.load(config, dir)
if err != nil { if err != nil {
@@ -148,7 +124,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, libcJob)
case "wasi-libc": case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a") path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) { if _, err := os.Stat(path); os.IsNotExist(err) {
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?") return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
} }
libcDependencies = append(libcDependencies, dummyCompileJob(path)) libcDependencies = append(libcDependencies, dummyCompileJob(path))
@@ -178,7 +154,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
Scheduler: config.Scheduler(), Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(), AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.StackSize(), DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(), NeedsStackObjects: config.NeedsStackObjects(),
Debug: true, Debug: true,
} }
@@ -190,10 +166,9 @@ 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, pkgName, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine), Sizes: compiler.Sizes(machine),
}) })
if err != nil { if err != nil {
@@ -211,23 +186,8 @@ 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
packageActionIDJobs := make(map[string]*compileJob) packageBitcodePaths := make(map[string]string)
packageActionIDs := make(map[string]string)
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
version += "-" + goenv.GitSha1
}
if config.Options.GlobalValues == nil {
config.Options.GlobalValues = make(map[string]map[string]string)
}
if config.Options.GlobalValues["runtime"] == nil {
config.Options.GlobalValues["runtime"] = make(map[string]string)
}
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
@@ -237,114 +197,58 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
sort.Strings(undefinedGlobals) sort.Strings(undefinedGlobals)
// Make compile jobs to load files to be embedded in the output binary. // Create a cache key: a hash from the action ID below that contains all
var actionIDDependencies []*compileJob // the parameters for the build.
allFiles := map[string][]*loader.EmbedFile{} actionID := packageAction{
for _, files := range pkg.EmbedGlobals { ImportPath: pkg.ImportPath,
for _, file := range files { CompilerBuildID: string(compilerBuildID),
allFiles[file.Name] = append(allFiles[file.Name], file) TinyGoVersion: goenv.Version,
} 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 name, files := range allFiles { for filePath, hash := range pkg.FileHashes {
name := name actionID.FileHashes[filePath] = hex.EncodeToString(hash)
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() {
job, ok := packageActionIDJobs[imported.Path()] hash, ok := packageActionIDs[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())
} }
importedPackages = append(importedPackages, job) actionID.Imports[imported.Path()] = hash
actionIDDependencies = append(actionIDDependencies, job)
} }
buf, err := json.Marshal(actionID)
// Create a job that will calculate the action ID for a package compile if err != nil {
// job. The action ID is the cache key that is used for caching this panic(err) // shouldn't happen
// 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
},
} }
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob hash := sha512.Sum512_224(buf)
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
// Now create the job to actually build the package. It will exit early // Determine the path of the bitcode file (which is a serialized version
// if the package is already compiled. // of a LLVM module).
cacheDir := goenv.Get("GOCACHE")
if cacheDir == "off" {
// Use temporary build directory instead, effectively disabling the
// build cache.
cacheDir = dir
}
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,
dependencies: []*compileJob{packageActionIDJob}, run: func(*compileJob) error {
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(job.result + ".lock") unlock := lock(bitcodePath + ".lock")
defer unlock() defer unlock()
if _, err := os.Stat(job.result); err == nil { if _, err := os.Stat(bitcodePath); err == nil {
// Already cached, don't recreate this package. // Already cached, don't recreate this package.
return nil return nil
} }
@@ -352,8 +256,6 @@ 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)
} }
@@ -370,7 +272,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Packages are compiled independently anyway. // Packages are compiled independently anyway.
for _, cgoHeader := range pkg.CGoHeaders { for _, cgoHeader := range pkg.CGoHeaders {
// Store the header text in a temporary file. // Store the header text in a temporary file.
f, err := os.CreateTemp(dir, "cgosnippet-*.c") f, err := ioutil.TempFile(dir, "cgosnippet-*.c")
if err != nil { if err != nil {
return err return err
} }
@@ -431,7 +333,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if pkgInit.IsNil() { if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path()) panic("init not found for " + pkg.Pkg.Path())
} }
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA()) err := interp.RunFunc(pkgInit, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }
@@ -439,13 +341,33 @@ 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())
} }
transform.OptimizePackage(mod, config) // Run function passes for each function in the module.
// 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 := os.CreateTemp(filepath.Dir(job.result), filepath.Base(job.result)) f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
if err != nil { if err != nil {
return err return err
} }
@@ -465,13 +387,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, job.result) return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
} }
err = f.Close() err = f.Close()
if err != nil { if err != nil {
return err return err
} }
return os.Rename(f.Name(), job.result) return os.Rename(f.Name(), bitcodePath)
}, },
} }
packageJobs = append(packageJobs, job) packageJobs = append(packageJobs, job)
@@ -479,13 +401,6 @@ 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)",
@@ -494,9 +409,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Load and link all the bitcode files. This does not yet optimize // Load and link all the bitcode files. This does not yet optimize
// 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("")
for _, pkgJob := range packageJobs { for _, pkg := range lprogram.Sorted() {
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result) pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
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)
} }
@@ -588,20 +503,13 @@ 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 os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc": case ".bc":
var buf llvm.MemoryBuffer data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes()
if config.UseThinLTO() { return ioutil.WriteFile(outpath, data, 0666)
buf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
}
defer buf.Dispose()
return os.WriteFile(outpath, buf.Bytes(), 0666)
case ".ll": case ".ll":
data := []byte(mod.String()) data := []byte(mod.String())
return os.WriteFile(outpath, data, 0666) return ioutil.WriteFile(outpath, data, 0666)
default: default:
panic("unreachable") panic("unreachable")
} }
@@ -618,18 +526,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
dependencies: []*compileJob{programJob}, dependencies: []*compileJob{programJob},
result: objfile, result: objfile,
run: func(*compileJob) error { run: func(*compileJob) error {
var llvmBuf llvm.MemoryBuffer llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if config.UseThinLTO() { if err != nil {
llvmBuf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod) return err
} else {
var err error
llvmBuf, err = machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
} }
defer llvmBuf.Dispose() return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
}, },
} }
@@ -661,7 +562,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
job := &compileJob{ job := &compileJob{
description: "compile extra file " + path, description: "compile extra file " + path,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.Options.PrintCommands)
job.result = result job.result = result
return err return err
}, },
@@ -679,7 +580,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
job := &compileJob{ job := &compileJob{
description: "compile CGo file " + abspath, description: "compile CGo file " + abspath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.Options.PrintCommands)
job.result = result job.result = result
return err return err
}, },
@@ -697,9 +598,6 @@ 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() {
@@ -709,12 +607,6 @@ 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
@@ -724,8 +616,21 @@ 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 {
// Other linkers may have different flags. switch config.GOOS() {
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker) case "linux":
// Either real linux or an embedded system (like AVR) that
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
// case (not ld.lld - that case is handled above).
ldflags = append(ldflags, "-Wl,--strip-debug")
case "darwin":
// MacOS (darwin) doesn't have a linker flag to strip debug
// information. Apple expects you to use the strip command
// instead.
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
default:
// Other OSes may have different flags.
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
}
} }
} }
@@ -744,37 +649,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if config.Options.PrintCommands != nil { if config.Options.PrintCommands != nil {
config.Options.PrintCommands(config.Target.Linker, ldflags...) config.Options.PrintCommands(config.Target.Linker, ldflags...)
} }
if config.UseThinLTO() {
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-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" {
ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel())
}
if sizeLevel >= 2 {
// Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 15.
ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
}
}
err = link(config.Target.Linker, ldflags...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {
return &commandError{"failed to link", executable, err} return &commandError{"failed to link", executable, err}
@@ -905,7 +779,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err return err
} }
case "esp32", "esp32-img", "esp32c3", "esp8266": case "esp32", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM // Special format for the ESP family of chips (parsed by the ROM
// bootloader). // bootloader).
tmppath = filepath.Join(dir, "main"+outext) tmppath = filepath.Join(dir, "main"+outext)
@@ -937,7 +811,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
return action(BuildResult{ return action(BuildResult{
Executable: executable,
Binary: tmppath, Binary: tmppath,
MainDir: lprogram.MainPkg().Dir, MainDir: lprogram.MainPkg().Dir,
ModuleRoot: moduleroot, ModuleRoot: moduleroot,
@@ -945,117 +818,11 @@ 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.
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error { func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA()) err := interp.Run(mod, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }
@@ -1072,7 +839,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
} }
} }
if config.GOOS() != "darwin" && !config.UseThinLTO() { if config.GOOS() != "darwin" {
transform.ApplyFunctionSections(mod) // -ffunction-sections transform.ApplyFunctionSections(mod) // -ffunction-sections
} }
@@ -1105,6 +872,17 @@ 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
} }
@@ -1367,10 +1145,10 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
// //
// It might print something like the following: // It might print something like the following:
// //
// function stack usage (in bytes) // function stack usage (in bytes)
// Reset_Handler 316 // Reset_Handler 316
// examples/blinky2.led1 92 // examples/blinky2.led1 92
// runtime.run$1 300 // runtime.run$1 300
func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) { func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
// Print the sizes of all stacks. // Print the sizes of all stacks.
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)") fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
+3 -2
View File
@@ -2,6 +2,7 @@ package builder
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@@ -25,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
"cortex-m0", "cortex-m0",
"cortex-m0plus", "cortex-m0plus",
"cortex-m3", "cortex-m3",
"cortex-m33", //"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
"cortex-m4", "cortex-m4",
"cortex-m7", "cortex-m7",
"esp32c3", "esp32c3",
@@ -89,7 +90,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
// Create a very simple C input file. // Create a very simple C input file.
srcpath := filepath.Join(testDir, "test.c") srcpath := filepath.Join(testDir, "test.c")
err = os.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666) err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
if err != nil { if err != nil {
t.Fatalf("could not write target file %s: %s", srcpath, err) t.Fatalf("could not write target file %s: %s", srcpath, err)
} }
+1 -1
View File
@@ -24,7 +24,7 @@ func ReadBuildID() ([]byte, error) {
defer f.Close() defer f.Close()
switch runtime.GOOS { switch runtime.GOOS {
case "linux", "freebsd", "android": case "linux", "freebsd":
// Read the GNU build id section. (Not sure about FreeBSD though...) // Read the GNU build id section. (Not sure about FreeBSD though...)
file, err := elf.NewFile(f) file, err := elf.NewFile(f)
if err != nil { if err != nil {
+3 -22
View File
@@ -1,11 +1,7 @@
package builder package builder
import ( import (
"os"
"path/filepath"
"strings" "strings"
"github.com/tinygo-org/tinygo/goenv"
) )
// These are the GENERIC_SOURCES according to CMakeList.txt. // These are the GENERIC_SOURCES according to CMakeList.txt.
@@ -40,6 +36,7 @@ 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",
@@ -75,7 +72,6 @@ var genericBuiltins = []string{
"floatunsisf.c", "floatunsisf.c",
"floatuntidf.c", "floatuntidf.c",
"floatuntisf.c", "floatuntisf.c",
"fp_mode.c",
//"int_util.c", //"int_util.c",
"lshrdi3.c", "lshrdi3.c",
"lshrti3.c", "lshrti3.c",
@@ -126,6 +122,7 @@ 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",
@@ -152,14 +149,6 @@ 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.
@@ -172,15 +161,7 @@ var CompilerRT = Library{
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"} return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
}, },
sourceDir: func() string { sourceDir: "lib/compiler-rt/lib/builtins",
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
if _, err := os.Stat(llvmDir); err == nil {
// Release build.
return llvmDir
}
// Development build.
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
},
librarySources: func(target string) []string { librarySources: func(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
+41 -50
View File
@@ -10,7 +10,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/fs" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@@ -33,30 +33,30 @@ import (
// Because of this complexity, every file has in fact two cached build outputs: // Because of this complexity, every file has in fact two cached build outputs:
// the file itself, and the list of dependencies. Its operation is as follows: // the file itself, and the list of dependencies. Its operation is as follows:
// //
// depfile = hash(path, compiler, cflags, ...) // depfile = hash(path, compiler, cflags, ...)
// if depfile exists: // if depfile exists:
// outfile = hash of all files and depfile name // outfile = hash of all files and depfile name
// if outfile exists: // if outfile exists:
// # cache hit // # cache hit
// return outfile // return outfile
// # cache miss // # cache miss
// tmpfile = compile file // tmpfile = compile file
// read dependencies (side effect of compile) // read dependencies (side effect of compile)
// write depfile // write depfile
// outfile = hash of all files and depfile name // outfile = hash of all files and depfile name
// rename tmpfile to outfile // rename tmpfile to outfile
// //
// There are a few edge cases that are not handled: // There are a few edge cases that are not handled:
// - If a file is added to an include path, that file may be included instead of // - If a file is added to an include path, that file may be included instead of
// some other file. This would be fixed by also including lookup failures in the // some other file. This would be fixed by also including lookup failures in the
// dependencies file, but I'm not aware of a compiler which does that. // dependencies file, but I'm not aware of a compiler which does that.
// - The Makefile syntax that compilers output has issues, see readDepFile for // - The Makefile syntax that compilers output has issues, see readDepFile for
// details. // details.
// - A header file may be changed to add/remove an include. This invalidates the // - A header file may be changed to add/remove an include. This invalidates the
// depfile but without invalidating its name. For this reason, the depfile is // depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it // written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache. // could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) { func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
// Hash input file. // Hash input file.
fileHash, err := hashFile(abspath) fileHash, err := hashFile(abspath)
if err != nil { if err != nil {
@@ -67,11 +67,6 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock")) unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
defer unlock() defer unlock()
ext := ".o"
if thinlto {
ext = ".bc"
}
// Create cache key for the dependencies file. // Create cache key for the dependencies file.
buf, err := json.Marshal(struct { buf, err := json.Marshal(struct {
Path string Path string
@@ -93,7 +88,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
// Load dependencies file, if possible. // Load dependencies file, if possible.
depfileName := "dep-" + depfileNameHash + ".json" depfileName := "dep-" + depfileNameHash + ".json"
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName) depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
depfileBuf, err := os.ReadFile(depfileCachePath) depfileBuf, err := ioutil.ReadFile(depfileCachePath)
var dependencies []string // sorted list of dependency paths var dependencies []string // sorted list of dependency paths
if err == nil { if err == nil {
// There is a dependency file, that's great! // There is a dependency file, that's great!
@@ -104,34 +99,31 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
} }
// Obtain hashes of all the files listed as a dependency. // Obtain hashes of all the files listed as a dependency.
outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext) outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
if err == nil { if err == nil {
if _, err := os.Stat(outpath); err == nil { if _, err := os.Stat(outpath); err == nil {
return outpath, nil return outpath, nil
} else if !errors.Is(err, fs.ErrNotExist) { } else if !os.IsNotExist(err) {
return "", err return "", err
} }
} }
} else if !errors.Is(err, fs.ErrNotExist) { } else if !os.IsNotExist(err) {
// expected either nil or IsNotExist // expected either nil or IsNotExist
return "", err return "", err
} }
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext) objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
if err != nil { if err != nil {
return "", err return "", err
} }
objTmpFile.Close() objTmpFile.Close()
depTmpFile, err := os.CreateTemp(tmpdir, "dep-*.d") depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
if err != nil { if err != nil {
return "", err return "", err
} }
depTmpFile.Close() depTmpFile.Close()
flags := append([]string{}, cflags...) // copy cflags flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
if thinlto {
flags = append(flags, "-flto=thin")
}
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath) flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if strings.ToLower(filepath.Ext(abspath)) == ".s" { if strings.ToLower(filepath.Ext(abspath)) == ".s" {
// If this is an assembly file (.s or .S, lowercase or uppercase), then // If this is an assembly file (.s or .S, lowercase or uppercase), then
@@ -166,7 +158,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
sort.Strings(dependencySlice) sort.Strings(dependencySlice)
// Write dependencies file. // Write dependencies file.
f, err := os.CreateTemp(filepath.Dir(depfileCachePath), depfileName) f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -189,7 +181,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
} }
// Move temporary object file to final location. // Move temporary object file to final location.
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext) outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -204,7 +196,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
// Create a cache path (a path in GOCACHE) to store the output of a compiler // Create a cache path (a path in GOCACHE) to store the output of a compiler
// job. This path is based on the dep file name (which is a hash of metadata // job. This path is based on the dep file name (which is a hash of metadata
// including compiler flags) and the hash of all input files in the paths slice. // including compiler flags) and the hash of all input files in the paths slice.
func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) { func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
// Hash all input files. // Hash all input files.
fileHashes := make(map[string]string, len(paths)) fileHashes := make(map[string]string, len(paths))
for _, path := range paths { for _, path := range paths {
@@ -229,7 +221,7 @@ func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, er
outFileNameBuf := sha512.Sum512_224(buf) outFileNameBuf := sha512.Sum512_224(buf)
cacheKey := hex.EncodeToString(outFileNameBuf[:]) cacheKey := hex.EncodeToString(outFileNameBuf[:])
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext) outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
return outpath, nil return outpath, nil
} }
@@ -252,14 +244,13 @@ func hashFile(path string) (string, error) {
// file is assumed to have a single target named deps. // file is assumed to have a single target named deps.
// //
// There are roughly three make syntax variants: // There are roughly three make syntax variants:
// - BSD make, which doesn't support any escaping. This means that many special // - BSD make, which doesn't support any escaping. This means that many special
// characters are not supported in file names. // characters are not supported in file names.
// - GNU make, which supports escaping using a backslash but when it fails to // - GNU make, which supports escaping using a backslash but when it fails to
// find a file it tries to fall back with the literal path name (to match BSD // find a file it tries to fall back with the literal path name (to match BSD
// make). // make).
// - NMake (Visual Studio) and Jom, which simply quote the string if there are // - NMake (Visual Studio) and Jom, which simply quote the string if there are
// any weird characters. // any weird characters.
//
// Clang supports two variants: a format that's a compromise between BSD and GNU // Clang supports two variants: a format that's a compromise between BSD and GNU
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which // make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
// is at least somewhat sane. This last format isn't perfect either: it does not // is at least somewhat sane. This last format isn't perfect either: it does not
@@ -267,7 +258,7 @@ func hashFile(path string) (string, error) {
// allowed on Windows, but of course can be used on POSIX like systems. Still, // allowed on Windows, but of course can be used on POSIX like systems. Still,
// it's the most sane of any of the formats so readDepFile will use that format. // it's the most sane of any of the formats so readDepFile will use that format.
func readDepFile(filename string) ([]string, error) { func readDepFile(filename string) ([]string, error) {
buf, err := os.ReadFile(filename) buf, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+4 -3
View File
@@ -38,7 +38,6 @@
#include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Option/Arg.h" #include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h" #include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h" #include "llvm/Option/OptTable.h"
@@ -52,6 +51,7 @@
#include "llvm/Support/Process.h" #include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h" #include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h" #include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
@@ -120,6 +120,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue()) llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
.Case("none", llvm::DebugCompressionType::None) .Case("none", llvm::DebugCompressionType::None)
.Case("zlib", llvm::DebugCompressionType::Z) .Case("zlib", llvm::DebugCompressionType::Z)
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
.Default(llvm::DebugCompressionType::None); .Default(llvm::DebugCompressionType::None);
} }
@@ -381,7 +382,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI, T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
Opts.RelaxAll, Opts.IncrementalLinkerCompatible, Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ true)); /*DWARFMustBeAtTheEnd*/ true));
Str.get()->initSections(Opts.NoExecStack, *STI); Str.get()->InitSections(Opts.NoExecStack);
} }
// When -fembed-bitcode is passed to clang_as, a 1-byte marker // When -fembed-bitcode is passed to clang_as, a 1-byte marker
@@ -441,7 +442,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts,
return Failed; return Failed;
} }
static void LLVMErrorHandler(void *UserData, const char *Message, static void LLVMErrorHandler(void *UserData, const std::string &Message,
bool GenCrashDiag) { bool GenCrashDiag) {
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+1 -1
View File
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
} }
return cmdName, nil return cmdName, nil
} }
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " ")) return "", errors.New("%#v: 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
View File
@@ -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 < 18 || minor > 19 { if major != 1 || minor < 15 || minor > 17 {
return nil, fmt.Errorf("requires go version 1.18 through 1.19, got go%d.%d", major, minor) return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
} }
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
-58
View File
@@ -1,58 +0,0 @@
package builder
import (
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
)
// Create a job that builds a Darwin libSystem.dylib stub library. This library
// contains all the symbols needed so that we can link against it, but it
// doesn't contain any real symbol implementations.
func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJob {
return &compileJob{
description: "compile Darwin libSystem.dylib",
run: func(job *compileJob) (err error) {
arch := strings.Split(config.Triple(), "-")[0]
job.result = filepath.Join(tmpdir, "libSystem.dylib")
objpath := filepath.Join(tmpdir, "libSystem.o")
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
// Compile assembly file to object file.
flags := []string{
"-nostdlib",
"--target=" + config.Triple(),
"-c",
"-o", objpath,
inpath,
}
if config.Options.PrintCommands != nil {
config.Options.PrintCommands("clang", flags...)
}
err = runCCompiler(flags...)
if err != nil {
return err
}
// Link object file to dynamic library.
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
flags = []string{
"-flavor", "darwin",
"-demangle",
"-dynamic",
"-dylib",
"-arch", arch,
"-platform_version", "macos", platformVersion, platformVersion,
"-install_name", "/usr/lib/libSystem.B.dylib",
"-o", job.result,
objpath,
}
if config.Options.PrintCommands != nil {
config.Options.PrintCommands("ld.lld", flags...)
}
return link("ld.lld", flags...)
},
}
}
+2 -4
View File
@@ -1,8 +1,6 @@
package builder package builder
import ( import (
"errors"
"io/fs"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
@@ -19,13 +17,13 @@ import (
func getClangHeaderPath(TINYGOROOT string) string { func getClangHeaderPath(TINYGOROOT string) string {
// Check whether we're running from the source directory. // Check whether we're running from the source directory.
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers") path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) { if _, err := os.Stat(path); !os.IsNotExist(err) {
return path return path
} }
// Check whether we're running from the installation directory. // Check whether we're running from the installation directory.
path = filepath.Join(TINYGOROOT, "lib", "clang", "include") path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) { if _, err := os.Stat(path); !os.IsNotExist(err) {
return path return path
} }
+5 -32
View File
@@ -13,9 +13,8 @@ import (
"debug/elf" "debug/elf"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"os" "io/ioutil"
"sort" "sort"
"strings"
) )
type espImageSegment struct { type espImageSegment struct {
@@ -79,31 +78,15 @@ func makeESPFirmareImage(infile, outfile, format string) error {
// An added benefit is that we don't need to check for errors all the time. // An added benefit is that we don't need to check for errors all the time.
outf := &bytes.Buffer{} outf := &bytes.Buffer{}
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
// image, not just a binary to be flashed at 0x1000 for example.
chip := format
makeImage := false
if strings.HasSuffix(format, "-img") {
makeImage = true
chip = format[:len(format)-len("-img")]
}
if makeImage {
// The bootloader starts at 0x1000, or 4096.
// TinyGo doesn't use a separate bootloader and runs the entire
// application in the bootloader location.
outf.Write(make([]byte, 4096))
}
// Chip IDs. Source: // Chip IDs. Source:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22 // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
chip_id := map[string]uint16{ chip_id := map[string]uint16{
"esp32": 0x0000, "esp32": 0x0000,
"esp32c3": 0x0005, "esp32c3": 0x0005,
}[chip] }[format]
// Image header. // Image header.
switch chip { switch format {
case "esp32", "esp32c3": case "esp32", "esp32c3":
// Header format: // Header format:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71 // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
@@ -172,22 +155,12 @@ func makeESPFirmareImage(infile, outfile, format string) error {
outf.Write(make([]byte, 15-outf.Len()%16)) outf.Write(make([]byte, 15-outf.Len()%16))
outf.WriteByte(checksum) outf.WriteByte(checksum)
if chip != "esp8266" { if format != "esp8266" {
// SHA256 hash (to protect against image corruption, not for security). // SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes()) hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:]) outf.Write(hash[:])
} }
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
// image to be a certain size.
if makeImage {
// Use a default image size of 4MB.
grow := 4096*1024 - outf.Len()
if grow > 0 {
outf.Write(make([]byte, grow))
}
}
// Write the image to the output file. // Write the image to the output file.
return os.WriteFile(outfile, outf.Bytes(), 0666) return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
} }
+11 -41
View File
@@ -1,8 +1,7 @@
package builder package builder
import ( import (
"errors" "io/ioutil"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@@ -25,8 +24,8 @@ type Library struct {
// cflags returns the C flags specific to this library // cflags returns the C flags specific to this library
cflags func(target, headerPath string) []string cflags func(target, headerPath string) []string
// The source directory. // The source directory, relative to TINYGOROOT.
sourceDir func() string sourceDir string
// The source files, relative to sourceDir. // The source files, relative to sourceDir.
librarySources func(target string) []string librarySources func(target string) []string
@@ -95,7 +94,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
target := config.Triple() target := config.Triple()
if l.makeHeaders != nil { if l.makeHeaders != nil {
if _, err = os.Stat(headerPath); err != nil { if _, err = os.Stat(headerPath); err != nil {
temporaryHeaderPath, err := os.MkdirTemp(outdir, "include.tmp*") temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*")
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -104,17 +103,13 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
err = os.Chmod(temporaryHeaderPath, 0o755) // TempDir uses 0o700 by default
if err != nil {
return nil, nil, err
}
err = os.Rename(temporaryHeaderPath, headerPath) err = os.Rename(temporaryHeaderPath, headerPath)
if err != nil { if err != nil {
switch { switch {
case errors.Is(err, fs.ErrExist): case os.IsExist(err):
// Another invocation of TinyGo also seems to have already created the headers. // Another invocation of TinyGo also seems to have already created the headers.
case runtime.GOOS == "windows" && errors.Is(err, fs.ErrPermission): case runtime.GOOS == "windows" && os.IsPermission(err):
// On Windows, a rename with a destination directory that already // On Windows, a rename with a destination directory that already
// exists does not result in an IsExist error, but rather in an // exists does not result in an IsExist error, but rather in an
// access denied error. To be sure, check for this case by checking // access denied error. To be sure, check for this case by checking
@@ -149,25 +144,12 @@ 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)
} }
} }
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
if strings.Split(target, "-")[2] == "linux" { args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables")
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
} else {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
}
}
if strings.HasPrefix(target, "avr") {
// 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")
@@ -175,12 +157,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") { if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc", "-mabi=lp64") args = append(args, "-march=rv64gc", "-mabi=lp64")
} }
if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port:
// https://github.com/espressif/llvm-project/issues/52
// Hopefully this will be fixed soon (LLVM 14).
args = append(args, "-D__ELF__")
}
var once sync.Once var once sync.Once
@@ -194,7 +170,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
defer once.Do(unlock) defer once.Do(unlock)
// Create an archive of all object files. // Create an archive of all object files.
f, err := os.CreateTemp(outdir, "libc.a.tmp*") f, err := ioutil.TempFile(outdir, "libc.a.tmp*")
if err != nil { if err != nil {
return err return err
} }
@@ -206,17 +182,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if err != nil { if err != nil {
return err return err
} }
err = os.Chmod(f.Name(), 0o644) // TempFile uses 0o600 by default
if err != nil {
return err
}
// Store this archive in the cache. // Store this archive in the cache.
return os.Rename(f.Name(), archiveFilePath) return os.Rename(f.Name(), archiveFilePath)
}, },
} }
sourceDir := l.sourceDir()
// Create jobs to compile all sources. These jobs are depended upon by the // Create jobs to compile all sources. These jobs are depended upon by the
// archive job above, so must be run first. // archive job above, so must be run first.
for _, path := range l.librarySources(target) { for _, path := range l.librarySources(target) {
@@ -225,7 +195,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
for strings.HasPrefix(cleanpath, "../") { for strings.HasPrefix(cleanpath, "../") {
cleanpath = cleanpath[3:] cleanpath = cleanpath[3:]
} }
srcpath := filepath.Join(sourceDir, path) srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
objpath := filepath.Join(dir, cleanpath+".o") objpath := filepath.Join(dir, cleanpath+".o")
os.MkdirAll(filepath.Dir(objpath), 0o777) os.MkdirAll(filepath.Dir(objpath), 0o777)
objs = append(objs, objpath) objs = append(objs, objpath)
@@ -249,13 +219,13 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// (It could be done in parallel with creating the ar file, but it probably // (It could be done in parallel with creating the ar file, but it probably
// won't make much of a difference in speed). // won't make much of a difference in speed).
if l.crt1Source != "" { if l.crt1Source != "" {
srcpath := filepath.Join(sourceDir, l.crt1Source) srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
job.dependencies = append(job.dependencies, &compileJob{ job.dependencies = append(job.dependencies, &compileJob{
description: "compile " + srcpath, description: "compile " + srcpath,
run: func(*compileJob) error { run: func(*compileJob) error {
var compileArgs []string var compileArgs []string
compileArgs = append(compileArgs, args...) compileArgs = append(compileArgs, args...)
tmpfile, err := os.CreateTemp(outdir, "crt1.o.tmp*") tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*")
if err != nil { if err != nil {
return err return err
} }
+3 -8
View File
@@ -8,22 +8,17 @@ extern "C" {
bool tinygo_link_elf(int argc, char **argv) { bool tinygo_link_elf(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false); return lld::elf::link(args, false, llvm::outs(), llvm::errs());
}
bool tinygo_link_macho(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
} }
bool tinygo_link_mingw(int argc, char **argv) { bool tinygo_link_mingw(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false); return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
} }
bool tinygo_link_wasm(int argc, char **argv) { bool tinygo_link_wasm(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false); return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
} }
} // external "C" } // external "C"
+1 -2
View File
@@ -26,7 +26,6 @@ var MinGW = Library{
_, err = io.Copy(outf, inf) _, err = io.Copy(outf, inf)
return err return err
}, },
sourceDir: func() string { return "" }, // unused
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
// No flags necessary because there are no files to compile. // No flags necessary because there are no files to compile.
return nil return nil
@@ -78,7 +77,7 @@ func makeMinGWExtraLibs(tmpdir string) []*compileJob {
// .in files need to be preprocessed by a preprocessor (-E) // .in files need to be preprocessed by a preprocessor (-E)
// first. // first.
defpath = outpath + ".def" defpath = outpath + ".def"
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-DDATA", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/") err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
if err != nil { if err != nil {
return err return err
} }
+4 -6
View File
@@ -3,6 +3,7 @@ package builder
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
@@ -34,7 +35,7 @@ var Musl = Library{
filepath.Join(muslDir, "include", "alltypes.h.in"), filepath.Join(muslDir, "include", "alltypes.h.in"),
} }
for _, infile := range infiles { for _, infile := range infiles {
data, err := os.ReadFile(infile) data, err := ioutil.ReadFile(infile)
if err != nil { if err != nil {
return err return err
} }
@@ -62,7 +63,7 @@ var Musl = Library{
if err != nil { if err != nil {
return err return err
} }
data, err := os.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in")) data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in"))
if err != nil { if err != nil {
return err return err
} }
@@ -89,7 +90,6 @@ var Musl = Library{
"-Wno-shift-op-parentheses", "-Wno-shift-op-parentheses",
"-Wno-ignored-attributes", "-Wno-ignored-attributes",
"-Wno-string-plus-int", "-Wno-string-plus-int",
"-Wno-ignored-pragmas",
"-Qunused-arguments", "-Qunused-arguments",
// Select include dirs. Don't include standard library includes // Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications), // (that would introduce host dependencies and other complications),
@@ -104,7 +104,7 @@ var Musl = Library{
"-fno-stack-protector", "-fno-stack-protector",
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, sourceDir: "lib/musl/src",
librarySources: func(target string) []string { librarySources: func(target string) []string {
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
globs := []string{ globs := []string{
@@ -115,10 +115,8 @@ 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",
"math/*.c",
"signal/*.c", "signal/*.c",
"stdio/*.c", "stdio/*.c",
"string/*.c", "string/*.c",
+2 -2
View File
@@ -2,7 +2,7 @@ package builder
import ( import (
"fmt" "fmt"
"io" "io/ioutil"
"os/exec" "os/exec"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
@@ -18,7 +18,7 @@ func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string)
} }
cmd := exec.Command(cmdLine[0], cmdLine[1:]...) cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
cmd.Stdout = io.Discard cmd.Stdout = ioutil.Discard
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return fmt.Errorf("could not run nrfutil pkg generate: %w", err) return fmt.Errorf("could not run nrfutil pkg generate: %w", err)
+2 -2
View File
@@ -2,7 +2,7 @@ package builder
import ( import (
"debug/elf" "debug/elf"
"io" "io/ioutil"
"os" "os"
"sort" "sort"
@@ -87,7 +87,7 @@ func extractROM(path string) (uint64, []byte, error) {
// Pad the difference // Pad the difference
rom = append(rom, make([]byte, diff)...) rom = append(rom, make([]byte, diff)...)
} }
data, err := io.ReadAll(prog.Open()) data, err := ioutil.ReadAll(prog.Open())
if err != nil { if err != nil {
return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err} return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err}
} }
+204 -380
View File
@@ -19,7 +19,7 @@ var Picolibc = Library{
return f.Close() return f.Close()
}, },
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
newlibDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
return []string{ return []string{
"-Werror", "-Werror",
"-Wall", "-Wall",
@@ -27,395 +27,219 @@ var Picolibc = Library{
"-D_COMPILING_NEWLIB", "-D_COMPILING_NEWLIB",
"-DHAVE_ALIAS_ATTRIBUTE", "-DHAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO", "-DTINY_STDIO",
"-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
"-D__OBSOLETE_MATH_DOUBLE=0",
"-nostdlibinc", "-nostdlibinc",
"-isystem", newlibDir + "/libc/include", "-isystem", picolibcDir + "/include",
"-I" + newlibDir + "/libc/tinystdio", "-I" + picolibcDir + "/tinystdio",
"-I" + newlibDir + "/libm/common",
"-I" + headerPath, "-I" + headerPath,
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") }, sourceDir: "lib/picolibc/newlib/libc",
librarySources: func(target string) []string { librarySources: func(target string) []string {
return picolibcSources return picolibcSources
}, },
} }
var picolibcSources = []string{ var picolibcSources = []string{
"../../picolibc-stdio.c", "../../../picolibc-stdio.c",
"libc/tinystdio/asprintf.c", "tinystdio/asprintf.c",
"libc/tinystdio/atod_engine.c", "tinystdio/atod_engine.c",
"libc/tinystdio/atod_ryu.c", "tinystdio/atod_ryu.c",
"libc/tinystdio/atof_engine.c", "tinystdio/atof_engine.c",
"libc/tinystdio/atof_ryu.c", "tinystdio/atof_ryu.c",
//"libc/tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double //"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
"libc/tinystdio/clearerr.c", "tinystdio/clearerr.c",
"libc/tinystdio/compare_exchange.c", "tinystdio/compare_exchange.c",
"libc/tinystdio/dtoa_data.c", "tinystdio/dtoa_data.c",
"libc/tinystdio/dtoa_engine.c", "tinystdio/dtoa_engine.c",
"libc/tinystdio/dtoa_ryu.c", "tinystdio/dtoa_ryu.c",
"libc/tinystdio/ecvtbuf.c", "tinystdio/ecvtbuf.c",
"libc/tinystdio/ecvt.c", "tinystdio/ecvt.c",
"libc/tinystdio/ecvt_data.c", "tinystdio/ecvt_data.c",
"libc/tinystdio/ecvtfbuf.c", "tinystdio/ecvtfbuf.c",
"libc/tinystdio/ecvtf.c", "tinystdio/ecvtf.c",
"libc/tinystdio/ecvtf_data.c", "tinystdio/ecvtf_data.c",
"libc/tinystdio/exchange.c", "tinystdio/exchange.c",
//"libc/tinystdio/fclose.c", // posix-io //"tinystdio/fclose.c", // posix-io
"libc/tinystdio/fcvtbuf.c", "tinystdio/fcvtbuf.c",
"libc/tinystdio/fcvt.c", "tinystdio/fcvt.c",
"libc/tinystdio/fcvtfbuf.c", "tinystdio/fcvtfbuf.c",
"libc/tinystdio/fcvtf.c", "tinystdio/fcvtf.c",
"libc/tinystdio/fdevopen.c", "tinystdio/fdevopen.c",
//"libc/tinystdio/fdopen.c", // posix-io //"tinystdio/fdopen.c", // posix-io
"libc/tinystdio/feof.c", "tinystdio/feof.c",
"libc/tinystdio/ferror.c", "tinystdio/ferror.c",
"libc/tinystdio/fflush.c", "tinystdio/fflush.c",
"libc/tinystdio/fgetc.c", "tinystdio/fgetc.c",
"libc/tinystdio/fgets.c", "tinystdio/fgets.c",
"libc/tinystdio/fileno.c", "tinystdio/fileno.c",
"libc/tinystdio/filestrget.c", "tinystdio/filestrget.c",
"libc/tinystdio/filestrputalloc.c", "tinystdio/filestrputalloc.c",
"libc/tinystdio/filestrput.c", "tinystdio/filestrput.c",
//"libc/tinystdio/fopen.c", // posix-io //"tinystdio/fopen.c", // posix-io
"libc/tinystdio/fprintf.c", "tinystdio/fprintf.c",
"libc/tinystdio/fputc.c", "tinystdio/fputc.c",
"libc/tinystdio/fputs.c", "tinystdio/fputs.c",
"libc/tinystdio/fread.c", "tinystdio/fread.c",
"libc/tinystdio/fscanf.c", "tinystdio/fscanf.c",
"libc/tinystdio/fseek.c", "tinystdio/fseek.c",
"libc/tinystdio/ftell.c", "tinystdio/ftell.c",
"libc/tinystdio/ftoa_data.c", "tinystdio/ftoa_data.c",
"libc/tinystdio/ftoa_engine.c", "tinystdio/ftoa_engine.c",
"libc/tinystdio/ftoa_ryu.c", "tinystdio/ftoa_ryu.c",
"libc/tinystdio/fwrite.c", "tinystdio/fwrite.c",
"libc/tinystdio/gcvtbuf.c", "tinystdio/gcvtbuf.c",
"libc/tinystdio/gcvt.c", "tinystdio/gcvt.c",
"libc/tinystdio/gcvtfbuf.c", "tinystdio/gcvtfbuf.c",
"libc/tinystdio/gcvtf.c", "tinystdio/gcvtf.c",
"libc/tinystdio/getchar.c", "tinystdio/getchar.c",
"libc/tinystdio/gets.c", "tinystdio/gets.c",
"libc/tinystdio/matchcaseprefix.c", "tinystdio/matchcaseprefix.c",
"libc/tinystdio/perror.c", "tinystdio/perror.c",
//"libc/tinystdio/posixiob.c", // posix-io //"tinystdio/posixiob.c", // posix-io
//"libc/tinystdio/posixio.c", // posix-io //"tinystdio/posixio.c", // posix-io
"libc/tinystdio/printf.c", "tinystdio/printf.c",
"libc/tinystdio/putchar.c", "tinystdio/putchar.c",
"libc/tinystdio/puts.c", "tinystdio/puts.c",
"libc/tinystdio/ryu_divpow2.c", "tinystdio/ryu_divpow2.c",
"libc/tinystdio/ryu_log10.c", "tinystdio/ryu_log10.c",
"libc/tinystdio/ryu_log2pow5.c", "tinystdio/ryu_log2pow5.c",
"libc/tinystdio/ryu_pow5bits.c", "tinystdio/ryu_pow5bits.c",
"libc/tinystdio/ryu_table.c", "tinystdio/ryu_table.c",
"libc/tinystdio/ryu_umul128.c", "tinystdio/ryu_umul128.c",
"libc/tinystdio/scanf.c", "tinystdio/scanf.c",
"libc/tinystdio/setbuf.c", "tinystdio/setbuf.c",
"libc/tinystdio/setvbuf.c", "tinystdio/setvbuf.c",
//"libc/tinystdio/sflags.c", // posix-io //"tinystdio/sflags.c", // posix-io
"libc/tinystdio/snprintf.c", "tinystdio/snprintf.c",
"libc/tinystdio/snprintfd.c", "tinystdio/snprintfd.c",
"libc/tinystdio/snprintff.c", "tinystdio/snprintff.c",
"libc/tinystdio/sprintf.c", "tinystdio/sprintf.c",
"libc/tinystdio/sprintfd.c", "tinystdio/sprintfd.c",
"libc/tinystdio/sprintff.c", "tinystdio/sprintff.c",
"libc/tinystdio/sscanf.c", "tinystdio/sscanf.c",
"libc/tinystdio/strfromd.c", "tinystdio/strfromd.c",
"libc/tinystdio/strfromf.c", "tinystdio/strfromf.c",
"libc/tinystdio/strtod.c", "tinystdio/strtod.c",
"libc/tinystdio/strtod_l.c", "tinystdio/strtod_l.c",
"libc/tinystdio/strtof.c", "tinystdio/strtof.c",
//"libc/tinystdio/strtold.c", // have_long_double and not long_double_equals_double //"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
//"libc/tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double //"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
"libc/tinystdio/ungetc.c", "tinystdio/ungetc.c",
"libc/tinystdio/vasprintf.c", "tinystdio/vasprintf.c",
"libc/tinystdio/vfiprintf.c", "tinystdio/vfiprintf.c",
"libc/tinystdio/vfiscanf.c", "tinystdio/vfiscanf.c",
"libc/tinystdio/vfprintf.c", "tinystdio/vfprintf.c",
"libc/tinystdio/vfprintff.c", "tinystdio/vfprintff.c",
"libc/tinystdio/vfscanf.c", "tinystdio/vfscanf.c",
"libc/tinystdio/vfscanff.c", "tinystdio/vfscanff.c",
"libc/tinystdio/vprintf.c", "tinystdio/vprintf.c",
"libc/tinystdio/vscanf.c", "tinystdio/vscanf.c",
"libc/tinystdio/vsnprintf.c", "tinystdio/vsnprintf.c",
"libc/tinystdio/vsprintf.c", "tinystdio/vsprintf.c",
"libc/tinystdio/vsscanf.c", "tinystdio/vsscanf.c",
"libc/string/bcmp.c", "string/bcmp.c",
"libc/string/bcopy.c", "string/bcopy.c",
"libc/string/bzero.c", "string/bzero.c",
"libc/string/explicit_bzero.c", "string/explicit_bzero.c",
"libc/string/ffsl.c", "string/ffsl.c",
"libc/string/ffsll.c", "string/ffsll.c",
"libc/string/fls.c", "string/fls.c",
"libc/string/flsl.c", "string/flsl.c",
"libc/string/flsll.c", "string/flsll.c",
"libc/string/gnu_basename.c", "string/gnu_basename.c",
"libc/string/index.c", "string/index.c",
"libc/string/memccpy.c", "string/memccpy.c",
"libc/string/memchr.c", "string/memchr.c",
"libc/string/memcmp.c", "string/memcmp.c",
"libc/string/memcpy.c", "string/memcpy.c",
"libc/string/memmem.c", "string/memmem.c",
"libc/string/memmove.c", "string/memmove.c",
"libc/string/mempcpy.c", "string/mempcpy.c",
"libc/string/memrchr.c", "string/memrchr.c",
"libc/string/memset.c", "string/memset.c",
"libc/string/rawmemchr.c", "string/rawmemchr.c",
"libc/string/rindex.c", "string/rindex.c",
"libc/string/stpcpy.c", "string/stpcpy.c",
"libc/string/stpncpy.c", "string/stpncpy.c",
"libc/string/strcasecmp.c", "string/strcasecmp.c",
"libc/string/strcasecmp_l.c", "string/strcasecmp_l.c",
"libc/string/strcasestr.c", "string/strcasestr.c",
"libc/string/strcat.c", "string/strcat.c",
"libc/string/strchr.c", "string/strchr.c",
"libc/string/strchrnul.c", "string/strchrnul.c",
"libc/string/strcmp.c", "string/strcmp.c",
"libc/string/strcoll.c", "string/strcoll.c",
"libc/string/strcoll_l.c", "string/strcoll_l.c",
"libc/string/strcpy.c", "string/strcpy.c",
"libc/string/strcspn.c", "string/strcspn.c",
"libc/string/strdup.c", "string/strdup.c",
"libc/string/strerror.c", "string/strerror.c",
"libc/string/strerror_r.c", "string/strerror_r.c",
"libc/string/strlcat.c", "string/strlcat.c",
"libc/string/strlcpy.c", "string/strlcpy.c",
"libc/string/strlen.c", "string/strlen.c",
"libc/string/strlwr.c", "string/strlwr.c",
"libc/string/strncasecmp.c", "string/strncasecmp.c",
"libc/string/strncasecmp_l.c", "string/strncasecmp_l.c",
"libc/string/strncat.c", "string/strncat.c",
"libc/string/strncmp.c", "string/strncmp.c",
"libc/string/strncpy.c", "string/strncpy.c",
"libc/string/strndup.c", "string/strndup.c",
"libc/string/strnlen.c", "string/strnlen.c",
"libc/string/strnstr.c", "string/strnstr.c",
"libc/string/strpbrk.c", "string/strpbrk.c",
"libc/string/strrchr.c", "string/strrchr.c",
"libc/string/strsep.c", "string/strsep.c",
"libc/string/strsignal.c", "string/strsignal.c",
"libc/string/strspn.c", "string/strspn.c",
"libc/string/strstr.c", "string/strstr.c",
"libc/string/strtok.c", "string/strtok.c",
"libc/string/strtok_r.c", "string/strtok_r.c",
"libc/string/strupr.c", "string/strupr.c",
"libc/string/strverscmp.c", "string/strverscmp.c",
"libc/string/strxfrm.c", "string/strxfrm.c",
"libc/string/strxfrm_l.c", "string/strxfrm_l.c",
"libc/string/swab.c", "string/swab.c",
"libc/string/timingsafe_bcmp.c", "string/timingsafe_bcmp.c",
"libc/string/timingsafe_memcmp.c", "string/timingsafe_memcmp.c",
"libc/string/u_strerr.c", "string/u_strerr.c",
"libc/string/wcpcpy.c", "string/wcpcpy.c",
"libc/string/wcpncpy.c", "string/wcpncpy.c",
"libc/string/wcscasecmp.c", "string/wcscasecmp.c",
"libc/string/wcscasecmp_l.c", "string/wcscasecmp_l.c",
"libc/string/wcscat.c", "string/wcscat.c",
"libc/string/wcschr.c", "string/wcschr.c",
"libc/string/wcscmp.c", "string/wcscmp.c",
"libc/string/wcscoll.c", "string/wcscoll.c",
"libc/string/wcscoll_l.c", "string/wcscoll_l.c",
"libc/string/wcscpy.c", "string/wcscpy.c",
"libc/string/wcscspn.c", "string/wcscspn.c",
"libc/string/wcsdup.c", "string/wcsdup.c",
"libc/string/wcslcat.c", "string/wcslcat.c",
"libc/string/wcslcpy.c", "string/wcslcpy.c",
"libc/string/wcslen.c", "string/wcslen.c",
"libc/string/wcsncasecmp.c", "string/wcsncasecmp.c",
"libc/string/wcsncasecmp_l.c", "string/wcsncasecmp_l.c",
"libc/string/wcsncat.c", "string/wcsncat.c",
"libc/string/wcsncmp.c", "string/wcsncmp.c",
"libc/string/wcsncpy.c", "string/wcsncpy.c",
"libc/string/wcsnlen.c", "string/wcsnlen.c",
"libc/string/wcspbrk.c", "string/wcspbrk.c",
"libc/string/wcsrchr.c", "string/wcsrchr.c",
"libc/string/wcsspn.c", "string/wcsspn.c",
"libc/string/wcsstr.c", "string/wcsstr.c",
"libc/string/wcstok.c", "string/wcstok.c",
"libc/string/wcswidth.c", "string/wcswidth.c",
"libc/string/wcsxfrm.c", "string/wcsxfrm.c",
"libc/string/wcsxfrm_l.c", "string/wcsxfrm_l.c",
"libc/string/wcwidth.c", "string/wcwidth.c",
"libc/string/wmemchr.c", "string/wmemchr.c",
"libc/string/wmemcmp.c", "string/wmemcmp.c",
"libc/string/wmemcpy.c", "string/wmemcpy.c",
"libc/string/wmemmove.c", "string/wmemmove.c",
"libc/string/wmempcpy.c", "string/wmempcpy.c",
"libc/string/wmemset.c", "string/wmemset.c",
"libc/string/xpg_strerror_r.c", "string/xpg_strerror_r.c",
"libm/common/sf_finite.c",
"libm/common/sf_copysign.c",
"libm/common/sf_modf.c",
"libm/common/sf_scalbn.c",
"libm/common/sf_cbrt.c",
"libm/common/sf_exp10.c",
"libm/common/sf_expm1.c",
"libm/common/sf_ilogb.c",
"libm/common/sf_infinity.c",
"libm/common/sf_isinf.c",
"libm/common/sf_isinff.c",
"libm/common/sf_isnan.c",
"libm/common/sf_isnanf.c",
"libm/common/sf_issignaling.c",
"libm/common/sf_log1p.c",
"libm/common/sf_nan.c",
"libm/common/sf_nextafter.c",
"libm/common/sf_pow10.c",
"libm/common/sf_rint.c",
"libm/common/sf_logb.c",
"libm/common/sf_fdim.c",
"libm/common/sf_fma.c",
"libm/common/sf_fmax.c",
"libm/common/sf_fmin.c",
"libm/common/sf_fpclassify.c",
"libm/common/sf_lrint.c",
"libm/common/sf_llrint.c",
"libm/common/sf_lround.c",
"libm/common/sf_llround.c",
"libm/common/sf_nearbyint.c",
"libm/common/sf_remquo.c",
"libm/common/sf_round.c",
"libm/common/sf_scalbln.c",
"libm/common/sf_trunc.c",
"libm/common/sf_exp.c",
"libm/common/sf_exp2.c",
"libm/common/sf_exp2_data.c",
"libm/common/sf_log.c",
"libm/common/sf_log_data.c",
"libm/common/sf_log2.c",
"libm/common/sf_log2_data.c",
"libm/common/sf_pow_log2_data.c",
"libm/common/sf_pow.c",
"libm/common/s_finite.c",
"libm/common/s_copysign.c",
"libm/common/s_modf.c",
"libm/common/s_scalbn.c",
"libm/common/s_cbrt.c",
"libm/common/s_exp10.c",
"libm/common/s_expm1.c",
"libm/common/s_ilogb.c",
"libm/common/s_infinity.c",
"libm/common/s_isinf.c",
"libm/common/s_isinfd.c",
"libm/common/s_isnan.c",
"libm/common/s_isnand.c",
"libm/common/s_issignaling.c",
"libm/common/s_log1p.c",
"libm/common/s_nan.c",
"libm/common/s_nextafter.c",
"libm/common/s_pow10.c",
"libm/common/s_rint.c",
"libm/common/s_logb.c",
"libm/common/s_log2.c",
"libm/common/s_fdim.c",
"libm/common/s_fma.c",
"libm/common/s_fmax.c",
"libm/common/s_fmin.c",
"libm/common/s_fpclassify.c",
"libm/common/s_lrint.c",
"libm/common/s_llrint.c",
"libm/common/s_lround.c",
"libm/common/s_llround.c",
"libm/common/s_nearbyint.c",
"libm/common/s_remquo.c",
"libm/common/s_round.c",
"libm/common/s_scalbln.c",
"libm/common/s_signbit.c",
"libm/common/s_trunc.c",
"libm/common/exp.c",
"libm/common/exp2.c",
"libm/common/exp_data.c",
"libm/common/math_err_with_errno.c",
"libm/common/math_err_xflow.c",
"libm/common/math_err_uflow.c",
"libm/common/math_err_oflow.c",
"libm/common/math_err_divzero.c",
"libm/common/math_err_invalid.c",
"libm/common/math_err_may_uflow.c",
"libm/common/math_err_check_uflow.c",
"libm/common/math_err_check_oflow.c",
"libm/common/log.c",
"libm/common/log_data.c",
"libm/common/log2.c",
"libm/common/log2_data.c",
"libm/common/pow.c",
"libm/common/pow_log_data.c",
"libm/math/e_acos.c",
"libm/math/e_acosh.c",
"libm/math/e_asin.c",
"libm/math/e_atan2.c",
"libm/math/e_atanh.c",
"libm/math/e_cosh.c",
"libm/math/e_exp.c",
"libm/math/ef_acos.c",
"libm/math/ef_acosh.c",
"libm/math/ef_asin.c",
"libm/math/ef_atan2.c",
"libm/math/ef_atanh.c",
"libm/math/ef_cosh.c",
"libm/math/ef_exp.c",
"libm/math/ef_fmod.c",
"libm/math/ef_hypot.c",
"libm/math/ef_j0.c",
"libm/math/ef_j1.c",
"libm/math/ef_jn.c",
"libm/math/ef_lgamma.c",
"libm/math/ef_log10.c",
"libm/math/ef_log.c",
"libm/math/e_fmod.c",
"libm/math/ef_pow.c",
"libm/math/ef_remainder.c",
"libm/math/ef_rem_pio2.c",
"libm/math/ef_scalb.c",
"libm/math/ef_sinh.c",
"libm/math/ef_sqrt.c",
"libm/math/ef_tgamma.c",
"libm/math/e_hypot.c",
"libm/math/e_j0.c",
"libm/math/e_j1.c",
"libm/math/e_jn.c",
"libm/math/e_lgamma.c",
"libm/math/e_log10.c",
"libm/math/e_log.c",
"libm/math/e_pow.c",
"libm/math/e_remainder.c",
"libm/math/e_rem_pio2.c",
"libm/math/erf_lgamma.c",
"libm/math/er_lgamma.c",
"libm/math/e_scalb.c",
"libm/math/e_sinh.c",
"libm/math/e_sqrt.c",
"libm/math/e_tgamma.c",
"libm/math/s_asinh.c",
"libm/math/s_atan.c",
"libm/math/s_ceil.c",
"libm/math/s_cos.c",
"libm/math/s_erf.c",
"libm/math/s_fabs.c",
"libm/math/sf_asinh.c",
"libm/math/sf_atan.c",
"libm/math/sf_ceil.c",
"libm/math/sf_cos.c",
"libm/math/sf_erf.c",
"libm/math/sf_fabs.c",
"libm/math/sf_floor.c",
"libm/math/sf_frexp.c",
"libm/math/sf_ldexp.c",
"libm/math/s_floor.c",
"libm/math/s_frexp.c",
"libm/math/sf_signif.c",
"libm/math/sf_sin.c",
"libm/math/sf_tan.c",
"libm/math/sf_tanh.c",
"libm/math/s_ldexp.c",
"libm/math/s_signif.c",
"libm/math/s_sin.c",
"libm/math/s_tan.c",
"libm/math/s_tanh.c",
} }
+7 -193
View File
@@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"debug/dwarf" "debug/dwarf"
"debug/elf" "debug/elf"
"debug/macho"
"debug/pe" "debug/pe"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@@ -117,7 +116,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|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`) packageSymbolRegexp = regexp.MustCompile(`\$(alloc|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.
@@ -127,7 +126,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, skipTombstone bool) ([]addressLine, error) { func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64) ([]addressLine, error) {
r := data.Reader() r := data.Reader()
var lines []*dwarf.LineFile var lines []*dwarf.LineFile
var addresses []addressLine var addresses []addressLine
@@ -169,7 +168,7 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone
return nil, err return nil, err
} }
if prevLineEntry.EndSequence && lineEntry.Address == 0 && skipTombstone { if prevLineEntry.EndSequence && lineEntry.Address == 0 {
// 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
@@ -178,10 +177,6 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone
// 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 {
@@ -260,65 +255,6 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset uint64, skipTombstone
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
@@ -341,7 +277,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, true) addresses, err = readProgramSizeFromDWARF(data, 0)
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.
@@ -432,133 +368,11 @@ 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, true) addresses, err = readProgramSizeFromDWARF(data, 0)
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.
@@ -628,9 +442,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, _ := file.DWARF() data, err := file.DWARF()
if data != nil { if data != nil {
addresses, err = readProgramSizeFromDWARF(data, codeOffset, true) addresses, err = readProgramSizeFromDWARF(data, codeOffset)
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.
+2 -11
View File
@@ -1,4 +1,3 @@
//go:build byollvm
// +build byollvm // +build byollvm
package builder package builder
@@ -14,7 +13,6 @@ import (
#include <stdlib.h> #include <stdlib.h>
bool tinygo_clang_driver(int argc, char **argv); bool tinygo_clang_driver(int argc, char **argv);
bool tinygo_link_elf(int argc, char **argv); bool tinygo_link_elf(int argc, char **argv);
bool tinygo_link_macho(int argc, char **argv);
bool tinygo_link_mingw(int argc, char **argv); bool tinygo_link_mingw(int argc, char **argv);
bool tinygo_link_wasm(int argc, char **argv); bool tinygo_link_wasm(int argc, char **argv);
*/ */
@@ -28,13 +26,8 @@ const hasBuiltinTools = true
// linking statically with LLVM (with the byollvm build tag). // linking statically with LLVM (with the byollvm build tag).
func RunTool(tool string, args ...string) error { func RunTool(tool string, args ...string) error {
linker := "elf" linker := "elf"
if tool == "ld.lld" && len(args) >= 2 { if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" {
if args[0] == "-m" && args[1] == "i386pep" { linker = "mingw"
linker = "mingw"
} else if args[0] == "-flavor" {
linker = args[1]
args = args[2:]
}
} }
args = append([]string{"tinygo:" + tool}, args...) args = append([]string{"tinygo:" + tool}, args...)
@@ -54,8 +47,6 @@ 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":
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))
case "mingw": case "mingw":
-1
View File
@@ -1,4 +1,3 @@
//go:build !byollvm
// +build !byollvm // +build !byollvm
package builder package builder
+2 -2
View File
@@ -10,7 +10,7 @@ package builder
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"os" "io/ioutil"
"strconv" "strconv"
) )
@@ -26,7 +26,7 @@ func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
if err != nil { if err != nil {
return err return err
} }
return os.WriteFile(outfile, output, 0644) return ioutil.WriteFile(outfile, output, 0644)
} }
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data. // convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
+551 -429
View File
File diff suppressed because it is too large Load Diff
+19 -4
View File
@@ -5,11 +5,12 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/ast" "go/ast"
"go/build"
"go/format" "go/format"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types" "go/types"
"os" "io/ioutil"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@@ -39,6 +40,20 @@ func TestCGo(t *testing.T) {
} { } {
name := name // avoid a race condition name := name // avoid a race condition
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
// Skip tests that require specific Go version.
if name == "errors" {
ok := false
for _, version := range build.Default.ReleaseTags {
if version == "go1.16" {
ok = true
break
}
}
if !ok {
t.Skip("Results for errors test are only valid for Go 1.16+")
}
}
// Read the AST in memory. // Read the AST in memory.
path := filepath.Join("testdata", name+".go") path := filepath.Join("testdata", name+".go")
fset := token.NewFileSet() fset := token.NewFileSet()
@@ -48,7 +63,7 @@ func TestCGo(t *testing.T) {
} }
// Process the AST with CGo. // Process the AST with CGo.
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "") cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
// Check the AST for type errors. // Check the AST for type errors.
var typecheckErrors []error var typecheckErrors []error
@@ -92,7 +107,7 @@ func TestCGo(t *testing.T) {
// Read the file with the expected output, to compare against. // Read the file with the expected output, to compare against.
outfile := filepath.Join("testdata", name+".out.go") outfile := filepath.Join("testdata", name+".out.go")
expectedBytes, err := os.ReadFile(outfile) expectedBytes, err := ioutil.ReadFile(outfile)
if err != nil { if err != nil {
t.Fatalf("could not read expected output: %v", err) t.Fatalf("could not read expected output: %v", err)
} }
@@ -103,7 +118,7 @@ func TestCGo(t *testing.T) {
// It is not. Test failed. // It is not. Test failed.
if *flagUpdate { if *flagUpdate {
// Update the file with the expected data. // Update the file with the expected data.
err := os.WriteFile(outfile, []byte(actual), 0666) err := ioutil.WriteFile(outfile, []byte(actual), 0666)
if err != nil { if err != nil {
t.Error("could not write updated output file:", err) t.Error("could not write updated output file:", err)
} }
+200 -426
View File
@@ -4,9 +4,7 @@ package cgo
// modification. It does not touch the AST itself. // modification. It does not touch the AST itself.
import ( import (
"crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/hex"
"fmt" "fmt"
"go/ast" "go/ast"
"go/scanner" "go/scanner"
@@ -15,13 +13,10 @@ 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, install libclang-11-dev
#include <llvm/Config/llvm-config.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
@@ -45,8 +40,6 @@ typedef struct {
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu); GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data); unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
CXString tinygo_clang_getCursorSpelling(GoCXCursor c); CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
CXString tinygo_clang_getCursorPrettyPrinted(GoCXCursor c, CXPrintingPolicy Policy);
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(GoCXCursor c);
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c); enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
CXType tinygo_clang_getCursorType(GoCXCursor c); CXType tinygo_clang_getCursorType(GoCXCursor c);
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t); GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
@@ -54,7 +47,6 @@ CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
CXType tinygo_clang_getCursorResultType(GoCXCursor c); CXType tinygo_clang_getCursorResultType(GoCXCursor c);
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c); int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i); GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(GoCXCursor c);
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c); CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c); CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c); CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
@@ -80,21 +72,7 @@ var diagnosticSeverity = [...]string{
C.CXDiagnostic_Fatal: "fatal", C.CXDiagnostic_Fatal: "fatal",
} }
// Alias so that cgo.go (which doesn't import Clang related stuff and is in func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
// 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)
@@ -141,8 +119,8 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
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 := f.getClangLocationPosition(location, unit) pos := p.getClangLocationPosition(location, unit)
f.addError(pos, severity+": "+spelling) p.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))
@@ -157,7 +135,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
} }
// Extract information required by CGo. // Extract information required by CGo.
ref := storedRefs.Put(f) ref := storedRefs.Put(p)
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))
@@ -177,88 +155,35 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
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 := f.visitedFiles[path]; !ok { if _, ok := p.visitedFiles[path]; !ok {
// already stored // already stored
sum := sha512.Sum512_224(data) sum := sha512.Sum512_224(data)
f.visitedFiles[path] = sum[:] p.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)
} }
// Convert the AST node under the given Clang cursor to a Go AST node and return //export tinygo_clang_globals_visitor
// it. func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) { p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
kind := C.tinygo_clang_getCursorKind(c) kind := C.tinygo_clang_getCursorKind(c)
pos := f.getCursorPosition(c) pos := p.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))
obj := &ast.Object{ fn := &functionInfo{
Kind: ast.Fun, pos: pos,
Name: "C." + name, variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
}
exportName := name
localName := name
var stringSignature string
if C.tinygo_clang_Cursor_getStorageClass(c) == C.CX_SC_Static {
// A static function is assigned a globally unique symbol name based
// on the file path (like _Cgo_static_2d09198adbf58f4f4655_foo) and
// has a different Go name in the form of C.foo!symbols.go instead
// of just C.foo.
path := f.importPath + "/" + filepath.Base(f.fset.File(f.file.Pos()).Name())
staticIDBuf := sha256.Sum256([]byte(path))
staticID := hex.EncodeToString(staticIDBuf[:10])
exportName = "_Cgo_static_" + staticID + "_" + name
localName = name + "!" + filepath.Base(path)
// Create a signature. This is necessary for MacOS to forward the
// call, because MacOS doesn't support aliases like ELF and PE do.
// (There is N_INDR but __attribute__((alias("..."))) doesn't work).
policy := C.tinygo_clang_getCursorPrintingPolicy(c)
defer C.clang_PrintingPolicy_dispose(policy)
C.clang_PrintingPolicy_setProperty(policy, C.CXPrintingPolicy_TerseOutput, 1)
stringSignature = getString(C.tinygo_clang_getCursorPrettyPrinted(c, policy))
stringSignature = strings.Replace(stringSignature, " "+name+"(", " "+exportName+"(", 1)
stringSignature = strings.TrimPrefix(stringSignature, "static ")
}
args := make([]*ast.Field, numArgs)
decl := &ast.FuncDecl{
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: pos - 1,
Text: "//export " + exportName,
},
},
},
Name: &ast.Ident{
NamePos: pos,
Name: "C." + localName,
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))
@@ -266,108 +191,50 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
if argName == "" { if argName == "" {
argName = "$" + strconv.Itoa(i) argName = "$" + strconv.Itoa(i)
} }
args[i] = &ast.Field{ fn.args = append(fn.args, paramInfo{
Names: []*ast.Ident{ name: argName,
{ 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 {
decl.Type.Results = &ast.FieldList{ fn.results = &ast.FieldList{
List: []*ast.Field{ List: []*ast.Field{
{ {
Type: f.makeASTType(resultType, pos), Type: p.makeASTType(resultType, pos),
}, },
}, },
} }
} }
obj.Decl = decl case C.CXCursor_StructDecl:
return decl, stringSignature typ := C.tinygo_clang_getCursorType(c)
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl: name := getString(C.tinygo_clang_getCursorSpelling(c))
typ := f.makeASTRecordType(c, pos) if _, required := p.missingSymbols["struct_"+name]; !required {
typeName := "C." + name return C.CXChildVisit_Continue
typeExpr := typ.typeExpr
if typ.unionSize != 0 {
// Convert to a single-field struct type.
typeExpr = f.makeUnionField(typ)
} }
obj := &ast.Object{ p.makeASTType(typ, pos)
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:
typeName := "C." + name typedefType := C.tinygo_clang_getCursorType(c)
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) name := getString(C.clang_getTypedefName(typedefType))
obj := &ast.Object{ if _, required := p.missingSymbols[name]; !required {
Kind: ast.Typ, return C.CXChildVisit_Continue
Name: typeName,
} }
typeSpec := &ast.TypeSpec{ p.makeASTType(typedefType, pos)
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)
typeExpr := f.makeASTType(cursorType, pos) p.globals[name] = globalInfo{
gen := &ast.GenDecl{ typeExpr: p.makeASTType(cursorType, pos),
TokPos: pos, pos: 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)
@@ -375,17 +242,17 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
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 {
f.addError(pos, "internal error: could not find file where macro is defined") p.addError(pos, "internal error: could not find file where macro is defined")
return nil, nil break
} }
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset) C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
if file != endFile { if file != endFile {
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file") p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
return nil, nil break
} }
if startOffset > endOffset { if startOffset > endOffset {
f.addError(pos, "internal error: start offset of macro is after end offset") p.addError(pos, "internal error: start offset of macro is after end offset")
return nil, nil break
} }
// read file contents and extract the relevant byte range // read file contents and extract the relevant byte range
@@ -393,94 +260,31 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
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) {
f.addError(pos, "internal error: end offset of macro lies after end of file") p.addError(pos, "internal error: end offset of macro lies after end of file")
return nil, nil break
} }
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) {
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source)) p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
return nil, nil break
} }
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)), f.fset, value) expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
if scannerError != nil { if scannerError != nil {
f.errors = append(f.errors, *scannerError) p.errors = append(p.errors, *scannerError)
return nil, nil
} }
if expr != nil {
gen := &ast.GenDecl{ // Parsing was successful.
TokPos: token.NoPos, p.constants[name] = constantInfo{expr, pos}
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:
obj := &ast.Object{ // Visit all enums, because the fields may be used even when the enum
Kind: ast.Typ, // type itself is not.
Name: "C." + name, typ := C.tinygo_clang_getCursorType(c)
} 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) {
@@ -490,49 +294,6 @@ 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))
@@ -630,7 +391,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 (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { func (p *cgoPackage) 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 {
@@ -656,15 +417,15 @@ func (f *cgoFile) 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: f.makeASTType(pointeeType, pos), X: p.makeASTType(pointeeType, pos),
} }
} }
return f.makeASTType(typ, pos) return p.makeASTType(typ, pos)
} }
// makeASTType return the ast.Expr for the given libclang type. In other words, // makeASTType return the ast.Expr for the given libclang type. In other words,
// it converts a libclang type to a type in the Go AST. // it converts a libclang type to a type in the Go AST.
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { func (p *cgoPackage) 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:
@@ -725,7 +486,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
return &ast.StarExpr{ return &ast.StarExpr{
Star: pos, Star: pos,
X: f.makeASTType(pointeeType, pos), X: p.makeASTType(pointeeType, pos),
} }
case C.CXType_ConstantArray: case C.CXType_ConstantArray:
return &ast.ArrayType{ return &ast.ArrayType{
@@ -735,7 +496,7 @@ func (f *cgoFile) 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: f.makeASTType(C.clang_getElementType(typ), pos), Elt: p.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
@@ -756,21 +517,71 @@ func (f *cgoFile) 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))
c := C.tinygo_clang_getTypeDeclaration(typ) if _, ok := p.typedefs[name]; !ok {
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: f.getASTDeclName(name, c, false), Name: "C." + name,
} }
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 f.makeASTType(underlying, pos) return p.makeASTType(underlying, pos)
case C.CXType_Enum: case C.CXType_Enum:
return f.makeASTType(underlying, pos) return p.makeASTType(underlying, pos)
default: default:
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind)) typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling)) p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
typeName = "<unknown>" typeName = "<unknown>"
} }
case C.CXType_Record: case C.CXType_Record:
@@ -788,46 +599,63 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
if name == "" { if name == "" {
// Anonymous record, probably inside a typedef. // Anonymous record, probably inside a typedef.
clangLocation := C.tinygo_clang_getCursorLocation(cursor) typeInfo := p.makeASTRecordType(cursor, pos)
var file C.CXFile if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
var line C.unsigned // This record is a union or is a struct with bitfields, so we
var column C.unsigned // have to declare it as a named type (for getters/setters to
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil) // work).
location := token.Position{ p.anonStructNum++
Filename: getString(C.clang_getFileName(file)), cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
Line: int(line), p.elaboratedTypes[cgoName] = typeInfo
Column: int(column), return &ast.Ident{
NamePos: pos,
Name: "C." + cgoName,
}
} }
if location.Filename == "" || location.Line == 0 { return typeInfo.typeExpr
// 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 {
name = cgoRecordPrefix + name cgoName := cgoRecordPrefix + name
} if _, ok := p.elaboratedTypes[cgoName]; !ok {
return &ast.Ident{ p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
NamePos: pos, p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, 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 == "" {
name = f.getUnnamedDeclName("_Ctype_enum___", cursor) // anonymous enum
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 {
name = "enum_" + name // named enum
} if _, ok := p.enums[name]; !ok {
return &ast.Ident{ ref := storedRefs.Put(p)
NamePos: pos, defer storedRefs.Remove(ref)
Name: f.getASTDeclName(name, cursor, false), C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
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))
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling)) p.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{
@@ -836,80 +664,9 @@ func (f *cgoFile) 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 (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo { func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
fieldList := &ast.FieldList{ fieldList := &ast.FieldList{
Opening: pos, Opening: pos,
Closing: pos, Closing: pos,
@@ -919,11 +676,11 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
bitfieldNum := 0 bitfieldNum := 0
ref := storedRefs.Put(struct { ref := storedRefs.Put(struct {
fieldList *ast.FieldList fieldList *ast.FieldList
file *cgoFile pkg *cgoPackage
inBitfield *bool inBitfield *bool
bitfieldNum *int bitfieldNum *int
bitfieldList *[]bitfieldInfo bitfieldList *[]bitfieldInfo
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList}) }{fieldList, p, &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)
@@ -952,13 +709,13 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
} }
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.
f.addError(pos, "bitfield in a union is not supported") p.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 {
f.addError(pos, "zero-length union is not supported") p.addError(pos, "zero-length union is not supported")
} }
typeInfo.unionSize = sizeInBytes typeInfo.unionSize = sizeInBytes
typeInfo.unionAlign = alignInBytes typeInfo.unionAlign = alignInBytes
@@ -966,7 +723,7 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
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))
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling)) p.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,
@@ -980,17 +737,17 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
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
file *cgoFile pkg *cgoPackage
inBitfield *bool inBitfield *bool
bitfieldNum *int bitfieldNum *int
bitfieldList *[]bitfieldInfo bitfieldList *[]bitfieldInfo
}) })
fieldList := passed.fieldList fieldList := passed.fieldList
f := passed.file p := passed.pkg
inBitfield := passed.inBitfield inBitfield := passed.inBitfield
bitfieldNum := passed.bitfieldNum bitfieldNum := passed.bitfieldNum
bitfieldList := passed.bitfieldList bitfieldList := passed.bitfieldList
pos := f.getCursorPosition(c) pos := p.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.
@@ -999,7 +756,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))
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling)) p.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))
@@ -1010,14 +767,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: f.makeASTType(typ, f.getCursorPosition(c)), Type: p.makeASTType(typ, p.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 {
f.addError(pos, "expected a bitfield") p.addError(pos, "expected a bitfield")
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
if !*inBitfield { if !*inBitfield {
@@ -1064,6 +821,23 @@ 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))
+16
View File
@@ -0,0 +1,16 @@
//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"
+16
View File
@@ -0,0 +1,16 @@
//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"
+16
View File
@@ -0,0 +1,16 @@
//go:build !byollvm && !llvm11 && !llvm12
// +build !byollvm,!llvm11,!llvm12
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-13/include
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@13/include
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@13/include
#cgo freebsd CFLAGS: -I/usr/local/llvm13/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-13/lib -lclang
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@13/lib -lclang -lffi
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@13/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm13/lib -lclang
*/
import "C"
-16
View File
@@ -1,16 +0,0 @@
//go:build !byollvm
// +build !byollvm
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
*/
import "C"
+1 -13
View File
@@ -3,7 +3,7 @@
// are slightly different from the ones defined in libclang.go, but they // are slightly different from the ones defined in libclang.go, but they
// should be ABI compatible. // should be ABI compatible.
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/ #include <clang-c/Index.h> // if this fails, install libclang-11-dev
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) { CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
return clang_getTranslationUnitCursor(tu); return clang_getTranslationUnitCursor(tu);
@@ -17,14 +17,6 @@ CXString tinygo_clang_getCursorSpelling(CXCursor c) {
return clang_getCursorSpelling(c); return clang_getCursorSpelling(c);
} }
CXString tinygo_clang_getCursorPrettyPrinted(CXCursor c, CXPrintingPolicy policy) {
return clang_getCursorPrettyPrinted(c, policy);
}
CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(CXCursor c) {
return clang_getCursorPrintingPolicy(c);
}
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) { enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
return clang_getCursorKind(c); return clang_getCursorKind(c);
} }
@@ -53,10 +45,6 @@ CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
return clang_Cursor_getArgument(c, i); return clang_Cursor_getArgument(c, i);
} }
enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(CXCursor c) {
return clang_Cursor_getStorageClass(c);
}
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) { CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
return clang_getCursorLocation(c); return clang_getCursorLocation(c);
} }
+20 -13
View File
@@ -24,16 +24,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type ( type C.int16_t = int16
C.char uint8 type C.int32_t = int32
C.schar int8 type C.int64_t = int64
C.uchar uint8 type C.int8_t = int8
C.short int16 type C.uint16_t = uint16
C.ushort uint16 type C.uint32_t = uint32
C.int int32 type C.uint64_t = uint64
C.uint uint32 type C.uint8_t = uint8
C.long int32 type C.uintptr_t = uintptr
C.ulong uint32 type C.char uint8
C.longlong int64 type C.int int32
C.ulonglong uint64 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
+22 -15
View File
@@ -24,19 +24,26 @@ 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.foo = 3
const C.bar = C.foo const C.bar = C.foo
const C.foo = 3
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
+1 -1
View File
@@ -27,7 +27,7 @@ import "C"
//line errors.go:100 //line errors.go:100
var ( var (
// constant too large // constant too large
_ C.char = 2 << 10 _ C.uint8_t = 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}
+24 -18
View File
@@ -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 C.char value in variable declaration (overflows) // testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
// testdata/errors.go:105: unknown field z in struct literal // testdata/errors.go:105: unknown field z in struct literal
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1 // testdata/errors.go:108: undeclared name: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows) // testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
@@ -38,23 +38,29 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type ( const C.SOME_CONST_3 = 1234
C.char uint8
C.schar int8 type C.int16_t = int16
C.uchar uint8 type C.int32_t = int32
C.short int16 type C.int64_t = int64
C.ushort uint16 type C.int8_t = int8
C.int int32 type C.uint16_t = uint16
C.uint uint32 type C.uint32_t = uint32
C.long int32 type C.uint64_t = uint64
C.ulong uint32 type C.uint8_t = uint8
C.longlong int64 type C.uintptr_t = uintptr
C.ulonglong uint64 type C.char uint8
) type C.int int32
type C._Ctype_struct___0 struct { type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
type C.point_t = struct {
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
+21 -14
View File
@@ -29,19 +29,26 @@ 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
-2
View File
@@ -5,7 +5,6 @@ package main
int foo(int a, int b); int foo(int a, int b);
void variadic0(); void variadic0();
void variadic2(int x, int y, ...); void variadic2(int x, int y, ...);
static void staticfunc(int x);
// Global variable signatures. // Global variable signatures.
extern int someValue; extern int someValue;
@@ -17,7 +16,6 @@ func accessFunctions() {
C.foo(3, 4) C.foo(3, 4)
C.variadic0() C.variadic0()
C.variadic2(3, 5) C.variadic2(3, 5)
C.staticfunc(3)
} }
func accessGlobals() { func accessGlobals() {
+23 -23
View File
@@ -24,41 +24,41 @@ 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
//export _Cgo_static_173c95a79b6df1980521_staticfunc
func C.staticfunc!symbols.go(x C.int)
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
//go:extern someValue //go:extern someValue
var C.someValue C.int var C.someValue C.int
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
+106 -100
View File
@@ -24,126 +24,132 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type ( const C.option2A = 20
C.char uint8 const C.optionA = 0
C.schar int8 const C.optionB = 1
C.uchar uint8 const C.optionC = -5
C.short int16 const C.optionD = -4
C.ushort uint16 const C.optionE = 10
C.int int32 const C.optionF = 11
C.uint uint32 const C.optionG = 12
C.long int32 const C.unused1 = 5
C.ulong uint32
C.longlong int64 type C.int16_t = int16
C.ulonglong uint64 type C.int32_t = int32
) type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
type C.bitfield_t = C.struct_4
type C.myIntArray = [10]C.int
type C.myint = C.int type C.myint = C.int
type C._Ctype_struct___0 struct { type C.option2_t = C.uint
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.point2d_t = C._Ctype_struct___0
type C.struct_point3d struct {
x C.int
y C.int
z C.int
}
type C.point3d_t = C.struct_point3d type C.point3d_t = C.struct_point3d
type C.struct_type1 struct { type C.struct_nested_t = struct {
_type C.int
__type C.int
___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._Ctype_union___2) unionfield_i() *C.int {
return (*C.int)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_d() *float64 {
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.union3_t = C._Ctype_union___2
type C.union_union2d struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
type C.union2d_t = C.union_union2d
type C._Ctype_union___3 struct{ arr [10]C.uchar }
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 begin C.point2d_t
end C.point2d_t end C.point2d_t
tag C.int tag C.int
coord C._Ctype_union___5 coord C.union_2
} }
type C.struct_nested_t = C._Ctype_struct___4 type C.types_t = struct {
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 f float32
d float64 d float64
ptr *C.int ptr *C.int
} }
type C.types_t = C._Ctype_struct___8 type C.union1_t = struct{ i C.int }
type C.myIntArray = [10]C.int type C.union2d_t = C.union_union2d
type C._Ctype_struct___9 struct { 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 start C.uchar
__bitfield_1 C.uchar __bitfield_1 C.uchar
d C.uchar d C.uchar
e C.uchar e C.uchar
} }
type C.struct_point3d struct {
x C.int
y C.int
z C.int
}
type C.struct_type1 struct {
_type C.int
__type C.int
___type C.int
}
type C.struct_type2 struct{ _type C.int }
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) { func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
}
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.union_1 struct{ $union uint64 }
type C.bitfield_t = C._Ctype_struct___9 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 {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
type C.union_2 struct{ $union [3]uint32 }
func (union *C.union_3) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_3) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_3) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union))
}
type C.union_3 struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
type C.union_union2d struct{ $union [2]uint64 }
type C.enum_option C.int
type C.enum_unused C.uint
+11 -111
View File
@@ -10,7 +10,6 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/google/shlex"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
@@ -73,7 +72,9 @@ func (c *Config) BuildTags() []string {
for i := 1; i <= c.GoMinorVersion; i++ { for i := 1; i <= c.GoMinorVersion; i++ {
tags = append(tags, fmt.Sprintf("go1.%d", i)) tags = append(tags, fmt.Sprintf("go1.%d", i))
} }
tags = append(tags, c.Options.Tags...) if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 {
tags = append(tags, extraTags...)
}
return tags return tags
} }
@@ -175,34 +176,6 @@ func (c *Config) AutomaticStackSize() bool {
return false return false
} }
// StackSize returns the default stack size to be used for goroutines, if the
// stack size could not be determined automatically at compile time.
func (c *Config) StackSize() uint64 {
if c.Options.StackSize != 0 {
return c.Options.StackSize
}
return c.Target.DefaultStackSize
}
// UseThinLTO returns whether ThinLTO should be used for the given target. Some
// targets (such as wasm) are not yet supported.
// We should try and remove as many exceptions as possible in the future, so
// that this optimization can be applied in more places.
func (c *Config) UseThinLTO() bool {
parts := strings.Split(c.Triple(), "-")
if parts[0] == "wasm32" {
// wasm-ld doesn't seem to support ThinLTO yet.
return false
}
if parts[0] == "avr" || parts[0] == "xtensa" {
// These use external (GNU) linkers which might perhaps support ThinLTO
// through a plugin, but it's too much hassle to set up.
return false
}
// Other architectures support ThinLTO.
return true
}
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that // RP2040BootPatch returns whether the RP2040 boot patch should be applied that
// calculates and patches in the checksum for the 2nd stage bootloader. // calculates and patches in the checksum for the 2nd stage bootloader.
func (c *Config) RP2040BootPatch() bool { func (c *Config) RP2040BootPatch() bool {
@@ -244,29 +217,6 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
} }
// DefaultBinaryExtension returns the default extension for binaries, such as
// .exe, .wasm, or no extension (depending on the target).
func (c *Config) DefaultBinaryExtension() string {
parts := strings.Split(c.Triple(), "-")
if parts[0] == "wasm32" {
// WebAssembly files always have the .wasm file extension.
return ".wasm"
}
if len(parts) >= 3 && parts[2] == "windows" {
// Windows uses .exe.
return ".exe"
}
if len(parts) >= 3 && parts[2] == "unknown" {
// There appears to be a convention to use the .elf file extension for
// ELF files intended for microcontrollers. I'm not aware of the origin
// of this, it's just something that is used by many projects.
// I think it's a good tradition, so let's keep it.
return ".elf"
}
// Linux, MacOS, etc, don't use a file extension. Use it as a fallback.
return ""
}
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo // CFlags returns the flags to pass to the C compiler. This is necessary for CGo
// preprocessing. // preprocessing.
func (c *Config) CFlags() []string { func (c *Config) CFlags() []string {
@@ -275,18 +225,12 @@ func (c *Config) CFlags() []string {
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT"))) cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
} }
switch c.Target.Libc { switch c.Target.Libc {
case "darwin-libSystem":
root := goenv.Get("TINYGOROOT")
cflags = append(cflags,
"--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"),
)
case "picolibc": case "picolibc":
root := goenv.Get("TINYGOROOT") root := goenv.Get("TINYGOROOT")
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc") picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
path, _ := c.LibcPath("picolibc") path, _ := c.LibcPath("picolibc")
cflags = append(cflags, cflags = append(cflags,
"--sysroot="+path, "--sysroot="+path,
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
"-isystem", filepath.Join(picolibcDir, "include"), "-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"), "-isystem", filepath.Join(picolibcDir, "tinystdio"),
) )
@@ -393,13 +337,6 @@ func (c *Config) BinaryFormat(ext string) string {
return c.Target.BinaryFormat return c.Target.BinaryFormat
} }
return "bin" return "bin"
case ".img":
// Image file. Only defined for the ESP32 at the moment, where it is a
// full (runnable) image that can be used in the Espressif QEMU fork.
if c.Target.BinaryFormat != "" {
return c.Target.BinaryFormat + "-img"
}
return "bin"
case ".hex": case ".hex":
// Similar to bin, but includes the start address and is thus usually a // Similar to bin, but includes the start address and is thus usually a
// better format. // better format.
@@ -449,13 +386,13 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
if openocdInterface == "" { if openocdInterface == "" {
return nil, errors.New("OpenOCD programmer not set") return nil, errors.New("OpenOCD programmer not set")
} }
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(openocdInterface) { if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(openocdInterface) {
return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface) return nil, fmt.Errorf("OpenOCD programmer has an invalid name: %#v", openocdInterface)
} }
if c.Target.OpenOCDTarget == "" { if c.Target.OpenOCDTarget == "" {
return nil, errors.New("OpenOCD chip not set") return nil, errors.New("OpenOCD chip not set")
} }
if !regexp.MustCompile(`^[\p{L}0-9_-]+$`).MatchString(c.Target.OpenOCDTarget) { if !regexp.MustCompile("^[\\p{L}0-9_-]+$").MatchString(c.Target.OpenOCDTarget) {
return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget) return nil, fmt.Errorf("OpenOCD target has an invalid name: %#v", c.Target.OpenOCDTarget)
} }
if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" { if c.Target.OpenOCDTransport != "" && c.Target.OpenOCDTransport != "swd" {
@@ -466,14 +403,7 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
args = append(args, "-c", cmd) args = append(args, "-c", cmd)
} }
if c.Target.OpenOCDTransport != "" { if c.Target.OpenOCDTransport != "" {
transport := c.Target.OpenOCDTransport args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
if transport == "swd" {
switch openocdInterface {
case "stlink-dap":
transport = "dapdirect_swd"
}
}
args = append(args, "-c", "transport select "+transport)
} }
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg") args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
return args, nil return args, nil
@@ -507,43 +437,13 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi return c.Target.WasmAbi
} }
// EmulatorName is a shorthand to get the command for this emulator, something // Emulator returns the emulator target config
// like qemu-system-arm or simavr. func (c *Config) Emulator() []string {
func (c *Config) EmulatorName() string {
parts := strings.SplitN(c.Target.Emulator, " ", 2)
if len(parts) > 1 {
return parts[0]
}
return ""
}
// EmulatorFormat returns the binary format for the emulator and the associated
// file extension. An empty string means to pass directly whatever the linker
// produces directly without conversion (usually ELF format).
func (c *Config) EmulatorFormat() (format, fileExt string) {
switch {
case strings.Contains(c.Target.Emulator, "{img}"):
return "img", ".img"
default:
return "", ""
}
}
// Emulator returns a ready-to-run command to run the given binary in an
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
// the compiled binary.
func (c *Config) Emulator(format, binary string) ([]string, error) {
parts, err := shlex.Split(c.Target.Emulator)
if err != nil {
return nil, fmt.Errorf("could not parse emulator command: %w", err)
}
var emulator []string var emulator []string
for _, s := range parts { for _, s := range c.Target.Emulator {
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")) emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
s = strings.ReplaceAll(s, "{"+format+"}", binary)
emulator = append(emulator, s)
} }
return emulator, nil return emulator
} }
type TestConfig struct { type TestConfig struct {
+3 -10
View File
@@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
"time"
) )
var ( var (
@@ -28,20 +27,17 @@ type Options struct {
GC string GC string
PanicStrategy string PanicStrategy string
Scheduler string Scheduler string
StackSize uint64 // goroutine stack size (if none could be automatically determined)
Serial string Serial string
Work bool // -work flag to print temporary build directory
InterpTimeout time.Duration
PrintIR bool PrintIR bool
DumpSSA bool DumpSSA bool
VerifyIR bool VerifyIR bool
PrintCommands func(cmd string, args ...string) `json:"-"` PrintCommands func(cmd string, args ...string)
Semaphore chan struct{} `json:"-"` // -p flag controls cap Semaphore chan struct{} // -p flag controls cap
Debug bool Debug bool
PrintSizes string PrintSizes string
PrintAllocs *regexp.Regexp // regexp string PrintAllocs *regexp.Regexp // regexp string
PrintStacks bool PrintStacks bool
Tags []string Tags string
WasmAbi string WasmAbi string
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
TestConfig TestConfig TestConfig TestConfig
@@ -49,9 +45,6 @@ type Options struct {
OpenOCDCommands []string OpenOCDCommands []string
LLVMFeatures string LLVMFeatures string
Directory string Directory string
PrintJSON bool
Monitor bool
BaudRate int
} }
// Verify performs a validation on the given options, raising an error if options are not valid. // Verify performs a validation on the given options, raising an error if options are not valid.
+43 -58
View File
@@ -12,9 +12,11 @@ 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.
@@ -42,8 +44,8 @@ type TargetSpec struct {
LDFlags []string `json:"ldflags"` LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"` LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"` ExtraFiles []string `json:"extra-files"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator"` Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
FlashCommand string `json:"flash-command"` FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"` GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"` PortReset string `json:"flash-1200-bps-reset"`
@@ -57,7 +59,6 @@ type TargetSpec struct {
OpenOCDTarget string `json:"openocd-target"` OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"` OpenOCDTransport string `json:"openocd-transport"`
OpenOCDCommands []string `json:"openocd-commands"` OpenOCDCommands []string `json:"openocd-commands"`
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device"` JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"` CodeModel string `json:"code-model"`
RelocationModel string `json:"relocation-model"` RelocationModel string `json:"relocation-model"`
@@ -65,7 +66,7 @@ type TargetSpec struct {
} }
// overrideProperties overrides all properties that are set in child into itself using reflection. // overrideProperties overrides all properties that are set in child into itself using reflection.
func (spec *TargetSpec) overrideProperties(child *TargetSpec) error { func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
specType := reflect.TypeOf(spec).Elem() specType := reflect.TypeOf(spec).Elem()
specValue := reflect.ValueOf(spec).Elem() specValue := reflect.ValueOf(spec).Elem()
childValue := reflect.ValueOf(child).Elem() childValue := reflect.ValueOf(child).Elem()
@@ -88,22 +89,23 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
if !src.IsNil() { if !src.IsNil() {
dst.Set(src) dst.Set(src)
} }
case reflect.Slice: // for slices, append the field and check for duplicates case reflect.Slice: // for slices...
dst.Set(reflect.AppendSlice(dst, src)) if src.Len() > 0 { // ... if not empty ...
for i := 0; i < dst.Len(); i++ { switch tag := field.Tag.Get("override"); tag {
v := dst.Index(i).String() case "copy":
for j := i + 1; j < dst.Len(); j++ { // copy the field of child to spec
w := dst.Index(j).String() dst.Set(src)
if v == w { case "append", "":
return fmt.Errorf("duplicate value '%s' in field %s", v, field.Name) // or append the field of child to spec
} dst.Set(reflect.AppendSlice(dst, src))
default:
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
} }
} }
default: default:
return fmt.Errorf("unknown field type: %s", kind) panic("unknown field type : " + kind.String())
} }
} }
return nil
} }
// load reads a target specification from the JSON in the given io.Reader. It // load reads a target specification from the JSON in the given io.Reader. It
@@ -118,10 +120,10 @@ func (spec *TargetSpec) load(r io.Reader) error {
} }
// loadFromGivenStr loads the TargetSpec from the given string that could be: // loadFromGivenStr loads the TargetSpec from the given string that could be:
// - targets/ directory inside the compiler sources // - targets/ directory inside the compiler sources
// - a relative or absolute path to custom (project specific) target specification .json file; // - a relative or absolute path to custom (project specific) target specification .json file;
// the Inherits[] could contain the files from target folder (ex. stm32f4disco) // the Inherits[] could contain the files from target folder (ex. stm32f4disco)
// as well as path to custom files (ex. myAwesomeProject.json) // as well as path to custom files (ex. myAwesomeProject.json)
func (spec *TargetSpec) loadFromGivenStr(str string) error { func (spec *TargetSpec) loadFromGivenStr(str string) error {
path := "" path := ""
if strings.HasSuffix(str, ".json") { if strings.HasSuffix(str, ".json") {
@@ -151,17 +153,11 @@ func (spec *TargetSpec) resolveInherits() error {
if err != nil { if err != nil {
return err return err
} }
err = newSpec.overrideProperties(subtarget) newSpec.overrideProperties(subtarget)
if err != nil {
return err
}
} }
// When all properties are loaded, make sure they are properly inherited. // When all properties are loaded, make sure they are properly inherited.
err := newSpec.overrideProperties(spec) newSpec.overrideProperties(spec)
if err != nil {
return err
}
*spec = *newSpec *spec = *newSpec
return nil return nil
@@ -209,10 +205,11 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
// triples for historical reasons) have the form: // triples for historical reasons) have the form:
// arch-vendor-os-environment // arch-vendor-os-environment
target := llvmarch + "-unknown-" + llvmos target := llvmarch + "-unknown-" + llvmos
if options.GOARCH == "arm" {
target += "-gnueabihf"
}
if options.GOOS == "windows" { if options.GOOS == "windows" {
target += "-gnu" target += "-gnu"
} else if options.GOARCH == "arm" {
target += "-gnueabihf"
} }
return defaultTarget(options.GOOS, options.GOARCH, target) return defaultTarget(options.GOOS, options.GOARCH, target)
} }
@@ -228,7 +225,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
// it includes all parents as specified in the "inherits" key. // it includes all parents as specified in the "inherits" key.
err = spec.resolveInherits() err = spec.resolveInherits()
if err != nil { if err != nil {
return nil, fmt.Errorf("%s : %w", options.Target, err) return nil, err
} }
if spec.Scheduler == "asyncify" { if spec.Scheduler == "asyncify" {
@@ -261,7 +258,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "arm": case "arm":
spec.CPU = "generic" spec.CPU = "generic"
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables") spec.CFlags = append(spec.CFlags, "-fno-unwind-tables")
switch strings.Split(triple, "-")[0] { switch strings.Split(triple, "-")[0] {
case "armv5": case "armv5":
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
@@ -275,16 +272,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Features = "+neon" spec.Features = "+neon"
} }
if goos == "darwin" { if goos == "darwin" {
spec.Linker = "ld.lld" spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
spec.Libc = "darwin-libSystem" spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
arch := strings.Split(triple, "-")[0]
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
spec.LDFlags = append(spec.LDFlags,
"-flavor", "darwin",
"-dead_strip",
"-arch", arch,
"-platform_version", "macos", platformVersion, platformVersion,
)
} else if goos == "linux" { } else if goos == "linux" {
spec.Linker = "ld.lld" spec.Linker = "ld.lld"
spec.RTLib = "compiler-rt" spec.RTLib = "compiler-rt"
@@ -305,8 +294,13 @@ 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
} }
@@ -317,31 +311,22 @@ 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/asm_"+goarch+suffix+".S") spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+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 {
// Some educated guesses as to how to invoke helper programs. // Some educated guesses as to how to invoke helper programs.
spec.GDB = []string{"gdb-multiarch"} spec.GDB = []string{"gdb-multiarch"}
if goos == "linux" { if goarch == "arm" && goos == "linux" {
switch goarch { spec.Emulator = []string{"qemu-arm"}
case "386": }
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case. if goarch == "arm64" && goos == "linux" {
if runtime.GOARCH != "amd64" { spec.Emulator = []string{"qemu-aarch64"}
spec.Emulator = "qemu-i386 {}"
}
case "amd64":
spec.Emulator = "qemu-x86_64 {}"
case "arm":
spec.Emulator = "qemu-arm {}"
case "arm64":
spec.Emulator = "qemu-aarch64 {}"
}
} }
} }
if goos != runtime.GOOS { if goos != runtime.GOOS {
if goos == "windows" { if goos == "windows" {
spec.Emulator = "wine {}" spec.Emulator = []string{"wine"}
} }
} }
return &spec, nil return &spec, nil
+7 -3
View File
@@ -1,8 +1,7 @@
package compileopts package compileopts
import ( import (
"errors" "os"
"io/fs"
"reflect" "reflect"
"testing" "testing"
) )
@@ -18,7 +17,7 @@ func TestLoadTarget(t *testing.T) {
t.Error("LoadTarget should have failed with non existing target") t.Error("LoadTarget should have failed with non existing target")
} }
if !errors.Is(err, fs.ErrNotExist) { if !os.IsNotExist(err) {
t.Error("LoadTarget failed for wrong reason:", err) t.Error("LoadTarget failed for wrong reason:", err)
} }
} }
@@ -30,6 +29,7 @@ func TestOverrideProperties(t *testing.T) {
CPU: "baseCpu", CPU: "baseCpu",
CFlags: []string{"-base-foo", "-base-bar"}, CFlags: []string{"-base-foo", "-base-bar"},
BuildTags: []string{"bt1", "bt2"}, BuildTags: []string{"bt1", "bt2"},
Emulator: []string{"be1", "be2"},
DefaultStackSize: 42, DefaultStackSize: 42,
AutoStackSize: &baseAutoStackSize, AutoStackSize: &baseAutoStackSize,
} }
@@ -38,6 +38,7 @@ func TestOverrideProperties(t *testing.T) {
GOOS: "", GOOS: "",
CPU: "chlidCpu", CPU: "chlidCpu",
CFlags: []string{"-child-foo", "-child-bar"}, CFlags: []string{"-child-foo", "-child-bar"},
Emulator: []string{"ce1", "ce2"},
AutoStackSize: &childAutoStackSize, AutoStackSize: &childAutoStackSize,
DefaultStackSize: 64, DefaultStackSize: 64,
} }
@@ -56,6 +57,9 @@ func TestOverrideProperties(t *testing.T) {
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) { if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
t.Errorf("Overriding failed : got %v", base.BuildTags) t.Errorf("Overriding failed : got %v", base.BuildTags)
} }
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
t.Errorf("Overriding failed : got %v", base.Emulator)
}
if *base.AutoStackSize != false { if *base.AutoStackSize != false {
t.Errorf("Overriding failed : got %v", base.AutoStackSize) t.Errorf("Overriding failed : got %v", base.AutoStackSize)
} }
+44 -3
View File
@@ -16,8 +16,6 @@ import "tinygo.org/x/go-llvm"
var stdlibAliases = map[string]string{ var stdlibAliases = map[string]string{
// crypto packages // crypto packages
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
"crypto/ed25519/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
"crypto/md5.block": "crypto/md5.blockGeneric", "crypto/md5.block": "crypto/md5.blockGeneric",
"crypto/sha1.block": "crypto/sha1.blockGeneric", "crypto/sha1.block": "crypto/sha1.blockGeneric",
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric", "crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
@@ -25,10 +23,53 @@ var stdlibAliases = map[string]string{
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric", "crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
// math package // math package
"math.Asin": "math.asin",
"math.Asinh": "math.asinh",
"math.Acos": "math.acos",
"math.Acosh": "math.acosh",
"math.Atan": "math.atan",
"math.Atanh": "math.atanh",
"math.Atan2": "math.atan2",
"math.Cbrt": "math.cbrt",
"math.Ceil": "math.ceil",
"math.archCeil": "math.ceil",
"math.Cos": "math.cos",
"math.Cosh": "math.cosh",
"math.Erf": "math.erf",
"math.Erfc": "math.erfc",
"math.Exp": "math.exp",
"math.archExp": "math.exp",
"math.Expm1": "math.expm1",
"math.Exp2": "math.exp2",
"math.archExp2": "math.exp2",
"math.Floor": "math.floor",
"math.archFloor": "math.floor",
"math.Frexp": "math.frexp",
"math.Hypot": "math.hypot",
"math.archHypot": "math.hypot", "math.archHypot": "math.hypot",
"math.Ldexp": "math.ldexp",
"math.Log": "math.log",
"math.archLog": "math.log",
"math.Log1p": "math.log1p",
"math.Log10": "math.log10",
"math.Log2": "math.log2",
"math.Max": "math.max",
"math.archMax": "math.max", "math.archMax": "math.max",
"math.Min": "math.min",
"math.archMin": "math.min", "math.archMin": "math.min",
"math.Mod": "math.mod",
"math.Modf": "math.modf",
"math.archModf": "math.modf", "math.archModf": "math.modf",
"math.Pow": "math.pow",
"math.Remainder": "math.remainder",
"math.Sin": "math.sin",
"math.Sinh": "math.sinh",
"math.Sqrt": "math.sqrt",
"math.archSqrt": "math.sqrt",
"math.Tan": "math.tan",
"math.Tanh": "math.tanh",
"math.Trunc": "math.trunc",
"math.archTrunc": "math.trunc",
} }
// createAlias implements the function (in the builder) as a call to the alias // createAlias implements the function (in the builder) as a call to the alias
@@ -45,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 := b.ctx.AddBasicBlock(b.llvmFn, "entry") entryBlock := llvm.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())
+1 -3
View File
@@ -240,10 +240,8 @@ 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.insertBasicBlock(blockPrefix + ".next") nextBlock := b.ctx.AddBasicBlock(b.llvmFn, 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.
+48 -25
View File
@@ -4,17 +4,19 @@ import (
"fmt" "fmt"
"strings" "strings"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic // createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
// operation. It returns the result of the operation, or a zero llvm.Value if // operation. It returns the result of the operation and true if the call could
// the result is void. // be lowered inline, and false otherwise.
func (b *builder) createAtomicOp(name string) llvm.Value { func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
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(b.fn.Params[0]) ptr := b.getValue(call.Args[0])
val := b.getValue(b.fn.Params[1]) val := b.getValue(call.Args[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)
@@ -27,18 +29,17 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
} }
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, "") return b.CreateAdd(oldVal, val, ""), true
} }
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, "") return b.CreateAdd(oldVal, val, ""), true
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer": case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr := b.getValue(b.fn.Params[0]) ptr := b.getValue(call.Args[0])
val := b.getValue(b.fn.Params[1]) val := b.getValue(call.Args[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), "")
} }
@@ -46,23 +47,47 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if isPointer { if isPointer {
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "") oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
} }
return oldVal return oldVal, true
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer": case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
ptr := b.getValue(b.fn.Params[0]) ptr := b.getValue(call.Args[0])
old := b.getValue(b.fn.Params[1]) old := b.getValue(call.Args[1])
newVal := b.getValue(b.fn.Params[2]) newVal := b.getValue(call.Args[2])
if strings.HasSuffix(name, "64") {
if strings.HasPrefix(b.Triple, "thumb") {
// Work around a bug in LLVM, at least LLVM 11:
// https://reviews.llvm.org/D95891
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
if compareAndSwap.IsNil() {
// Declare the function if it isn't already declared.
i64Type := b.ctx.Int64Type()
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
}
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
// The __sync_val_compare_and_swap_8 function returns the old
// value. However, we shouldn't return the old value, we should
// return whether the compare/exchange was successful. This is
// easily done by comparing the returned (actual) old value with
// the expected old value passed to
// __sync_val_compare_and_swap_8.
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
return swapped, true
}
}
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 return swapped, true
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer": case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
ptr := b.getValue(b.fn.Params[0]) ptr := b.getValue(call.Args[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 return val, true
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer": case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
ptr := b.getValue(b.fn.Params[0]) ptr := b.getValue(call.Args[0])
val := b.getValue(b.fn.Params[1]) val := b.getValue(call.Args[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()
@@ -78,15 +103,13 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
if fn.IsNil() { if fn.IsNil() {
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false)) fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false))
} }
b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "") return b.createCall(fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, ""), true
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 llvm.Value{} return store, true
default: default:
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name()) return llvm.Value{}, false
return llvm.Value{}
} }
} }
+2 -30
View File
@@ -33,36 +33,17 @@ const (
paramIsDeferenceableOrNull = 1 << iota paramIsDeferenceableOrNull = 1 << iota
) )
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or // createCall creates a new call to runtime.<fnName> with the given arguments.
// createRuntimeInvoke instead. func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function) fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
llvmFn := b.getFunction(fn) 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 {
@@ -74,15 +55,6 @@ 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 {
+87 -328
View File
@@ -9,7 +9,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"math/bits" "math/bits"
"path"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv" "strconv"
@@ -18,7 +17,6 @@ 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"
) )
@@ -67,7 +65,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 typeutil.Map llvmTypes map[types.Type]llvm.Type
machine llvm.TargetMachine machine llvm.TargetMachine
targetData llvm.TargetData targetData llvm.TargetData
intType llvm.Type intType llvm.Type
@@ -78,7 +76,6 @@ 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
@@ -92,6 +89,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
DumpSSA: dumpSSA, DumpSSA: dumpSSA,
difiles: make(map[string]llvm.Metadata), difiles: make(map[string]llvm.Metadata),
ditypes: make(map[types.Type]llvm.Metadata), ditypes: make(map[types.Type]llvm.Metadata),
llvmTypes: make(map[types.Type]llvm.Type),
machine: machine, machine: machine,
targetData: machine.CreateTargetData(), targetData: machine.CreateTargetData(),
astComments: map[string]*ast.CommentGroup{}, astComments: map[string]*ast.CommentGroup{},
@@ -139,8 +137,6 @@ 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
@@ -238,15 +234,10 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
panic("unknown pointer size") panic("unknown pointer size")
} }
// Construct a complex128 type because that's likely the type with the
// biggest alignment on most/all ABIs.
ctx := llvm.NewContext()
defer ctx.Dispose()
complex128Type := ctx.StructType([]llvm.Type{ctx.DoubleType(), ctx.DoubleType()}, false)
return &stdSizes{ return &stdSizes{
IntSize: int64(intWidth / 8), IntSize: int64(intWidth / 8),
PtrSize: int64(targetData.PointerSize()), PtrSize: int64(targetData.PointerSize()),
MaxAlign: int64(targetData.ABITypeAlignment(complex128Type)), MaxAlign: int64(targetData.PrefTypeAlignment(targetData.IntPtrType())),
} }
} }
@@ -254,7 +245,6 @@ 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
@@ -306,7 +296,6 @@ 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
@@ -325,16 +314,12 @@ 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.
// Note: *types.Named isn't unique when working with generics. if t, ok := c.llvmTypes[goType]; ok {
// See https://github.com/golang/go/issues/53914 return t
// 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.Set(goType, llvmType) c.llvmTypes[goType] = llvmType
return llvmType return llvmType
} }
@@ -388,9 +373,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.String() llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
llvmType := c.ctx.StructCreateNamed(llvmName) llvmType := c.ctx.StructCreateNamed(llvmName)
c.llvmTypes.Set(goType, llvmType) // avoid infinite recursion c.llvmTypes[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
@@ -415,8 +400,6 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
members[i] = c.getLLVMType(typ.Field(i).Type()) members[i] = c.getLLVMType(typ.Field(i).Type())
} }
return c.ctx.StructType(members, false) return c.ctx.StructType(members, false)
case *types.TypeParam:
return c.getLLVMType(typ.Underlying())
case *types.Tuple: case *types.Tuple:
members := make([]llvm.Type, typ.Len()) members := make([]llvm.Type, typ.Len())
for i := 0; i < typ.Len(); i++ { for i := 0; i < typ.Len(); i++ {
@@ -615,8 +598,6 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
}) })
temporaryMDNode.ReplaceAllUsesWith(md) temporaryMDNode.ReplaceAllUsesWith(md)
return md return md
case *types.TypeParam:
return c.getDIType(typ.Underlying())
default: default:
panic("unknown type while generating DWARF debug type: " + typ.String()) panic("unknown type while generating DWARF debug type: " + typ.String())
} }
@@ -686,7 +667,7 @@ func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
Name: param.Name(), Name: param.Name(),
File: b.getDIFile(pos.Filename), File: b.getDIFile(pos.Filename),
Line: pos.Line, Line: pos.Line,
Type: b.getDIType(param.Type()), Type: b.getDIType(variable.Type()),
AlwaysPreserve: true, AlwaysPreserve: true,
ArgNo: i + 1, ArgNo: i + 1,
}) })
@@ -784,25 +765,10 @@ 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 _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
// The body of this function (if there is one) is ignored and
// replaced with a LLVM intrinsic call.
b.defineMathOp()
continue
}
if member.Blocks == nil { if member.Blocks == nil {
// Try to define this as an intrinsic function. continue // external 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:
@@ -819,9 +785,6 @@ 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
} }
@@ -846,9 +809,7 @@ 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 files, ok := c.embedGlobals[member.Name()]; ok { if !info.extern {
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 != "" {
@@ -882,155 +843,10 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
} }
} }
// createEmbedGlobal creates an initializer for a //go:embed global variable. // createFunction builds the LLVM IR implementation for this function. The
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) { // function must not yet be defined, otherwise this function will create a
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) { // diagnostic.
case *types.Basic: func (b *builder) createFunction() {
// 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(intrinsic bool) {
if b.DumpSSA { if b.DumpSSA {
fmt.Printf("\nfunc %s:\n", b.fn) fmt.Printf("\nfunc %s:\n", b.fn)
} }
@@ -1043,17 +859,9 @@ func (b *builder) createFunctionStart(intrinsic bool) {
b.addError(b.fn.Pos(), errValue) b.addError(b.fn.Pos(), errValue)
return return
} }
b.addStandardDefinedAttributes(b.llvmFn) b.addStandardDefinedAttributes(b.llvmFn)
if !b.info.exported { if !b.info.exported {
// Do not set visibility for local linkage (internal or private). b.llvmFn.SetVisibility(llvm.HiddenVisibility)
// Otherwise a "local linkage requires default visibility"
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
// is thrown.
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
b.llvmFn.Linkage() != llvm.PrivateLinkage {
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
}
b.llvmFn.SetUnnamedAddr(true) b.llvmFn.SetUnnamedAddr(true)
} }
if b.info.section != "" { if b.info.section != "" {
@@ -1103,21 +911,12 @@ func (b *builder) createFunctionStart(intrinsic bool) {
} }
// Pre-create all basic blocks in the function. // Pre-create all basic blocks in the function.
var entryBlock llvm.BasicBlock for _, block := range b.fn.DomPreorder() {
if intrinsic { llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
// This function isn't defined in Go SSA. It is probably a compiler b.blockEntries[block] = llvmBlock
// intrinsic (like an atomic operation). Create the entry block b.blockExits[block] = llvmBlock
// manually.
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
} else {
for _, block := range b.fn.DomPreorder() {
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
b.blockEntries[block] = llvmBlock
b.blockExits[block] = llvmBlock
}
// Normal functions have an entry block.
entryBlock = b.blockEntries[b.fn.Blocks[0]]
} }
entryBlock := b.blockEntries[b.fn.Blocks[0]]
b.SetInsertPointAtEnd(entryBlock) b.SetInsertPointAtEnd(entryBlock)
if b.fn.Synthetic == "package initializer" { if b.fn.Synthetic == "package initializer" {
@@ -1153,10 +952,10 @@ func (b *builder) createFunctionStart(intrinsic bool) {
} else { } else {
fieldOffsets := b.expandFormalParamOffsets(llvmType) fieldOffsets := b.expandFormalParamOffsets(llvmType)
for i, field := range fields { for i, field := range fields {
expr := b.dibuilder.CreateExpression([]uint64{ expr := b.dibuilder.CreateExpression([]int64{
0x1000, // DW_OP_LLVM_fragment 0x1000, // DW_OP_LLVM_fragment
fieldOffsets[i] * 8, // offset in bits int64(fieldOffsets[i]) * 8, // offset in bits
b.targetData.TypeAllocSize(field.Type()) * 8, // size in bits int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
}) })
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock) b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
} }
@@ -1192,13 +991,6 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// 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(false)
// Fill blocks with instructions. // Fill blocks with instructions.
for _, block := range b.fn.DomPreorder() { for _, block := range b.fn.DomPreorder() {
@@ -1246,12 +1038,6 @@ 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()
@@ -1277,7 +1063,6 @@ func (b *builder) createFunction() {
// Create anonymous functions (closures etc.). // Create anonymous functions (closures etc.).
for _, sub := range b.fn.AnonFuncs { for _, sub := range b.fn.AnonFuncs {
b := newBuilder(b.compilerContext, b.Builder, sub) b := newBuilder(b.compilerContext, b.Builder, sub)
b.llvmFn.SetLinkage(llvm.InternalLinkage)
b.createFunction() b.createFunction()
} }
} }
@@ -1380,12 +1165,9 @@ 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.createRuntimeInvoke("_panic", []llvm.Value{value}, "") b.createRuntimeCall("_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 {
@@ -1446,7 +1228,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case "cap": case "cap":
value := argValues[0] value := argValues[0]
var llvmCap llvm.Value var llvmCap llvm.Value
switch argTypes[0].Underlying().(type) { switch argTypes[0].(type) {
case *types.Chan: case *types.Chan:
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap") llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
case *types.Slice: case *types.Slice:
@@ -1562,12 +1344,6 @@ 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())
} }
@@ -1580,13 +1356,7 @@ 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":
useParentFrame := uint64(0) return b.createRuntimeCall("_recover", nil, ""), nil
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
@@ -1598,20 +1368,6 @@ 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
@@ -1660,6 +1416,15 @@ 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":
result, ok := b.createMathOp(instr)
if ok {
return result, nil
}
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
return b.createInlineAsm(instr.Args) return b.createInlineAsm(instr.Args)
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
@@ -1670,16 +1435,22 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitSV64Call(instr.Args) return b.emitSV64Call(instr.Args)
case strings.HasPrefix(name, "(device/riscv.CSR)."): case strings.HasPrefix(name, "(device/riscv.CSR)."):
return b.emitCSROperation(instr) return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"): case strings.HasPrefix(name, "syscall.Syscall"):
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 name == "runtime.supportsRecover": case strings.HasPrefix(name, "runtime/volatile.Load"):
supportsRecover := uint64(0) return b.createVolatileLoad(instr)
if b.supportsRecover() { case strings.HasPrefix(name, "runtime/volatile.Store"):
supportsRecover = 1 return b.createVolatileStore(instr)
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
} }
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil // This call couldn't be lowered as an atomic operation, it's
// probably something else. Continue as usual.
case name == "runtime/interrupt.New": case name == "runtime/interrupt.New":
return b.createInterruptGlobal(instr) return b.createInterruptGlobal(instr)
} }
@@ -1693,14 +1464,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
case *ssa.Function: case *ssa.Function:
// Regular function call. No context is necessary. // Regular function call. No context is necessary.
context = llvm.Undef(b.i8ptrType) context = llvm.Undef(b.i8ptrType)
if info.variadic && len(fn.Params) == 0 {
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
// Eventually we might be able to eliminate this special case
// entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
}
case *ssa.MakeClosure: case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For // A call on a func value, but the callee is trivial to find. For
// example: immediately applied functions. // example: immediately applied functions.
@@ -1742,7 +1505,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append(params, context) params = append(params, context)
} }
return b.createInvoke(callee, params, ""), nil return b.createCall(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
@@ -1786,11 +1549,7 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
// Determine the maximum allowed size for a slice. The biggest possible // Determine the maximum allowed size for a slice. The biggest possible
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so // pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
// divide by the element type to get the real maximum size. // divide by the element type to get the real maximum size.
elementSize := c.targetData.TypeAllocSize(elementType) maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
if elementSize == 0 {
elementSize = 1
}
maxSize := maxPointerValue / elementSize
// len(slice) is an int. Make sure the length remains small enough to fit in // len(slice) is an int. Make sure the length remains small enough to fit in
// an int. // an int.
@@ -1812,9 +1571,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
switch expr := expr.(type) { switch expr := expr.(type) {
case *ssa.Alloc: case *ssa.Alloc:
typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem()) typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
size := b.targetData.TypeAllocSize(typ) if expr.Heap {
// Move all "large" allocations to the heap. This value is also transform.maxStackAlloc. size := b.targetData.TypeAllocSize(typ)
if expr.Heap || size > 256 {
// Calculate ^uintptr(0) // Calculate ^uintptr(0)
maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue() maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue()
if size > maxSize { if size > maxSize {
@@ -2693,41 +2451,42 @@ 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 (c *compilerContext) createConst(expr *ssa.Const) llvm.Value { func (b *builder) createConst(expr *ssa.Const) llvm.Value {
switch typ := expr.Type().Underlying().(type) { switch typ := expr.Type().Underlying().(type) {
case *types.Basic: case *types.Basic:
llvmType := c.getLLVMType(typ) llvmType := b.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 constant.BoolVal(expr.Value) { if b {
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(c.uintptrType, uint64(len(str)), false) strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
var strPtr llvm.Value var strPtr llvm.Value
if str != "" { if str != "" {
objname := c.pkg.Path() + "$string" objname := b.pkg.Path() + "$string"
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname) global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
global.SetInitializer(c.ctx.ConstString(str, false)) global.SetInitializer(b.ctx.ConstString(str, false))
global.SetLinkage(llvm.InternalLinkage) global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
global.SetAlignment(1) global.SetAlignment(1)
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero}) strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
} else { } else {
strPtr = llvm.ConstNull(c.i8ptrType) strPtr = llvm.ConstNull(b.i8ptrType)
} }
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) strObj := llvm.ConstNamedStruct(b.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(c.uintptrType, value, false), c.i8ptrType) return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
} }
return llvm.ConstNull(c.i8ptrType) return llvm.ConstNull(b.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)
@@ -2738,18 +2497,18 @@ func (c *compilerContext) 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 := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32])) r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32])) i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)) cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0}) cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1}) cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx return cplx
} else if typ.Kind() == types.Complex128 { } else if typ.Kind() == types.Complex128 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64])) r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64])) i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)) cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0}) cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1}) cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx return cplx
} else { } else {
panic("unknown constant of basic type: " + expr.String()) panic("unknown constant of basic type: " + expr.String())
@@ -2758,35 +2517,35 @@ func (c *compilerContext) 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(c.getLLVMType(expr.Type())) return llvm.ConstNull(b.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(c.getLLVMType(expr.Type())) return llvm.ConstNull(b.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(c.uintptrType, 0, false), llvm.ConstInt(b.uintptrType, 0, false),
llvm.ConstPointerNull(c.i8ptrType), llvm.ConstPointerNull(b.i8ptrType),
} }
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields) return llvm.ConstNamedStruct(b.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(c.getLLVMType(typ)) return llvm.ConstPointerNull(b.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 := c.getLLVMType(typ.Elem()) elemType := b.getLLVMType(typ.Elem())
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0)) llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
llvmLen := llvm.ConstInt(c.uintptrType, 0, false) llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
slice := c.ctx.ConstStruct([]llvm.Value{ slice := b.ctx.ConstStruct([]llvm.Value{
llvmPtr, // backing array llvmPtr, // backing array
llvmLen, // len llvmLen, // len
llvmLen, // cap llvmLen, // cap
@@ -2797,7 +2556,7 @@ func (c *compilerContext) 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 := c.getLLVMType(typ) llvmType := b.getLLVMType(typ)
return llvm.ConstNull(llvmType) return llvm.ConstNull(llvmType)
default: default:
panic("unknown constant: " + expr.String()) panic("unknown constant: " + expr.String())
+41 -10
View File
@@ -3,12 +3,13 @@ package compiler
import ( import (
"flag" "flag"
"go/types" "go/types"
"os" "io/ioutil"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader" "github.com/tinygo-org/tinygo/loader"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -27,6 +28,25 @@ type testCase struct {
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
t.Parallel() t.Parallel()
// Check LLVM version.
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil {
t.Fatal("could not parse LLVM version:", llvm.Version)
}
if llvmMajor < 11 {
// It is likely this version needs to be bumped in the future.
// The goal is to at least test the LLVM version that's used by default
// in TinyGo and (if possible without too many workarounds) also some
// previous versions.
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
}
// Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
if err != nil {
t.Fatal("could not read Go version:", err)
}
// Determine which tests to run, depending on the Go and LLVM versions. // Determine which tests to run, depending on the Go and LLVM versions.
tests := []testCase{ tests := []testCase{
{"basic.go", "", ""}, {"basic.go", "", ""},
@@ -36,13 +56,21 @@ 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"},
{"channel.go", "", ""}, {"channel.go", "", ""},
{"intrinsics.go", "cortex-m-qemu", ""},
{"intrinsics.go", "wasm", ""},
{"gc.go", "", ""}, {"gc.go", "", ""},
} }
if llvmMajor >= 12 {
tests = append(tests, testCase{"intrinsics.go", "cortex-m-qemu", ""})
tests = append(tests, testCase{"intrinsics.go", "wasm", ""})
}
if goMinor >= 17 {
tests = append(tests, testCase{"go1.17.go", "", ""})
}
for _, tc := range tests { for _, tc := range tests {
name := tc.file name := tc.file
@@ -72,24 +100,22 @@ 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(),
RelocationModel: config.RelocationModel(), RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(), Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(), AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.StackSize(), DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(), NeedsStackObjects: config.NeedsStackObjects(),
} }
machine, err := NewTargetMachine(compilerConfig) machine, err := NewTargetMachine(compilerConfig)
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, "./testdata/"+tc.file, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
Sizes: Sizes(machine), Sizes: Sizes(machine),
}) })
if err != nil { if err != nil {
@@ -137,14 +163,14 @@ func TestCompiler(t *testing.T) {
// Update test if needed. Do not check the result. // Update test if needed. Do not check the result.
if *flagUpdate { if *flagUpdate {
err := os.WriteFile(outPath, []byte(mod.String()), 0666) err := ioutil.WriteFile(outPath, []byte(mod.String()), 0666)
if err != nil { if err != nil {
t.Error("failed to write updated output file:", err) t.Error("failed to write updated output file:", err)
} }
return return
} }
expected, err := os.ReadFile(outPath) expected, err := ioutil.ReadFile(outPath)
if err != nil { if err != nil {
t.Fatal("failed to read golden file:", err) t.Fatal("failed to read golden file:", err)
} }
@@ -195,9 +221,14 @@ func filterIrrelevantIRLines(lines []string) []string {
if strings.HasPrefix(line, "source_filename = ") { if strings.HasPrefix(line, "source_filename = ") {
continue continue
} }
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") { if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
// Ignore attribute groups. These may change between LLVM versions.
// Right now test outputs are for LLVM 12 and higher.
continue
}
if llvmVersion < 13 && strings.HasPrefix(line, "target datalayout = ") {
// 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 13 and higher.
continue continue
} }
out = append(out, line) out = append(out, line)
+28 -201
View File
@@ -15,39 +15,12 @@ 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.
@@ -63,151 +36,6 @@ 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.
@@ -373,31 +201,30 @@ func (b *builder) createDefer(instr *ssa.Defer) {
} }
} }
// Make a struct out of the collected values to put in the deferred call // Make a struct out of the collected values to put in the defer frame.
// struct. deferFrameType := b.ctx.StructType(valueTypes, false)
deferredCallType := b.ctx.StructType(valueTypes, false) deferFrame := llvm.ConstNull(deferFrameType)
deferredCall := llvm.ConstNull(deferredCallType)
for i, value := range values { for i, value := range values {
deferredCall = b.CreateInsertValue(deferredCall, value, i, "") deferFrame = b.CreateInsertValue(deferFrame, 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, deferredCallType, "defer.alloca") alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca")
} else { } else {
// This may be hit a variable number of times, so use a heap allocation. // This may be hit a variable number of times, so use a heap allocation.
size := b.targetData.TypeAllocSize(deferredCallType) size := b.targetData.TypeAllocSize(deferFrameType)
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(deferredCallType, 0), "defer.alloc") alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
} }
if b.NeedsStackObjects { if b.NeedsStackObjects {
b.trackPointer(alloca) b.trackPointer(alloca)
} }
b.CreateStore(deferredCall, alloca) b.CreateStore(deferFrame, 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")
@@ -421,11 +248,11 @@ func (b *builder) createRunDefers() {
// } // }
// } // }
// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end. // Create loop.
end := b.insertBasicBlock("rundefers.end") loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default") loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop") unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead") end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
b.CreateBr(loophead) b.CreateBr(loophead)
// Create loop head: // Create loop head:
@@ -457,7 +284,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.insertBasicBlock("rundefers.callback" + strconv.Itoa(i)) block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
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) {
@@ -468,7 +295,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 deferred call. //Expect funcValue to be passed through the defer frame.
valueTypes = append(valueTypes, b.getFuncType(callback.Signature())) valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
} else { } else {
//Expect typecode //Expect typecode
@@ -479,14 +306,14 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.getLLVMType(arg.Type())) valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
} }
deferredCallType := b.ctx.StructType(valueTypes, false) deferFrameType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// 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(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") gep := b.CreateInBoundsGEP(deferFramePtr, []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)
} }
@@ -526,14 +353,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()))
} }
deferredCallType := b.ctx.StructType(valueTypes, false) deferFrameType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// 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(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") gep := b.CreateInBoundsGEP(deferFramePtr, []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)
} }
@@ -547,7 +374,7 @@ func (b *builder) createRunDefers() {
} }
// Call real function. // Call real function.
b.createInvoke(b.getFunction(callback), forwardParams, "") b.createCall(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.
@@ -558,14 +385,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
deferredCallType := b.ctx.StructType(valueTypes, false) deferFrameType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// 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(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") gep := b.CreateInBoundsGEP(deferFramePtr, []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)
} }
@@ -584,14 +411,14 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
} }
deferredCallType := b.ctx.StructType(valueTypes, false) deferFrameType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// 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(deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") gep := b.CreateInBoundsGEP(deferFramePtr, []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)
} }
+7 -7
View File
@@ -120,16 +120,16 @@ func (b *builder) createGo(instr *ssa.Go) {
// createGoroutineStartWrapper creates a wrapper for the task-based // createGoroutineStartWrapper creates a wrapper for the task-based
// implementation of goroutines. For example, to call a function like this: // implementation of goroutines. For example, to call a function like this:
// //
// func add(x, y int) int { ... } // func add(x, y int) int { ... }
// //
// It creates a wrapper like this: // It creates a wrapper like this:
// //
// func add$gowrapper(ptr *unsafe.Pointer) { // func add$gowrapper(ptr *unsafe.Pointer) {
// args := (*struct{ // args := (*struct{
// x, y int // x, y int
// })(ptr) // })(ptr)
// add(args.x, args.y) // add(args.x, args.y)
// } // }
// //
// This is useful because the task-based goroutine start implementation only // This is useful because the task-based goroutine start implementation only
// allows a single (pointer) argument to the newly started goroutine. Also, it // allows a single (pointer) argument to the newly started goroutine. Also, it
+23 -26
View File
@@ -17,7 +17,7 @@ import (
// operands or return values. It is useful for trivial instructions, like wfi in // operands or return values. It is useful for trivial instructions, like wfi in
// ARM or sleep in AVR. // ARM or sleep in AVR.
// //
// func Asm(asm string) // func Asm(asm string)
// //
// The provided assembly must be a constant. // The provided assembly must be a constant.
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) { func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
@@ -31,17 +31,17 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
// This is a compiler builtin, which allows assembly to be called in a flexible // This is a compiler builtin, which allows assembly to be called in a flexible
// way. // way.
// //
// func AsmFull(asm string, regs map[string]interface{}) uintptr // func AsmFull(asm string, regs map[string]interface{}) uintptr
// //
// The asm parameter must be a constant string. The regs parameter must be // The asm parameter must be a constant string. The regs parameter must be
// provided immediately. For example: // provided immediately. For example:
// //
// arm.AsmFull( // arm.AsmFull(
// "str {value}, {result}", // "str {value}, {result}",
// map[string]interface{}{ // map[string]interface{}{
// "value": 1 // "value": 1
// "result": &dest, // "result": &dest,
// }) // })
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) { func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value) asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
registers := map[string]llvm.Value{} registers := map[string]llvm.Value{}
@@ -98,10 +98,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
case llvm.IntegerTypeKind: case llvm.IntegerTypeKind:
constraints = append(constraints, "r") constraints = append(constraints, "r")
case llvm.PointerTypeKind: case llvm.PointerTypeKind:
// Memory references require a type in LLVM 14, probably as a constraints = append(constraints, "*m")
// preparation for opaque pointers.
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
return s
default: default:
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
return s return s
@@ -132,11 +129,11 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
// This is a compiler builtin which emits an inline SVCall instruction. It can // This is a compiler builtin which emits an inline SVCall instruction. It can
// be one of: // be one of:
// //
// func SVCall0(num uintptr) uintptr // func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr // func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr // func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr // func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr // func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
// //
// The num parameter must be a constant. All other parameters may be any scalar // The num parameter must be a constant. All other parameters may be any scalar
// value supported by LLVM inline assembly. // value supported by LLVM inline assembly.
@@ -169,11 +166,11 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
// This is a compiler builtin which emits an inline SVCall instruction. It can // This is a compiler builtin which emits an inline SVCall instruction. It can
// be one of: // be one of:
// //
// func SVCall0(num uintptr) uintptr // func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr // func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr // func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr // func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr // func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
// //
// The num parameter must be a constant. All other parameters may be any scalar // The num parameter must be a constant. All other parameters may be any scalar
// value supported by LLVM inline assembly. // value supported by LLVM inline assembly.
@@ -206,10 +203,10 @@ func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
// This is a compiler builtin which emits CSR instructions. It can be one of: // This is a compiler builtin which emits CSR instructions. It can be one of:
// //
// func (csr CSR) Get() uintptr // func (csr CSR) Get() uintptr
// func (csr CSR) Set(uintptr) // func (csr CSR) Set(uintptr)
// func (csr CSR) SetBits(uintptr) uintptr // func (csr CSR) SetBits(uintptr) uintptr
// func (csr CSR) ClearBits(uintptr) uintptr // func (csr CSR) ClearBits(uintptr) uintptr
// //
// The csr parameter (method receiver) must be a constant. Other parameter can // The csr parameter (method receiver) must be a constant. Other parameter can
// be any value. // be any value.
+51 -85
View File
@@ -152,27 +152,6 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
return structGlobal return structGlobal
} }
var basicTypes = [...]string{
types.Bool: "bool",
types.Int: "int",
types.Int8: "int8",
types.Int16: "int16",
types.Int32: "int32",
types.Int64: "int64",
types.Uint: "uint",
types.Uint8: "uint8",
types.Uint16: "uint16",
types.Uint32: "uint32",
types.Uint64: "uint64",
types.Uintptr: "uintptr",
types.Float32: "float32",
types.Float64: "float64",
types.Complex64: "complex64",
types.Complex128: "complex128",
types.String: "string",
types.UnsafePointer: "unsafe.Pointer",
}
// getTypeCodeName returns a name for this type that can be used in the // getTypeCodeName returns a name for this type that can be used in the
// interface lowering pass to assign type codes as expected by the reflect // interface lowering pass to assign type codes as expected by the reflect
// package. See getTypeCodeNum. // package. See getTypeCodeNum.
@@ -183,7 +162,48 @@ func getTypeCodeName(t types.Type) string {
case *types.Array: case *types.Array:
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem()) return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic: case *types.Basic:
return "basic:" + basicTypes[t.Kind()] var kind string
switch t.Kind() {
case types.Bool:
kind = "bool"
case types.Int:
kind = "int"
case types.Int8:
kind = "int8"
case types.Int16:
kind = "int16"
case types.Int32:
kind = "int32"
case types.Int64:
kind = "int64"
case types.Uint:
kind = "uint"
case types.Uint8:
kind = "uint8"
case types.Uint16:
kind = "uint16"
case types.Uint32:
kind = "uint32"
case types.Uint64:
kind = "uint64"
case types.Uintptr:
kind = "uintptr"
case types.Float32:
kind = "float32"
case types.Float64:
kind = "float64"
case types.Complex64:
kind = "complex64"
case types.Complex128:
kind = "complex128"
case types.String:
kind = "string"
case types.UnsafePointer:
kind = "unsafeptr"
default:
panic("unknown basic type: " + t.Name())
}
return "basic:" + kind
case *types.Chan: case *types.Chan:
return "chan:" + getTypeCodeName(t.Elem()) return "chan:" + getTypeCodeName(t.Elem())
case *types.Interface: case *types.Interface:
@@ -389,8 +409,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// value. // value.
prevBlock := b.GetInsertBlock() prevBlock := b.GetInsertBlock()
okBlock := b.insertBasicBlock("typeassert.ok") okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
nextBlock := b.insertBasicBlock("typeassert.next") nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "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)
@@ -550,8 +570,8 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
// internally to match interfaces and to call the correct method on an // internally to match interfaces and to call the correct method on an
// interface. Examples: // interface. Examples:
// //
// String() string // String() string
// Read([]byte) (int, error) // Read([]byte) (int, error)
func methodSignature(method *types.Func) string { func methodSignature(method *types.Func) string {
return method.Name() + signature(method.Type().(*types.Signature)) return method.Name() + signature(method.Type().(*types.Signature))
} }
@@ -559,8 +579,8 @@ func methodSignature(method *types.Func) string {
// Make a readable version of a function (pointer) signature. // Make a readable version of a function (pointer) signature.
// Examples: // Examples:
// //
// () string // () string
// (string, int) (int, error) // (string, int) (int, error)
func signature(sig *types.Signature) string { func signature(sig *types.Signature) string {
s := "" s := ""
if sig.Params().Len() == 0 { if sig.Params().Len() == 0 {
@@ -571,77 +591,23 @@ func signature(sig *types.Signature) string {
if i > 0 { if i > 0 {
s += ", " s += ", "
} }
s += typestring(sig.Params().At(i).Type()) s += sig.Params().At(i).Type().String()
} }
s += ")" s += ")"
} }
if sig.Results().Len() == 0 { if sig.Results().Len() == 0 {
// keep as-is // keep as-is
} else if sig.Results().Len() == 1 { } else if sig.Results().Len() == 1 {
s += " " + typestring(sig.Results().At(0).Type()) s += " " + sig.Results().At(0).Type().String()
} else { } else {
s += " (" s += " ("
for i := 0; i < sig.Results().Len(); i++ { for i := 0; i < sig.Results().Len(); i++ {
if i > 0 { if i > 0 {
s += ", " s += ", "
} }
s += typestring(sig.Results().At(i).Type()) s += sig.Results().At(i).Type().String()
} }
s += ")" s += ")"
} }
return s return s
} }
// typestring returns a stable (human-readable) type string for the given type
// that can be used for interface equality checks. It is almost (but not
// exactly) the same as calling t.String(). The main difference is some
// normalization around `byte` vs `uint8` for example.
func typestring(t types.Type) string {
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
switch t := t.(type) {
case *types.Array:
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
case *types.Basic:
return basicTypes[t.Kind()]
case *types.Chan:
switch t.Dir() {
case types.SendRecv:
return "chan (" + typestring(t.Elem()) + ")"
case types.SendOnly:
return "chan<- (" + typestring(t.Elem()) + ")"
case types.RecvOnly:
return "<-chan (" + typestring(t.Elem()) + ")"
default:
panic("unknown channel direction")
}
case *types.Interface:
methods := make([]string, t.NumMethods())
for i := range methods {
method := t.Method(i)
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
}
return "interface{" + strings.Join(methods, ";") + "}"
case *types.Map:
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
case *types.Named:
return t.String()
case *types.Pointer:
return "*" + typestring(t.Elem())
case *types.Signature:
return "func" + signature(t)
case *types.Slice:
return "[]" + typestring(t.Elem())
case *types.Struct:
fields := make([]string, t.NumFields())
for i := range fields {
field := t.Field(i)
fields[i] = field.Name() + " " + typestring(field.Type())
if tag := t.Tag(i); tag != "" {
fields[i] += " " + strconv.Quote(tag)
}
}
return "struct{" + strings.Join(fields, ";") + "}"
default:
panic("unknown type: " + t.String())
}
}
+49 -69
View File
@@ -3,67 +3,36 @@ 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"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// Define unimplemented intrinsic functions. // createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove
//
// 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(true)
returnValue := b.createAtomicOp(b.fn.Name())
if !returnValue.IsNil() {
b.CreateRet(returnValue)
} else {
b.CreateRetVoid()
}
}
}
// createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
// function, declaring this function if needed. These calls are treated // 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) createMemoryCopyImpl() { func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) {
b.createFunctionStart(true) fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
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 b.fn.Params { for _, param := range args {
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, "")
b.CreateRetVoid() return llvm.Value{}, nil
} }
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of // createMemoryZeroCall 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) createMemoryZeroImpl() { func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
b.createFunctionStart(true)
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() {
@@ -71,54 +40,65 @@ func (b *builder) createMemoryZeroImpl() {
llvmFn = llvm.AddFunction(b.mod, fnName, fnType) llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
} }
params := []llvm.Value{ params := []llvm.Value{
b.getValue(b.fn.Params[0]), b.getValue(args[0]),
llvm.ConstInt(b.ctx.Int8Type(), 0, false), llvm.ConstInt(b.ctx.Int8Type(), 0, false),
b.getValue(b.fn.Params[1]), b.getValue(args[1]),
llvm.ConstInt(b.ctx.Int1Type(), 0, false), llvm.ConstInt(b.ctx.Int1Type(), 0, false),
} }
b.CreateCall(llvmFn, params, "") b.CreateCall(llvmFn, params, "")
b.CreateRetVoid() return llvm.Value{}, nil
} }
var mathToLLVMMapping = map[string]string{ var mathToLLVMMapping = map[string]string{
"math.Ceil": "llvm.ceil.f64",
"math.Exp": "llvm.exp.f64",
"math.Exp2": "llvm.exp2.f64",
"math.Floor": "llvm.floor.f64",
"math.Log": "llvm.log.f64",
"math.Sqrt": "llvm.sqrt.f64", "math.Sqrt": "llvm.sqrt.f64",
"math.Floor": "llvm.floor.f64",
"math.Ceil": "llvm.ceil.f64",
"math.Trunc": "llvm.trunc.f64", "math.Trunc": "llvm.trunc.f64",
} }
// defineMathOp defines a math function body as a call to a LLVM intrinsic, // createMathOp tries to lower the given call as a LLVM math intrinsic, if
// instead of the regular Go implementation. This allows LLVM to reason about // possible. It returns the call result if possible, and a boolean whether it
// the math operation and (depending on the architecture) allows it to lower the // succeeded. If it doesn't succeed, the architecture doesn't support the given
// operation to very fast floating point instructions. If this is not possible, // intrinsic.
// LLVM will emit a call to a libm function that implements the same operation. func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
// // Check whether this intrinsic is supported on the given GOARCH.
// One example of an optimization that LLVM can do is to convert // If it is unsupported, this can have two reasons:
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is //
// beneficial on architectures where 64-bit floating point operations are (much) // 1. LLVM can expand the intrinsic inline (using float instructions), but
// more expensive than 32-bit ones. // the result doesn't pass the tests of the math package.
func (b *builder) defineMathOp() { // 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
b.createFunctionStart(true) // libm function call, but the libm function call also fails the math
llvmName := mathToLLVMMapping[b.fn.RelString(nil)] // package tests.
if llvmName == "" { //
panic("unreachable: unknown math operation") // sanity check // Whatever the implementation, it must pass the tests in the math package
// so unfortunately only the below intrinsic+architecture combinations are
// supported.
name := call.StaticCallee().RelString(nil)
switch name {
case "math.Ceil", "math.Floor", "math.Trunc":
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
return llvm.Value{}, false
}
case "math.Sqrt":
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
return llvm.Value{}, false
}
default:
return llvm.Value{}, false // only the above functions are supported.
} }
llvmFn := b.mod.NamedFunction(llvmName)
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
if llvmFn.IsNil() { if llvmFn.IsNil() {
// The intrinsic doesn't exist yet, so declare it. // The intrinsic doesn't exist yet, so declare it.
// At the moment, all supported intrinsics have the form "double // At the moment, all supported intrinsics have the form "double
// foo(double %x)" so we can hardcode the signature here. // foo(double %x)" so we can hardcode the signature here.
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false) llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
llvmFn = llvm.AddFunction(b.mod, llvmName, llvmType) llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
} }
// Create a call to the intrinsic. // Create a call to the intrinsic.
args := make([]llvm.Value, len(b.fn.Params)) args := make([]llvm.Value, len(call.Args))
for i, param := range b.fn.Params { for i, arg := range call.Args {
args[i] = b.getValue(param) args[i] = b.getValue(arg)
} }
result := b.CreateCall(llvmFn, args, "") return b.CreateCall(llvmFn, args, ""), true
b.CreateRet(result)
} }
+1 -1
View File
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
return fmt.Errorf("type %q uses global context", t.String()) return fmt.Errorf("type %q uses global context", t.String())
default: default:
// we used some other context by accident // we used some other context by accident
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx) return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
} }
// if this is a composite type, check the components of the type // if this is a composite type, check the components of the type
-61
View File
@@ -5,7 +5,6 @@ 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"
@@ -24,23 +23,6 @@ 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.
@@ -271,46 +253,3 @@ 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, "")
}
-2
View File
@@ -34,7 +34,6 @@ 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")
@@ -47,7 +46,6 @@ 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)
+2 -4
View File
@@ -15,9 +15,8 @@ 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(targetData.PointerSize() * 8) uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
valueTypes := make([]llvm.Type, len(values)) valueTypes := make([]llvm.Type, len(values))
for i, value := range values { for i, value := range values {
@@ -128,9 +127,8 @@ 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(targetData.PointerSize() * 8) uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
packedType := ctx.StructType(valueTypes, false) packedType := ctx.StructType(valueTypes, false)
+1 -13
View File
@@ -10,13 +10,6 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// constants for hashmap algorithms; must match src/runtime/hashmap.go
const (
hashmapAlgorithmBinary = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
// createMakeMap creates a new map object (runtime.hashmap) by allocating and // createMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object. // initializing an appropriately sized object.
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
@@ -24,27 +17,22 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
keyType := mapType.Key().Underlying() keyType := mapType.Key().Underlying()
llvmValueType := b.getLLVMType(mapType.Elem().Underlying()) llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
var llvmKeyType llvm.Type var llvmKeyType llvm.Type
var alg uint64 // must match values in src/runtime/hashmap.go
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// String keys. // String keys.
llvmKeyType = b.getLLVMType(keyType) llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmString
} else if hashmapIsBinaryKey(keyType) { } else if hashmapIsBinaryKey(keyType) {
// Trivially comparable keys. // Trivially comparable keys.
llvmKeyType = b.getLLVMType(keyType) llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmBinary
} else { } else {
// All other keys. Implemented as map[interface{}]valueType for ease of // All other keys. Implemented as map[interface{}]valueType for ease of
// implementation. // implementation.
llvmKeyType = b.getLLVMRuntimeType("_interface") llvmKeyType = b.getLLVMRuntimeType("_interface")
alg = hashmapAlgorithmInterface
} }
keySize := b.targetData.TypeAllocSize(llvmKeyType) keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType) valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false) llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false) llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(b.uintptrType, 8, false) sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
if expr.Reserve != nil { if expr.Reserve != nil {
sizeHint = b.getValue(expr.Reserve) sizeHint = b.getValue(expr.Reserve)
var err error var err error
@@ -53,7 +41,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
return llvm.Value{}, err return llvm.Value{}, err
} }
} }
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "") hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
return hashmap, nil return hashmap, nil
} }
+1 -5
View File
@@ -45,11 +45,7 @@ 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 {
@@ -152,7 +148,7 @@ func (s *stdSizes) Sizeof(T types.Type) int64 {
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign) return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
case *types.Interface: case *types.Interface:
return s.PtrSize * 2 return s.PtrSize * 2
case *types.Pointer, *types.Chan, *types.Map: case *types.Pointer:
return s.PtrSize return s.PtrSize
case *types.Signature: case *types.Signature:
// Func values in TinyGo are two words in size. // Func values in TinyGo are two words in size.
+11 -13
View File
@@ -163,20 +163,18 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
// External/exported functions may not retain pointer values. // External/exported functions may not retain pointer values.
// https://golang.org/cmd/cgo/#hdr-Passing_pointers // https://golang.org/cmd/cgo/#hdr-Passing_pointers
if info.exported { if info.exported {
if c.archFamily() == "wasm32" { // Set the wasm-import-module attribute if the function's module is set.
// We need to add the wasm-import-module and the wasm-import-name if info.module != "" {
// attributes.
module := info.module
if module == "" {
module = "env"
}
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
name := info.importName // We need to add the wasm-import-module and the wasm-import-name
if name == "" { wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module)
name = info.linkName llvmFn.AddFunctionAttr(wasmImportModuleAttr)
// Add the Wasm Import Name, if we are a named wasm import
if info.importName != "" {
wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName)
llvmFn.AddFunctionAttr(wasmImportNameAttr)
} }
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
} }
nocaptureKind := llvm.AttributeKindID("nocapture") nocaptureKind := llvm.AttributeKindID("nocapture")
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0) nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
@@ -193,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" && fn.Synthetic != "generic function" { if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
irbuilder := c.ctx.NewBuilder() irbuilder := c.ctx.NewBuilder()
b := newBuilder(c, irbuilder, fn) b := newBuilder(c, irbuilder, fn)
b.createFunction() b.createFunction()
+34 -1
View File
@@ -16,7 +16,20 @@ 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" && b.GOOS == "linux": case b.GOARCH == "amd64":
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
@@ -166,6 +179,26 @@ 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
+41 -43
View File
@@ -1,41 +1,45 @@
; ModuleID = 'basic.go' ; ModuleID = 'basic.go'
source_filename = "basic.go" source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%main.kv = type { float } %main.kv = type { float }
%main.kv.0 = type { i8 } %main.kv.0 = type { i8 }
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 { define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = add i32 %x, %y %0 = add i32 %x, %y
ret i32 %0 ret i32 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 { define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = icmp eq i32 %x, %y %0 = icmp eq i32 %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #1 { define hidden i32 @main.divInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
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
@@ -43,35 +47,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*) #0 declare void @runtime.divideByZeroPanic(i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #1 { define hidden i32 @main.divUint(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
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 #1 { define hidden i32 @main.remInt(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
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
@@ -79,83 +83,79 @@ 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 #1 { define hidden i32 @main.remUint(i32 %x, i32 %y, i8* %context) unnamed_addr #0 {
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 #1 { define hidden i1 @main.floatEQ(float %x, float %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fcmp oeq float %x, %y %0 = fcmp oeq float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatNE(float %x, float %y, i8* %context) unnamed_addr #1 { define hidden i1 @main.floatNE(float %x, float %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fcmp une float %x, %y %0 = fcmp une float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatLower(float %x, float %y, i8* %context) unnamed_addr #1 { define hidden i1 @main.floatLower(float %x, float %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fcmp olt float %x, %y %0 = fcmp olt float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context) unnamed_addr #1 { define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fcmp ole float %x, %y %0 = fcmp ole float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context) unnamed_addr #1 { define hidden i1 @main.floatGreater(float %x, float %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fcmp ogt float %x, %y %0 = fcmp ogt float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context) unnamed_addr #1 { define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fcmp oge float %x, %y %0 = fcmp oge float %x, %y
ret i1 %0 ret i1 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #1 { define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context) unnamed_addr #0 {
entry: entry:
ret float %x.r ret float %x.r
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context) unnamed_addr #1 { define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context) unnamed_addr #0 {
entry: entry:
ret float %x.i ret float %x.i
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #1 { define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fadd float %x.r, %y.r %0 = fadd float %x.r, %y.r
%1 = fadd float %x.i, %y.i %1 = fadd float %x.i, %y.i
@@ -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 #1 { define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fsub float %x.r, %y.r %0 = fsub float %x.r, %y.r
%1 = fsub float %x.i, %y.i %1 = fsub float %x.i, %y.i
@@ -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 #1 { define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = fmul float %x.r, %y.r %0 = fmul float %x.r, %y.r
%1 = fmul float %x.i, %y.i %1 = fmul float %x.i, %y.i
@@ -189,18 +189,16 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #1 { define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context) unnamed_addr #0 {
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 internal void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #1 { define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
+23 -25
View File
@@ -1,28 +1,28 @@
; ModuleID = 'channel.go' ; ModuleID = 'channel.go'
source_filename = "channel.go" source_filename = "channel.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.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", i8* } %"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 { define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
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) #3 call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
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) #2 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*) #0 declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*)
; Function Attrs: argmemonly nofree nosync nounwind willreturn ; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 { define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
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) #3 %0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
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*) #0 declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 { define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
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) #3 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%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) #3 call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
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 #1 { define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
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) #3 %0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef) #0
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 #1 { define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context) unnamed_addr #0 {
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) #3 %select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef) #0
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,9 +122,7 @@ select.body: ; preds = %select.next
br label %select.done br label %select.done
} }
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*) #0 declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*)
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { argmemonly nofree nosync nounwind willreturn }
attributes #2 = { argmemonly nofree nosync nounwind willreturn }
attributes #3 = { nounwind }
-266
View File
@@ -1,266 +0,0 @@
; 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 internal 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 internal 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 internal 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 }
-20
View File
@@ -1,20 +0,0 @@
package main
func external()
func deferSimple() {
defer func() {
print(3)
}()
external()
}
func deferMultiple() {
defer func() {
print(3)
}()
defer func() {
print(5)
}()
external()
}
+13 -14
View File
@@ -1,20 +1,20 @@
; ModuleID = 'float.go' ; ModuleID = 'float.go'
source_filename = "float.go" source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #1 { define hidden i32 @main.f32tou32(float %v, i8* %context) unnamed_addr #0 {
entry: entry:
%positive = fcmp oge float %v, 0.000000e+00 %positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -26,25 +26,25 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.maxu32f(i8* %context) unnamed_addr #1 { define hidden float @main.maxu32f(i8* %context) unnamed_addr #0 {
entry: entry:
ret float 0x41F0000000000000 ret float 0x41F0000000000000
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #1 { define hidden i32 @main.maxu32tof32(i8* %context) unnamed_addr #0 {
entry: entry:
ret i32 -1 ret i32 -1
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context) unnamed_addr #1 { define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context) unnamed_addr #0 {
entry: entry:
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 } ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #1 { define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = uitofp i32 %v to float %0 = uitofp i32 %v to float
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000 %withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
@@ -54,7 +54,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #1 { define hidden float @main.f32tou32tof32(float %v, i8* %context) unnamed_addr #0 {
entry: entry:
%positive = fcmp oge float %v, 0.000000e+00 %positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
@@ -67,7 +67,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #1 { define hidden i8 @main.f32tou8(float %v, i8* %context) unnamed_addr #0 {
entry: entry:
%positive = fcmp oge float %v, 0.000000e+00 %positive = fcmp oge float %v, 0.000000e+00
%withinmax = fcmp ole float %v, 2.550000e+02 %withinmax = fcmp ole float %v, 2.550000e+02
@@ -79,7 +79,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #1 { define hidden i8 @main.f32toi8(float %v, i8* %context) unnamed_addr #0 {
entry: entry:
%abovemin = fcmp oge float %v, -1.280000e+02 %abovemin = fcmp oge float %v, -1.280000e+02
%belowmax = fcmp ole float %v, 1.270000e+02 %belowmax = fcmp ole float %v, 1.270000e+02
@@ -92,5 +92,4 @@ entry:
ret i8 %0 ret i8 %0
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
+14 -16
View File
@@ -1,49 +1,47 @@
; ModuleID = 'func.go' ; ModuleID = 'func.go'
source_filename = "func.go" source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #1 { define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context) unnamed_addr #0 {
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) #2 call void %1(i32 3, i8* %callback.context) #0
ret void ret void
fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(i8* undef) #2
unreachable
} }
declare void @runtime.nilPanic(i8*) #0 declare void @runtime.nilPanic(i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.bar(i8* %context) unnamed_addr #1 { define hidden void @main.bar(i8* %context) unnamed_addr #0 {
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 #1 { define hidden void @main.someFunc(i32 %arg0, i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
+44 -46
View File
@@ -1,6 +1,6 @@
; ModuleID = 'gc.go' ; ModuleID = 'gc.go'
source_filename = "gc.go" source_filename = "gc.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
@@ -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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.newScalar(i8* %context) unnamed_addr #1 { define hidden void @main.newScalar(i8* %context) unnamed_addr #0 {
entry: entry:
%new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2 %new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
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) #2 %new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
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) #2 %new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
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) #2 %new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
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 #1 { define hidden void @main.newArray(i8* %context) unnamed_addr #0 {
entry: entry:
%new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2 %new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
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) #2 %new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
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) #2 %new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
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 #1 { define hidden void @main.newStruct(i8* %context) unnamed_addr #0 {
entry: entry:
%new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2 %new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
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) #2 %new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new1, i8* undef) #0
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) #2 %new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new2, i8* undef) #0
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) #2 %new3 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-0001" to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new3, i8* undef) #0
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 #1 { define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context) unnamed_addr #0 {
entry: entry:
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #2 %new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef) #0
%0 = bitcast i8* %new to { i8*, void ()* }* %0 = bitcast i8* %new to { i8*, void ()* }*
call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %new, i8* undef) #0
ret { i8*, void ()* }* %0 ret { i8*, void ()* }* %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.makeSlice(i8* %context) unnamed_addr #1 { define hidden void @main.makeSlice(i8* %context) unnamed_addr #0 {
entry: entry:
%makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2 %makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #0
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) #2 %makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice1, i8* undef) #0
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) #2 %makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %makeslice3, i8* undef) #0
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,20 +118,18 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #1 { define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #2 %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%.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) #2 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
ret %runtime._interface %2 ret %runtime._interface %2
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
-30
View File
@@ -1,30 +0,0 @@
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)
-259
View File
@@ -1,259 +0,0 @@
; 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 }
+41
View File
@@ -0,0 +1,41 @@
package main
// Test changes to the language introduced in Go 1.17.
// For details, see: https://tip.golang.org/doc/go1.17#language
// These tests should be merged into the regular slice tests once Go 1.17 is the
// minimun Go version for TinyGo.
import "unsafe"
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
return unsafe.Add(p, len)
}
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
return unsafe.Add(p, len)
}
func SliceToArray(s []int) *[4]int {
return (*[4]int)(s)
}
func SliceToArrayConst() *[4]int {
s := make([]int, 6)
return (*[4]int)(s)
}
func SliceInt(ptr *int, len int) []int {
return unsafe.Slice(ptr, len)
}
func SliceUint16(ptr *byte, len uint16) []byte {
return unsafe.Slice(ptr, len)
}
func SliceUint64(ptr *int, len uint64) []int {
return unsafe.Slice(ptr, len)
}
func SliceInt64(ptr *int, len int64) []int {
return unsafe.Slice(ptr, len)
}
+159
View File
@@ -0,0 +1,159 @@
; ModuleID = 'go1.17.go'
source_filename = "go1.17.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = getelementptr i8, i8* %p, i32 %len
call void @runtime.trackPointer(i8* %0, i8* undef) #0
ret i8* %0
}
; Function Attrs: nounwind
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = trunc i64 %len to i32
%1 = getelementptr i8, i8* %p, i32 %0
call void @runtime.trackPointer(i8* %1, i8* undef) #0
ret i8* %1
}
; Function Attrs: nounwind
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ult i32 %s.len, 4
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.throw: ; preds = %entry
call void @runtime.sliceToArrayPointerPanic(i8* undef) #0
unreachable
slicetoarray.next: ; preds = %entry
%1 = bitcast i32* %s.data to [4 x i32]*
ret [4 x i32]* %1
}
declare void @runtime.sliceToArrayPointerPanic(i8*)
; 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
unreachable
slicetoarray.next: ; preds = %entry
%0 = bitcast i8* %makeslice to [4 x i32]*
ret [4 x i32]* %0
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ugt i32 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i32 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
%8 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %8, i8* undef) #0
ret { i32*, i32, i32 } %7
}
declare void @runtime.unsafeSlicePanic(i8*)
; Function Attrs: nounwind
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp eq i8* %ptr, null
%1 = icmp ne i16 %len, 0
%2 = and i1 %0, %1
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%3 = zext i16 %len to i32
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
ret { i8*, i32, i32 } %6
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
%9 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %9, i8* undef) #0
ret { i32*, i32, i32 } %8
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #0 {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #0
unreachable
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
%9 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %9, i8* undef) #0
ret { i32*, i32, i32 } %8
}
attributes #0 = { nounwind }
+52 -54
View File
@@ -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", i8* } %"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* undef) #8 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), 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 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
ret void ret void
} }
declare void @main.regularFunction(i32, i8*) #0 declare void @main.regularFunction(i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 { define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
entry: entry:
%unpack.int = ptrtoint i8* %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8 call void @main.regularFunction(i32 %unpack.int, i8* undef) #0
ret void ret void
} }
declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*) #0 declare i32 @"internal/task.getGoroutineStackSize"(i32, i8*)
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0 declare void @"internal/task.start"(i32, i8*, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), 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 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
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 { define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 { define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
entry: entry:
%unpack.int = ptrtoint i8* %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef) 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 #1 { define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8 %n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%0 = bitcast i8* %n to i32* %0 = bitcast i8* %n to i32*
store i32 3, i32* %0, align 4 store i32 3, i32* %0, align 4
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8 %1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
%2 = bitcast i8* %1 to i32* %2 = bitcast i8* %1 to i32*
store i32 5, i32* %2, align 4 store i32 5, i32* %2, align 4
%3 = getelementptr inbounds i8, i8* %1, i32 4 %3 = getelementptr inbounds i8, i8* %1, i32 4
%4 = bitcast i8* %3 to i8** %4 = bitcast i8* %3 to i8**
store i8* %n, i8** %4, align 4 store i8* %n, i8** %4, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* undef) #8 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), 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 call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 %stacksize, i8* undef) #0
%5 = load i32, i32* %0, align 4 %5 = load i32, i32* %0, align 4
call void @runtime.printint32(i32 %5, i8* undef) #8 call void @runtime.printint32(i32 %5, i8* undef) #0
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 { define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
entry: entry:
%unpack.ptr = bitcast i8* %context to i32* %unpack.ptr = bitcast i8* %context to i32*
store i32 7, i32* %unpack.ptr, align 4 store i32 7, i32* %unpack.ptr, align 4
@@ -92,7 +92,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 { define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
entry: entry:
%1 = bitcast i8* %0 to i32* %1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4 %2 = load i32, i32* %1, align 4
@@ -103,12 +103,12 @@ entry:
ret void ret void
} }
declare void @runtime.printint32(i32, i8*) #0 declare void @runtime.printint32(i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 { define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8 %0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
%1 = bitcast i8* %0 to i32* %1 = bitcast i8* %0 to i32*
store i32 5, i32* %1, align 4 store i32 5, i32* %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4 %2 = getelementptr inbounds i8, i8* %0, i32 4
@@ -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) #8 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), 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 call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 %stacksize, i8* undef) #0
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 { define linkonce_odr void @main.funcGoroutine.gowrapper(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
@@ -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) #8 call void %8(i32 %2, i8* %5) #0
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 { define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
entry: entry:
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8 %copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #0
ret void ret void
} }
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0 declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 { define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8 call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
ret void ret void
} }
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0 declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 { define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8 %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
%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) #8 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), 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 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
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
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,16 +197,14 @@ 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) #8 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #0
ret void ret void
} }
attributes #0 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
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 #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$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 #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
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 #4 = { nounwind "tinygo-gowrapper" }
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 #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
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 #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
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 }
+60 -62
View File
@@ -1,11 +1,11 @@
; ModuleID = 'goroutine.go' ; ModuleID = 'goroutine.go'
source_filename = "goroutine.go" source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.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", i8* } %"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.regularFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8 call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #0
ret void ret void
} }
declare void @main.regularFunction(i32, i8*) #0 declare void @main.regularFunction(i32, i8*)
declare void @runtime.deadlock(i8*) #0 declare void @runtime.deadlock(i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #2 { define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
entry: entry:
%unpack.int = ptrtoint i8* %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, i8* undef) #8 call void @main.regularFunction(i32 %unpack.int, i8* undef) #0
call void @runtime.deadlock(i8* undef) #8 call void @runtime.deadlock(i8* undef) #0
unreachable unreachable
} }
declare void @"internal/task.start"(i32, i8*, i32, i8*) #0 declare void @"internal/task.start"(i32, i8*, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.inlineFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #8 call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 16384, i8* undef) #0
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 { define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 { define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
entry: entry:
%unpack.int = ptrtoint i8* %0 to i32 %unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef) call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef)
call void @runtime.deadlock(i8* undef) #8 call void @runtime.deadlock(i8* undef) #0
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.closureFunctionGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #8 %n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%0 = bitcast i8* %n to i32* %0 = bitcast i8* %n to i32*
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8 call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
store i32 3, i32* %0, align 4 store i32 3, i32* %0, align 4
call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #8 call void @runtime.trackPointer(i8* nonnull %n, i8* undef) #0
call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #8 call void @runtime.trackPointer(i8* bitcast (void (i32, i8*)* @"main.closureFunctionGoroutine$1" to i8*), i8* undef) #0
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #8 %1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #8 call void @runtime.trackPointer(i8* nonnull %1, i8* undef) #0
%2 = bitcast i8* %1 to i32* %2 = bitcast i8* %1 to i32*
store i32 5, i32* %2, align 4 store i32 5, i32* %2, align 4
%3 = getelementptr inbounds i8, i8* %1, i32 4 %3 = getelementptr inbounds i8, i8* %1, i32 4
%4 = bitcast i8* %3 to i8** %4 = bitcast i8* %3 to i8**
store i8* %n, i8** %4, align 4 store i8* %n, i8** %4, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #8 call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 16384, i8* undef) #0
%5 = load i32, i32* %0, align 4 %5 = load i32, i32* %0, align 4
call void @runtime.printint32(i32 %5, i8* undef) #8 call void @runtime.printint32(i32 %5, i8* undef) #0
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #1 { define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context) unnamed_addr #0 {
entry: entry:
%unpack.ptr = bitcast i8* %context to i32* %unpack.ptr = bitcast i8* %context to i32*
store i32 7, i32* %unpack.ptr, align 4 store i32 7, i32* %unpack.ptr, align 4
@@ -98,7 +98,7 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #4 { define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
entry: entry:
%1 = bitcast i8* %0 to i32* %1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4 %2 = load i32, i32* %1, align 4
@@ -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) #8 call void @runtime.deadlock(i8* undef) #0
unreachable unreachable
} }
declare void @runtime.printint32(i32, i8*) #0 declare void @runtime.printint32(i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #1 { define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #8 %0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%1 = bitcast i8* %0 to i32* %1 = bitcast i8* %0 to i32*
store i32 5, i32* %1, align 4 store i32 5, i32* %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4 %2 = getelementptr inbounds i8, i8* %0, i32 4
@@ -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) #8 call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 16384, i8* undef) #0
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #5 { define linkonce_odr void @main.funcGoroutine.gowrapper(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
@@ -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) #8 call void %8(i32 %2, i8* %5) #0
call void @runtime.deadlock(i8* undef) #8 call void @runtime.deadlock(i8* undef) #0
unreachable unreachable
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #1 { define hidden void @main.recoverBuiltinGoroutine(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #1 { define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context) unnamed_addr #0 {
entry: entry:
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #8 %copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef) #0
ret void ret void
} }
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0 declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #1 { define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #8 call void @runtime.chanClose(%runtime.channel* %ch, i8* undef) #0
ret void ret void
} }
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*) #0 declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 { define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #8 %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #8 call void @runtime.trackPointer(i8* nonnull %0, i8* undef) #0
%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) #8 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
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #6 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*) #5
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #7 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
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,17 +205,15 @@ 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) #8 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef) #0
call void @runtime.deadlock(i8* undef) #8 call void @runtime.deadlock(i8* undef) #0
unreachable unreachable
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" } attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } attributes #4 = { nounwind "tinygo-gowrapper" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" } attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #7 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #8 = { nounwind }
+45 -47
View File
@@ -1,6 +1,6 @@
; ModuleID = 'interface.go' ; ModuleID = 'interface.go'
source_filename = "interface.go" source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
@@ -22,113 +22,111 @@ 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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #1 { define hidden %runtime._interface @main.simpleType(i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* null, i8* undef) #6 call void @runtime.trackPointer(i8* null, i8* undef) #0
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 #1 { define hidden %runtime._interface @main.pointerType(i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* null, i8* undef) #6 call void @runtime.trackPointer(i8* null, i8* undef) #0
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 #1 { define hidden %runtime._interface @main.interfaceType(i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* null, i8* undef) #6 call void @runtime.trackPointer(i8* null, i8* undef) #0
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) #2 declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #1 { define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* null, i8* undef) #6 call void @runtime.trackPointer(i8* null, i8* undef) #0
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) #3 declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 { define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry: entry:
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #6 %typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef) #0
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
typeassert.ok: ; preds = %entry
br label %typeassert.next
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 { define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6 %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
br i1 %0, label %typeassert.ok, label %typeassert.next br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.ok: ; preds = %entry
br label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry typeassert.next: ; preds = %typeassert.ok, %entry
ret i1 %0 ret i1 %0
typeassert.ok: ; preds = %entry
br label %typeassert.next
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 { define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #6 %0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef) #0
ret i8 %0 ret i8 %0
} }
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #4 declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #1 { define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #6 %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef) #0
%1 = extractvalue %runtime._string %0, 0 %1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(i8* %1, i8* undef) #6 call void @runtime.trackPointer(i8* %1, i8* undef) #0
ret %runtime._string %0 ret %runtime._string %0
} }
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #5 declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*) #4
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
attributes #2 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" } attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" } attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
attributes #4 = { "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 #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
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 }
+32
View File
@@ -0,0 +1,32 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-unknown-eabi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @math.Sqrt(double %x, i8* undef) #0
ret double %0
}
declare double @math.Sqrt(double, i8*)
; Function Attrs: nounwind
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @math.Trunc(double %x, i8* undef) #0
ret double %0
}
declare double @math.Trunc(double, i8*)
attributes #0 = { nounwind }
+37
View File
@@ -0,0 +1,37 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden double @main.mySqrt(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @llvm.sqrt.f64(double %x)
ret double %0
}
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare double @llvm.sqrt.f64(double) #1
; Function Attrs: nounwind
define hidden double @main.myTrunc(double %x, i8* %context) unnamed_addr #0 {
entry:
%0 = call double @llvm.trunc.f64(double %x)
ret double %0
}
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare double @llvm.trunc.f64(double) #1
attributes #0 = { nounwind }
attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
+14
View File
@@ -0,0 +1,14 @@
package main
// Test how intrinsics are lowered: either as regular calls to the math
// functions or as LLVM builtins (such as llvm.sqrt.f64).
import "math"
func mySqrt(x float64) float64 {
return math.Sqrt(x)
}
func myTrunc(x float64) float64 {
return math.Trunc(x)
}
+24 -26
View File
@@ -1,80 +1,78 @@
; ModuleID = 'pointer.go' ; ModuleID = 'pointer.go'
source_filename = "pointer.go" 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-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #1 { define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context) unnamed_addr #0 {
entry: entry:
ret [0 x i32] zeroinitializer ret [0 x i32] zeroinitializer
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #1 { define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = bitcast i8* %x to i32* %0 = bitcast i8* %x to i32*
call void @runtime.trackPointer(i8* %x, i8* undef) #2 call void @runtime.trackPointer(i8* %x, i8* undef) #0
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 #1 { define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = bitcast i32* %x to i8* %0 = bitcast i32* %x to i8*
call void @runtime.trackPointer(i8* %0, i8* undef) #2 call void @runtime.trackPointer(i8* %0, i8* undef) #0
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 #1 { define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* %x, i8* undef) #2 call void @runtime.trackPointer(i8* %x, i8* undef) #0
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 #1 { define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2 call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
%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) #2 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
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 #1 { define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context) unnamed_addr #0 {
entry: entry:
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2 call void @runtime.trackPointer(i8* %ptr, i8* undef) #0
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset %0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
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 call void @runtime.trackPointer(i8* %0, i8* undef) #0
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 #1 { define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = bitcast i32* %ptr to i8* %0 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %0, i8* undef) #2 call void @runtime.trackPointer(i8* %0, i8* undef) #0
%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) #2 call void @runtime.trackPointer(i8* %2, i8* undef) #0
%3 = bitcast i32* %1 to i8* %3 = bitcast i32* %1 to i8*
call void @runtime.trackPointer(i8* %3, i8* undef) #2 call void @runtime.trackPointer(i8* %3, i8* undef) #0
ret i32* %1 ret i32* %1
} }
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
-10
View File
@@ -3,53 +3,44 @@ package main
import _ "unsafe" import _ "unsafe"
// Creates an external global with name extern_global. // Creates an external global with name extern_global.
//
//go:extern extern_global //go:extern extern_global
var externGlobal [0]byte var externGlobal [0]byte
// Creates a // Creates a
//
//go:align 32 //go:align 32
var alignedGlobal [4]uint32 var alignedGlobal [4]uint32
// Test conflicting pragmas (the last one counts). // Test conflicting pragmas (the last one counts).
//
//go:align 64 //go:align 64
//go:align 16 //go:align 16
var alignedGlobal16 [4]uint32 var alignedGlobal16 [4]uint32
// Test exported functions. // Test exported functions.
//
//export extern_func //export extern_func
func externFunc() { func externFunc() {
} }
// Define a function in a different package using go:linkname. // Define a function in a different package using go:linkname.
//
//go:linkname withLinkageName1 somepkg.someFunction1 //go:linkname withLinkageName1 somepkg.someFunction1
func withLinkageName1() { func withLinkageName1() {
} }
// Import a function from a different package using go:linkname. // Import a function from a different package using go:linkname.
//
//go:linkname withLinkageName2 somepkg.someFunction2 //go:linkname withLinkageName2 somepkg.someFunction2
func withLinkageName2() func withLinkageName2()
// Function has an 'inline hint', similar to the inline keyword in C. // Function has an 'inline hint', similar to the inline keyword in C.
//
//go:inline //go:inline
func inlineFunc() { func inlineFunc() {
} }
// Function should never be inlined, equivalent to GCC // Function should never be inlined, equivalent to GCC
// __attribute__((noinline)). // __attribute__((noinline)).
//
//go:noinline //go:noinline
func noinlineFunc() { func noinlineFunc() {
} }
// This function should have the specified section. // This function should have the specified section.
//
//go:section .special_function_section //go:section .special_function_section
func functionInSection() { func functionInSection() {
} }
@@ -60,7 +51,6 @@ func exportedFunctionInSection() {
} }
// This function should not: it's only a declaration and not a definition. // This function should not: it's only a declaration and not a definition.
//
//go:section .special_function_section //go:section .special_function_section
func undefinedFunctionNotInSection() func undefinedFunctionNotInSection()
+17 -18
View File
@@ -1,6 +1,6 @@
; ModuleID = 'pragma.go' ; ModuleID = 'pragma.go'
source_filename = "pragma.go" source_filename = "pragma.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-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1 @extern_global = external global [0 x i8], align 1
@@ -10,59 +10,58 @@ 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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define void @extern_func() #2 { define void @extern_func() #1 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #1 { define hidden void @somepkg.someFunction1(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
declare void @somepkg.someFunction2(i8*) #0 declare void @somepkg.someFunction2(i8*)
; Function Attrs: inlinehint nounwind ; Function Attrs: inlinehint nounwind
define hidden void @main.inlineFunc(i8* %context) unnamed_addr #3 { define hidden void @main.inlineFunc(i8* %context) unnamed_addr #2 {
entry: entry:
ret void ret void
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #4 { define hidden void @main.noinlineFunc(i8* %context) unnamed_addr #3 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.functionInSection(i8* %context) unnamed_addr #1 section ".special_function_section" { define hidden void @main.functionInSection(i8* %context) unnamed_addr #0 section ".special_function_section" {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define void @exportedFunctionInSection() #5 section ".special_function_section" { define void @exportedFunctionInSection() #4 section ".special_function_section" {
entry: entry:
ret void ret void
} }
declare void @main.undefinedFunctionNotInSection(i8*) #0 declare void @main.undefinedFunctionNotInSection(i8*)
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "wasm-export-name"="extern_func" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" } attributes #2 = { inlinehint nounwind }
attributes #3 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #3 = { noinline nounwind }
attributes #4 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #4 = { nounwind "wasm-export-name"="exportedFunctionInSection" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" }
-35
View File
@@ -1,7 +1,5 @@
package main package main
import "unsafe"
func sliceLen(ints []int) int { func sliceLen(ints []int) int {
return len(ints) return len(ints)
} }
@@ -43,36 +41,3 @@ func makeArraySlice(len int) [][3]byte {
func makeInt32Slice(len int) []int32 { func makeInt32Slice(len int) []int32 {
return make([]int32, len) return make([]int32, len)
} }
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
return unsafe.Add(p, len)
}
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
return unsafe.Add(p, len)
}
func SliceToArray(s []int) *[4]int {
return (*[4]int)(s)
}
func SliceToArrayConst() *[4]int {
s := make([]int, 6)
return (*[4]int)(s)
}
func SliceInt(ptr *int, len int) []int {
return unsafe.Slice(ptr, len)
}
func SliceUint16(ptr *byte, len uint16) []byte {
return unsafe.Slice(ptr, len)
}
func SliceUint64(ptr *int, len uint64) []int {
return unsafe.Slice(ptr, len)
}
func SliceInt64(ptr *int, len int64) []int {
return unsafe.Slice(ptr, len)
}
+54 -199
View File
@@ -1,53 +1,53 @@
; ModuleID = 'slice.go' ; ModuleID = 'slice.go'
source_filename = "slice.go" 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-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 { define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
entry: entry:
ret i32 %ints.len ret i32 %ints.len
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 { define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
entry: entry:
ret i32 %ints.cap ret i32 %ints.cap
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #1 { define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context) unnamed_addr #0 {
entry: entry:
%.not = icmp ult i32 %index, %ints.len %.not = icmp ult i32 %index, %ints.len
br i1 %.not, label %lookup.next, label %lookup.throw br i1 %.not, label %lookup.next, label %lookup.throw
lookup.throw: ; preds = %entry
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*) #0 declare void @runtime.lookupPanic(i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #1 { define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context) unnamed_addr #0 {
entry: entry:
%varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2 %varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #2 call void @runtime.trackPointer(i8* nonnull %varargs, i8* undef) #0
%0 = bitcast i8* %varargs to i32* %0 = bitcast i8* %varargs to i32*
store i32 1, i32* %0, align 4 store i32 1, i32* %0, align 4
%1 = getelementptr inbounds i8, i8* %varargs, i32 4 %1 = getelementptr inbounds i8, i8* %varargs, i32 4
@@ -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) #2 %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.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) #2 call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #0
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*) #0 declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context) unnamed_addr #1 { define hidden { 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 {
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) #2 %append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef) #0
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0 %append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
%append.newBuf = bitcast i8* %append.newPtr to i32* %append.newBuf = bitcast i8* %append.newPtr to i32*
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1 %append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
@@ -84,248 +84,103 @@ 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) #2 call void @runtime.trackPointer(i8* %append.newPtr, i8* undef) #0
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 #1 { 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 {
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) #2 %copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef) #0
ret i32 %copy.n ret i32 %copy.n
} }
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*) #0 declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #1 { define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context) unnamed_addr #0 {
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) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%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) #2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
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*) #0 declare void @runtime.slicePanic(i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #1 { define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context) unnamed_addr #0 {
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) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%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) #2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
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 #1 { define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context) unnamed_addr #0 {
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) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%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) #2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
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 #1 { define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context) unnamed_addr #0 {
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) #2 %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #0
%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) #2 call void @runtime.trackPointer(i8* nonnull %makeslice.buf, i8* undef) #0
ret { i32*, i32, i32 } %2 ret { i32*, i32, i32 } %2
slice.throw: ; preds = %entry
call void @runtime.slicePanic(i8* undef) #2
unreachable
} }
; Function Attrs: nounwind attributes #0 = { nounwind }
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context) unnamed_addr #1 {
entry:
%0 = getelementptr i8, i8* %p, i32 %len
call void @runtime.trackPointer(i8* %0, i8* undef) #2
ret i8* %0
}
; Function Attrs: nounwind
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context) unnamed_addr #1 {
entry:
%0 = trunc i64 %len to i32
%1 = getelementptr i8, i8* %p, i32 %0
call void @runtime.trackPointer(i8* %1, i8* undef) #2
ret i8* %1
}
; Function Attrs: nounwind
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context) unnamed_addr #1 {
entry:
%0 = icmp ult i32 %s.len, 4
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.next: ; preds = %entry
%1 = bitcast i32* %s.data to [4 x i32]*
ret [4 x i32]* %1
slicetoarray.throw: ; preds = %entry
call void @runtime.sliceToArrayPointerPanic(i8* undef) #2
unreachable
}
declare void @runtime.sliceToArrayPointerPanic(i8*) #0
; Function Attrs: nounwind
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context) unnamed_addr #1 {
entry:
%makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef) #2
call void @runtime.trackPointer(i8* nonnull %makeslice, i8* undef) #2
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.next: ; preds = %entry
%0 = bitcast i8* %makeslice to [4 x i32]*
ret [4 x i32]* %0
slicetoarray.throw: ; preds = %entry
unreachable
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context) unnamed_addr #1 {
entry:
%0 = icmp ugt i32 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i32 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.next: ; preds = %entry
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
%8 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %8, i8* undef) #2
ret { i32*, i32, i32 } %7
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable
}
declare void @runtime.unsafeSlicePanic(i8*) #0
; Function Attrs: nounwind
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context) unnamed_addr #1 {
entry:
%0 = icmp eq i8* %ptr, null
%1 = icmp ne i16 %len, 0
%2 = and i1 %0, %1
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.next: ; preds = %entry
%3 = zext i16 %len to i32
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
call void @runtime.trackPointer(i8* %ptr, i8* undef) #2
ret { i8*, i32, i32 } %6
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
%9 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %9, i8* undef) #2
ret { i32*, i32, i32 } %8
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable
}
; Function Attrs: nounwind
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context) unnamed_addr #1 {
entry:
%0 = icmp ugt i64 %len, 1073741823
%1 = icmp eq i32* %ptr, null
%2 = icmp ne i64 %len, 0
%3 = and i1 %1, %2
%4 = or i1 %3, %0
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
unsafe.Slice.next: ; preds = %entry
%5 = trunc i64 %len to i32
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
%9 = bitcast i32* %ptr to i8*
call void @runtime.trackPointer(i8* %9, i8* undef) #2
ret { i32*, i32, i32 } %8
unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(i8* undef) #2
unreachable
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
+27 -29
View File
@@ -1,101 +1,99 @@
; ModuleID = 'string.go' ; ModuleID = 'string.go'
source_filename = "string.go" source_filename = "string.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-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { i8*, i32 } %runtime._string = type { i8*, i32 }
@"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*) #0 declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
declare void @runtime.trackPointer(i8* nocapture readonly, i8*) #0 declare void @runtime.trackPointer(i8* nocapture readonly, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.init(i8* %context) unnamed_addr #1 { define hidden void @main.init(i8* %context) unnamed_addr #0 {
entry: entry:
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #1 { define hidden %runtime._string @main.someString(i8* %context) unnamed_addr #0 {
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 #1 { define hidden %runtime._string @main.zeroLengthString(i8* %context) unnamed_addr #0 {
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 #1 { define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context) unnamed_addr #0 {
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 #1 { define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context) unnamed_addr #0 {
entry: entry:
%.not = icmp ult i32 %index, %s.len %.not = icmp ult i32 %index, %s.len
br i1 %.not, label %lookup.next, label %lookup.throw br i1 %.not, label %lookup.next, label %lookup.throw
lookup.throw: ; preds = %entry
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*) #0 declare void @runtime.lookupPanic(i8*)
; 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 #1 { define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2 %0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #0
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*) #0 declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*)
; 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 #1 { define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #2 %0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef) #0
%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 #1 { define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context) unnamed_addr #0 {
entry: entry:
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #2 %0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef) #0
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*) #0 declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*)
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #1 { define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context) unnamed_addr #0 {
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 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { nounwind }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind }
+14 -11
View File
@@ -3,25 +3,28 @@ 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() { func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) {
b.createFunctionStart(true) addr := b.getValue(instr.Args[0])
addr := b.getValue(b.fn.Params[0]) b.createNilCheck(instr.Args[0], addr, "deref")
b.createNilCheck(b.fn.Params[0], addr, "deref")
val := b.CreateLoad(addr, "") val := b.CreateLoad(addr, "")
val.SetVolatile(true) val.SetVolatile(true)
b.CreateRet(val) return val, nil
} }
// 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() { func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) {
b.createFunctionStart(true) addr := b.getValue(instr.Args[0])
addr := b.getValue(b.fn.Params[0]) val := b.getValue(instr.Args[1])
val := b.getValue(b.fn.Params[1]) b.createNilCheck(instr.Args[0], addr, "deref")
b.createNilCheck(b.fn.Params[0], addr, "deref")
store := b.CreateStore(val, addr) store := b.CreateStore(val, addr)
store.SetVolatile(true) store.SetVolatile(true)
b.CreateRetVoid() return llvm.Value{}, nil
} }
+2 -5
View File
@@ -8,7 +8,6 @@ import (
"sync" "sync"
"testing" "testing"
"golang.org/x/tools/go/buildutil"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
@@ -112,11 +111,9 @@ func TestCorpus(t *testing.T) {
opts := optionsFromTarget(target, sema) opts := optionsFromTarget(target, sema)
opts.Directory = dir opts.Directory = dir
var tags buildutil.TagsFlag opts.Tags = repo.Tags
tags.Set(repo.Tags)
opts.Tags = []string(tags)
passed, err := Test(path, out, out, &opts, false, testing.Verbose(), false, "", "", "", false, "") passed, err := Test(path, out, out, &opts, false, testing.Verbose(), false, "", "", "", "")
if err != nil { if err != nil {
t.Errorf("test error: %v", err) t.Errorf("test error: %v", err)
} }
+6 -20
View File
@@ -1,33 +1,19 @@
module github.com/tinygo-org/tinygo module github.com/tinygo-org/tinygo
go 1.18 go 1.15
require ( require (
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
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
github.com/gofrs/flock v0.8.1 github.com/gofrs/flock v0.8.1
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-tty v0.0.4 go.bug.st/serial v1.1.3
go.bug.st/serial v1.3.5 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 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-20220802112859-5bb0b77907a7 tinygo.org/x/go-llvm v0.0.0-20220121152956-4fa2ab2718f3
)
require (
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
) )

Some files were not shown because too many files have changed in this diff Show More