Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem e54711a4b0 Impossible stack trace... 2024-06-06 19:55:30 +02:00
991 changed files with 13282 additions and 57098 deletions
+20 -16
View File
@@ -10,12 +10,12 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-19-v1 - llvm-source-18-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-19-v1 key: llvm-source-18-v1
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project/clang/lib/Headers
- llvm-project/clang/include - llvm-project/clang/include
@@ -73,6 +73,14 @@ commands:
- go-cache-v4-{{ checksum "go.mod" }} - go-cache-v4-{{ 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:
keys:
- wasi-libc-sysroot-systemclang-v7
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v7
paths:
- lib/wasi-libc/sysroot
- when: - when:
condition: <<parameters.fmt-check>> condition: <<parameters.fmt-check>>
steps: steps:
@@ -82,8 +90,6 @@ commands:
name: Check Go code formatting name: Check Go code formatting
command: make fmt-check lint command: make fmt-check lint
- run: make gen-device -j4 - run: make gen-device -j4
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
- run: make smoketest XTENSA=0 - run: make smoketest XTENSA=0
- save_cache: - save_cache:
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }} key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
@@ -92,28 +98,26 @@ commands:
- /go/pkg/mod - /go/pkg/mod
jobs: jobs:
test-oldest: test-llvm15-go118:
# This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass.
docker: docker:
- image: golang:1.23-bullseye - image: golang:1.18-bullseye
steps: steps:
- test-linux: - test-linux:
llvm: "15" llvm: "15"
resource_class: large resource_class: large
test-newest: test-llvm18-go122:
# This tests the latest supported LLVM version when linking against system
# libraries.
docker: docker:
- image: golang:1.26-bookworm - image: golang:1.22-bullseye
steps: steps:
- test-linux: - test-linux:
llvm: "20" llvm: "18"
resource_class: large resource_class: large
workflows: workflows:
test-all: test-all:
jobs: jobs:
- test-oldest # This tests our lowest supported versions of Go and LLVM, to make sure at
# disable this test, since CircleCI seems unable to download due to rate-limits on Dockerhub. # least the smoke tests still pass.
# - test-newest - test-llvm15-go118
# This tests LLVM 18 support when linking against system libraries.
- test-llvm18-go122
-3
View File
@@ -1,3 +0,0 @@
# These are supported funding model platforms
open_collective: tinygo
+47 -33
View File
@@ -16,37 +16,34 @@ jobs:
name: build-macos name: build-macos
strategy: strategy:
matrix: matrix:
# macos-14: arm64 (oldest supported version as of 18-11-2025) # macos-12: amd64 (oldest supported version as of 05-02-2024)
# macos-15-intel: amd64 (last intel version to be supported by github runners) # macos-14: arm64 (oldest arm64 version)
# See https://github.com/actions/runner-images/issues/13046 os: [macos-12, macos-14]
os: [macos-14, macos-15-intel]
include: include:
- os: macos-12
goarch: amd64
- os: macos-14 - os: macos-14
goarch: arm64 goarch: arm64
- os: macos-15-intel
goarch: amd64
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Install Dependencies - name: Install Dependencies
shell: bash
run: | run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Extract TinyGo version
id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-${{ matrix.os }}-v1 key: llvm-source-18-${{ matrix.os }}-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -57,7 +54,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Save LLVM source cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -68,55 +65,73 @@ jobs:
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Restore LLVM build cache - name: Restore LLVM build cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-20-${{ matrix.os }}-v2 key: llvm-build-18-${{ matrix.os }}-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'
shell: bash
run: | run: |
# fetch LLVM source # fetch LLVM source
rm -rf llvm-project rm -rf llvm-project
make llvm-source make llvm-source
# install dependencies # install dependencies
HOMEBREW_NO_AUTO_UPDATE=1 brew install ninja HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
# build! # build!
make llvm-build make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache - name: Save LLVM build cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build path: llvm-build
- name: Cache wasi-libc sysroot
uses: actions/cache@v4
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-${{ matrix.os }}-v1
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: make gen-device - name: make gen-device
run: make -j3 gen-device run: make -j3 gen-device
- name: Test TinyGo - name: Test TinyGo
run: make test GOTESTFLAGS="-only-current-os" shell: bash
run: make test GOTESTFLAGS="-short"
- name: Build TinyGo release tarball - name: Build TinyGo release tarball
run: make release -j3 run: make release -j3
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test run: make tinygo-test
- name: Make release artifact - name: Make release artifact
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz shell: bash
run: cp -p build/release.tar.gz build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v7 # Note: this release artifact is double-zipped, see:
# https://github.com/actions/upload-artifact/issues/39
# We can essentially pick one of these:
# - have a double-zipped artifact when downloaded from the UI
# - have a very slow artifact upload
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v4
with: with:
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }} name: darwin-${{ matrix.goarch }}-double-zipped
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz path: build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
archive: false
- name: Smoke tests - name: Smoke tests
shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo run: make smoketest TINYGO=$(PWD)/build/tinygo
test-macos-homebrew: test-macos-homebrew:
name: homebrew-install name: homebrew-install
runs-on: macos-latest runs-on: macos-latest
strategy: strategy:
matrix: matrix:
version: [16, 17, 18, 19, 20] version: [16, 17, 18]
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@main uses: Homebrew/actions/setup-homebrew@master
- name: Fix Python symlinks - name: Fix Python symlinks
run: | run: |
# Github runners have broken symlinks, so relink # Github runners have broken symlinks, so relink
@@ -125,21 +140,20 @@ jobs:
- name: Install LLVM - name: Install LLVM
run: | run: |
brew install llvm@${{ matrix.version }} brew install llvm@${{ matrix.version }}
brew link llvm@${{ matrix.version }}
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Build TinyGo (LLVM ${{ matrix.version }}) - name: Build TinyGo (LLVM ${{ matrix.version }})
run: go install -tags=llvm${{ matrix.version }} run: go install -tags=llvm${{ matrix.version }}
- name: Check binary - name: Check binary
run: tinygo version run: tinygo version
- name: Build TinyGo (default LLVM) - name: Build TinyGo (default LLVM)
if: matrix.version == 20 if: matrix.version == 18
run: go install run: go install
- name: Check binary - name: Check binary
if: matrix.version == 20 if: matrix.version == 18
run: tinygo version run: tinygo version
+48 -6
View File
@@ -31,14 +31,14 @@ jobs:
sudo rm -rf /usr/local/share/boost sudo rm -rf /usr/local/share/boost
df -h df -h
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v3
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v6 uses: docker/metadata-action@v5
with: with:
images: | images: |
tinygo/tinygo-dev tinygo/tinygo-dev
@@ -47,18 +47,18 @@ jobs:
type=sha,format=long type=sha,format=long
type=raw,value=latest type=raw,value=latest
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to Github Container Registry - name: Log in to Github Container Registry
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v7 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
@@ -66,3 +66,45 @@ 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
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger Bluetooth repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFS repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFont repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyDraw repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyTerm repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
+91 -96
View File
@@ -18,37 +18,32 @@ jobs:
# statically linked binary. # statically linked binary.
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: golang:1.26-alpine image: golang:1.22-alpine
outputs:
version: ${{ steps.version.outputs.version }}
steps: steps:
- name: Install apk dependencies - name: Install apk dependencies
# tar: needed for actions/cache@v5 # tar: needed for actions/cache@v4
# git+openssh: needed for checkout (I think?) # git+openssh: needed for checkout (I think?)
# ruby: needed to install fpm # ruby: needed to install fpm
run: apk add tar git openssh make g++ ruby-dev mold run: apk add tar git openssh make g++ ruby-dev
- name: Work around CVE-2022-24765 - name: Work around CVE-2022-24765
# We're not on a multi-user machine, so this is safe. # We're not on a multi-user machine, so this is safe.
run: git config --global --add safe.directory "$GITHUB_WORKSPACE" run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Extract TinyGo version
id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Cache Go - name: Cache Go
uses: actions/cache@v5 uses: actions/cache@v4
with: with:
key: go-cache-linux-alpine-v2-${{ hashFiles('go.mod') }} key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-linux-alpine-v2 key: llvm-source-18-linux-alpine-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -59,7 +54,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Save LLVM source cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -70,10 +65,10 @@ jobs:
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Restore LLVM build cache - name: Restore LLVM build cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-20-linux-alpine-v2 key: llvm-build-18-linux-alpine-v1
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -88,22 +83,31 @@ jobs:
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache - name: Save LLVM build cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v5 uses: actions/cache@v4
id: cache-binaryen id: cache-binaryen
with: with:
key: binaryen-linux-alpine-v2 key: binaryen-linux-alpine-v1
path: build/wasm-opt path: build/wasm-opt
- name: Build Binaryen - name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true' if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: | run: |
apk add cmake samurai python3 apk add cmake samurai python3
make binaryen STATIC=1 make binaryen STATIC=1
- name: Cache wasi-libc
uses: actions/cache@v4
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-alpine-v2
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install fpm - name: Install fpm
run: | run: |
gem install --version 4.0.7 public_suffix gem install --version 4.0.7 public_suffix
@@ -111,57 +115,47 @@ jobs:
gem install --no-document fpm gem install --no-document fpm
- name: Run linter - name: Run linter
run: make lint run: make lint
- name: Run spellcheck
run: make spell
- name: Build TinyGo release - name: Build TinyGo release
run: | run: |
make release deb -j3 STATIC=1 make release deb -j3 STATIC=1
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb cp -p build/release.deb /tmp/tinygo_amd64.deb
- name: "Publish release artifact: tarball" - name: Publish release artifact
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz name: linux-amd64-double-zipped
path: /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz path: |
archive: false /tmp/tinygo.linux-amd64.tar.gz
- name: "Publish release artifact: Debian package" /tmp/tinygo_amd64.deb
uses: actions/upload-artifact@v7
with:
name: tinygo_${{ steps.version.outputs.version }}_amd64.deb
path: /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
archive: false
test-linux-build: test-linux-build:
# Test the binaries built in the build-linux job by running the smoke tests. # Test the binaries built in the build-linux job by running the smoke tests.
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with:
submodules: true
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Install wasmtime - name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1 run: |
with: mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
version: "29.0.1" curl https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
- name: Install wasm-tools tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
uses: bytecodealliance/actions/wasm-tools/setup@v1 echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Download release artifact - name: Download release artifact
uses: actions/download-artifact@v8 uses: actions/download-artifact@v4
with: with:
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz name: linux-amd64-double-zipped
- name: Extract release tarball - name: Extract release tarball
run: | run: |
mkdir -p ~/lib mkdir -p ~/lib
tar -C ~/lib -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- run: make tinygo-test-wasi-fast
- run: make tinygo-test-wasip1-fast - run: make tinygo-test-wasip1-fast
- run: make tinygo-test-wasip2-fast
- run: make tinygo-test-wasm
- run: make smoketest - run: make smoketest
assert-test-linux: assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch # Run all tests that can run on Linux, with LLVM assertions enabled to catch
@@ -169,7 +163,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Install apt dependencies - name: Install apt dependencies
@@ -184,25 +178,25 @@ jobs:
simavr \ simavr \
ninja-build ninja-build
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version: '18' node-version: '18'
- name: Install wasmtime - name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1 run: |
with: mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
version: "29.0.1" curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
- name: Setup `wasm-tools` tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
uses: bytecodealliance/actions/wasm-tools/setup@v1 echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-linux-asserts-v1 key: llvm-source-18-linux-asserts-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -213,7 +207,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Save LLVM source cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -224,10 +218,10 @@ jobs:
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Restore LLVM build cache - name: Restore LLVM build cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-20-linux-asserts-v1 key: llvm-build-18-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'
@@ -240,13 +234,13 @@ jobs:
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache - name: Save LLVM build cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v5 uses: actions/cache@v4
id: cache-binaryen id: cache-binaryen
with: with:
key: binaryen-linux-asserts-v1 key: binaryen-linux-asserts-v1
@@ -254,6 +248,15 @@ jobs:
- name: Build Binaryen - name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true' if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen run: make binaryen
- name: Cache wasi-libc
uses: actions/cache@v4
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v6
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- run: make gen-device -j4 - run: make gen-device -j4
- name: Test TinyGo - name: Test TinyGo
run: make ASSERT=1 test run: make ASSERT=1 test
@@ -265,7 +268,7 @@ jobs:
run: make tinygo-test run: make tinygo-test
- run: make smoketest - run: make smoketest
- run: make wasmtest - run: make wasmtest
- run: make tinygo-test-baremetal - run: make tinygo-baremetal
build-linux-cross: build-linux-cross:
# Build ARM Linux binaries, ready for release. # Build ARM Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against # This intentionally uses an older Linux image, so that we compile against
@@ -285,14 +288,11 @@ jobs:
- goarch: arm - goarch: arm
toolchain: arm-linux-gnueabihf toolchain: arm-linux-gnueabihf
libc: armhf libc: armhf
runs-on: ubuntu-22.04 # note: use the oldest image available! (see above) runs-on: ubuntu-20.04
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Get TinyGo version
id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Install apt dependencies - name: Install apt dependencies
run: | run: |
sudo apt-get update sudo apt-get update
@@ -301,15 +301,15 @@ jobs:
g++-${{ matrix.toolchain }} \ g++-${{ matrix.toolchain }} \
libc6-dev-${{ matrix.libc }}-cross libc6-dev-${{ matrix.libc }}-cross
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-linux-v1 key: llvm-source-18-linux-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -320,7 +320,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Save LLVM source cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -331,10 +331,10 @@ jobs:
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Restore LLVM build cache - name: Restore LLVM build cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-20-linux-${{ matrix.goarch }}-v1 key: llvm-build-18-linux-${{ matrix.goarch }}-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'
@@ -349,13 +349,13 @@ jobs:
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache - name: Save LLVM build cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v5 uses: actions/cache@v4
id: cache-binaryen id: cache-binaryen
with: with:
key: binaryen-linux-${{ matrix.goarch }}-v4 key: binaryen-linux-${{ matrix.goarch }}-v4
@@ -375,13 +375,13 @@ jobs:
run: | run: |
make CROSS=${{ matrix.toolchain }} make CROSS=${{ matrix.toolchain }}
- name: Download amd64 release - name: Download amd64 release
uses: actions/download-artifact@v8 uses: actions/download-artifact@v4
with: with:
name: tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz name: linux-amd64-double-zipped
- name: Extract amd64 release - name: Extract amd64 release
run: | run: |
mkdir -p build/release mkdir -p build/release
tar -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz -C build/release tinygo tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release - name: Modify release
run: | run: |
cp -p build/tinygo build/release/tinygo/bin cp -p build/tinygo build/release/tinygo/bin
@@ -389,17 +389,12 @@ jobs:
- name: Create ${{ matrix.goarch }} release - name: Create ${{ matrix.goarch }} release
run: | run: |
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }} make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
cp -p build/release.tar.gz /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
cp -p build/release.deb /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
- name: "Publish release artifact: tarball" - name: Publish release artifact
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }} name: linux-${{ matrix.goarch }}-double-zipped
path: /tmp/tinygo${{ needs.build-linux.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz path: |
archive: false /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
- name: "Publish release artifact: Debian package" /tmp/tinygo_${{ matrix.libc }}.deb
uses: actions/upload-artifact@v7
with:
name: linux-${{ matrix.goarch }}-double-zipped-${{ needs.build-linux.outputs.version }}
path: /tmp/tinygo_${{ needs.build-linux.outputs.version }}_${{ matrix.libc }}.deb
archive: false
+7 -7
View File
@@ -25,18 +25,18 @@ jobs:
contents: read contents: read
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v3
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v6 uses: docker/metadata-action@v5
with: with:
images: | images: |
tinygo/llvm-20 tinygo/llvm-18
ghcr.io/${{ github.repository_owner }}/llvm-20 ghcr.io/${{ github.repository_owner }}/llvm-18
tags: | tags: |
type=sha,format=long type=sha,format=long
type=raw,value=latest type=raw,value=latest
@@ -46,13 +46,13 @@ jobs:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to Github Container Registry - name: Log in to Github Container Registry
uses: docker/login-action@v4 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v7 uses: docker/build-push-action@v5
with: with:
target: tinygo-llvm-build target: tinygo-llvm-build
context: . context: .
+7 -12
View File
@@ -15,34 +15,29 @@ jobs:
nix-test: nix-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Uninstall system LLVM
# Hack to work around issue where we still include system headers for
# some reason.
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
run: sudo apt-get remove llvm-18
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Pull musl, bdwgc - name: Pull musl
run: | run: |
git submodule update --init lib/musl lib/bdwgc git submodule update --init lib/musl
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-linux-nix-v1 key: llvm-source-18-linux-nix-v1
path: | path: |
llvm-project/compiler-rt llvm-project/compiler-rt
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Save LLVM source cache
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: | path: |
llvm-project/compiler-rt llvm-project/compiler-rt
- uses: cachix/install-nix-action@v31 - uses: cachix/install-nix-action@v22
- name: Test - name: Test
run: | run: |
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo" nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
+5 -5
View File
@@ -2,11 +2,11 @@
# still works after checking out the dev branch (that is, when going from LLVM # still works after checking out the dev branch (that is, when going from LLVM
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed). # 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-20 main' | sudo tee /etc/apt/sources.list.d/llvm.list echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends -y \ sudo apt-get install --no-install-recommends -y \
llvm-20-dev \ llvm-18-dev \
clang-20 \ clang-18 \
libclang-20-dev \ libclang-18-dev \
lld-20 lld-18
+5 -7
View File
@@ -9,9 +9,7 @@ concurrency:
jobs: jobs:
sizediff: sizediff:
# Note: when updating the Ubuntu version, also update the Ubuntu version in runs-on: ubuntu-22.04
# sizediff-install-pkgs.sh
runs-on: ubuntu-24.04
permissions: permissions:
pull-requests: write pull-requests: write
steps: steps:
@@ -20,24 +18,24 @@ jobs:
run: | run: |
echo "$HOME/go/bin" >> $GITHUB_PATH echo "$HOME/go/bin" >> $GITHUB_PATH
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # fetch all history (no sparse checkout) fetch-depth: 0 # fetch all history (no sparse checkout)
submodules: true submodules: true
- name: Install apt dependencies - name: Install apt dependencies
run: ./.github/workflows/sizediff-install-pkgs.sh run: ./.github/workflows/sizediff-install-pkgs.sh
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache@v5 uses: actions/cache@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-sizediff-v1 key: llvm-source-18-sizediff-v1
path: | path: |
llvm-project/compiler-rt llvm-project/compiler-rt
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Cache Go - name: Cache Go
uses: actions/cache@v5 uses: actions/cache@v4
with: with:
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }} key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
path: | path: |
@@ -1,4 +0,0 @@
#!/bin/sh
# Extract the version string from the source code, to be stored in a variable.
grep 'const version' goenv/version.go | sed 's/^const version = "\(.*\)"$/version=\1/g'
+68 -56
View File
@@ -14,8 +14,6 @@ concurrency:
jobs: jobs:
build-windows: build-windows:
runs-on: windows-2022 runs-on: windows-2022
outputs:
version: ${{ steps.version.outputs.version }}
steps: steps:
- name: Configure pagefile - name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.4 uses: al-cheb/configure-pagefile-action@v1.4
@@ -23,30 +21,27 @@ jobs:
minimum-size: 8GB minimum-size: 8GB
maximum-size: 24GB maximum-size: 24GB
disk-root: "C:" disk-root: "C:"
- uses: MinoruSekine/setup-scoop@v4 - uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
scoop config use_external_7zip true
scoop install ninja binaryen scoop install ninja binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Extract TinyGo version
id: version
shell: bash
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Restore cached LLVM source - name: Restore cached LLVM source
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-20-windows-v1 key: llvm-source-18-windows-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -57,7 +52,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save cached LLVM source - name: Save cached LLVM source
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -68,10 +63,10 @@ jobs:
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Restore cached LLVM build - name: Restore cached LLVM build
uses: actions/cache/restore@v5 uses: actions/cache/restore@v4
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-20-windows-v2 key: llvm-build-18-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'
@@ -85,40 +80,46 @@ jobs:
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save cached LLVM build - name: Save cached LLVM build
uses: actions/cache/save@v5 uses: actions/cache/save@v4
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with: with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build path: llvm-build
- name: Cache Go cache - name: Cache wasi-libc sysroot
uses: actions/cache@v5 uses: actions/cache@v4
id: cache-wasi-libc
with: with:
key: go-cache-windows-v2-${{ hashFiles('go.mod') }} key: wasi-libc-sysroot-v5
path: | path: lib/wasi-libc/sysroot
C:/Users/runneradmin/AppData/Local/go-build - name: Build wasi-libc
C:/Users/runneradmin/go/pkg/mod if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install wasmtime - name: Install wasmtime
run: | run: |
scoop config use_external_7zip true scoop install wasmtime@14.0.4
scoop install wasmtime@29.0.1
- name: make gen-device - name: make gen-device
run: make -j3 gen-device run: make -j3 gen-device
- name: Test TinyGo - name: Test TinyGo
shell: bash shell: bash
run: make test GOTESTFLAGS="-only-current-os" run: make test GOTESTFLAGS="-short"
- name: Build TinyGo release tarball - name: Build TinyGo release tarball
shell: bash shell: bash
run: make build/release -j4 run: make build/release -j4
- name: Make release artifact - name: Make release artifact
shell: bash shell: bash
working-directory: build/release working-directory: build/release
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo run: 7z -tzip a release.zip tinygo
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v7 # Note: this release artifact is double-zipped, see:
# https://github.com/actions/upload-artifact/issues/39
# We can essentially pick one of these:
# - have a dobule-zipped artifact when downloaded from the UI
# - have a very slow artifact upload
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v4
with: with:
name: tinygo${{ steps.version.outputs.version }}.windows-amd64.zip name: windows-amd64-double-zipped
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip path: build/release/release.zip
archive: false
smoke-test-windows: smoke-test-windows:
runs-on: windows-2022 runs-on: windows-2022
@@ -130,25 +131,29 @@ jobs:
minimum-size: 8GB minimum-size: 8GB
maximum-size: 24GB maximum-size: 24GB
disk-root: "C:" disk-root: "C:"
- uses: MinoruSekine/setup-scoop@v4 - uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
scoop config use_external_7zip true
scoop install binaryen scoop install binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v8 uses: actions/download-artifact@v4
with: with:
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip name: windows-amd64-double-zipped
path: build/ path: build/
# This build is already unzipped. - name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Smoke tests - name: Smoke tests
shell: bash shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
@@ -164,18 +169,21 @@ jobs:
maximum-size: 24GB maximum-size: 24GB
disk-root: "C:" disk-root: "C:"
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v8 uses: actions/download-artifact@v4
with: with:
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip name: windows-amd64-double-zipped
path: build/ path: build/
# This build is already unzipped. - name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
@@ -189,24 +197,28 @@ jobs:
minimum-size: 8GB minimum-size: 8GB
maximum-size: 24GB maximum-size: 24GB
disk-root: "C:" disk-root: "C:"
- uses: MinoruSekine/setup-scoop@v4 - uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
scoop config use_external_7zip true scoop install binaryen && scoop install wasmtime@14.0.4
scoop install binaryen wasmtime@29.0.1
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.26.2' go-version: '1.22'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v8 uses: actions/download-artifact@v4
with: with:
name: tinygo${{ needs.build-windows.outputs.version }}.windows-amd64.zip name: windows-amd64-double-zipped
path: build/ path: build/
# This build is already unzipped. - name: Unzip TinyGo build
- name: Test stdlib packages on wasip1 shell: bash
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo working-directory: build
run: 7z x release.zip -r
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
+1 -12
View File
@@ -1,8 +1,3 @@
.DS_Store
.vscode
go.work
go.work.sum
docs/_build docs/_build
src/device/avr/*.go src/device/avr/*.go
src/device/avr/*.ld src/device/avr/*.ld
@@ -20,11 +15,9 @@ src/device/stm32/*.go
src/device/stm32/*.s src/device/stm32/*.s
src/device/kendryte/*.go src/device/kendryte/*.go
src/device/kendryte/*.s src/device/kendryte/*.s
src/device/renesas/*.go
src/device/renesas/*.s
src/device/rp/*.go src/device/rp/*.go
src/device/rp/*.s src/device/rp/*.s
./vendor vendor
llvm-build llvm-build
llvm-project llvm-project
build/* build/*
@@ -37,9 +30,5 @@ test.exe
test.gba test.gba
test.hex test.hex
test.nro test.nro
test.uf2
test.wasm test.wasm
wasm.wasm wasm.wasm
*.uf2
*.elf
+6 -8
View File
@@ -16,13 +16,13 @@
url = https://github.com/WebAssembly/wasi-libc url = https://github.com/WebAssembly/wasi-libc
[submodule "lib/picolibc"] [submodule "lib/picolibc"]
path = lib/picolibc path = lib/picolibc
url = https://github.com/picolibc/picolibc.git url = https://github.com/keith-packard/picolibc.git
[submodule "lib/stm32-svd"] [submodule "lib/stm32-svd"]
path = lib/stm32-svd path = lib/stm32-svd
url = https://github.com/tinygo-org/stm32-svd url = https://github.com/tinygo-org/stm32-svd
[submodule "lib/musl"] [submodule "lib/musl"]
path = lib/musl path = lib/musl
url = https://github.com/tinygo-org/musl-libc.git url = git://git.musl-libc.org/musl
[submodule "lib/binaryen"] [submodule "lib/binaryen"]
path = lib/binaryen path = lib/binaryen
url = https://github.com/WebAssembly/binaryen.git url = https://github.com/WebAssembly/binaryen.git
@@ -32,12 +32,10 @@
[submodule "lib/macos-minimal-sdk"] [submodule "lib/macos-minimal-sdk"]
path = lib/macos-minimal-sdk path = lib/macos-minimal-sdk
url = https://github.com/aykevl/macos-minimal-sdk.git url = https://github.com/aykevl/macos-minimal-sdk.git
[submodule "lib/renesas-svd"]
path = lib/renesas-svd
url = https://github.com/tinygo-org/renesas-svd.git
[submodule "src/net"] [submodule "src/net"]
path = src/net path = src/net
url = https://github.com/tinygo-org/net.git url = https://github.com/tinygo-org/net.git
[submodule "lib/wasi-cli"] branch = dev
path = lib/wasi-cli
url = https://github.com/WebAssembly/wasi-cli
[submodule "lib/bdwgc"]
path = lib/bdwgc
url = https://github.com/ivmai/bdwgc.git
+3 -24
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.19+) * Go (1.18+)
* GNU Make * GNU Make
* Standard build tools (gcc/clang) * Standard build tools (gcc/clang)
* git * git
@@ -28,21 +28,6 @@ build tools to be built. Go is of course necessary to build TinyGo itself.
The rest of this guide assumes you're running Linux, but it should be equivalent The rest of this guide assumes you're running Linux, but it should be equivalent
on a different system like Mac. on a different system like Mac.
## Using GNU Make
The static build of TinyGo is driven by GNUmakefile, which provides a help target for quick reference:
% make help
clean Remove build directory
fmt Reformat source
fmt-check Warn if any source needs reformatting
gen-device Generate microcontroller-specific sources
llvm-source Get LLVM sources
llvm-build Build LLVM
tinygo Build the TinyGo compiler
lint Lint source tree
spell Spellcheck source tree
## Download the source ## Download the source
The first step is to download the TinyGo sources (use `--recursive` if you clone The first step is to download the TinyGo sources (use `--recursive` if you clone
@@ -85,17 +70,11 @@ Try running TinyGo:
./build/tinygo help ./build/tinygo help
Also, make sure the `tinygo` binary really is statically linked. The command to check for Also, make sure the `tinygo` binary really is statically linked. Check this
dynamic dependencies differs depending on your operating system. using `ldd` (not to be confused with `lld`):
On Linux, use `ldd` (not to be confused with `lld`):
ldd ./build/tinygo ldd ./build/tinygo
On macOS, use otool -L:
otool -L ./build/tinygo
The result should not contain libclang or libLLVM. The result should not contain libclang or libLLVM.
## Make a release tarball ## Make a release tarball
+2 -765
View File
@@ -1,766 +1,3 @@
0.41.1
---
* **machine**
- esp32c3: correct pin interrupt setup call that was overlooked from #5320
* **runtime**
- esp32s3: wait for TIMG0 update register to clear before reading timer registers
* **net**
- update net module to a version that is backwards compatible with Go 1.25.x to fix #5332
0.41.0
---
* **general**
- go.mod, compiler: bump minimum Go version to 1.23
- builder: support Go 1.26 now
- feat: add inheritable-only field to filter processor-level targets (#5270)
- feature: add esp32flash flash method for esp32s3/esp32c3/esp32/esp8266
- flashing: introduce flash method 'esp32jtag' for esp32c3/esp32s3 targets
- fashing: add "adb" flash method using Android Debug Bridge
- main: auto-use best available flashing options on esp32
- espflasher: update to espflasher 0.6.0
* **compiler**
- implement method-set based AssignableTo and Implements (#5304)
- simplify createObjectLayout
- implement copy directly
- fix min/max on floats by using intrinsics
- add element layout to sliceAppend
- add intrinsic for crypto/internal/constanttime.boolToUint8
- fix SyscallN handling for Windows on Go 1.26+
* **core**
- reflect: add TypeAssert
- reflect: fix TypeAssert for structs
- sync: add WaitGroup.Go
- os: add UserCacheDir and UserConfigDir
- os: implement Lchown function for changing file ownership (#5161)
- testing: add b.ReportMetric()
- errors: add errors package to passing list
- internal/gclayout: use correct lengths
- internal/abi: add EscapeNonString, EscapeToResultNonString
- internal, runtime: add internal fips bool
- internal/itoa: resurrect from Go stdlib
- internal/syscall/unix: implement GetRandom for WASI targets
- interp: make ptrtoint size mismatch a recoverable error
- loader: support "all:" prefix in //go:embed patterns
- loader, crypto/internal/entropy: fix 32 MiB RAM overflow on microcontrollers
- loader, unicode/utf8: fix hiBits overflow on 16-bit AVR targets
- builder: order embedded files deterministically
- builder: fix panic in findPackagePath when using -size short
- builder: fix SIGSEGV when stripping duplicate function definitions
- builder, runtime: fix duplicate symbol error with Go 1.26+ on Windows
- builder: use target-specific -march for RISC-V library compilation
- transform: modify output format from the -print-allocs flag to match the go coverage tool format
- cgo: allow for changes to 'short-enums' flag
- wasm: add more stubbed file operations
* **machine**
- esp32c3: implement BlockDevice for esp32c3 flash
- esp32c3: clear GPIO STATUS before dispatching pin callbacks
- esp32c3: implement USBDevice using interrupts
- esp32c3: add Enable stub for QEMU test target
- esp32c3: fix interrupt disable handling
- esp32c3: clear MCAUSE after handling interrupt
- esp32c3: map missing IRAM sections in linker script
- esp32c3: fix to use PROVIDE() for WiFi/BLE ROM function addresses to allow blob overrides
- esp32c3: add WiFi/BLE ROM linker support
- esp32c3: use TIMG0 alarm interrupt for sleepTicks instead of busy-waiting
- esp32s3: implement Pin.SetInterrupt for GPIO pin change interrupts
- esp32s3: use edge-triggered CPU interrupt for GPIO pin interrupts
- esp32s3: add interrupt support (#5244)
- esp32s3: switch USB implementation to use interrupts instead of polling
- esp32s3: replace inline ISR with full interrupt vector handler
- esp32s3: use TIMG0 alarm interrupt for sleepTicks
- esp32s3: improve exception handlers, add procPin/procUnpin, and linker wrap flags
- esp32s3: fix Xtensa register window spill using recursive call4 in task switching
- esp32s3: update linker script and boot assembly for multi-page flash XIP mapping
- esp32s3: add flash XIP boot assembly with cache/MMU init
- esp32s3: implement SPI (#5169)
- esp32s3: implement I2C interface (#5210)
- esp32s3: implement RNG based on onboard hardware random number generator
- esp32s3,esp32c3: add USB serial support
- esp32s3,esp32c3: add txStalled flag to skip USB serial spin when no host
- esp32s3,esp32c3: make USB Serial/JTAG writes non-blocking when FIFO is full
- esp32c3/esp32s3: refactor ADC implementation to reduce code duplication
- esp32s3,esp32c3: add ADC support (#5231)
- esp32s3,esp32c3: add PWM support (#5215)
- esp32c3/esp32s3: refactoring and corrections for SPI implementation
- esp32xx: add WASM simulator support for ESP32-C3/ESP32-S3 targets
- esp32: default SPI CS pin to NoPin when unset
- esp: fix tinygo_scanCurrentStack to spill register windows
- stm32: fix UART interrupt storm caused by uncleared overrun error
- stm32: fix PWM problem due to register shifting
- stm32: add STM32U585 target definition and runtime
- stm32: add STM32U5 GPIO, pin interrupts, and UART support
- stm32: add STM32U5 I2C support
- stm32: add STM32U5 SPI support
- stm32u585: implement ADC
- stm32g0b1: add support (#5150)
- stm32g0b1: add Watchdog + ADC support (#5158)
- stm32l0: add flash support
- stm32l0x1,l0x2: TIM: adapt to 32-bit register access
- stm32g0: sync with updated stm32 device files
- attiny85: add USI-based SPI support (#5181)
- attiny85: add PWM support (#5171)
- rp: add Close function to UART to allow for removing all system resources/power usage
- rp: use blockReset() and unresetBlockWait() helper functions for peripheral reset/unreset
- rp2: prevent PWM Period method overflow
- rp2: add per-byte timeout budget for I2C (#5189)
- feather-m0: export UART0 and pins
- usb/cdc: better ring buffer implementation (#5209)
- fix: replace ! with ~ for register mask
- atmega328p_simulator: add correct build tag for arduino_uno after renaming
* **net**
- update to Go 1.26.2 net package with UDP and JS improvements
* **runtime**
- implement fminimum/fmaximum
- make timeoffset atomic
- add MemStats.HeapObjects
- handle GODEBUG on wasip2 (#5312)
- fix Darwin syscall return value truncation and lost arguments
- add syscall.runtimeClearenv for Go 1.26
- split syscall functions for darwin changes for 1.26
- fix: init heap before random number seed on wasm platforms
* **targets**
- add esp32s3-supermini board target
- add support for Seeedstudio Xiao-RP2350 board
- add Shrike Lite board (#5170)
- rename arduino target to arduino-uno
- add pins/setup for Arduino UNO Q board QWIIC connector
- correct pin mapping for Arduino UNO Q STM32 MCU
- add stm32u585 openocd commands for flashing on Arduino UNO Q
- correct name/tag use for esp32s3-wroom1 board
- modify esp32, esp32s3, esp32c3, & esp8266 targets to use built-in esp32flash
- switch rp2040/rp2350 to default to tasks scheduler
- change default stack size for esp32c3 and esp32s3 to 8196
- swan: rename group build tag stm32l4x5 => stm32l4y5 to avoid clash with stm32 device name
- nucleo-f722ze: add build-tag stm32f722
- fix: set stm32u5x clock rate to default
- create generic targets for esp32 boards
* **libs**
- stm32-svd: update submodule
- update tools/gen-device-svd for recent changes to stm32-svd file structure (#5212)
* **build/test**
- build: use Golang 1.26 for all builds
- build: actually use the version of llvm we are supposed to be testing by using brew link command
- build: let scoop run updates to resolve binaryen install failures on Windows
- ci: don't double zip release artifacts
- test: increase default timeout from 1 to 2 minutes to prevent spurious CI fails
- testdata: fix flaky timers test by draining ticker channel after Stop
- testdata: more corpus entries (#5182)
- add soypat/lneto to corpus
- GNUmakefile: add context and expvar to stdlib tests
- GNUmakefile: add encoding/xml to stdlib tests on Linux
- main (test): skip mipsle test if the emulator is missing
- make: remove machine without board from smoketest
- stm32: add Arduino UNO Q to smoketests
- flake: bump to nixpkgs 25.11
- chore: update all github actions to latest versions
- fix: use external 7zip for scoop installs on Windows
0.40.1
---
* **machine**
- nrf: fix flash writes when SoftDevice is enabled
* **runtime**
- runtime: avoid fixed math/rand sequence on RP2040/RP2350 (#5124)
- runtime: add calls to initRand() during run() for all schedulers
- runtime: call initRand() before initHeap() during initialization
- runtime: use rand_hwrng hardwareRand for RP2040/RP2350 (#5135)
* **libs**
- picolibc: use updated location for git repo
0.40.0
---
* **general**
- all: add full LLVM 20 support
- core: feat: enable //go:linkname pragma for globals
- core: feature: Add flag to ignore go compatibility matrix (#5078)
- chore: update version for 0.40 development cycle
* **compiler**
- emit an error when the actual arch doesn't match GOARCH
- mark string parameters as readonly
- use Tarjan's SCC algorithm to detect loops for defer
- lower "large stack" limit to 16kb
* **core**
- shrink bdwgc library
- Fix linker errors for runtime.vgetrandom and crypto/internal/sysrand.fatal
- fix: add TryLock to sync.RWMutex
- fix: correct linter issues exposed by the fix in #4679
- fix: don't hardcode success return state
- fix: expand RTT debugger compatibility
- internal/task (threads): save stack bounds instead of scanning under a lock
- internal/task: create detached threads and fix error handling
- interp: better errors when debugging interp
- transform (gc): create stack slots in callers of external functions
- internal/task: prevent semaphore resource leak for threads scheduler
* **machine**
- cortexm: optimize code size for the HardFault_Handler
- fe310: add I2C pins for the HiFive1b
- clarify WriteAt semantics of BlockDevice
- fix deprecated AsmFull comment (#5005)
- make sure DMA buffers do not escape unnecessarily
- only enable USB-CDC when needed
- use larger SPI MAXCNT on nrf52833 and nrf52840
- fix: update m.queuedBytes when clamping output to avoid corrupting sentBytes
- fix: use int64 in ReadTemperature to avoid overflow
- fix(rp2): disable DBGPAUSE on startup
- fix(rp2): possible integer overflow while computing factors for SPI baudrate
- fix(rp2): reset spinlocks at startup
- fix(rp2): switch spinlock busy loop to wfe
- fix(rp2): use side-effect-free spinlocks
- nrf: add ADC_VDDH which is an ADC pin for VDDH
- nrf: don't block SPI transfer
- nrf: don't set PSELN, it's ignored in single ended mode anyway
- nrf: fix typo in ADC configuration
- nrf: refactor SoftDevice enabled check
- nrf: rename pwmPin to adcPin
- nrf: support flash operations while the SoftDevice is enabled
- rp2040: allow writing to the UART inside interrupts
- machine,nrf528: stop the bus only once on I2C bus error and ensures the first error is returned
* **net**
- update submodule to latest commits
* **runtime**
- (avr): fix infinite longjmp loop if stack is aligned to 256 bytes
- (gc_blocks.go): clear full size of allocation
- (gc_blocks.go): make sweep branchless
- (gc_blocks.go): simplify scanning logic
- (gc_blocks.go): use a linked stack to scan marked objects
- (gc_blocks.go): use best-fit allocation
- (gc_boehm.go): fix world already stopped check
- (wasm): scan the system stack
- fix sleep duration for long sleeps
- remove copied code for nrf52840
- src/syscall: update src buffer after write
- wasm: fix C realloc and optimize it a bit
* **targets**
- add xiao-esp32s3 board target
- Add ESP32-S3 support (#5091)
- Added Gopher ARCADE board
- Create "pico2-ice" target board (#5062)
* **build/test**
- Add testing.T.Context() and testing.B.Context()
- create separate go.mod file for testing dependencies for wasm tests that use Chromium headless browser to avoid use of older incompatible version.
- go back to normal scheduler instead of tasks scheduler for macos CI
- update CI to use Go 1.25.5
- update macOS GH actions builds to handle sunset of macOS 13
- use task scheduler on macOS builds to avoid test race condition lockups
- update all CI builds to use latest stable Go release. Also update some of the actions to their latest releases.
- update GH actions builds to use Go 1.25.4
- uninstall cmake before install
- fix: point the submodule for musl-lib to a mirror in the TinyGo GitHub org
- fix: remove macOS 15 from CI build matrix (conflicts with macOS 14 build)
- fix: separate host expected bytes from device intended bytes
- fix/typo: makeESPFirmwareImage
- make: GNUmakefile: shrink TinyGo binaries on Linux
- move the directory list into a variable
- several improvements to the macOS GH actions build
- Fix for #4678: top-level 'make lint' wasn't working
- fix: increase the timeout for chromedp to connect to the headless browser used for running the wasm tests.
- testdata: some more packages for the test corpus
0.39.0
---
* **general**
- all: add Go 1.25 support
- net: update to latest tinygo net package
- docs: clarify build verification step for macOS users
- Add flag to skip Renesas SVD builds
* **build**
- Makefile: install missing dlmalloc files
- flash: add -o flag support to save built binary (Fixes #4937) (#4942)
- fix: update version of clang to 17 to accommodate latest Go 1.25 docker base image
* **ci**
- chore: update all CI builds to test Go 1.25 release
- fix: disable test-newest since CircleCI seems unable to download due to rate-limits on Dockerhub
- ci: rename some jobs to avoid churn on every Go/LLVM version bump
- ci: make the goroutines test less racy
- tests: de-flake goroutines test
* **compiler**
- compiler: implement internal/abi.Escape
* **main**
- main: show the compiler error (if any) for `tinygo test -c`
- chore: correct GOOS=js name in error messages for WASM
* **machine**
- machine: add international keys
- machine: remove some unnecessary "// peripherals:" comments
- machine: add I2C pin comments
- machine: standardize I2C errors with "i2c:" prefix
- machine: make I2C usable in the simulator
- fix: add SPI and I2C to teensy 4.1 (#4943)
- `rp2`: use the correct channel mask for rp2350 ADC; hold lock during read (#4938)
- `rp2`: disable digital input for analog inputs
* **runtime**
- runtime: ensure time.Sleep(d) sleeps at least d
- runtime: stub out weak pointer support
- runtime: implement dummy AddCleanup
- runtime: enable multi-core scheduler for rp2350
- `internal/task`: use -stack-size flag when starting a new thread
- `internal/task`: add SA_RESTART flag to GC interrupts
- `internal/task`: a few small correctness fixes
- `internal/gclayout`: make gclayout values constants
- darwin: add threading support and use it by default
* **standard library**
- `sync`: implement sync.Swap
- `reflect`: implement Method.IsExported
* **testing**
- testing: stub out testing.B.Loop
* **targets**
- `stm32`: add support for the STM32L031G6U6
- add metro-rp2350 board definition (#4989)
- `rp2040/rp2350`: set the default stack size to 8k for rp2040/rp2350 based boards where this was not already the case
0.38.0
---
* **general**
- `go.*`: upgrade `golang.org/x/tools` to v0.30.0
- `all`: add support for LLVM 20
* **build**
- go back to using MinoruSekine/setup-scoop for Windows CI builds
- `flake.*`: upgrade to nixpkgs 25.05, LLVM 20
- `Makefile`: only detect ccache command when needed
- `Makefile`: create random filename inside rule
- `Makefile`: don't set GOROOT
- `Makefile`: call uname at most once
- `Makefile`: only read NodeJS version when it is needed
* **compiler**
- add support for `GODEBUG=gotypesalias=1`
- `interp`: fix `copy()` from/to external buffers
- add `-nobounds` (similar to `-gcflags=-B`)
- `compileopts`: add library version to cached library path
- `builder`: build wasi-libc inside TinyGo
- `builder`: simplify bdwgc libc dependency
- `builder`: don't use precompiled libraries
- `compileopts`: enable support for `GOARCH=wasm` in `tinygo test`
* **fixes**
- `rp2350`: Fix DMA to SPI transmits on RP2350 (#4903)
- `microbit v2`: use OpenOCD flash method on microbit v2 when using Nordic Semi SoftDevice
- `main`: display all of the current GC options for the `-gc` flag
- Remove duplicated error handling
- `sync`: fix `TestMutexConcurrent` test
- fix race condition in `testdata/goroutines.go`
- fix build warnings on Windows ARM
* **machine**
- `usb`: add USB mass storage class support
- implement usb receive message throttling
- declare usb endpoints per-platform
- `samd21`: implement watchdog
- `samd51`: write to flash memory in 512 byte long chunks
- `samd21`: write to flash memory in 64 byte long chunks
- don't inline RTT `WriteByte` everywhere
- `rp2`: unexport machine-specific errors
- `rp2`: discount scheduling delays in I2C timeouts (#4876)
- use pointer receiver in simulated PWM peripherals
- add simulated PWM/timer peripherals
- `rp2`: expose usb endpoint stall handling
- `arm`: clear pending interrupts before enabling them
- `rp2`: merge common usb code (#4856)
* **main**
- add "cores" and "threads" schedulers to help text
- add `StartPos` and `EndPos` to `-json` build output
- change `-json` flag to match upstream Go
* **runtime**
- don't lock the print output inside interrupts
- don't try to interrupt other cores before they are started
- implement `NumCPU` for the multicore scheduler
- add support for multicore scheduler
- refactor obtaining the system stack
- `interrupt`: add `Checkpoint` type
- add `exportedFuncPtr`
- avoid an allocation in `(*time.Timer).Reset`
- stub runtime signal functions for `os/signal` on wasip1
- move `timeUnit` to a single place
- implement `NumCPU` for `-scheduler=threads`
- move `mainExited` boolean
- `internal/task`: rename `tinygo_pause` to `tinygo_task_exit`
- map every goroutine to a new OS thread
- refactor `timerQueue`
- make conservative and precise GC MT-safe
- `internal/task`: implement atomic primitives for preemptive scheduling
- Use diskutil on macOS to extract volume name and path for FAT mounts #4928
* **standard library**
- `net`: update submodule to latest commits
- `runtime/debug`: add GC related stubs
- `metrics`: flesh out some of the metric types
- `reflect`: Chan related stubs
- `os`: handle relative and abs paths in `Executable()`
- `os`: add `os.Executable()` for Darwin
- `sync`: implement `RWMutex` using futexes
- `reflect`: Add `SliceOf`, `ArrayOf`, `StructOf`, `MapOf`, `FuncOf`
* **targets**
- `rp2040`: add multicore support
- `riscv32`: use `gdb` binary as a fallback
- add target for Microbit v2 with SoftDevice S140 support for both peripheral and central
- `windows`: use MSVCRT.DLL instead of UCRT on i386
- `windows`: add windows/386 support
- `arm64`: remove unnecessary `.section` directive
- `riscv-qemu`: actually sleep in `time.Sleep()`
- `riscv`: define CSR constants and use them where possible
- `darwin`: support Boehm GC (and use by default)
- `windows`: add support for the Boehm-Demers-Weiser GC
- `windows`: fix wrong register for first parameter
* **wasm**
- add Boehm GC support
- refactor/modify stub signal handling
- don't block `//go:wasmexport` because of running goroutines
- use `int64` instead of `float64` for the `timeUnit`
* **boards**
- Add board support for BigTreeTech SKR Pico (#4842)
0.37.0
---
* **general**
- add the Boehm-Demers-Weiser GC on Linux
* **ci**
- add more tests for wasm and baremetal
* **compiler**
- crypto/internal/sysrand is allowed to use unsafe signatures
* **examples**
- add goroutine benchmark to examples
* **fixes**
- ensure use of pointers for SPI interface on atsam21/atsam51 and other machines/boards that were missing implementation (#4798)
- replace loop counter with hw timer for USB SetAddressReq on rp2040 (#4796)
* **internal**
- update to go.bytecodealliance.org@v0.6.2 in GNUmakefile and internal/wasm-tools
- exclude certain files when copying package in internal/cm
- update to go.bytecodealliance.org/cm@v0.2.2 in internal/cm
- remove old reflect.go in internal/reflectlite
* **loader**
- use build tags for package iter and iter methods on reflect.Value in loader, iter, reflect
- add shim for go1.22 and earlier in loader, iter
* **machine**
- bump rp2040 to 200MHz (#4768)
- correct register address for Pin.SetInterrupt for rp2350 (#4782)
- don't block the rp2xxx UART interrupt handler
- fix RP2040 Pico board on the playground
- add flash support for rp2350 (#4803)
* **os**
- add stub Symlink for wasm
* **refactor**
- use *SPI everywhere to make consistent for implementations. Fixes #4663 "in reverse" by making SPI a pointer everywhere, as discussed in the comments.
* **reflect**
- add Value.SetIter{Key,Value} and MapIter.Reset in reflect, internal/reflectlite
- embed reflectlite types into reflect types in reflect, internal/reflectlite
- add Go 1.24 iter.Seq[2] methods
- copy reflect iter tests from upstream Go
- panic on Type.CanSeq[2] instead of returning false
- remove strconv.go
- remove unused go:linkname functions
* **riscv-qemu**
- add VirtIO RNG device
- increase stack size
* **runtime**
- only allocate heap memory when needed
- remove unused file func.go
- use package reflectlite
* **transform**
- cherry-pick from #4774
0.36.0
---
* **general**
- add initial Go 1.24 support
- add support for LLVM 19
- update license for 2025
- make small corrections for README regarding wasm
- use GOOS and GOARCH for building wasm simulated boards
- only infer target for wasm when GOOS and GOARCH are set correctly, not just based on file extension
- add test-corpus-wasip2
- use older image for cross-compiling builds
- update Linux builds to run on ubuntu-latest since 20.04 is being retired
- ensure build output directory is created
- add NoSandbox flag to chrome headless that is run during WASM tests, since this is now required for Ubuntu 23+ and we are using Ubuntu 24+ when running Github Actions
- update wasmtime used for CI to 29.0.1 to fix issue with install during CI tests
- update to use `Get-CimInstance` as `wmic` is being deprecated on WIndows
- remove unnecessary executable permissions
- `goenv`: update to new v0.36.0 development version
* **compiler**
- `builder`: fix parsing of external ld.lld error messages
- `cgo`: mangle identifier names
- `interp`: correctly mark functions as modifying memory
- add buildmode=wasi-legacy to support existing base of users who expected the older behavior for wasi modules to not return an exit code as if they were reactors
* **standard library**
- `crypto/tls`: add Dialer.DialContext() to fix websocket client
- `crypto/tls`: add VersionTLS constants and VersionName(version uint16) method that turns it into a string, copied from big go
- `internal/syscall/unix`: use our own version of this package
- `machine`: replace hard-coded cpu frequencies on rp2xxx
- `machine`: bump rp2350 CPUFrequency to 150 MHz
- `machine`: compute rp2 clock dividers from crystal and target frequency
- `machine`: remove bytes package dependency in flash code
- `machine/usb/descriptor`: avoid bytes package
- `net`: update to latest submodule with httptest subpackage and ResolveIPAddress implementation
- `os`: add File.Chdir support
- `os`: implement stub Chdir for non-OS systems
- `os/file`: add file.Chmod
- `reflect`: implement Value.Equal
- `runtime`: add FIPS helper functions
- `runtime`: manually initialize xorshift state
- `sync`: move Mutex to internal/task
- `syscall`: add wasip1 RandomGet
- `testing`: add Chdir
- `wasip2`: add stubs to get internal/syscall/unix to work
* **fixes**
- correctly handle calls for GetRNG() when being made from nrf devices with SoftDevice enabled
- fix stm32f103 ADC
- `wasm`: correctly handle id lookup for finalizeRef call
- `wasm`: avoid total failure on wasm finalizer call
- `wasm`: convert offset as signed int into unsigned int in syscall/js.stringVal in wasm_exec.js
* **targets**
- rp2350: add pll generalized solution; fix ADC handles; pwm period fix
- rp2350: extending support to include the rp2350b
- rp2350: cleanup: unexport internal USB and clock package variable, consts and types
- nrf: make ADC resolution changeable
- turn on GC for TKey1 device, since it does in fact work
- match Pico2 stack size to Pico
* **boards**
- add support for Pimoroni Pico Plus2
- add target for pico2-w board
- add comboat_fw tag for elecrow W5 boards with Combo-AT Wifi firmware
- add support for Elecrow Pico rp2350 W5 boards
- add support for Elecrow Pico rp2040 W5 boards
- add support for NRF51 HW-651
- add support for esp32c3-supermini
- add support for waveshare-rp2040-tiny
* **examples**
- add naive debouncing for pininterrupt example
0.35.0
---
* **general**
- update cmsis-svd library
- use default UART settings in the echo example
- `goenv`: also show git hash with custom build of TinyGo
- `goenv`: support parsing development versions of Go
- `main`: parse extldflags early so we can report the error message
* **compiler**
- `builder`: whitelist temporary directory env var for Clang invocation to fix Windows bug
- `builder`: fix cache paths in `-size=full` output
- `builder`: work around incorrectly escaped DWARF paths on Windows (Clang bug)
- `builder`: fix wasi-libc path names on Windows with `-size=full`
- `builder`: write HTML size report
- `cgo`: support C identifiers only referred to from within macros
- `cgo`: support function-like macros
- `cgo`: support errno value as second return parameter
- `cgo`: add support for `#cgo noescape` lines
- `compiler`: fix bug in interrupt lowering
- `compiler`: allow panic directly in `defer`
- `compiler`: fix wasmimport -> wasmexport in error message
- `compiler`: support `//go:noescape` pragma
- `compiler`: report error instead of crashing when instantiating a generic function without body
- `interp`: align created globals
* **standard library**
- `machine`: modify i2s interface/implementation to better match specification
- `os`: implement `StartProcess`
- `reflect`: add `Value.Clear`
- `reflect`: add interface support to `NumMethods`
- `reflect`: fix `AssignableTo` for named + non-named types
- `reflect`: implement `CanConvert`
- `reflect`: handle more cases in `Convert`
- `reflect`: fix Copy of non-pointer array with size > 64bits
- `runtime`: don't call sleepTicks with a negative duration
- `runtime`: optimize GC scanning (findHead)
- `runtime`: move constants into shared package
- `runtime`: add `runtime.fcntl` function for internal/syscall/unix
- `runtime`: heapptr only needs to be initialized once
- `runtime`: refactor scheduler (this fixes a few bugs with `-scheduler=none`)
- `runtime`: rewrite channel implementation to be smaller and more flexible
- `runtime`: use `SA_RESTART` when registering a signal for os/signal
- `runtime`: implement race-free signals using futexes
- `runtime`: run deferred functions in `Goexit`
- `runtime`: remove `Cond` which seems to be unused
- `runtime`: properly handle unix read on directory
- `runtime/trace`: stub all public methods
- `sync`: don't use volatile in `Mutex`
- `sync`: implement `WaitGroup` using a (pseudo)futex
- `sync`: make `Cond` parallelism-safe
- `syscall`: use wasi-libc tables for wasm/js target
* **targets**
- `mips`: fix a bug when scanning the stack
- `nintendoswitch`: get this target to compile again
- `rp2350`: add support for the new RP2350
- `rp2040/rp2350` : make I2C implementation shared for rp2040/rp2350
- `rp2040/rp2350` : make SPI implementation shared for rp2040/rp2350
- `rp2040/rp2350` : make RNG implementation shared for rp2040/rp2350
- `wasm`: revise and simplify wasmtime argument handling
- `wasm`: support `//go:wasmexport` functions after a call to `time.Sleep`
- `wasm`: correctly return from run() in wasm_exec.js
- `wasm`: call process.exit() when go.run() returns
- `windows`: don't return, exit via exit(0) instead to flush stdout buffer
* **boards**
- add support for the Tillitis TKey
- add support for the Raspberry Pi Pico2 (based on the RP2040)
- add support for Pimoroni Tiny2350
0.34.0
---
* **general**
- fix `GOOS=wasip1` for `tinygo test`
- add `-C DIR` flag
- add initial documentation for project governance
- add `-ldflags='-extldflags=...'` support
- improve usage message with `tinygo help` and when passing invalid parameters
* **compiler**
- `builder`: remove environment variables when invoking Clang, to avoid the environment changing the behavior
- `builder`: check for the Go toolchain version used to compile TinyGo
- `cgo`: add `C.CBytes` implementation
- `compiler`: fix passing weirdly-padded structs as parameters to new goroutines
- `compiler`: support pragmas on generic functions
- `compiler`: do not let the slice buffer escape when casting a `[]byte` or `[]rune` to a string, to help escape analysis
- `compiler`: conform to the latest iteration of the wasm types proposal
- `loader`: don't panic when main package is not named 'main'
- `loader`: make sure we always return type checker errors even without type errors
- `transform`: optimize range over `[]byte(string)`
* **standard library**
- `crypto/x509`: add package stub to build crypto/x509 on macOS
- `machine/usb/adc/midi`: fix `PitchBend`
- `os`: add `Truncate` stub for baremetal
- `os`: add stubs for `os.File` deadlines
- `os`: add internal `net.newUnixFile` for the net package
- `runtime`: stub runtime_{Before,After}Exec for linkage
- `runtime`: randomize map accesses
- `runtime`: support `maps.Clone`
- `runtime`: add more fields to `MemStats`
- `runtime`: implement newcoro, coroswitch to support package iter
- `runtime`: disallow defer in interrupts
- `runtime`: add support for os/signal on Linux and MacOS
- `runtime`: add gc layout info for some basic types to help the precise GC
- `runtime`: bump GC mark stack size to avoid excessive heap rescans
* **targets**
- `darwin`: use Go standard library syscall package instead of a custom one
- `fe310`: support GPIO `PinInput`
- `mips`: fix compiler crash with GOMIPS=softfloat and defer
- `mips`: add big-endian (GOARCH=mips) support
- `mips`: use MIPS32 (instead of MIPS32R2) as the instruction set for wider compatibility
- `wasi`: add relative and absolute --dir options to wasmtime args
- `wasip2`: add wasmtime -S args to support network interfaces
- `wasm`: add `//go:wasmexport` support (for all WebAssembly targets)
- `wasm`: use precise instead of conservative GC for WebAssembly (including WASI)
- `wasm-unknown`: add bulk memory flags since basically every runtime has it now
* **boards**
- add RAKwireless RAK4631
- add WaveShare ESP-C3-32S-Kit
0.33.0
---
* **general**
- use latest version of x/tools
- add chromeos 9p support for flashing
- sort compiler error messages by source position in a package
- don't include prebuilt libraries in the release to simplify packaging and reduce the release tarball size
- show runtime panic addresses for `tinygo run`
- support Go 1.23 (including all new language features)
- `test`: support GOOS/GOARCH pairs in the `-target` flag
- `test`: remove message after test binary built
* **compiler**
- remove unused registers for x86_64 linux syscalls
- remove old atomics workaround for AVR (not necessary in modern LLVM versions)
- support `golang.org/x/sys/unix` syscalls
- `builder`: remove workaround for generics race condition
- `builder`: add package ID to compiler and optimization error messages
- `builder`: show better error messages for some common linker errors
- `cgo`: support preprocessor macros passed on the command line
- `cgo`: use absolute paths for error messages
- `cgo`: add support for printf
- `loader`: handle `go list` errors inside TinyGo (for better error messages)
- `transform`: fix incorrect alignment of heap-to-stack transform
- `transform`: use thinlto-pre-link passes (instead of the full pipeline) to speed up compilation speed slightly
* **standard library**
- `crypto/tls`: add CipherSuiteName and some extra fields to ConnectionSTate
- `internal/abi`: implement initial version of this package
- `machine`: use new `internal/binary` package
- `machine`: rewrite Reply() to fix sending long replies in I2C Target Mode
- `machine/usb/descriptor`: Reset joystick physical
- `machine/usb/descriptor`: Drop second joystick hat
- `machine/usb/descriptor`: Add more HID... functions
- `machine/usb/descriptor`: Fix encoding of values
- `machine/usb/hid/joystick`: Allow more hat switches
- `os`: add `Chown`, `Truncate`
- `os/user`: use stdlib version of this package
- `reflect`: return correct name for the `unsafe.Pointer` type
- `reflect`: implement `Type.Overflow*` functions
- `runtime`: implement dummy `getAuxv` to satisfy golang.org/x/sys/
- `runtime`: don't zero out new allocations for `-gc=leaking` when they are already zeroed
- `runtime`: simplify slice growing/appending code
- `runtime`: print a message when a fatal signal like SIGSEGV happens
- `runtime/debug`: add `GoVersion` to `debug.BuildInfo`
- `sync`: add `Map.Clear()`
- `sync/atomic`: add And* and Or* compiler intrinsics needed for Go 1.23
- `syscall`: add `Fork` and `Execve`
- `syscall`: add all MacOS errno values
- `testing`: stub out `T.Deadline`
- `unique`: implement custom (naive) version of the unique package
* **targets**
- `arm`: support `GOARM=*,softfloat` (softfloat support for ARM v5, v6, and v7)
- `mips`: add linux/mipsle (and experimental linux/mips) support
- `mips`: add `GOMIPS=softfloat` support
- `wasip2`: add WASI preview 2 support
- `wasm/js`: add `node:` prefix in `require()` call of wasm_exec.js
- `wasm-unknown`: make sure the `os` package can be imported
- `wasm-unknown`: remove import-memory flag
0.32.0
---
* **general**
- fix wasi-libc include headers on Nix
- apply OpenOCD commands after target configuration
- fix a minor race condition when determining the build tags
- support UF2 drives with a space in their name on Linux
- add LLVM 18 support
- drop support for Go 1.18 to be able to stay up to date
* **compiler**
- move `-panic=trap` support to the compiler/runtime
- fix symbol table index for WebAssembly archives
- fix ed25519 build errors by adjusting the alias names
- add aliases to generic AES functions
- fix race condition by temporarily applying a proposed patch
- `builder`: keep un-wasm-opt'd .wasm if -work was passed
- `builder`: make sure wasm-opt command line is printed if asked
- `cgo`: implement shift operations in preprocessor macros
- `interp`: checking for methodset existence
* **standard library**
- `machine`: add `__tinygo_spi_tx` function to simulator
- `machine`: fix simulator I2C support
- `machine`: add GetRNG support to simulator
- `machine`: add `TxFifoFreeLevel` for CAN
- `os`: add `Link`
- `os`: add `FindProcess` for posix
- `os`: add `Process.Release` for unix
- `os`: add `SetReadDeadline` stub
- `os`, `os/signal`: add signal stubs
- `os/user`: add stubs for `Lookup{,Group}` and `Group`
- `reflect`: use int in `StringHeader` and `SliceHeader` on non-AVR platforms
- `reflect`: fix `NumMethods` for Interface type
- `runtime`: skip negative sleep durations in sleepTicks
* **targets**
- `esp32`: add I2C support
- `rp2040`: move UART0 and UART1 to common file
- `rp2040`: make all RP2040 boards available for simulation
- `rp2040`: fix timeUnit type
- `stm32`: add i2c `Frequency` and `SetBaudRate` function for chips that were missing implementation
- `wasm-unknown`: add math and memory builtins that LLVM needs
- `wasip1`: replace existing `-target=wasi` support with wasip1 as supported in Go 1.21+
* **boards**
- `adafruit-esp32-feather-v2`: add the Adafruit ESP32 Feather V2
- `badger2040-w`: add support for the Badger2040 W
- `feather-nrf52840-sense`: fix lack of LXFO
- `m5paper`: add support for the M5 Paper
- `mksnanov3`: limit programming speed to 1800 kHz
- `nucleol476rg`: add stm32 nucleol476rg support
- `pico-w`: add the Pico W (which is near-idential to the pico target)
- `thingplus-rp2040`, `waveshare-rp2040-zero`: add WS2812 definition
- `pca10059-s140v7`: add this variant to the PCA10059 board
0.31.2 0.31.2
--- ---
@@ -922,7 +159,7 @@
- `reflect`: add SetZero - `reflect`: add SetZero
- `reflect`: fix iterating over maps with interface{} keys - `reflect`: fix iterating over maps with interface{} keys
- `reflect`: implement Value.Grow - `reflect`: implement Value.Grow
- `reflect`: remove unnecessary heap allocations - `reflect`: remove unecessary heap allocations
- `reflect`: use .key() instead of a type assert - `reflect`: use .key() instead of a type assert
- `sync`: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues - `sync`: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
* **targets** * **targets**
@@ -2601,7 +1838,7 @@
- allow packages like github.com/tinygo-org/tinygo/src/\* by aliasing it - allow packages like github.com/tinygo-org/tinygo/src/\* by aliasing it
- remove `//go:volatile` support - remove `//go:volatile` support
It has been replaced with the runtime/volatile package. It has been replaced with the runtime/volatile package.
- allow pointers in map keys - allow poiners in map keys
- support non-constant syscall numbers - support non-constant syscall numbers
- implement non-blocking selects - implement non-blocking selects
- add support for the `-tags` flag - add support for the `-tags` flag
+3 -3
View File
@@ -1,8 +1,8 @@
# tinygo-llvm stage obtains the llvm source for TinyGo # tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.26 AS tinygo-llvm FROM golang:1.22 AS tinygo-llvm
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-17 ninja-build && \ apt-get install -y apt-utils make cmake clang-15 ninja-build && \
rm -rf \ rm -rf \
/var/lib/apt/lists/* \ /var/lib/apt/lists/* \
/var/log/* \ /var/log/* \
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
# tinygo-compiler copies the compiler build over to a base Go container (without # tinygo-compiler copies the compiler build over to a base Go container (without
# all the build tools etc). # all the build tools etc).
FROM golang:1.26 AS tinygo-compiler FROM golang:1.22 AS tinygo-compiler
# Copy tinygo build. # Copy tinygo build.
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
+117 -406
View File
@@ -8,20 +8,13 @@ LLVM_PROJECTDIR ?= llvm-project
CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
ifeq ($(OS),Windows_NT)
# avoid calling uname on Windows
uname := Windows_NT
else
uname := $(shell uname -s)
endif
# 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 = 19 18 17 16 15 LLVM_VERSIONS = 18 17 16 15
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)
ifeq ($(uname),Darwin) ifeq ($(shell uname -s),Darwin)
# Also explicitly search Brew's copy, which is not in PATH by default. # Also explicitly search Brew's copy, which is not in PATH by default.
BREW_PREFIX := $(shell brew --prefix) BREW_PREFIX := $(shell brew --prefix)
toolSearchPathsVersion += $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)-$(2) $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1) toolSearchPathsVersion += $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)-$(2) $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)
@@ -34,6 +27,7 @@ LLVM_NM ?= $(call findLLVMTool,llvm-nm)
# Go binary and GOROOT to select # Go binary and GOROOT to select
GO ?= go GO ?= go
export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test. # Flags to pass to go test.
GOTESTFLAGS ?= GOTESTFLAGS ?=
@@ -44,10 +38,14 @@ TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
# Check for ccache if the user hasn't set it to on or off. # Check for ccache if the user hasn't set it to on or off.
ifeq (, $(CCACHE)) ifeq (, $(CCACHE))
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(if $(shell command -v ccache 2> /dev/null),ON,OFF)' # Use CCACHE for LLVM if possible
else ifneq (, $(shell command -v ccache 2> /dev/null))
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)' 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))
@@ -79,26 +77,6 @@ ifeq (1, $(STATIC))
BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static" BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static"
endif endif
# Optimize the binary size for Linux.
# These flags may work on other platforms, but have only been tested on Linux.
ifeq ($(uname),Linux)
HAS_MOLD := $(shell command -v ld.mold 2> /dev/null)
HAS_LLD := $(shell command -v ld.lld 2> /dev/null)
LLVM_CFLAGS := -ffunction-sections -fdata-sections -fvisibility=hidden
LLVM_LDFLAGS := -Wl,--gc-sections
ifneq ($(HAS_MOLD),)
# Mold might be slightly faster.
LLVM_LDFLAGS += -fuse-ld=mold -Wl,--icf=all
else ifneq ($(HAS_LLD),)
# LLD is more commonly available.
LLVM_LDFLAGS += -fuse-ld=lld -Wl,--icf=all
endif
LLVM_OPTION += \
-DCMAKE_C_FLAGS="$(LLVM_CFLAGS)" \
-DCMAKE_CXX_FLAGS="$(LLVM_CFLAGS)"
CGO_LDFLAGS += $(LLVM_LDFLAGS)
endif
# Cross compiling support. # Cross compiling support.
ifneq ($(CROSS),) ifneq ($(CROSS),)
CC = $(CROSS)-gcc CC = $(CROSS)-gcc
@@ -149,14 +127,14 @@ ifeq ($(OS),Windows_NT)
USE_SYSTEM_BINARYEN ?= 1 USE_SYSTEM_BINARYEN ?= 1
else ifeq ($(uname),Darwin) else ifeq ($(shell uname -s),Darwin)
MD5SUM ?= md5 MD5SUM ?= md5
CGO_LDFLAGS += -lxar CGO_LDFLAGS += -lxar
USE_SYSTEM_BINARYEN ?= 1 USE_SYSTEM_BINARYEN ?= 1
else ifeq ($(uname),FreeBSD) else ifeq ($(shell uname -s),FreeBSD)
MD5SUM ?= md5 MD5SUM ?= md5
START_GROUP = -Wl,--start-group START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group END_GROUP = -Wl,--end-group
@@ -169,7 +147,7 @@ endif
MD5SUM ?= md5sum MD5SUM ?= md5sum
# Libraries that should be linked in for the statically linked Clang. # Libraries that should be linked in for the statically linked Clang.
CLANG_LIB_NAMES = clangAnalysis clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangInstallAPI clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions CLANG_LIB_NAMES = clangAnalysis clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++ CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD. # Libraries that should be linked in for the statically linked LLD.
@@ -197,20 +175,17 @@ ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
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_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif endif
clean: ## Remove build directory clean:
@rm -rf build @rm -rf build
FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform FMT_PATHS = ./*.go builder cgo/*.go compiler interp loader src transform
fmt: ## Reformat source fmt:
@gofmt -l -w $(FMT_PATHS) @gofmt -l -w $(FMT_PATHS)
fmt-check: ## Warn if any source needs reformatting fmt-check:
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 @unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp ## Generate microcontroller-specific sources gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp
ifneq ($(RENESAS), 0)
gen-device: gen-device-renesas
endif
ifneq ($(STM32), 0) ifneq ($(STM32), 0)
gen-device: gen-device-stm32 gen-device: gen-device-stm32
endif endif
@@ -259,19 +234,21 @@ gen-device-rp: build/gen-device-svd
GO111MODULE=off $(GO) fmt ./src/device/rp GO111MODULE=off $(GO) fmt ./src/device/rp
gen-device-renesas: build/gen-device-svd gen-device-renesas: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/cmsis-svd/cmsis-svd-data/tree/master/data/Renesas lib/cmsis-svd/data/Renesas/ src/device/renesas/ ./build/gen-device-svd -source=https://github.com/tinygo-org/renesas-svd lib/renesas-svd/ src/device/renesas/
GO111MODULE=off $(GO) fmt ./src/device/renesas GO111MODULE=off $(GO) fmt ./src/device/renesas
# Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
git clone -b tinygo_20.x --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR) git clone -b tinygo_xtensa_release_18.1.2 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm ## Get LLVM sources llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: $(LLVM_BUILDDIR)/build.ninja:
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;AVR;Mips;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION) mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja ## Build LLVM # Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS) cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS)
ifneq ($(USE_SYSTEM_BINARYEN),1) ifneq ($(USE_SYSTEM_BINARYEN),1)
@@ -280,39 +257,34 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
binaryen: build/wasm-opt$(EXE) binaryen: build/wasm-opt$(EXE)
build/wasm-opt$(EXE): build/wasm-opt$(EXE):
mkdir -p build mkdir -p build
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF -DENABLE_WERROR=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE) cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE) cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
endif endif
# Generate WASI syscall bindings # Build wasi-libc sysroot
WASM_TOOLS_MODULE=go.bytecodealliance.org .PHONY: wasi-libc
.PHONY: wasi-syscall wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
wasi-syscall: wasi-cm lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
rm -rf ./src/internal/wasi/* @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
go run $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC="$(CLANG)" AR=$(LLVM_AR) NM=$(LLVM_NM)
# Copy package cm into src/internal/cm
.PHONY: wasi-cm
wasi-cm:
rm -rf ./src/internal/cm/*
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
# Check for Node.js used during WASM tests. # Check for Node.js used during WASM tests.
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
MIN_NODEJS_VERSION=18 MIN_NODEJS_VERSION=18
.PHONY: check-nodejs-version .PHONY: check-nodejs-version
check-nodejs-version: check-nodejs-version:
@# Check whether NodeJS is available. ifeq (, $(shell which node))
@if ! command -v node 2>&1 >/dev/null; then echo "Install NodeJS version ${MIN_NODEJS_VERSION}+ to run tests."; exit 1; fi @echo "Install NodeJS version 18+ to run tests."; exit 1;
endif
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 18+ to run tests."; exit 1; fi
@# Check whether the version is high enough. # Build the Go compiler.
@if [ "`node -v | sed 's/v\([0-9]\+\).*/\\1/g'`" -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version $(MIN_NODEJS_VERSION)+ to run tests."; exit 1; fi tinygo:
tinygo: ## Build the TinyGo compiler
@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" . CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: check-nodejs-version test: wasi-libc check-nodejs-version
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS) CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi # Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
TEST_PACKAGES_SLOW = \ TEST_PACKAGES_SLOW = \
@@ -322,35 +294,26 @@ TEST_PACKAGES_SLOW = \
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows # Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
TEST_PACKAGES_FAST = \ TEST_PACKAGES_FAST = \
cmp \
compress/lzw \ compress/lzw \
compress/zlib \ compress/zlib \
container/heap \ container/heap \
container/list \ container/list \
container/ring \ container/ring \
crypto/ecdsa \ crypto/des \
crypto/elliptic \
crypto/md5 \ crypto/md5 \
crypto/rc4 \
crypto/sha1 \ crypto/sha1 \
crypto/sha256 \ crypto/sha256 \
crypto/sha512 \ crypto/sha512 \
database/sql/driver \
debug/macho \ debug/macho \
embed/internal/embedtest \ embed/internal/embedtest \
encoding \ encoding \
encoding/ascii85 \ encoding/ascii85 \
errors \
encoding/asn1 \
encoding/base32 \ encoding/base32 \
encoding/base64 \ encoding/base64 \
encoding/csv \ encoding/csv \
encoding/hex \ encoding/hex \
expvar \
go/ast \
go/format \
go/scanner \ go/scanner \
go/token \
go/version \
hash \ hash \
hash/adler32 \ hash/adler32 \
hash/crc64 \ hash/crc64 \
@@ -362,7 +325,6 @@ TEST_PACKAGES_FAST = \
math/cmplx \ math/cmplx \
net/http/internal/ascii \ net/http/internal/ascii \
net/mail \ net/mail \
net/url \
os \ os \
path \ path \
reflect \ reflect \
@@ -373,100 +335,57 @@ TEST_PACKAGES_FAST = \
unicode \ unicode \
unicode/utf16 \ unicode/utf16 \
unicode/utf8 \ unicode/utf8 \
unique \
$(nil) $(nil)
# Assume this will go away before Go2, so only check minor version.
ifeq ($(filter $(shell $(GO) env GOVERSION | cut -f 2 -d.), 16 17 18), )
TEST_PACKAGES_FAST += crypto/internal/nistec/fiat
else
TEST_PACKAGES_FAST += crypto/elliptic/internal/fiat
endif
# archive/zip requires os.ReadAt, which is not yet supported on windows # archive/zip requires os.ReadAt, which is not yet supported on windows
# bytes requires mmap # bytes requires mmap
# compress/flate appears to hang on wasi # compress/flate appears to hang on wasi
# crypto/aes fails on wasi, needs panic()/recover()
# crypto/des fails on wasi, needs panic()/recover()
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic # crypto/hmac fails on wasi, it exits with a "slice out of range" panic
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows # debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# encoding/xml takes a minute on linux and gives a stack overflow on wasi # image requires recover(), which is not yet supported on wasi
# image requires recover(), which is not yet supported on wasi
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi # io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
# mime: fail on wasi; neds panic()/recover()
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
# mime/quotedprintable requires syscall.Faccessat # mime/quotedprintable requires syscall.Faccessat
# net/mail: needs wasip1 syscall.FDFLAG_NONBLOCK
# net/ntextproto: needs wasip1 syscall.FDFLAG_NONBLOCK
# regexp/syntax: fails on wasip1; needs panic()/recover()
# strconv requires recover() which is not yet supported on wasi # strconv requires recover() which is not yet supported on wasi
# text/tabwriter requires recover(), which is not yet supported on wasi # text/tabwriter requries recover(), which is not yet supported on wasi
# text/template/parse requires recover(), which is not yet supported on wasi # text/template/parse requires recover(), which is not yet supported on wasi
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi # testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
# Additional standard library packages that pass tests on individual platforms # Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \ TEST_PACKAGES_LINUX := \
archive/zip \ archive/zip \
bytes \
compress/flate \ compress/flate \
context \
crypto/aes \
crypto/des \
crypto/hmac \ crypto/hmac \
debug/dwarf \ debug/dwarf \
debug/plan9obj \ debug/plan9obj \
encoding/xml \
image \ image \
io/ioutil \ io/ioutil \
mime \
mime/multipart \
mime/quotedprintable \ mime/quotedprintable \
net \ net \
net/mail \
net/textproto \
os/user \
regexp/syntax \
strconv \ strconv \
testing/fstest \
text/tabwriter \ text/tabwriter \
text/template/parse text/template/parse
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX) TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
# os/user requires t.Skip() support
TEST_PACKAGES_WINDOWS := \ TEST_PACKAGES_WINDOWS := \
compress/flate \ compress/flate \
crypto/des \
crypto/hmac \ crypto/hmac \
strconv \ strconv \
text/template/parse \ text/template/parse \
$(nil) $(nil)
# These packages cannot be tested on wasm, mostly because these tests assume a
# working filesystem. This could perhaps be fixed, by supporting filesystem
# access when running inside Node.js.
TEST_PACKAGES_WASM = $(filter-out $(TEST_PACKAGES_NONWASM), $(TEST_PACKAGES_FAST))
TEST_PACKAGES_NONWASM = \
compress/lzw \
compress/zlib \
crypto/ecdsa \
debug/macho \
embed/internal/embedtest \
expvar \
go/format \
os \
testing \
$(nil)
# These packages cannot be tested on baremetal.
#
# Some reasons why the tests don't pass on baremetal:
#
# * No filesystem is available, so packages like compress/zlib can't be tested
# (just like wasm).
# * picolibc math functions apparently are less precise, the math package
# fails on baremetal.
TEST_PACKAGES_BAREMETAL = $(filter-out $(TEST_PACKAGES_NONBAREMETAL), $(TEST_PACKAGES_FAST))
TEST_PACKAGES_NONBAREMETAL = \
$(TEST_PACKAGES_NONWASM) \
math \
$(nil)
# Report platforms on which each standard library package is known to pass tests # Report platforms on which each standard library package is known to pass tests
jointmp := $(shell echo /tmp/join.$$$$)
report-stdlib-tests-pass: report-stdlib-tests-pass:
$(eval jointmp := $(shell echo /tmp/join.$$$$))
@for t in $(TEST_PACKAGES_DARWIN); do echo "$$t darwin"; done | sort > $(jointmp).darwin @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_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 @for t in $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW); do echo "$$t darwin linux wasi windows"; done | sort > $(jointmp).portable
@@ -475,11 +394,11 @@ report-stdlib-tests-pass:
@rm $(jointmp).* @rm $(jointmp).*
# Standard library packages that pass tests quickly on the current platform # Standard library packages that pass tests quickly on the current platform
ifeq ($(uname),Darwin) ifeq ($(shell uname),Darwin)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN) TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
TEST_IOFS := true TEST_IOFS := true
endif endif
ifeq ($(uname),Linux) ifeq ($(shell uname),Linux)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX) TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
TEST_IOFS := true TEST_IOFS := true
endif endif
@@ -488,16 +407,11 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
TEST_IOFS := false TEST_IOFS := false
endif endif
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|TestAsValidation'
TEST_ADDITIONAL_FLAGS ?=
# 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:
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented. $(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also @# 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. @# requires a large stack-size. Hence, io/fs is only run conditionally.
@# For more details, see the comments on issue #3143. @# For more details, see the comments on issue #3143.
@@ -505,74 +419,41 @@ ifeq ($(TEST_IOFS),true)
$(TINYGO) test -stack-size=6MB io/fs $(TINYGO) test -stack-size=6MB io/fs
endif endif
tinygo-test-fast: tinygo-test-fast:
$(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TINYGO) test $(TEST_PACKAGES_HOST)
tinygo-bench: tinygo-bench:
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW) $(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
tinygo-bench-fast: tinygo-bench-fast:
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TINYGO) test -bench . $(TEST_PACKAGES_HOST)
# Same thing, except for wasi rather than the current platform. # Same thing, except for wasi rather than the current platform.
tinygo-test-wasm:
$(TINYGO) test -target wasm $(TEST_SKIP_FLAG) $(TEST_PACKAGES_WASM)
tinygo-test-wasi: tinygo-test-wasi:
$(TINYGO) test -target wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi $(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasip1: tinygo-test-wasip1:
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasi-fast:
$(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip1-fast: tinygo-test-wasip1-fast:
$(TINYGO) test -target=wasip1 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) ./tests/runtime_wasi GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-bench-wasi:
tinygo-test-wasip2-slow:
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_SLOW)
tinygo-test-wasip2-fast:
$(TINYGO) test -target=wasip2 $(TEST_SKIP_FLAG) $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip2-sum-slow:
TINYGO=$(TINYGO) \
TARGET=wasip2 \
TESTOPTS="-x -work" \
PACKAGES="$(TEST_PACKAGES_SLOW)" \
gotestsum --raw-command -- ./tools/tgtestjson.sh
tinygo-test-wasip2-sum-fast:
TINYGO=$(TINYGO) \
TARGET=wasip2 \
TESTOPTS="-x -work" \
PACKAGES="$(TEST_PACKAGES_FAST)" \
gotestsum --raw-command -- ./tools/tgtestjson.sh
tinygo-bench-wasip1:
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) $(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasip1-fast: tinygo-bench-wasi-fast:
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST) $(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST)
tinygo-bench-wasip2:
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasip2-fast:
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST)
# Run tests on riscv-qemu since that one provides a large amount of memory.
tinygo-test-baremetal:
$(TINYGO) test -target riscv-qemu $(TEST_SKIP_FLAG) $(TEST_PACKAGES_BAREMETAL)
# Test external packages in a large corpus. # Test external packages in a large corpus.
test-corpus: test-corpus:
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 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
test-corpus-fast: test-corpus-fast:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml
test-corpus-wasi: 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=wasip1 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=wasip1
test-corpus-wasip2:
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=wasip2
.PHONY: testchdir tinygo-baremetal:
testchdir: # Regression tests that run on a baremetal target and don't fit in either main_test.go or smoketest.
# test 'build' command with{,out} -C argument # regression test for #2666: e.g. encoding/hex must pass on baremetal
$(TINYGO) build -C tests/testing/chdir chdir.go && rm tests/testing/chdir/chdir $(TINYGO) test -target cortex-m-qemu encoding/hex
$(TINYGO) build ./tests/testing/chdir/chdir.go && rm chdir
# test 'run' command with{,out} -C argument
EXPECT_DIR=$(PWD)/tests/testing/chdir $(TINYGO) run -C tests/testing/chdir chdir.go
EXPECT_DIR=$(PWD) $(TINYGO) run ./tests/testing/chdir/chdir.go
.PHONY: smoketest .PHONY: smoketest
smoketest: testchdir smoketest:
$(TINYGO) version $(TINYGO) version
$(TINYGO) targets > /dev/null $(TINYGO) targets > /dev/null
# regression test for #2892 # regression test for #2892
@@ -582,8 +463,6 @@ smoketest: testchdir
# 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)
$(TINYGO) build -size short -o test.hex -target=pga2350 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc $(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
@@ -612,7 +491,7 @@ smoketest: testchdir
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt $(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/machinetest $(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick $(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -630,29 +509,23 @@ smoketest: testchdir
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org # test simulated boards on play.tinygo.org
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino_uno examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
GOOS=js GOARCH=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
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink $(TINYGO) build -size short -o test.wasm -tags=microbit examples/microbit-blink
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
GOOS=js GOARCH=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
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/machinetest $(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/serial
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1
@$(MD5SUM) test.wasm
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=pico examples/blinky1
@$(MD5SUM) test.wasm
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=xiao_esp32s3 examples/blinky1
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
endif endif
# test all targets/boards # test all targets/boards
@@ -666,12 +539,8 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink $(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s140v7 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1 $(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=btt-skr-pico examples/uart
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1 $(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
@@ -742,8 +611,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1 $(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=rak4631 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/dac $(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/dac
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pyportal examples/dac $(TINYGO) build -size short -o test.hex -target=pyportal examples/dac
@@ -754,7 +621,7 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=qtpy examples/machinetest $(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 $(TINYGO) build -size short -o test.hex -target=teensy41 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -808,26 +675,10 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1 $(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=gopher-arcade examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo $(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo $(TINYGO) build -size short -o test.hex -target=thumby examples/echo
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico2 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=tiny2350 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico-plus2 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=metro-rp2350 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=vicharak_shrike-lite examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=xiao-rp2350 examples/blinky1
@$(MD5SUM) test.hex
# test pwm # test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -842,11 +693,7 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi $(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico examples/usb-storage $(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico2 examples/usb-storage
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
@$(MD5SUM) test.hex @$(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
@@ -885,26 +732,18 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinkm
@$(MD5SUM) test.hex
endif endif
$(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm $(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/machinetest $(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-uno examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino-leonardo examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-uno examples/pwm $(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-uno -scheduler=tasks examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -916,99 +755,34 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1 $(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark examples/mcp3008
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1 $(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
ifneq ($(XTENSA), 0) ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=esp32-generic examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-generic examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32s3-generic examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1 $(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1 $(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/machinetest $(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/serial
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target m5stack examples/machinetest $(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 m5stick-c examples/machinetest $(TINYGO) build -size short -o test.bin -target m5stick-c examples/serial
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target m5paper examples/machinetest $(TINYGO) build -size short -o test.bin -target m5paper examples/serial
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest $(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
@$(MD5SUM) test.bin
# xiao-esp32s3
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinkm
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/mcp3008
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/pwm
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/adc
@$(MD5SUM) test.bin
# esp32s3-supermini
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/blinkm
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/mcp3008
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32s3-supermini examples/adc
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
endif endif
# esp32c3-supermini $(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/serial
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm $(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=esp32c3-supermini examples/mcp3008 $(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/pwm
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/adc
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32-c3-devkit-rust-1 examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest
@$(MD5SUM) test.bin @$(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=maixbit examples/blinky1 $(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=tkey examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=elecrow-rp2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=elecrow-rp2350 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=hw-651 examples/machinetest
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=hw-651-s110v8 examples/machinetest
@$(MD5SUM) test.hex
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export $(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main $(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
@@ -1023,12 +797,11 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo $(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -o test.nro -target=nintendoswitch examples/echo2 $(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
@$(MD5SUM) test.nro @$(MD5SUM) test.nro
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go $(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=linux GOARCH=mips $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
@@ -1041,16 +814,13 @@ endif
wasmtest: wasmtest:
cd ./tests/wasm && $(GO) test . $(GO) test ./tests/wasm
build/release: tinygo gen-device $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen) build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
@mkdir -p build/release/tinygo/bin @mkdir -p build/release/tinygo/bin
@mkdir -p build/release/tinygo/lib/bdwgc
@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/macos-minimal-sdk
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
@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
@@ -1059,17 +829,17 @@ build/release: tinygo gen-device $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binary
@mkdir -p build/release/tinygo/lib/nrfx @mkdir -p build/release/tinygo/lib/nrfx
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc @mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm @mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
@mkdir -p build/release/tinygo/lib/wasi-libc/dlmalloc @mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch @mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@mkdir -p build/release/tinygo/lib/wasi-cli/ @mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus
@mkdir -p build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4
@echo copying source files @echo copying source files
@cp -p build/tinygo$(EXE) build/release/tinygo/bin @cp -p build/tinygo$(EXE) build/release/tinygo/bin
ifneq ($(USE_SYSTEM_BINARYEN),1) ifneq ($(USE_SYSTEM_BINARYEN),1)
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin @cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
endif endif
@cp -rp lib/bdwgc/* build/release/tinygo/lib/bdwgc
@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
@@ -1078,50 +848,31 @@ endif
@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
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt @cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl @cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
@cp -rp lib/musl/include build/release/tinygo/lib/musl @cp -rp lib/musl/include build/release/tinygo/lib/musl
@cp -rp lib/musl/src/conf build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/ctype build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/fcntl build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/locale build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/linux 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/math build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/misc build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/sched 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/stdlib 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
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src @cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/process build/release/tinygo/lib/musl/src
@cp -rp lib/mingw-w64/mingw-w64-crt/crt/pseudo-reloc.c build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt @cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/gdtoa build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common @cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/advapi32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common @cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/msvcrt.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/math/x86 build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
@cp -rp lib/mingw-w64/mingw-w64-crt/misc build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers @cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults @cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@cp -rp lib/mingw-w64/mingw-w64-headers/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx @cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc @cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc @cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
@@ -1131,42 +882,25 @@ endif
@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/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/dlmalloc/src build/release/tinygo/lib/wasi-libc/dlmalloc @cp -rp lib/wasi-libc/libc-bottom-half/headers/public build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
@cp -rp lib/wasi-libc/libc-bottom-half/cloudlibc build/release/tinygo/lib/wasi-libc/libc-bottom-half
@cp -rp lib/wasi-libc/libc-bottom-half/headers build/release/tinygo/lib/wasi-libc/libc-bottom-half
@cp -rp lib/wasi-libc/libc-bottom-half/sources build/release/tinygo/lib/wasi-libc/libc-bottom-half
@cp -rp lib/wasi-libc/libc-top-half/headers build/release/tinygo/lib/wasi-libc/libc-top-half
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/generic build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch @cp -rp lib/wasi-libc/libc-top-half/musl/arch/generic build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/wasm32 build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch @cp -rp lib/wasi-libc/libc-top-half/musl/arch/wasm32 build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
@cp -rp lib/wasi-libc/libc-top-half/musl/src/conf build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/dirent build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/env build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/errno build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/exit build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/fcntl build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/fenv build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @cp -rp lib/wasi-libc/libc-top-half/musl/src/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/internal build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @cp -rp lib/wasi-libc/libc-top-half/musl/src/internal build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/legacy build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/locale build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/math build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @cp -rp lib/wasi-libc/libc-top-half/musl/src/math build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/misc build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/multibyte build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/network build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stat build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stdio build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stdlib build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/src/thread build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
@cp -rp lib/wasi-libc/libc-top-half/musl/src/time build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src @cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp lib/wasi-libc/libc-top-half/musl/src/unistd build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/sources build/release/tinygo/lib/wasi-libc/libc-top-half
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins @cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins @cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
@cp -rp src build/release/tinygo/src @cp -rp src build/release/tinygo/src
@cp -rp targets build/release/tinygo/targets @cp -rp targets build/release/tinygo/targets
./build/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/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/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/release/tinygo/bin/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/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
release: release:
tar -czf build/release.tar.gz -C build/release tinygo tar -czf build/release.tar.gz -C build/release tinygo
@@ -1184,37 +918,14 @@ release: build/release
deb: build/release deb: build/release
endif endif
.PHONY: tools lint:
tools: go run github.com/mgechev/revive -version
go generate -tags tools ./
LINTDIRS=src/os/ src/reflect/
.PHONY: lint
lint: tools ## Lint source tree
revive -version
# TODO: lint more directories! # TODO: lint more directories!
# revive.toml isn't flexible enough to filter out just one kind of error from a checker, so do it with grep here. # revive.toml isn't flexible enough to filter out just one kind of error from a checker, so do it with grep here.
# Can't use grep with friendly formatter. Plain output isn't too bad, though. # Can't use grep with friendly formatter. Plain output isn't too bad, though.
# Use 'grep .' to get rid of stray blank line # Use 'grep .' to get rid of stray blank line
revive -config revive.toml compiler/... $$( find $(LINTDIRS) -type f -name '*.go' ) \ go run github.com/mgechev/revive -config revive.toml compiler/... src/{os,reflect}/*.go | grep -v "should have comment or be unexported" | grep '.' | awk '{print}; END {exit NR>0}'
| grep -v "should have comment or be unexported" \
| grep '.' \
| awk '{print}; END {exit NR>0}'
SPELLDIRSCMD=find . -depth 1 -type d | egrep -wv '.git|lib|llvm|src'; find src -depth 1 | egrep -wv 'device|internal|net|vendor'; find src/internal -depth 1 -type d | egrep -wv src/internal/wasi spell:
.PHONY: spell # Check for typos in comments. Skip git submodules etc.
spell: tools ## Spellcheck source tree go run github.com/client9/misspell/cmd/misspell -i 'ackward,devided,extint,inbetween,programmmer,rela' $$( find . -depth 1 -type d | egrep -w -v 'lib|llvm|src/net' )
misspell -error --dict misspell.csv -i 'ackward,devided,extint,rela' $$( $(SPELLDIRSCMD) ) *.go *.md
.PHONY: spellfix
spellfix: tools ## Same as spell, but fixes what it finds
misspell -w --dict misspell.csv -i 'ackward,devided,extint,rela' $$( $(SPELLDIRSCMD) ) *.go *.md
# https://www.client9.com/self-documenting-makefiles/
.PHONY: help
help:
@awk -F ':|##' '/^[^\t].+?:.*?##/ {\
gsub(/\$$\(LLVM_BUILDDIR\)/, "$(LLVM_BUILDDIR)"); \
printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \
}' $(MAKEFILE_LIST)
#.DEFAULT_GOAL=help
-32
View File
@@ -1,32 +0,0 @@
TinyGo Team Members
===================
The team of humans who maintain TinyGo.
* **Purpose**: To maintain the community, code, documentation, and tools for the TinyGo compiler.
* **Board**: The group of people who share responsibility for key decisions for the TinyGo organization.
* **Majority Voting**: The board makes decisions by majority vote.
* **Membership**: The board elects its own members.
* **Do-ocracy**: Those who step forward to do a given task propose how it should be done. Then other interested people can make comments.
* **Proof of Work**: Power in decision-making is slightly weighted based on a participant's labor for the community.
* **Initiation**: We need to establish a procedure for how people join the team of maintainers.
* **Transparency**: Important information should be made publicly available, ideally in a way that allows for public comment.
* **Code of Conduct**: Participants agree to abide by the current project Code of Conduct.
## Members
* Ayke van Laethem (@aykevl)
* Daniel Esteban (@conejoninja)
* Ron Evans (@deadprogram)
* Damian Gryski (@dgryski)
* Masaaki Takasago (@sago35)
* Patricio Whittingslow (@soypat)
* Yurii Soldak (@ysoldak)
## Experimental
* **Monthly Meeting**: A monthly meeting for the team and any other interested participants.
Duration: 1 hour
Facilitation: @deadprogram
Schedule: See https://github.com/tinygo-org/tinygo/wiki/Meetings for more information
+2 -3
View File
@@ -1,8 +1,7 @@
Copyright (c) 2018-2026 The TinyGo Authors. All rights reserved. Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved.
TinyGo includes portions of the Go standard library. TinyGo includes portions of the Go standard library.
Copyright 2009 The Go Authors. All rights reserved. Copyright (c) 2009-2023 The Go Authors. All rights reserved.
See https://github.com/golang/go/blob/master/LICENSE for license information.
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information. LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
+11 -16
View File
@@ -6,9 +6,6 @@ TinyGo is a Go compiler intended for use in small places such as microcontroller
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language. It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
> [!IMPORTANT]
> You can help TinyGo with a financial contribution using OpenCollective. Please see https://opencollective.com/tinygo for more information. Thank you!
## Embedded ## Embedded
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED: Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
@@ -34,10 +31,10 @@ func main() {
} }
``` ```
The above program can be compiled and run without modification on an [Arduino Uno](https://tinygo.org/docs/reference/microcontrollers/boards/arduino-uno), an [Adafruit Circuit Playground Express](https://tinygo.org/docs/reference/microcontrollers/featured/circuitplay-express), a [Seeed Studio XIAO-ESP32S3](https://tinygo.org/docs/reference/microcontrollers/featured/xiao-esp32s3) or any of the many supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno: The above program can be compiled and run without modification on an Arduino Uno, an Adafruit ItsyBitsy M0, or any of the supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
```shell ```shell
tinygo flash -target arduino-uno examples/blinky1 tinygo flash -target arduino examples/blinky1
``` ```
## WebAssembly ## WebAssembly
@@ -51,22 +48,20 @@ Here is a small TinyGo program for use by a WASI host application:
```go ```go
package main package main
//go:wasmexport add //go:wasm-module yourmodulename
//export add
func add(x, y uint32) uint32 { func add(x, y uint32) uint32 {
return x + y return x + y
} }
// main is required for the `wasip1` target, even if it isn't used.
func main() {}
``` ```
This compiles the above TinyGo program for use on any WASI Preview 1 runtime: This compiles the above TinyGo program for use on any WASI runtime:
```shell ```shell
tinygo build -buildmode=c-shared -o add.wasm -target=wasip1 add.go tinygo build -o main.wasm -target=wasip1 main.go
```
You can also use the same syntax as Go 1.24+:
```shell
GOOS=wasip1 GOARCH=wasm tinygo build -buildmode=c-shared -o add.wasm add.go
``` ```
## Installation ## Installation
@@ -77,7 +72,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
### Embedded ### Embedded
You can compile TinyGo programs for over 150 different microcontroller boards. You can compile TinyGo programs for over 94 different microcontroller boards.
For more information, please see https://tinygo.org/docs/reference/microcontrollers/ For more information, please see https://tinygo.org/docs/reference/microcontrollers/
@@ -142,7 +137,7 @@ Non-goals:
## Why this project exists ## Why this project exists
> We never expected Go to be an embedded language, and so its got serious problems... > We never expected Go to be an embedded language and so its got serious problems...
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799) -- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
+3 -18
View File
@@ -3,7 +3,6 @@ package builder
import ( import (
"bytes" "bytes"
"debug/elf" "debug/elf"
"debug/macho"
"debug/pe" "debug/pe"
"encoding/binary" "encoding/binary"
"errors" "errors"
@@ -17,7 +16,7 @@ import (
"github.com/blakesmith/ar" "github.com/blakesmith/ar"
) )
// makeArchive creates an archive 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...>
@@ -63,20 +62,6 @@ func makeArchive(arfile *os.File, objs []string) error {
fileIndex int fileIndex int
}{symbol.Name, i}) }{symbol.Name, i})
} }
} else if dbg, err := macho.NewFile(objfile); err == nil {
for _, symbol := range dbg.Symtab.Syms {
// See mach-o/nlist.h
if symbol.Type&0x0e != 0xe { // (symbol.Type & N_TYPE) != N_SECT
continue // undefined symbol
}
if symbol.Type&0x01 == 0 { // (symbol.Type & N_EXT) == 0
continue // internal symbol (static, etc)
}
symbolTable = append(symbolTable, struct {
name string
fileIndex int
}{symbol.Name, i})
}
} else if dbg, err := pe.NewFile(objfile); err == nil { } else if dbg, err := pe.NewFile(objfile); err == nil {
for _, symbol := range dbg.Symbols { for _, symbol := range dbg.Symbols {
if symbol.StorageClass != 2 { if symbol.StorageClass != 2 {
@@ -165,7 +150,7 @@ func makeArchive(arfile *os.File, objs []string) error {
} }
// Keep track of the start of the symbol table. // Keep track of the start of the symbol table.
symbolTableStart, err := arfile.Seek(0, io.SeekCurrent) symbolTableStart, err := arfile.Seek(0, os.SEEK_CUR)
if err != nil { if err != nil {
return err return err
} }
@@ -187,7 +172,7 @@ func makeArchive(arfile *os.File, objs []string) error {
// Store the start index, for when we'll update the symbol table with // Store the start index, for when we'll update the symbol table with
// the correct file start indices. // the correct file start indices.
offset, err := arfile.Seek(0, io.SeekCurrent) offset, err := arfile.Seek(0, os.SEEK_CUR)
if err != nil { if err != nil {
return err return err
} }
-91
View File
@@ -1,91 +0,0 @@
package builder
// The well-known conservative Boehm-Demers-Weiser GC.
// This file provides a way to compile this GC for use with TinyGo.
import (
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/goenv"
)
var BoehmGC = Library{
name: "bdwgc",
cflags: func(target, headerPath string) []string {
libdir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
flags := []string{
// use a modern environment
"-DUSE_MMAP", // mmap is available
"-DUSE_MUNMAP", // return memory to the OS using munmap
"-DGC_BUILTIN_ATOMIC", // use compiler intrinsics for atomic operations
"-DNO_EXECUTE_PERMISSION", // don't make the heap executable
// specific flags for TinyGo
"-DALL_INTERIOR_POINTERS", // scan interior pointers (needed for Go)
"-DIGNORE_DYNAMIC_LOADING", // we don't support dynamic loading at the moment
"-DNO_GETCONTEXT", // musl doesn't support getcontext()
"-DGC_DISABLE_INCREMENTAL", // don't mess with SIGSEGV and such
// Use a minimal environment.
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
"-DDONT_USE_ATEXIT",
"-DNO_GETENV", // smaller binary, more predictable configuration
"-DNO_CLOCK", // don't use system clock
"-DNO_DEBUGGING", // reduce code size
"-DGC_NO_FINALIZATION", // finalization is not used at the moment
// Special flag to work around the lack of __data_start in ld.lld.
// TODO: try to fix this in LLVM/lld directly so we don't have to
// work around it anymore.
"-DGC_DONT_REGISTER_MAIN_STATIC_DATA",
// Do not scan the stack. We have our own mechanism to do this.
"-DSTACK_NOT_SCANNED",
"-DNO_PROC_STAT", // we scan the stack manually (don't read /proc/self/stat on Linux)
"-DSTACKBOTTOM=0", // dummy value, we scan the stack manually
// Assertions can be enabled while debugging GC issues.
//"-DGC_ASSERTIONS",
// We use our own way of dealing with threads (that is a bit hacky).
// See src/runtime/gc_boehm.go.
//"-DGC_THREADS",
//"-DTHREAD_LOCAL_ALLOC",
"-I" + libdir + "/include",
}
return flags
},
needsLibc: true,
sourceDir: func() string {
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
},
librarySources: func(target string, _ bool) ([]string, error) {
sources := []string{
"allchblk.c",
"alloc.c",
"blacklst.c",
"dbg_mlc.c",
"dyn_load.c",
"headers.c",
"mach_dep.c",
"malloc.c",
"mark.c",
"mark_rts.c",
"misc.c",
"new_hblk.c",
"os_dep.c",
"reclaim.c",
}
if strings.Split(target, "-")[2] == "windows" {
// Due to how the linker on Windows works (that doesn't allow
// undefined functions), we need to include these extra files.
sources = append(sources,
"mallocx.c",
"ptr_chck.c",
)
}
return sources, nil
},
}
+102 -246
View File
@@ -14,12 +14,12 @@ import (
"fmt" "fmt"
"go/types" "go/types"
"hash/crc32" "hash/crc32"
"io/fs"
"math/bits" "math/bits"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -61,10 +61,6 @@ type BuildResult struct {
// correctly printing test results: the import path isn't always the same as // correctly printing test results: the import path isn't always the same as
// the path listed on the command line. // the path listed on the command line.
ImportPath string ImportPath string
// Map from path to package name. It is needed to attribute binary size to
// the right Go package.
PackagePathMap map[string]string
} }
// packageAction is the struct that is serialized to JSON and hashed, to work as // packageAction is the struct that is serialized to JSON and hashed, to work as
@@ -149,45 +145,42 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
var libcDependencies []*compileJob var libcDependencies []*compileJob
switch config.Target.Libc { switch config.Target.Libc {
case "darwin-libSystem": case "darwin-libSystem":
libcJob := makeDarwinLibSystemJob(config, tmpdir) job := makeDarwinLibSystemJob(config, tmpdir)
libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, job)
case "musl": case "musl":
var unlock func() job, unlock, err := Musl.load(config, tmpdir)
libcJob, unlock, err := libMusl.load(config, tmpdir)
if err != nil { if err != nil {
return BuildResult{}, err return BuildResult{}, err
} }
defer unlock() defer unlock()
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(libcJob.result), "crt1.o"))) libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, job)
case "picolibc": case "picolibc":
libcJob, unlock, err := libPicolibc.load(config, tmpdir) libcJob, unlock, err := Picolibc.load(config, tmpdir)
if err != nil { if err != nil {
return BuildResult{}, err return BuildResult{}, err
} }
defer unlock() defer unlock()
libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, libcJob)
case "wasi-libc": case "wasi-libc":
libcJob, unlock, err := libWasiLibc.load(config, tmpdir) path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
if err != nil { if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
return BuildResult{}, err return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
} }
defer unlock() libcDependencies = append(libcDependencies, dummyCompileJob(path))
libcDependencies = append(libcDependencies, libcJob)
case "wasmbuiltins": case "wasmbuiltins":
libcJob, unlock, err := libWasmBuiltins.load(config, tmpdir) libcJob, unlock, err := WasmBuiltins.load(config, tmpdir)
if err != nil { if err != nil {
return BuildResult{}, err return BuildResult{}, err
} }
defer unlock() defer unlock()
libcDependencies = append(libcDependencies, libcJob) libcDependencies = append(libcDependencies, libcJob)
case "mingw-w64": case "mingw-w64":
libcJob, unlock, err := libMinGW.load(config, tmpdir) _, unlock, err := MinGW.load(config, tmpdir)
if err != nil { if err != nil {
return BuildResult{}, err return BuildResult{}, err
} }
defer unlock() unlock()
libcDependencies = append(libcDependencies, libcJob)
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...) libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
case "": case "":
// no library specified, so nothing to do // no library specified, so nothing to do
@@ -203,7 +196,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ABI: config.ABI(), ABI: config.ABI(),
GOOS: config.GOOS(), GOOS: config.GOOS(),
GOARCH: config.GOARCH(), GOARCH: config.GOARCH(),
BuildMode: config.BuildMode(),
CodeModel: config.CodeModel(), CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(), RelocationModel: config.RelocationModel(),
SizeLevel: sizeLevel, SizeLevel: sizeLevel,
@@ -215,7 +207,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
MaxStackAlloc: config.MaxStackAlloc(), MaxStackAlloc: config.MaxStackAlloc(),
NeedsStackObjects: config.NeedsStackObjects(), NeedsStackObjects: config.NeedsStackObjects(),
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
Nobounds: config.Options.Nobounds,
PanicStrategy: config.PanicStrategy(), PanicStrategy: config.PanicStrategy(),
} }
@@ -249,15 +240,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return result, err return result, err
} }
// Store which filesystem paths map to which package name.
result.PackagePathMap = make(map[string]string, len(lprogram.Packages))
for _, pkg := range lprogram.Sorted() {
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
}
// Create the *ssa.Program. This does not yet build the entire SSA of the // Create the *ssa.Program. This does not yet build the entire SSA of the
// program so it's pretty fast and doesn't need to be parallelized. // program so it's pretty fast and doesn't need to be parallelized.
program := lprogram.LoadSSA() program := lprogram.LoadSSA()
program.Build()
// Add jobs to compile each package. // Add jobs to compile each package.
// Packages that have a cache hit will not be compiled again. // Packages that have a cache hit will not be compiled again.
@@ -282,13 +268,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
allFiles[file.Name] = append(allFiles[file.Name], file) allFiles[file.Name] = append(allFiles[file.Name], file)
} }
} }
// Sort embedded files by name to maintain output determinism. for name, files := range allFiles {
embedNames := make([]string, 0, len(allFiles)) name := name
for _, files := range allFiles { files := files
embedNames = append(embedNames, files[0].Name)
}
slices.Sort(embedNames)
for _, name := range embedNames {
job := &compileJob{ job := &compileJob{
description: "make object file for " + name, description: "make object file for " + name,
run: func(job *compileJob) error { run: func(job *compileJob) error {
@@ -303,7 +285,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
sum := sha256.Sum256(data) sum := sha256.Sum256(data)
hexSum := hex.EncodeToString(sum[:16]) hexSum := hex.EncodeToString(sum[:16])
for _, file := range allFiles[name] { for _, file := range files {
file.Size = uint64(len(data)) file.Size = uint64(len(data))
file.Hash = hexSum file.Hash = hexSum
if file.NeedsData { if file.NeedsData {
@@ -372,6 +354,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
// Build the SSA for the given package.
//ssaPkg := program.Package(pkg.Pkg)
//ssaPkg.Build()
// Now create the job to actually build the package. It will exit early // Now create the job to actually build the package. It will exit early
// if the package is already compiled. // if the package is already compiled.
job := &compileJob{ job := &compileJob{
@@ -385,7 +371,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if _, err := os.Stat(job.result); err == nil { if _, err := os.Stat(job.result); err == nil {
// Already cached, don't recreate this package. // Already cached, don't recreate this package.
return nil switch pkg.ImportPath {
case "context": // seems to be needed
case "time": // often crashes in this package, so probably needed
default:
return nil
}
} }
// Compile AST to IR. The compiler.CompilePackage function will // Compile AST to IR. The compiler.CompilePackage function will
@@ -394,7 +385,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
defer mod.Context().Dispose() defer mod.Context().Dispose()
defer mod.Dispose() defer mod.Dispose()
if errs != nil { if errs != nil {
return newMultiError(errs, pkg.ImportPath) return newMultiError(errs)
} }
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after compiling package " + pkg.ImportPath) return errors.New("verification error after compiling package " + pkg.ImportPath)
@@ -456,15 +447,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if global.IsNil() { if global.IsNil() {
return errors.New("global not found: " + globalName) return errors.New("global not found: " + globalName)
} }
globalType := global.GlobalValueType()
if globalType.TypeKind() != llvm.StructTypeKind || globalType.StructName() != "runtime._string" {
// Verify this is indeed a string. This is needed so
// that makeGlobalsModule can just create the right
// globals of string type without checking.
return fmt.Errorf("%s: not a string", globalName)
}
name := global.Name() name := global.Name()
newGlobal := llvm.AddGlobal(mod, globalType, name+".tmp") newGlobal := llvm.AddGlobal(mod, global.GlobalValueType(), name+".tmp")
global.ReplaceAllUsesWith(newGlobal) global.ReplaceAllUsesWith(newGlobal)
global.EraseFromParentAsGlobal() global.EraseFromParentAsGlobal()
newGlobal.SetName(name) newGlobal.SetName(name)
@@ -539,6 +523,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
run: func(*compileJob) error { run: func(*compileJob) error {
// 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.
println("exit")
os.Exit(0)
ctx := llvm.NewContext() ctx := llvm.NewContext()
mod = ctx.NewModule("main") mod = ctx.NewModule("main")
for _, pkgJob := range packageJobs { for _, pkgJob := range packageJobs {
@@ -546,38 +532,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if err != nil { if err != nil {
return fmt.Errorf("failed to load bitcode file: %w", err) return fmt.Errorf("failed to load bitcode file: %w", err)
} }
// Resolve duplicate function definitions before linking.
// This can happen when a newer Go version adds a function
// body in a standard library package that was previously
// just a declaration provided by //go:linkname from the
// runtime. In that case, keep the existing (runtime)
// definition by weakening the new one's linkage so the
// LLVM linker discards it in favor of the existing one.
for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if fn.IsDeclaration() {
continue
}
existing := mod.NamedFunction(fn.Name())
if existing.IsNil() || existing.IsDeclaration() {
continue
}
fn.SetLinkage(llvm.LinkOnceODRLinkage)
}
err = llvm.LinkModules(mod, pkgMod) err = llvm.LinkModules(mod, pkgMod)
if err != nil { if err != nil {
return fmt.Errorf("failed to link module: %w", err) return fmt.Errorf("failed to link module: %w", err)
} }
} }
// Insert values from -ldflags="-X ..." into the IR.
// This is a separate module, so that the "runtime._string" type
// doesn't need to match precisely. LLVM tends to rename that type
// sometimes, leading to errors. But linking in a separate module
// works fine. See:
// https://github.com/tinygo-org/tinygo/issues/4810
globalsMod := makeGlobalsModule(ctx, globalValues, machine)
llvm.LinkModules(mod, globalsMod)
// Create runtime.initAll function that calls the runtime // Create runtime.initAll function that calls the runtime
// initializer of each package. // initializer of each package.
llvmInitFn := mod.NamedFunction("runtime.initAll") llvmInitFn := mod.NamedFunction("runtime.initAll")
@@ -630,7 +590,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run all optimization passes, which are much more effective now // Run all optimization passes, which are much more effective now
// that the optimizer can see the whole program at once. // that the optimizer can see the whole program at once.
err := optimizeProgram(mod, config) err := optimizeProgram(mod, config, globalValues)
if err != nil { if err != nil {
return err return err
} }
@@ -644,11 +604,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}, },
} }
// Create the output directory, if needed
if err := os.MkdirAll(filepath.Dir(outpath), 0777); err != nil {
return result, err
}
// Check whether we only need to create an object file. // Check whether we only need to create an object file.
// If so, we don't need to link anything and will be finished quickly. // If so, we don't need to link anything and will be finished quickly.
outext := filepath.Ext(outpath) outext := filepath.Ext(outpath)
@@ -705,27 +660,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
result.Binary = result.Executable // final file result.Binary = result.Executable // final file
ldflags := append(config.LDFlags(), "-o", result.Executable) ldflags := append(config.LDFlags(), "-o", result.Executable)
if config.Options.BuildMode == "c-shared" {
if !strings.HasPrefix(config.Triple(), "wasm32-") {
return result, fmt.Errorf("buildmode c-shared is only supported on wasm at the moment")
}
ldflags = append(ldflags, "--no-entry")
}
if config.Options.BuildMode == "wasi-legacy" {
if !strings.HasPrefix(config.Triple(), "wasm32-") {
return result, fmt.Errorf("buildmode wasi-legacy is only supported on wasm")
}
if config.Options.Scheduler != "none" {
return result, fmt.Errorf("buildmode wasi-legacy only supports scheduler=none")
}
}
// Add compiler-rt dependency if needed. Usually this is a simple load from // Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache. // a cache.
if config.Target.RTLib == "compiler-rt" { if config.Target.RTLib == "compiler-rt" {
job, unlock, err := libCompilerRT.load(config, tmpdir) job, unlock, err := CompilerRT.load(config, tmpdir)
if err != nil { if err != nil {
return result, err return result, err
} }
@@ -733,16 +671,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
} }
// The Boehm collector is stored in a separate C library.
if config.GC() == "boehm" {
job, unlock, err := BoehmGC.load(config, tmpdir)
if err != nil {
return BuildResult{}, err
}
defer unlock()
linkerDependencies = append(linkerDependencies, job)
}
// Add jobs to compile extra files. These files are in C or assembly and // Add jobs to compile extra files. These files are in C or assembly and
// contain things like the interrupt vector table and low level operations // contain things like the interrupt vector table and low level operations
// such as stack switching. // such as stack switching.
@@ -765,7 +693,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
pkg := pkg pkg := pkg
for _, filename := range pkg.CFiles { for _, filename := range pkg.CFiles {
abspath := filepath.Join(pkg.OriginalDir(), filename) abspath := filepath.Join(pkg.Dir, filename)
job := &compileJob{ job := &compileJob{
description: "compile CGo file " + abspath, description: "compile CGo file " + abspath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
@@ -829,7 +757,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ldflags = append(ldflags, dependency.result) ldflags = append(ldflags, dependency.result)
} }
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
if config.GOOS() == "windows" { if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker. // Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags, ldflags = append(ldflags,
@@ -863,7 +790,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
err = link(config.Target.Linker, ldflags...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {
return err return &commandError{"failed to link", result.Executable, err}
} }
var calculatedStacks []string var calculatedStacks []string
@@ -887,12 +814,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return fmt.Errorf("could not modify stack sizes: %w", err) return fmt.Errorf("could not modify stack sizes: %w", err)
} }
} }
// Apply patches of bootloader in the order they appear.
if len(config.Target.BootPatches) > 0 {
err = applyPatches(result.Executable, config.Target.BootPatches)
}
if config.RP2040BootPatch() { if config.RP2040BootPatch() {
// Patch the second stage bootloader CRC into the .boot2 section // Patch the second stage bootloader CRC into the .boot2 section
err = patchRP2040BootCRC(result.Executable) err = patchRP2040BootCRC(result.Executable)
@@ -912,20 +833,26 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
args = append(args, "--asyncify") args = append(args, "--asyncify")
} }
inputFile := result.Binary exeunopt := result.Executable
result.Binary = result.Executable + ".wasmopt"
if config.Options.Work {
// Keep the work direction around => don't overwrite the .wasm binary with the optimized version
exeunopt += ".pre-wasm-opt"
os.Rename(result.Executable, exeunopt)
}
args = append(args, args = append(args,
opt, opt,
"-g", "-g",
inputFile, exeunopt,
"--output", result.Binary, "--output", result.Executable,
) )
wasmopt := goenv.Get("WASMOPT")
if config.Options.PrintCommands != nil { if config.Options.PrintCommands != nil {
config.Options.PrintCommands(wasmopt, args...) config.Options.PrintCommands(goenv.Get("WASMOPT"), args...)
} }
cmd := exec.Command(wasmopt, args...)
cmd := exec.Command(goenv.Get("WASMOPT"), args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -935,77 +862,20 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
} }
// Run wasm-tools for component-model binaries
witPackage := strings.ReplaceAll(config.Target.WITPackage, "{root}", goenv.Get("TINYGOROOT"))
if config.Options.WITPackage != "" {
witPackage = config.Options.WITPackage
}
witWorld := config.Target.WITWorld
if config.Options.WITWorld != "" {
witWorld = config.Options.WITWorld
}
if witPackage != "" && witWorld != "" {
// wasm-tools component embed -w wasi:cli/command
// $$(tinygo env TINYGOROOT)/lib/wasi-cli/wit/ main.wasm -o embedded.wasm
componentEmbedInputFile := result.Binary
result.Binary = result.Executable + ".wasm-component-embed"
args := []string{
"component",
"embed",
"-w", witWorld,
witPackage,
componentEmbedInputFile,
"-o", result.Binary,
}
wasmtools := goenv.Get("WASMTOOLS")
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(wasmtools, args...)
}
cmd := exec.Command(wasmtools, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("`wasm-tools component embed` failed: %w", err)
}
// wasm-tools component new embedded.wasm -o component.wasm
componentNewInputFile := result.Binary
result.Binary = result.Executable + ".wasm-component-new"
args = []string{
"component",
"new",
componentNewInputFile,
"-o", result.Binary,
}
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(wasmtools, args...)
}
cmd = exec.Command(wasmtools, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("`wasm-tools component new` failed: %w", err)
}
}
// Print code size if requested. // Print code size if requested.
if config.Options.PrintSizes != "" { if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
sizes, err := loadProgramSize(result.Executable, result.PackagePathMap) packagePathMap := make(map[string]string, len(lprogram.Packages))
for _, pkg := range lprogram.Sorted() {
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
}
sizes, err := loadProgramSize(result.Executable, packagePathMap)
if err != nil { if err != nil {
return err return err
} }
switch config.Options.PrintSizes { if config.Options.PrintSizes == "short" {
case "short":
fmt.Printf(" code data bss | flash ram\n") fmt.Printf(" code data bss | flash ram\n")
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM()) fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM())
case "full": } else {
if !config.Debug() { if !config.Debug() {
fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail") fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail")
} }
@@ -1017,13 +887,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
fmt.Printf("------------------------------- | --------------- | -------\n") fmt.Printf("------------------------------- | --------------- | -------\n")
fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS) fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS)
case "html":
const filename = "size-report.html"
err := writeSizeReport(sizes, filename, pkgName)
if err != nil {
return err
}
fmt.Println("Wrote size report to", filename)
} }
} }
@@ -1064,11 +927,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if err != nil { if err != nil {
return result, err return result, err
} }
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp8266": case "esp32", "esp32-img", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM // Special format for the ESP family of chips (parsed by the ROM
// bootloader). // bootloader).
result.Binary = filepath.Join(tmpdir, "main"+outext) result.Binary = filepath.Join(tmpdir, "main"+outext)
err := makeESPFirmwareImage(result.Executable, result.Binary, outputBinaryFormat) err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
if err != nil { if err != nil {
return result, err return result, err
} }
@@ -1195,7 +1058,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
// 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, globalValues map[string]map[string]string) error {
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA()) err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
if err != nil { if err != nil {
return err return err
@@ -1213,11 +1076,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
} }
} }
// Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, globalValues)
if err != nil {
return err
}
// Run most of the whole-program optimizations (including the whole // Run most of the whole-program optimizations (including the whole
// O0/O1/O2/Os/Oz optimization pipeline). // O0/O1/O2/Os/Oz optimization pipeline).
errs := transform.Optimize(mod, config) errs := transform.Optimize(mod, config)
if len(errs) > 0 { if len(errs) > 0 {
return newMultiError(errs, "") return newMultiError(errs)
} }
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification failure after LLVM optimization passes") return errors.New("verification failure after LLVM optimization passes")
@@ -1226,19 +1095,10 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return nil return nil
} }
func makeGlobalsModule(ctx llvm.Context, globals map[string]map[string]string, machine llvm.TargetMachine) llvm.Module { // setGlobalValues sets the global values from the -ldflags="-X ..." compiler
mod := ctx.NewModule("cmdline-globals") // option in the given module. An error may be returned if the global is not of
targetData := machine.CreateTargetData() // the expected type.
defer targetData.Dispose() func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) error {
mod.SetDataLayout(targetData.String())
stringType := ctx.StructCreateNamed("runtime._string")
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
stringType.StructSetBody([]llvm.Type{
llvm.PointerType(ctx.Int8Type(), 0),
uintptrType,
}, false)
var pkgPaths []string var pkgPaths []string
for pkgPath := range globals { for pkgPath := range globals {
pkgPaths = append(pkgPaths, pkgPath) pkgPaths = append(pkgPaths, pkgPath)
@@ -1254,6 +1114,24 @@ func makeGlobalsModule(ctx llvm.Context, globals map[string]map[string]string, m
for _, name := range names { for _, name := range names {
value := pkg[name] value := pkg[name]
globalName := pkgPath + "." + name globalName := pkgPath + "." + name
global := mod.NamedGlobal(globalName)
if global.IsNil() || !global.Initializer().IsNil() {
// The global either does not exist (optimized away?) or has
// some value, in which case it has already been initialized at
// package init time.
continue
}
// A strin is a {ptr, len} pair. We need these types to build the
// initializer.
initializerType := global.GlobalValueType()
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
return fmt.Errorf("%s: not a string", globalName)
}
elementTypes := initializerType.StructElementTypes()
if len(elementTypes) != 2 {
return fmt.Errorf("%s: not a string", globalName)
}
// Create a buffer for the string contents. // Create a buffer for the string contents.
bufInitializer := mod.Context().ConstString(value, false) bufInitializer := mod.Context().ConstString(value, false)
@@ -1264,20 +1142,22 @@ func makeGlobalsModule(ctx llvm.Context, globals map[string]map[string]string, m
buf.SetLinkage(llvm.PrivateLinkage) buf.SetLinkage(llvm.PrivateLinkage)
// Create the string value, which is a {ptr, len} pair. // Create the string value, which is a {ptr, len} pair.
length := llvm.ConstInt(uintptrType, uint64(len(value)), false) zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
initializer := llvm.ConstNamedStruct(stringType, []llvm.Value{ ptr := llvm.ConstGEP(bufInitializer.Type(), buf, []llvm.Value{zero, zero})
buf, if ptr.Type() != elementTypes[0] {
return fmt.Errorf("%s: not a string", globalName)
}
length := llvm.ConstInt(elementTypes[1], uint64(len(value)), false)
initializer := llvm.ConstNamedStruct(initializerType, []llvm.Value{
ptr,
length, length,
}) })
// Create the string global. // Set the initializer. No initializer should be set at this point.
global := llvm.AddGlobal(mod, stringType, globalName)
global.SetInitializer(initializer) global.SetInitializer(initializer)
global.SetAlignment(targetData.PrefTypeAlignment(stringType))
} }
} }
return nil
return mod
} }
// functionStackSizes keeps stack size information about a single function // functionStackSizes keeps stack size information about a single function
@@ -1333,7 +1213,7 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
} }
// Goroutines need to be started and finished and take up some stack space // Goroutines need to be started and finished and take up some stack space
// that way. This can be measured by measuring the stack size of // that way. This can be measured by measuing the stack size of
// tinygo_startTask. // tinygo_startTask.
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 { if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs) return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
@@ -1496,23 +1376,6 @@ func printStacks(calculatedStacks []string, stackSizes map[string]functionStackS
} }
} }
func applyPatches(executable string, bootPatches []string) (err error) {
for _, patch := range bootPatches {
switch patch {
case "rp2040":
err = patchRP2040BootCRC(executable)
// case "rp2350":
// err = patchRP2350BootIMAGE_DEF(executable)
default:
err = errors.New("undefined boot patch name")
}
if err != nil {
return fmt.Errorf("apply boot patch %q: %w", patch, err)
}
}
return nil
}
// RP2040 second stage bootloader CRC32 calculation // RP2040 second stage bootloader CRC32 calculation
// //
// Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf // Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
@@ -1524,7 +1387,7 @@ func patchRP2040BootCRC(executable string) error {
} }
if len(bytes) != 256 { if len(bytes) != 256 {
return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes, got %d", len(bytes)) return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes")
} }
// From the 'official' RP2040 checksum script: // From the 'official' RP2040 checksum script:
@@ -1563,10 +1426,3 @@ func lock(path string) func() {
return func() { flock.Close() } return func() { flock.Close() }
} }
func b2u8(b bool) uint8 {
if b {
return 1
}
return 0
}
+4 -17
View File
@@ -28,15 +28,12 @@ func TestClangAttributes(t *testing.T) {
"cortex-m4", "cortex-m4",
"cortex-m7", "cortex-m7",
"esp32c3", "esp32c3",
"esp32s3",
"fe310", "fe310",
"gameboy-advance", "gameboy-advance",
"k210", "k210",
"nintendoswitch", "nintendoswitch",
"riscv-qemu", "riscv-qemu",
"tkey",
"wasip1", "wasip1",
"wasip2",
"wasm", "wasm",
"wasm-unknown", "wasm-unknown",
} }
@@ -55,30 +52,20 @@ func TestClangAttributes(t *testing.T) {
for _, options := range []*compileopts.Options{ for _, options := range []*compileopts.Options{
{GOOS: "linux", GOARCH: "386"}, {GOOS: "linux", GOARCH: "386"},
{GOOS: "linux", GOARCH: "amd64"}, {GOOS: "linux", GOARCH: "amd64"},
{GOOS: "linux", GOARCH: "arm", GOARM: "5,softfloat"}, {GOOS: "linux", GOARCH: "arm", GOARM: "5"},
{GOOS: "linux", GOARCH: "arm", GOARM: "6,softfloat"}, {GOOS: "linux", GOARCH: "arm", GOARM: "6"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7,softfloat"}, {GOOS: "linux", GOARCH: "arm", GOARM: "7"},
{GOOS: "linux", GOARCH: "arm", GOARM: "5,hardfloat"},
{GOOS: "linux", GOARCH: "arm", GOARM: "6,hardfloat"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7,hardfloat"},
{GOOS: "linux", GOARCH: "arm64"}, {GOOS: "linux", GOARCH: "arm64"},
{GOOS: "linux", GOARCH: "mips", GOMIPS: "hardfloat"},
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "hardfloat"},
{GOOS: "linux", GOARCH: "mips", GOMIPS: "softfloat"},
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
{GOOS: "darwin", GOARCH: "amd64"}, {GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"}, {GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "386"},
{GOOS: "windows", GOARCH: "amd64"}, {GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"}, {GOOS: "windows", GOARCH: "arm64"},
{GOOS: "wasip1", GOARCH: "wasm"},
} { } {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" { if options.GOARCH == "arm" {
name += ",GOARM=" + options.GOARM name += ",GOARM=" + options.GOARM
} }
if options.GOARCH == "mips" || options.GOARCH == "mipsle" {
name += ",GOMIPS=" + options.GOMIPS
}
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
testClangAttributes(t, options) testClangAttributes(t, options)
}) })
+7 -51
View File
@@ -5,7 +5,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
@@ -133,38 +132,6 @@ var genericBuiltins = []string{
"umodti3.c", "umodti3.c",
} }
// These are the GENERIC_TF_SOURCES as of LLVM 18.
// They are not needed on all platforms (32-bit platforms usually don't need
// these) but they seem to compile fine so it's easier to include them.
var genericBuiltins128 = []string{
"addtf3.c",
"comparetf2.c",
"divtc3.c",
"divtf3.c",
"extenddftf2.c",
"extendhftf2.c",
"extendsftf2.c",
"fixtfdi.c",
"fixtfsi.c",
"fixtfti.c",
"fixunstfdi.c",
"fixunstfsi.c",
"fixunstfti.c",
"floatditf.c",
"floatsitf.c",
"floattitf.c",
"floatunditf.c",
"floatunsitf.c",
"floatuntitf.c",
"multc3.c",
"multf3.c",
"powitf2.c",
"subtf3.c",
"trunctfdf2.c",
"trunctfhf2.c",
"trunctfsf2.c",
}
var aeabiBuiltins = []string{ var aeabiBuiltins = []string{
"arm/aeabi_cdcmp.S", "arm/aeabi_cdcmp.S",
"arm/aeabi_cdcmpeq_check_nan.c", "arm/aeabi_cdcmpeq_check_nan.c",
@@ -202,17 +169,12 @@ var avrBuiltins = []string{
"avr/udivmodqi4.S", "avr/udivmodqi4.S",
} }
// Builtins needed specifically for windows/386. // CompilerRT is a library with symbols required by programs compiled with LLVM.
var windowsI386Builtins = []string{ // These symbols are for operations that cannot be emitted with a single
"i386/chkstk.S", // also _alloca
}
// libCompilerRT is a library with symbols required by programs compiled with
// LLVM. These symbols are for operations that cannot be emitted with a single
// instruction or a short sequence of instructions for that target. // instruction or a short sequence of instructions for that target.
// //
// For more information, see: https://compiler-rt.llvm.org/ // For more information, see: https://compiler-rt.llvm.org/
var libCompilerRT = Library{ var CompilerRT = Library{
name: "compiler-rt", name: "compiler-rt",
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"}
@@ -226,19 +188,13 @@ var libCompilerRT = Library{
// Development build. // Development build.
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins") return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
}, },
librarySources: func(target string, _ bool) ([]string, error) { librarySources: func(target string) ([]string, error) {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
switch compileopts.CanonicalArchName(target) { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
case "arm":
builtins = append(builtins, aeabiBuiltins...) builtins = append(builtins, aeabiBuiltins...)
case "avr": }
if strings.HasPrefix(target, "avr") {
builtins = append(builtins, avrBuiltins...) builtins = append(builtins, avrBuiltins...)
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
builtins = append(builtins, genericBuiltins128...)
case "i386":
if strings.Split(target, "-")[2] == "windows" {
builtins = append(builtins, windowsI386Builtins...)
}
} }
return builtins, nil return builtins, nil
}, },
+19 -24
View File
@@ -144,6 +144,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.Default(llvm::DebugCompressionType::None); .Default(llvm::DebugCompressionType::None);
} }
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32)) if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64); Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags); Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
@@ -233,10 +234,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical =
Args.hasArg(OPT_femit_compact_unwind_non_canonical); Args.hasArg(OPT_femit_compact_unwind_non_canonical);
Opts.Crel = Args.hasArg(OPT_crel);
Opts.ImplicitMapsyms = Args.hasArg(OPT_mmapsyms_implicit);
Opts.X86RelaxRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
Opts.X86Sse2Avx = Args.hasArg(OPT_msse2avx);
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file); Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
@@ -290,15 +287,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
assert(MRI && "Unable to create target register info!"); assert(MRI && "Unable to create target register info!");
MCTargetOptions MCOptions; MCTargetOptions MCOptions;
MCOptions.MCRelaxAll = Opts.RelaxAll;
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind; MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical; MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
MCOptions.MCSaveTempLabels = Opts.SaveTemporaryLabels;
MCOptions.Crel = Opts.Crel;
MCOptions.ImplicitMapSyms = Opts.ImplicitMapsyms;
MCOptions.X86RelaxRelocations = Opts.X86RelaxRelocations;
MCOptions.X86Sse2Avx = Opts.X86Sse2Avx;
MCOptions.CompressDebugSections = Opts.CompressDebugSections;
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile; MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
std::unique_ptr<MCAsmInfo> MAI( std::unique_ptr<MCAsmInfo> MAI(
@@ -307,7 +297,9 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections // Ensure MCAsmInfo initialization occurs before any use, otherwise sections
// may be created with a combination of default and explicit settings. // may be created with a combination of default and explicit settings.
MAI->setCompressDebugSections(Opts.CompressDebugSections);
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
if (Opts.OutputPath.empty()) if (Opts.OutputPath.empty())
@@ -345,8 +337,14 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
// MCObjectFileInfo needs a MCContext reference in order to initialize itself. // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
std::unique_ptr<MCObjectFileInfo> MOFI( std::unique_ptr<MCObjectFileInfo> MOFI(
TheTarget->createMCObjectFileInfo(Ctx, PIC)); TheTarget->createMCObjectFileInfo(Ctx, PIC));
if (Opts.DarwinTargetVariantTriple)
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
if (!Opts.DarwinTargetVariantSDKVersion.empty())
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
Ctx.setObjectFileInfo(MOFI.get()); Ctx.setObjectFileInfo(MOFI.get());
if (Opts.SaveTemporaryLabels)
Ctx.setAllowTemporaryLabels(false);
if (Opts.GenDwarfForAssembly) if (Opts.GenDwarfForAssembly)
Ctx.setGenDwarfForAssembly(true); Ctx.setGenDwarfForAssembly(true);
if (!Opts.DwarfDebugFlags.empty()) if (!Opts.DwarfDebugFlags.empty())
@@ -383,9 +381,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCOptions.MCNoWarn = Opts.NoWarn; MCOptions.MCNoWarn = Opts.NoWarn;
MCOptions.MCFatalWarnings = Opts.FatalWarnings; MCOptions.MCFatalWarnings = Opts.FatalWarnings;
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck; MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
MCOptions.ShowMCInst = Opts.ShowInst;
MCOptions.AsmVerbose = true;
MCOptions.MCUseDwarfDirectory = MCTargetOptions::EnableDwarfDirectory;
MCOptions.ABIName = Opts.TargetABI; MCOptions.ABIName = Opts.TargetABI;
// FIXME: There is a bit of code duplication with addPassesToEmitFile. // FIXME: There is a bit of code duplication with addPassesToEmitFile.
@@ -400,8 +395,10 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
auto FOut = std::make_unique<formatted_raw_ostream>(*Out); auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), IP, Str.reset(TheTarget->createAsmStreamer(
std::move(CE), std::move(MAB))); Ctx, std::move(FOut), /*asmverbose*/ true,
/*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
Opts.ShowInst));
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) { } else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
Str.reset(createNullStreamer(Ctx)); Str.reset(createNullStreamer(Ctx));
} else { } else {
@@ -424,15 +421,10 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
Triple T(Opts.Triple); Triple T(Opts.Triple);
Str.reset(TheTarget->createMCObjectStreamer( Str.reset(TheTarget->createMCObjectStreamer(
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI)); T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ true));
Str.get()->initSections(Opts.NoExecStack, *STI); Str.get()->initSections(Opts.NoExecStack, *STI);
if (T.isOSBinFormatMachO() && T.isOSDarwin()) {
Triple *TVT = Opts.DarwinTargetVariantTriple
? &*Opts.DarwinTargetVariantTriple
: nullptr;
Str->emitVersionForTarget(T, VersionTuple(), TVT,
Opts.DarwinTargetVariantSDKVersion);
}
} }
// When -fembed-bitcode is passed to clang_as, a 1-byte marker // When -fembed-bitcode is passed to clang_as, a 1-byte marker
@@ -444,6 +436,9 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
Str.get()->emitZeros(1); Str.get()->emitZeros(1);
} }
// Assembly to object compilation should leverage assembly info.
Str->setUseAssemblerInfoForParsing(true);
bool Failed = false; bool Failed = false;
std::unique_ptr<MCAsmParser> Parser( std::unique_ptr<MCAsmParser> Parser(
+2 -31
View File
@@ -38,13 +38,10 @@ struct AssemblerInvocation {
/// @{ /// @{
std::vector<std::string> IncludePaths; std::vector<std::string> IncludePaths;
LLVM_PREFERRED_TYPE(bool)
unsigned NoInitialTextSection : 1; unsigned NoInitialTextSection : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned SaveTemporaryLabels : 1; unsigned SaveTemporaryLabels : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned GenDwarfForAssembly : 1; unsigned GenDwarfForAssembly : 1;
LLVM_PREFERRED_TYPE(bool) unsigned RelaxELFRelocations : 1;
unsigned Dwarf64 : 1; unsigned Dwarf64 : 1;
unsigned DwarfVersion; unsigned DwarfVersion;
std::string DwarfDebugFlags; std::string DwarfDebugFlags;
@@ -69,9 +66,7 @@ struct AssemblerInvocation {
FT_Obj ///< Object file output. FT_Obj ///< Object file output.
}; };
FileType OutputType; FileType OutputType;
LLVM_PREFERRED_TYPE(bool)
unsigned ShowHelp : 1; unsigned ShowHelp : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned ShowVersion : 1; unsigned ShowVersion : 1;
/// @} /// @}
@@ -79,48 +74,28 @@ struct AssemblerInvocation {
/// @{ /// @{
unsigned OutputAsmVariant; unsigned OutputAsmVariant;
LLVM_PREFERRED_TYPE(bool)
unsigned ShowEncoding : 1; unsigned ShowEncoding : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned ShowInst : 1; unsigned ShowInst : 1;
/// @} /// @}
/// @name Assembler Options /// @name Assembler Options
/// @{ /// @{
LLVM_PREFERRED_TYPE(bool)
unsigned RelaxAll : 1; unsigned RelaxAll : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned NoExecStack : 1; unsigned NoExecStack : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FatalWarnings : 1; unsigned FatalWarnings : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned NoWarn : 1; unsigned NoWarn : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned NoTypeCheck : 1; unsigned NoTypeCheck : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned IncrementalLinkerCompatible : 1; unsigned IncrementalLinkerCompatible : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned EmbedBitcode : 1; unsigned EmbedBitcode : 1;
/// Whether to emit DWARF unwind info. /// Whether to emit DWARF unwind info.
EmitDwarfUnwindType EmitDwarfUnwind; EmitDwarfUnwindType EmitDwarfUnwind;
// Whether to emit compact-unwind for non-canonical entries. // Whether to emit compact-unwind for non-canonical entries.
// Note: maybe overridden by other constraints. // Note: maybe overriden by other constraints.
LLVM_PREFERRED_TYPE(bool)
unsigned EmitCompactUnwindNonCanonical : 1; unsigned EmitCompactUnwindNonCanonical : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned Crel : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned ImplicitMapsyms : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned X86RelaxRelocations : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned X86Sse2Avx : 1;
/// The name of the relocation model to use. /// The name of the relocation model to use.
std::string RelocationModel; std::string RelocationModel;
@@ -161,10 +136,6 @@ public:
EmbedBitcode = 0; EmbedBitcode = 0;
EmitDwarfUnwind = EmitDwarfUnwindType::Default; EmitDwarfUnwind = EmitDwarfUnwindType::Default;
EmitCompactUnwindNonCanonical = false; EmitCompactUnwindNonCanonical = false;
Crel = false;
ImplicitMapsyms = 0;
X86RelaxRelocations = 0;
X86Sse2Avx = 0;
} }
static bool CreateFromArgs(AssemblerInvocation &Res, static bool CreateFromArgs(AssemblerInvocation &Res,
+1 -1
View File
@@ -60,7 +60,7 @@ bool tinygo_clang_driver(int argc, char **argv) {
} }
// Create the actual diagnostics engine. // Create the actual diagnostics engine.
Clang->createDiagnostics(*llvm::vfs::getRealFileSystem()); Clang->createDiagnostics();
if (!Clang->hasDiagnostics()) { if (!Clang->hasDiagnostics()) {
return false; return false;
} }
+12
View File
@@ -3,6 +3,7 @@ package builder
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"runtime" "runtime"
"strings" "strings"
@@ -75,3 +76,14 @@ func LookupCommand(name string) (string, error) {
} }
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " ")) return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
} }
func execCommand(name string, args ...string) error {
name, err := LookupCommand(name)
if err != nil {
return err
}
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
+6 -27
View File
@@ -2,7 +2,6 @@ package builder
import ( import (
"fmt" "fmt"
"runtime"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
@@ -24,40 +23,20 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
spec.OpenOCDCommands = options.OpenOCDCommands spec.OpenOCDCommands = options.OpenOCDCommands
} }
// Version range supported by TinyGo. major, minor, err := goenv.GetGorootVersion()
const minorMin = 19
const minorMax = 26
// Check that we support this Go toolchain version.
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if major != 1 || minor < 18 || minor > 22 {
if options.GoCompatibility { // Note: when this gets updated, also update the Go compatibility matrix:
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax { // https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
// Note: when this gets updated, also update the Go compatibility matrix: return nil, fmt.Errorf("requires go version 1.18 through 1.22, got go%d.%d", major, minor)
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
}
}
// Check that the Go toolchain version isn't too new, if we haven't been
// compiled with the latest Go version.
// This may be a bit too aggressive: if the newer version doesn't change the
// Go language we will most likely be able to compile it.
buildMajor, buildMinor, _, err := goenv.Parse(runtime.Version())
if err != nil {
return nil, err
}
if buildMajor != 1 || buildMinor < gorootMinor {
return nil, fmt.Errorf("cannot compile with Go toolchain version go%d.%d (TinyGo was built using toolchain version %s)", gorootMajor, gorootMinor, runtime.Version())
} }
return &compileopts.Config{ return &compileopts.Config{
Options: options, Options: options,
Target: spec, Target: spec,
GoMinorVersion: gorootMinor, GoMinorVersion: minor,
TestConfig: options.TestConfig, TestConfig: options.TestConfig,
}, nil }, nil
} }
+5 -7
View File
@@ -3,8 +3,7 @@ package builder
// MultiError is a list of multiple errors (actually: diagnostics) returned // MultiError is a list of multiple errors (actually: diagnostics) returned
// during LLVM IR generation. // during LLVM IR generation.
type MultiError struct { type MultiError struct {
ImportPath string Errs []error
Errs []error
} }
func (e *MultiError) Error() string { func (e *MultiError) Error() string {
@@ -15,16 +14,15 @@ func (e *MultiError) Error() string {
// newMultiError returns a *MultiError if there is more than one error, or // newMultiError returns a *MultiError if there is more than one error, or
// returns that error directly when there is only one. Passing an empty slice // returns that error directly when there is only one. Passing an empty slice
// will return nil (because there is no error). // will lead to a panic.
// The importPath may be passed if this error is for a single package. func newMultiError(errs []error) error {
func newMultiError(errs []error, importPath string) error {
switch len(errs) { switch len(errs) {
case 0: case 0:
return nil panic("attempted to create empty MultiError")
case 1: case 1:
return errs[0] return errs[0]
default: default:
return &MultiError{importPath, errs} return &MultiError{errs}
} }
} }
+3 -4
View File
@@ -23,7 +23,7 @@ type espImageSegment struct {
data []byte data []byte
} }
// makeESPFirmwareImage converts an input ELF file to an image file for an ESP32 or // makeESPFirmareImage converts an input ELF file to an image file for an ESP32 or
// ESP8266 chip. This is a special purpose image format just for the ESP chip // ESP8266 chip. This is a special purpose image format just for the ESP chip
// family, and is parsed by the on-chip mask ROM bootloader. // family, and is parsed by the on-chip mask ROM bootloader.
// //
@@ -31,7 +31,7 @@ type espImageSegment struct {
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format // https://github.com/espressif/esptool/wiki/Firmware-Image-Format
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58 // https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
// https://github.com/espressif/esptool/blob/master/esptool.py // https://github.com/espressif/esptool/blob/master/esptool.py
func makeESPFirmwareImage(infile, outfile, format string) error { func makeESPFirmareImage(infile, outfile, format string) error {
inf, err := elf.Open(infile) inf, err := elf.Open(infile)
if err != nil { if err != nil {
return err return err
@@ -100,12 +100,11 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
chip_id := map[string]uint16{ chip_id := map[string]uint16{
"esp32": 0x0000, "esp32": 0x0000,
"esp32c3": 0x0005, "esp32c3": 0x0005,
"esp32s3": 0x0009,
}[chip] }[chip]
// Image header. // Image header.
switch chip { switch chip {
case "esp32", "esp32c3", "esp32s3": 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
// Note: not adding a SHA256 hash as the binary is modified by // Note: not adding a SHA256 hash as the binary is modified by
+16 -6
View File
@@ -17,6 +17,14 @@ import (
// concurrency or performance issues. // concurrency or performance issues.
const jobRunnerDebug = false const jobRunnerDebug = false
type jobState uint8
const (
jobStateQueued jobState = iota // not yet running
jobStateRunning // running
jobStateFinished // finished running
)
// compileJob is a single compiler job, comparable to a single Makefile target. // compileJob is a single compiler job, comparable to a single Makefile target.
// It is used to orchestrate various compiler tasks that can be run in parallel // It is used to orchestrate various compiler tasks that can be run in parallel
// but that have dependencies and thus have limitations in how they can be run. // but that have dependencies and thus have limitations in how they can be run.
@@ -47,11 +55,12 @@ func dummyCompileJob(result string) *compileJob {
// ordered as such in the job dependencies. // ordered as such in the job dependencies.
func runJobs(job *compileJob, sema chan struct{}) error { func runJobs(job *compileJob, sema chan struct{}) error {
if sema == nil { if sema == nil {
// Have a default, if the semaphore isn't set. This is useful for tests. // Have a default, if the semaphore isn't set. This is useful for
// tests.
sema = make(chan struct{}, runtime.NumCPU()) sema = make(chan struct{}, runtime.NumCPU())
} }
if cap(sema) == 0 { if cap(sema) == 0 {
return errors.New("cannot run 0 jobs at a time") return errors.New("cannot 0 jobs at a time")
} }
// Create a slice of jobs to run, where all dependencies are run in order. // Create a slice of jobs to run, where all dependencies are run in order.
@@ -72,10 +81,10 @@ func runJobs(job *compileJob, sema chan struct{}) error {
waiting := make(map[*compileJob]map[*compileJob]struct{}, len(jobs)) waiting := make(map[*compileJob]map[*compileJob]struct{}, len(jobs))
dependents := make(map[*compileJob][]*compileJob, len(jobs)) dependents := make(map[*compileJob][]*compileJob, len(jobs))
compileJobs := make(map[*compileJob]int) jidx := make(map[*compileJob]int)
var ready intHeap var ready intHeap
for i, job := range jobs { for i, job := range jobs {
compileJobs[job] = i jidx[job] = i
if len(job.dependencies) == 0 { if len(job.dependencies) == 0 {
// This job is ready to run. // This job is ready to run.
ready.Push(i) ready.Push(i)
@@ -96,7 +105,8 @@ func runJobs(job *compileJob, sema chan struct{}) error {
// Create a channel to accept notifications of completion. // Create a channel to accept notifications of completion.
doneChan := make(chan *compileJob) doneChan := make(chan *compileJob)
// Send each job in the jobs slice to a worker, taking care of job dependencies. // Send each job in the jobs slice to a worker, taking care of job
// dependencies.
numRunningJobs := 0 numRunningJobs := 0
var totalTime time.Duration var totalTime time.Duration
start := time.Now() start := time.Now()
@@ -146,7 +156,7 @@ func runJobs(job *compileJob, sema chan struct{}) error {
delete(wait, completed) delete(wait, completed)
if len(wait) == 0 { if len(wait) == 0 {
// This job is now ready to run. // This job is now ready to run.
ready.Push(compileJobs[j]) ready.Push(jidx[j])
delete(waiting, j) delete(waiting, j)
} }
} }
+32 -58
View File
@@ -15,9 +15,6 @@ import (
// Library is a container for information about a single C library, such as a // Library is a container for information about a single C library, such as a
// compiler runtime or libc. // compiler runtime or libc.
//
// Note: whenever a library gets changed, the version in compileopts/config.go
// probably also needs to be incremented.
type Library struct { type Library struct {
// The library name, such as compiler-rt or picolibc. // The library name, such as compiler-rt or picolibc.
name string name string
@@ -28,22 +25,29 @@ 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
// cflagsForFile returns additional C flags for a particular source file.
cflagsForFile func(path string) []string
// needsLibc is set to true if this library needs libc headers.
needsLibc bool
// The source directory. // The source directory.
sourceDir func() string sourceDir func() string
// The source files, relative to sourceDir. // The source files, relative to sourceDir.
librarySources func(target string, libcNeedsMalloc bool) ([]string, error) librarySources func(target string) ([]string, error)
// The source code for the crt1.o file, relative to sourceDir. // The source code for the crt1.o file, relative to sourceDir.
crt1Source string crt1Source string
} }
// Load the library archive, possibly generating and caching it if needed.
// The resulting directory may be stored in the provided tmpdir, which is
// expected to be removed after the Load call.
func (l *Library) Load(config *compileopts.Config, tmpdir string) (dir string, err error) {
job, unlock, err := l.load(config, tmpdir)
if err != nil {
return "", err
}
defer unlock()
err = runJobs(job, config.Options.Semaphore)
return filepath.Dir(job.result), err
}
// load returns a compile job to build this library file for the given target // load returns a compile job to build this library file for the given target
// and CPU. It may return a dummy compileJob if the library build is already // and CPU. It may return a dummy compileJob if the library build is already
// cached. The path is stored as job.result but is only valid after the job has // cached. The path is stored as job.result but is only valid after the job has
@@ -53,8 +57,13 @@ type Library struct {
// As a side effect, this call creates the library header files if they didn't // As a side effect, this call creates the library header files if they didn't
// exist yet. // exist yet.
func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, abortLock func(), err error) { func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, abortLock func(), err error) {
outdir := config.LibraryPath(l.name) outdir, precompiled := config.LibcPath(l.name)
archiveFilePath := filepath.Join(outdir, "lib.a") archiveFilePath := filepath.Join(outdir, "lib.a")
if precompiled {
// Found a precompiled library for this OS/architecture. Return the path
// directly.
return dummyCompileJob(archiveFilePath), func() {}, nil
}
// Create a lock on the output (if supported). // Create a lock on the output (if supported).
// This is a bit messy, but avoids a deadlock because it is ordered consistently with other library loads within a build. // This is a bit messy, but avoids a deadlock because it is ordered consistently with other library loads within a build.
@@ -153,40 +162,25 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if config.ABI() != "" { if config.ABI() != "" {
args = append(args, "-mabi="+config.ABI()) args = append(args, "-mabi="+config.ABI())
} }
switch compileopts.CanonicalArchName(target) { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
case "arm":
if strings.Split(target, "-")[2] == "linux" { if strings.Split(target, "-")[2] == "linux" {
args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables") args = append(args, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
} else { } else {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables") args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
} }
case "avr": }
if strings.HasPrefix(target, "avr") {
// AVR defaults to C float and double both being 32-bit. This deviates // AVR defaults to C float and double both being 32-bit. This deviates
// from what most code (and certainly compiler-rt) expects. So we need // from what most code (and certainly compiler-rt) expects. So we need
// to force the compiler to use 64-bit floating point numbers for // to force the compiler to use 64-bit floating point numbers for
// double. // double.
args = append(args, "-mdouble=64") args = append(args, "-mdouble=64")
case "riscv32":
args = append(args, "-march="+riscvMarch(config, "rv32imac"), "-fforce-enable-int128")
case "riscv64":
args = append(args, "-march="+riscvMarch(config, "rv64gc"))
case "mips":
args = append(args, "-fno-pic")
} }
if config.Target.SoftFloat { if strings.HasPrefix(target, "riscv32-") {
// Use softfloat instead of floating point instructions. This is args = append(args, "-march=rv32imac", "-fforce-enable-int128")
// supported on many architectures.
args = append(args, "-msoft-float")
} else {
if strings.HasPrefix(target, "armv5") {
// On ARMv5 we need to explicitly enable hardware floating point
// instructions: Clang appears to assume the hardware doesn't have a
// FPU otherwise.
args = append(args, "-mfpu=vfpv2")
}
} }
if l.needsLibc { if strings.HasPrefix(target, "riscv64-") {
args = append(args, config.LibcCFlags()...) args = append(args, "-march=rv64gc")
} }
var once sync.Once var once sync.Once
@@ -226,13 +220,12 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// Create jobs to compile all sources. These jobs are depended upon by the // Create jobs to compile all sources. These jobs are depended upon by the
// archive job above, so must be run first. // archive job above, so must be run first.
paths, err := l.librarySources(target, config.LibcNeedsMalloc()) paths, err := l.librarySources(target)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
for _, path := range paths { for _, path := range paths {
// Strip leading "../" parts off the path. // Strip leading "../" parts off the path.
path := path
cleanpath := path cleanpath := path
for strings.HasPrefix(cleanpath, "../") { for strings.HasPrefix(cleanpath, "../") {
cleanpath = cleanpath[3:] cleanpath = cleanpath[3:]
@@ -241,14 +234,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
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)
objfile := &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...)
if l.cflagsForFile != nil {
compileArgs = append(compileArgs, l.cflagsForFile(path)...)
}
compileArgs = append(compileArgs, "-o", objpath, srcpath) compileArgs = append(compileArgs, "-o", objpath, srcpath)
if config.Options.PrintCommands != nil { if config.Options.PrintCommands != nil {
config.Options.PrintCommands("clang", compileArgs...) config.Options.PrintCommands("clang", compileArgs...)
@@ -259,8 +249,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
} }
return nil return nil
}, },
} })
job.dependencies = append(job.dependencies, objfile)
} }
// Create crt1.o job, if needed. // Create crt1.o job, if needed.
@@ -269,7 +258,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// 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(sourceDir, l.crt1Source)
crt1Job := &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
@@ -289,8 +278,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
} }
return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o")) return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o"))
}, },
} })
job.dependencies = append(job.dependencies, crt1Job)
} }
ok = true ok = true
@@ -298,17 +286,3 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
once.Do(unlock) once.Do(unlock)
}, nil }, nil
} }
// riscvMarch returns the -march value for RISC-V library compilation.
// It extracts the value from the target's cflags if present, otherwise
// falls back to the provided default. This ensures libraries are compiled
// with the correct ISA extensions for each target (e.g. rv32imc for
// ESP32-C3 which lacks the atomic extension).
func riscvMarch(config *compileopts.Config, defaultMarch string) string {
for _, flag := range config.Target.CFlags {
if strings.HasPrefix(flag, "-march=") {
return flag[len("-march="):]
}
}
return defaultMarch
}
+28 -98
View File
@@ -10,7 +10,7 @@ import (
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
var libMinGW = Library{ var MinGW = Library{
name: "mingw-w64", name: "mingw-w64",
makeHeaders: func(target, includeDir string) error { makeHeaders: func(target, includeDir string) error {
// copy _mingw.h // copy _mingw.h
@@ -27,67 +27,14 @@ var libMinGW = Library{
_, err = io.Copy(outf, inf) _, err = io.Copy(outf, inf)
return err return err
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") }, sourceDir: func() string { return "" }, // unused
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") // No flags necessary because there are no files to compile.
flags := []string{ return nil
"-nostdlibinc",
"-isystem", mingwDir + "/mingw-w64-crt/include",
"-isystem", mingwDir + "/mingw-w64-headers/crt",
"-isystem", mingwDir + "/mingw-w64-headers/include",
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
"-I" + headerPath,
}
if strings.Split(target, "-")[0] == "i386" {
flags = append(flags,
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
"-D_WIN32_WINNT=0x0501", // target Windows XP
"-D_CRTBLD",
"-Wno-pragma-pack",
)
}
return flags
}, },
librarySources: func(target string, _ bool) ([]string, error) { librarySources: func(target string) ([]string, error) {
// These files are needed so that printf and the like are supported. // We only use the UCRT DLL file. No source files necessary.
var sources []string return nil, nil
if strings.Split(target, "-")[0] == "i386" {
// Old 32-bit x86 systems use msvcrt.dll.
sources = []string{
"mingw-w64-crt/crt/pseudo-reloc.c",
"mingw-w64-crt/gdtoa/dmisc.c",
"mingw-w64-crt/gdtoa/gdtoa.c",
"mingw-w64-crt/gdtoa/gmisc.c",
"mingw-w64-crt/gdtoa/misc.c",
"mingw-w64-crt/math/x86/exp2.S",
"mingw-w64-crt/math/x86/trunc.S",
"mingw-w64-crt/misc/___mb_cur_max_func.c",
"mingw-w64-crt/misc/lc_locale_func.c",
"mingw-w64-crt/misc/mbrtowc.c",
"mingw-w64-crt/misc/strnlen.c",
"mingw-w64-crt/misc/wcrtomb.c",
"mingw-w64-crt/misc/wcsnlen.c",
"mingw-w64-crt/stdio/acrt_iob_func.c",
"mingw-w64-crt/stdio/mingw_lock.c",
"mingw-w64-crt/stdio/mingw_pformat.c",
"mingw-w64-crt/stdio/mingw_vfprintf.c",
"mingw-w64-crt/stdio/mingw_vsnprintf.c",
}
} else {
// Anything somewhat modern (amd64, arm64) uses UCRT.
sources = []string{
"mingw-w64-crt/stdio/ucrt_fprintf.c",
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
"mingw-w64-crt/stdio/ucrt_printf.c",
"mingw-w64-crt/stdio/ucrt_snprintf.c",
"mingw-w64-crt/stdio/ucrt_sprintf.c",
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
"mingw-w64-crt/stdio/ucrt_vprintf.c",
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
}
}
return sources, nil
}, },
} }
@@ -100,41 +47,27 @@ var libMinGW = Library{
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob { func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
var jobs []*compileJob var jobs []*compileJob
root := goenv.Get("TINYGOROOT") root := goenv.Get("TINYGOROOT")
var libs []string // Normally all the api-ms-win-crt-*.def files are all compiled to a single
if goarch == "386" { // .lib file. But to simplify things, we're going to leave them as separate
libs = []string{ // files.
// x86 uses msvcrt.dll instead of UCRT for compatibility with old for _, name := range []string{
// Windows versions. "kernel32.def.in",
"advapi32.def.in", "api-ms-win-crt-conio-l1-1-0.def",
"kernel32.def.in", "api-ms-win-crt-convert-l1-1-0.def.in",
"msvcrt.def.in", "api-ms-win-crt-environment-l1-1-0.def",
} "api-ms-win-crt-filesystem-l1-1-0.def",
} else { "api-ms-win-crt-heap-l1-1-0.def",
// Use the modernized UCRT on new systems. "api-ms-win-crt-locale-l1-1-0.def",
// Normally all the api-ms-win-crt-*.def files are all compiled to a "api-ms-win-crt-math-l1-1-0.def.in",
// single .lib file. But to simplify things, we're going to leave them "api-ms-win-crt-multibyte-l1-1-0.def",
// as separate files. "api-ms-win-crt-private-l1-1-0.def.in",
libs = []string{ "api-ms-win-crt-process-l1-1-0.def",
"advapi32.def.in", "api-ms-win-crt-runtime-l1-1-0.def.in",
"kernel32.def.in", "api-ms-win-crt-stdio-l1-1-0.def",
"api-ms-win-crt-conio-l1-1-0.def", "api-ms-win-crt-string-l1-1-0.def",
"api-ms-win-crt-convert-l1-1-0.def.in", "api-ms-win-crt-time-l1-1-0.def",
"api-ms-win-crt-environment-l1-1-0.def", "api-ms-win-crt-utility-l1-1-0.def",
"api-ms-win-crt-filesystem-l1-1-0.def", } {
"api-ms-win-crt-heap-l1-1-0.def",
"api-ms-win-crt-locale-l1-1-0.def",
"api-ms-win-crt-math-l1-1-0.def.in",
"api-ms-win-crt-multibyte-l1-1-0.def",
"api-ms-win-crt-private-l1-1-0.def.in",
"api-ms-win-crt-process-l1-1-0.def",
"api-ms-win-crt-runtime-l1-1-0.def.in",
"api-ms-win-crt-stdio-l1-1-0.def",
"api-ms-win-crt-string-l1-1-0.def",
"api-ms-win-crt-time-l1-1-0.def",
"api-ms-win-crt-utility-l1-1-0.def",
}
}
for _, name := range libs {
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib") outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name) inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
job := &compileJob{ job := &compileJob{
@@ -144,9 +77,6 @@ func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
defpath := inpath defpath := inpath
var archDef, emulation string var archDef, emulation string
switch goarch { switch goarch {
case "386":
archDef = "-DDEF_I386"
emulation = "i386pe"
case "amd64": case "amd64":
archDef = "-DDEF_X64" archDef = "-DDEF_X64"
emulation = "i386pep" emulation = "i386pep"
+32 -65
View File
@@ -12,46 +12,7 @@ import (
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
// Create the alltypes.h file from the appropriate alltypes.h.in files. var Musl = Library{
func buildMuslAllTypes(arch, muslDir, outputBitsDir string) error {
// Create the file alltypes.h.
f, err := os.Create(filepath.Join(outputBitsDir, "alltypes.h"))
if err != nil {
return err
}
infiles := []string{
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
filepath.Join(muslDir, "include", "alltypes.h.in"),
}
for _, infile := range infiles {
data, err := os.ReadFile(infile)
if err != nil {
return err
}
lines := strings.Split(string(data), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "TYPEDEF ") {
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
value := matches[1]
name := matches[2]
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
}
if strings.HasPrefix(line, "STRUCT ") {
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
name := matches[1]
value := matches[2]
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
}
_, err := f.WriteString(line + "\n")
if err != nil {
return err
}
}
}
return f.Close()
}
var libMusl = Library{
name: "musl", name: "musl",
makeHeaders: func(target, includeDir string) error { makeHeaders: func(target, includeDir string) error {
bits := filepath.Join(includeDir, "bits") bits := filepath.Join(includeDir, "bits")
@@ -63,13 +24,41 @@ var libMusl = Library{
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl") muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl")
err = buildMuslAllTypes(arch, muslDir, bits) // Create the file alltypes.h.
f, err := os.Create(filepath.Join(bits, "alltypes.h"))
if err != nil { if err != nil {
return err return err
} }
infiles := []string{
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
filepath.Join(muslDir, "include", "alltypes.h.in"),
}
for _, infile := range infiles {
data, err := os.ReadFile(infile)
if err != nil {
return err
}
lines := strings.Split(string(data), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "TYPEDEF ") {
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
value := matches[1]
name := matches[2]
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
}
if strings.HasPrefix(line, "STRUCT ") {
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
name := matches[1]
value := matches[2]
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
}
f.WriteString(line + "\n")
}
}
f.Close()
// Create the file syscall.h. // Create the file syscall.h.
f, err := os.Create(filepath.Join(bits, "syscall.h")) f, err = os.Create(filepath.Join(bits, "syscall.h"))
if err != nil { if err != nil {
return err return err
} }
@@ -103,8 +92,6 @@ var libMusl = Library{
"-Wno-ignored-pragmas", "-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare", "-Wno-tautological-constant-out-of-range-compare",
"-Wno-deprecated-non-prototype", "-Wno-deprecated-non-prototype",
"-Wno-format",
"-Wno-parentheses",
"-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),
@@ -121,55 +108,35 @@ var libMusl = Library{
return cflags return cflags
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
librarySources: func(target string, _ bool) ([]string, error) { librarySources: func(target string) ([]string, error) {
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
globs := []string{ globs := []string{
"conf/*.c",
"ctype/*.c",
"env/*.c", "env/*.c",
"errno/*.c", "errno/*.c",
"exit/*.c", "exit/*.c",
"fcntl/*.c",
"internal/defsysinfo.c", "internal/defsysinfo.c",
"internal/intscan.c",
"internal/libc.c", "internal/libc.c",
"internal/shgetc.c",
"internal/syscall_ret.c", "internal/syscall_ret.c",
"internal/vdso.c", "internal/vdso.c",
"legacy/*.c", "legacy/*.c",
"locale/*.c",
"linux/*.c", "linux/*.c",
"locale/*.c",
"malloc/*.c", "malloc/*.c",
"malloc/mallocng/*.c", "malloc/mallocng/*.c",
"mman/*.c", "mman/*.c",
"math/*.c", "math/*.c",
"misc/*.c",
"multibyte/*.c",
"sched/*.c",
"signal/" + arch + "/*.s",
"signal/*.c", "signal/*.c",
"stdio/*.c", "stdio/*.c",
"stdlib/*.c",
"string/*.c", "string/*.c",
"thread/" + arch + "/*.s", "thread/" + arch + "/*.s",
"thread/*.c", "thread/*.c",
"time/*.c", "time/*.c",
"unistd/*.c", "unistd/*.c",
"process/*.c",
} }
if arch == "arm" { if arch == "arm" {
// These files need to be added to the start for some reason. // These files need to be added to the start for some reason.
globs = append([]string{"thread/arm/*.c"}, globs...) globs = append([]string{"thread/arm/*.c"}, globs...)
} }
if arch != "aarch64" && arch != "mips" {
//aarch64 and mips have no architecture specific code, either they
// are not supported or don't need any?
globs = append([]string{"process/" + arch + "/*.s"}, globs...)
}
var sources []string var sources []string
seenSources := map[string]struct{}{} seenSources := map[string]struct{}{}
basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/" basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/"
+3 -5
View File
@@ -8,9 +8,9 @@ import (
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
// libPicolibc is a C library for bare metal embedded devices. It was originally // Picolibc is a C library for bare metal embedded devices. It was originally
// based on newlib. // based on newlib.
var libPicolibc = Library{ var Picolibc = Library{
name: "picolibc", name: "picolibc",
makeHeaders: func(target, includeDir string) error { makeHeaders: func(target, includeDir string) error {
f, err := os.Create(filepath.Join(includeDir, "picolibc.h")) f, err := os.Create(filepath.Join(includeDir, "picolibc.h"))
@@ -29,12 +29,10 @@ var libPicolibc = Library{
"-D_HAVE_ALIAS_ATTRIBUTE", "-D_HAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO", "-DTINY_STDIO",
"-DPOSIX_IO", "-DPOSIX_IO",
"-DFORMAT_DEFAULT_INTEGER", // use __i_vfprintf and __i_vfscanf by default
"-D_IEEE_LIBM", "-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU "-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
"-D__OBSOLETE_MATH_DOUBLE=0", "-D__OBSOLETE_MATH_DOUBLE=0",
"-D_WANT_IO_C99_FORMATS", "-D_WANT_IO_C99_FORMATS",
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
"-nostdlibinc", "-nostdlibinc",
"-isystem", newlibDir + "/libc/include", "-isystem", newlibDir + "/libc/include",
"-I" + newlibDir + "/libc/tinystdio", "-I" + newlibDir + "/libc/tinystdio",
@@ -43,7 +41,7 @@ var libPicolibc = Library{
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
librarySources: func(target string, _ bool) ([]string, error) { librarySources: func(target string) ([]string, error) {
sources := append([]string(nil), picolibcSources...) sources := append([]string(nil), picolibcSources...)
if !strings.HasPrefix(target, "avr") { if !strings.HasPrefix(target, "avr") {
// Small chips without long jumps can't compile many files (printf, // Small chips without long jumps can't compile many files (printf,
-56
View File
@@ -1,56 +0,0 @@
package builder
import (
_ "embed"
"fmt"
"html/template"
"os"
)
//go:embed size-report.html
var sizeReportBase string
func writeSizeReport(sizes *programSize, filename, pkgName string) error {
tmpl, err := template.New("report").Parse(sizeReportBase)
if err != nil {
return err
}
f, err := os.Create(filename)
if err != nil {
return fmt.Errorf("could not open report file: %w", err)
}
defer f.Close()
// Prepare data for the report.
type sizeLine struct {
Name string
Size *packageSize
}
programData := []sizeLine{}
for _, name := range sizes.sortedPackageNames() {
pkgSize := sizes.Packages[name]
programData = append(programData, sizeLine{
Name: name,
Size: pkgSize,
})
}
sizeTotal := map[string]uint64{
"code": sizes.Code,
"rodata": sizes.ROData,
"data": sizes.Data,
"bss": sizes.BSS,
"flash": sizes.Flash(),
}
// Write the report.
err = tmpl.Execute(f, map[string]any{
"pkgName": pkgName,
"sizes": programData,
"sizeTotal": sizeTotal,
})
if err != nil {
return fmt.Errorf("could not create report file: %w", err)
}
return nil
}
-109
View File
@@ -1,109 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Size Report for {{.pkgName}}</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
.table-vertical-border {
border-left: calc(var(--bs-border-width) * 2) solid currentcolor;
}
/* Hover on only the rows that are clickable. */
.row-package:hover > * {
--bs-table-color-state: var(--bs-table-hover-color);
--bs-table-bg-state: var(--bs-table-hover-bg);
}
</style>
</head>
<body>
<div class="container-xxl">
<h1>Size Report for {{.pkgName}}</h1>
<p>How much space is used by Go packages, C libraries, and other bits to set up the program environment.</p>
<ul>
<li><strong>Code</strong> is the actual program code (machine code instructions).</li>
<li><strong>Read-only data</strong> are read-only global variables. On most microcontrollers, these are stored in flash and do not take up any RAM.</li>
<li><strong>Data</strong> are writable global variables with a non-zero initializer. On microcontrollers, they are copied from flash to RAM on reset.</li>
<li><strong>BSS</strong> are writable global variables that are zero initialized. They do not take up any space in the binary, but do take up RAM. On microcontrollers, this area is zeroed on reset.</li>
</ul>
<p>The binary size consists of code, read-only data, and data. On microcontrollers, this is exactly the size of the firmware image. On other systems, there is some extra overhead: binary metadata (headers of the ELF/MachO/COFF file), debug information, exception tables, symbol names, etc. Using <code>-no-debug</code> strips most of those.</p>
<h2>Program breakdown</h2>
<p>You can click on the rows below to see which files contribute to the binary size.</p>
<div class="table-responsive">
<table class="table w-auto">
<thead>
<tr>
<th>Package</th>
<th class="table-vertical-border">Code</th>
<th>Read-only data</th>
<th>Data</th>
<th title="zero-initialized data">BSS</th>
<th class="table-vertical-border" style="min-width: 16em">Binary size</th>
</tr>
</thead>
<tbody class="table-group-divider">
{{range $i, $pkg := .sizes}}
<tr class="row-package" data-collapse=".collapse-row-{{$i}}">
<td>{{.Name}}</td>
<td class="table-vertical-border">{{.Size.Code}}</td>
<td>{{.Size.ROData}}</td>
<td>{{.Size.Data}}</td>
<td>{{.Size.BSS}}</td>
<td class="table-vertical-border" style="background: linear-gradient(to right, var(--bs-info-bg-subtle) {{.Size.FlashPercent}}%, var(--bs-table-bg) {{.Size.FlashPercent}}%)">
{{.Size.Flash}}
</td>
</tr>
{{range $filename, $sizes := .Size.Sub}}
<tr class="table-secondary collapse collapse-row-{{$i}}">
<td class="ps-4">
{{if eq $filename ""}}
(unknown file)
{{else}}
{{$filename}}
{{end}}
</td>
<td class="table-vertical-border">{{$sizes.Code}}</td>
<td>{{$sizes.ROData}}</td>
<td>{{$sizes.Data}}</td>
<td>{{$sizes.BSS}}</td>
<td class="table-vertical-border" style="background: linear-gradient(to right, var(--bs-info-bg-subtle) {{$sizes.FlashPercent}}%, var(--bs-table-bg) {{$sizes.FlashPercent}}%)">
{{$sizes.Flash}}
</td>
</tr>
{{end}}
{{end}}
</tbody>
<tfoot class="table-group-divider">
<tr>
<th>Total</th>
<td class="table-vertical-border">{{.sizeTotal.code}}</td>
<td>{{.sizeTotal.rodata}}</td>
<td>{{.sizeTotal.data}}</td>
<td>{{.sizeTotal.bss}}</td>
<td class="table-vertical-border">{{.sizeTotal.flash}}</td>
</tr>
</tfoot>
</table>
</div>
</div>
<script>
// Make table rows toggleable to show filenames.
for (let clickable of document.querySelectorAll('.row-package')) {
clickable.addEventListener('click', e => {
for (let row of document.querySelectorAll(clickable.dataset.collapse)) {
row.classList.toggle('show');
}
});
}
</script>
</body>
</html>
+48 -107
View File
@@ -12,7 +12,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"sort" "sort"
"strings" "strings"
@@ -25,7 +24,7 @@ const sizesDebug = false
// programSize contains size statistics per package of a compiled program. // programSize contains size statistics per package of a compiled program.
type programSize struct { type programSize struct {
Packages map[string]*packageSize Packages map[string]packageSize
Code uint64 Code uint64
ROData uint64 ROData uint64
Data uint64 Data uint64
@@ -53,29 +52,13 @@ func (ps *programSize) RAM() uint64 {
return ps.Data + ps.BSS return ps.Data + ps.BSS
} }
// Return the package size information for a given package path, creating it if
// it doesn't exist yet.
func (ps *programSize) getPackage(path string) *packageSize {
if field, ok := ps.Packages[path]; ok {
return field
}
field := &packageSize{
Program: ps,
Sub: map[string]*packageSize{},
}
ps.Packages[path] = field
return field
}
// packageSize contains the size of a package, calculated from the linked object // packageSize contains the size of a package, calculated from the linked object
// file. // file.
type packageSize struct { type packageSize struct {
Program *programSize Code uint64
Code uint64 ROData uint64
ROData uint64 Data uint64
Data uint64 BSS uint64
BSS uint64
Sub map[string]*packageSize
} }
// Flash usage in regular microcontrollers. // Flash usage in regular microcontrollers.
@@ -88,31 +71,6 @@ func (ps *packageSize) RAM() uint64 {
return ps.Data + ps.BSS return ps.Data + ps.BSS
} }
// Flash usage in regular microcontrollers, as a percentage of the total flash
// usage of the program.
func (ps *packageSize) FlashPercent() float64 {
return float64(ps.Flash()) / float64(ps.Program.Flash()) * 100
}
// Add a single size data point to this package.
// This must only be called while calculating package size, not afterwards.
func (ps *packageSize) addSize(getField func(*packageSize, bool) *uint64, filename string, size uint64, isVariable bool) {
if size == 0 {
return
}
// Add size for the package.
*getField(ps, isVariable) += size
// Add size for file inside package.
sub, ok := ps.Sub[filename]
if !ok {
sub = &packageSize{Program: ps.Program}
ps.Sub[filename] = sub
}
*getField(sub, isVariable) += size
}
// A mapping of a single chunk of code or data to a file path. // A mapping of a single chunk of code or data to a file path.
type addressLine struct { type addressLine struct {
Address uint64 Address uint64
@@ -236,22 +194,11 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset, codeAlignment uint64
if !prevLineEntry.EndSequence { if !prevLineEntry.EndSequence {
// The chunk describes the code from prevLineEntry to // The chunk describes the code from prevLineEntry to
// lineEntry. // lineEntry.
path := prevLineEntry.File.Name
if runtime.GOOS == "windows" {
// Work around a Clang bug on Windows:
// https://github.com/llvm/llvm-project/issues/117317
path = strings.ReplaceAll(path, "\\\\", "\\")
// wasi-libc likes to use forward slashes, but we
// canonicalize everything to use backwards slashes as
// is common on Windows.
path = strings.ReplaceAll(path, "/", "\\")
}
line := addressLine{ line := addressLine{
Address: prevLineEntry.Address + codeOffset, Address: prevLineEntry.Address + codeOffset,
Length: lineEntry.Address - prevLineEntry.Address, Length: lineEntry.Address - prevLineEntry.Address,
Align: codeAlignment, Align: codeAlignment,
File: path, File: prevLineEntry.File.Name,
} }
if line.Length != 0 { if line.Length != 0 {
addresses = append(addresses, line) addresses = append(addresses, line)
@@ -490,7 +437,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
continue continue
} }
if section.Type == elf.SHT_NOBITS { if section.Type == elf.SHT_NOBITS {
if strings.HasPrefix(section.Name, ".stack") { if section.Name == ".stack" {
// TinyGo emits stack sections on microcontroller using the // TinyGo emits stack sections on microcontroller using the
// ".stack" name. // ".stack" name.
// This is a bit ugly, but I don't think there is a way to // This is a bit ugly, but I don't think there is a way to
@@ -826,40 +773,49 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
// Now finally determine the binary/RAM size usage per package by going // Now finally determine the binary/RAM size usage per package by going
// through each allocated section. // through each allocated section.
sizes := make(map[string]*packageSize) sizes := make(map[string]packageSize)
program := &programSize{
Packages: sizes,
}
for _, section := range sections { for _, section := range sections {
switch section.Type { switch section.Type {
case memoryCode: case memoryCode:
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 { readSection(section, addresses, func(path string, size uint64, isVariable bool) {
field := sizes[path]
if isVariable { if isVariable {
return &ps.ROData field.ROData += size
} else {
field.Code += size
} }
return &ps.Code sizes[path] = field
}, packagePathMap) }, packagePathMap)
case memoryROData: case memoryROData:
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 { readSection(section, addresses, func(path string, size uint64, isVariable bool) {
return &ps.ROData field := sizes[path]
field.ROData += size
sizes[path] = field
}, packagePathMap) }, packagePathMap)
case memoryData: case memoryData:
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 { readSection(section, addresses, func(path string, size uint64, isVariable bool) {
return &ps.Data field := sizes[path]
field.Data += size
sizes[path] = field
}, packagePathMap) }, packagePathMap)
case memoryBSS: case memoryBSS:
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 { readSection(section, addresses, func(path string, size uint64, isVariable bool) {
return &ps.BSS field := sizes[path]
field.BSS += size
sizes[path] = field
}, packagePathMap) }, packagePathMap)
case memoryStack: case memoryStack:
// We store the C stack as a pseudo-package. // We store the C stack as a pseudo-package.
program.getPackage("C stack").addSize(func(ps *packageSize, isVariable bool) *uint64 { sizes["C stack"] = packageSize{
return &ps.BSS BSS: section.Size,
}, "", section.Size, false) }
} }
} }
// ...and summarize the results. // ...and summarize the results.
program := &programSize{
Packages: sizes,
}
for _, pkg := range sizes { for _, pkg := range sizes {
program.Code += pkg.Code program.Code += pkg.Code
program.ROData += pkg.ROData program.ROData += pkg.ROData
@@ -870,8 +826,8 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
} }
// readSection determines for each byte in this section to which package it // readSection determines for each byte in this section to which package it
// belongs. // belongs. It reports this usage through the addSize callback.
func readSection(section memorySection, addresses []addressLine, program *programSize, getField func(*packageSize, bool) *uint64, packagePathMap map[string]string) { func readSection(section memorySection, addresses []addressLine, addSize func(string, uint64, bool), packagePathMap map[string]string) {
// The addr variable tracks at which address we are while going through this // The addr variable tracks at which address we are while going through this
// section. We start at the beginning. // section. We start at the beginning.
addr := section.Address addr := section.Address
@@ -893,9 +849,9 @@ func readSection(section memorySection, addresses []addressLine, program *progra
addrAligned := (addr + line.Align - 1) &^ (line.Align - 1) addrAligned := (addr + line.Align - 1) &^ (line.Align - 1)
if line.Align > 1 && addrAligned >= line.Address { if line.Align > 1 && addrAligned >= line.Address {
// It is, assume that's what causes the gap. // It is, assume that's what causes the gap.
program.getPackage("(padding)").addSize(getField, "", line.Address-addr, true) addSize("(padding)", line.Address-addr, true)
} else { } else {
program.getPackage("(unknown)").addSize(getField, "", line.Address-addr, false) addSize("(unknown)", line.Address-addr, false)
if sizesDebug { if sizesDebug {
fmt.Printf("%08x..%08x %5d: unknown (gap), alignment=%d\n", addr, line.Address, line.Address-addr, line.Align) fmt.Printf("%08x..%08x %5d: unknown (gap), alignment=%d\n", addr, line.Address, line.Address-addr, line.Align)
} }
@@ -917,8 +873,7 @@ func readSection(section memorySection, addresses []addressLine, program *progra
length = line.Length - (addr - line.Address) length = line.Length - (addr - line.Address)
} }
// Finally, mark this chunk of memory as used by the given package. // Finally, mark this chunk of memory as used by the given package.
packagePath, filename := findPackagePath(line.File, packagePathMap) addSize(findPackagePath(line.File, packagePathMap), length, line.IsVariable)
program.getPackage(packagePath).addSize(getField, filename, length, line.IsVariable)
addr = line.Address + line.Length addr = line.Address + line.Length
} }
if addr < sectionEnd { if addr < sectionEnd {
@@ -927,9 +882,9 @@ func readSection(section memorySection, addresses []addressLine, program *progra
if section.Align > 1 && addrAligned >= sectionEnd { if section.Align > 1 && addrAligned >= sectionEnd {
// The gap is caused by the section alignment. // The gap is caused by the section alignment.
// For example, if a .rodata section ends with a non-aligned string. // For example, if a .rodata section ends with a non-aligned string.
program.getPackage("(padding)").addSize(getField, "", sectionEnd-addr, true) addSize("(padding)", sectionEnd-addr, true)
} else { } else {
program.getPackage("(unknown)").addSize(getField, "", sectionEnd-addr, false) addSize("(unknown)", sectionEnd-addr, false)
if sizesDebug { if sizesDebug {
fmt.Printf("%08x..%08x %5d: unknown (end), alignment=%d\n", addr, sectionEnd, sectionEnd-addr, section.Align) fmt.Printf("%08x..%08x %5d: unknown (end), alignment=%d\n", addr, sectionEnd, sectionEnd-addr, section.Align)
} }
@@ -939,29 +894,17 @@ func readSection(section memorySection, addresses []addressLine, program *progra
// findPackagePath returns the Go package (or a pseudo package) for the given // findPackagePath returns the Go package (or a pseudo package) for the given
// path. It uses some heuristics, for example for some C libraries. // path. It uses some heuristics, for example for some C libraries.
func findPackagePath(path string, packagePathMap map[string]string) (packagePath, filename string) { func findPackagePath(path string, packagePathMap map[string]string) string {
// Check whether this path is part of one of the compiled packages. // Check whether this path is part of one of the compiled packages.
packagePath, ok := packagePathMap[filepath.Dir(path)] packagePath, ok := packagePathMap[filepath.Dir(path)]
if ok { if !ok {
// Directory is known as a Go package.
// Add the file itself as well.
filename = filepath.Base(path)
} else {
if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")) { if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")) {
// Emit C libraries (in the lib subdirectory of TinyGo) as a single // Emit C libraries (in the lib subdirectory of TinyGo) as a single
// package, with a "C" prefix. For example: "C picolibc" for the // package, with a "C" prefix. For example: "C compiler-rt" for the
// baremetal libc. // compiler runtime library from LLVM.
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator)) packagePath = "C " + strings.Split(strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")), string(os.PathSeparator))[1]
parts := strings.SplitN(libPath, string(os.PathSeparator), 2) } else if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project")) {
packagePath = "C " + parts[0]
if len(parts) > 1 {
filename = parts[1]
} else {
filename = parts[0]
}
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
packagePath = "C compiler-rt" packagePath = "C compiler-rt"
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
} else if packageSymbolRegexp.MatchString(path) { } else if packageSymbolRegexp.MatchString(path) {
// Parse symbol names like main$alloc or runtime$string. // Parse symbol names like main$alloc or runtime$string.
packagePath = path[:strings.LastIndex(path, "$")] packagePath = path[:strings.LastIndex(path, "$")]
@@ -984,11 +927,9 @@ func findPackagePath(path string, packagePathMap map[string]string) (packagePath
// fixed in the compiler. // fixed in the compiler.
packagePath = "-" packagePath = "-"
} else { } else {
// This is some other path. Not sure what it is, so just emit its // This is some other path. Not sure what it is, so just emit its directory.
// directory as a fallback. packagePath = filepath.Dir(path) // fallback
packagePath = filepath.Dir(path)
filename = filepath.Base(path)
} }
} }
return return packagePath
} }
+23 -71
View File
@@ -1,7 +1,6 @@
package builder package builder
import ( import (
"regexp"
"runtime" "runtime"
"testing" "testing"
"time" "time"
@@ -42,9 +41,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test. // This is a small number of very diverse targets that we want to test.
tests := []sizeTest{ tests := []sizeTest{
// microcontrollers // microcontrollers
{"hifive1b", "examples/echo", 3680, 280, 0, 2252}, {"hifive1b", "examples/echo", 4484, 280, 0, 2252},
{"microbit", "examples/serial", 2694, 342, 8, 2248}, {"microbit", "examples/serial", 2732, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248}, {"wioterminal", "examples/pininterrupt", 6016, 1484, 116, 6816},
// TODO: also check wasm. Right now this is difficult, because // TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the // wasm binaries are run through wasm-opt and therefore the
@@ -56,7 +55,26 @@ func TestBinarySize(t *testing.T) {
t.Parallel() t.Parallel()
// Build the binary. // Build the binary.
result := buildBinary(t, tc.target, tc.path) options := compileopts.Options{
Target: tc.target,
Opt: "z",
Semaphore: sema,
InterpTimeout: 60 * time.Second,
Debug: true,
VerifyIR: true,
}
target, err := compileopts.LoadTarget(&options)
if err != nil {
t.Fatal("could not load target:", err)
}
config := &compileopts.Config{
Options: &options,
Target: target,
}
result, err := Build(tc.path, "", t.TempDir(), config)
if err != nil {
t.Fatal("could not build:", err)
}
// Check whether the size of the binary matches the expected size. // Check whether the size of the binary matches the expected size.
sizes, err := loadProgramSize(result.Executable, nil) sizes, err := loadProgramSize(result.Executable, nil)
@@ -72,69 +90,3 @@ func TestBinarySize(t *testing.T) {
}) })
} }
} }
// Check that the -size=full flag attributes binary size to the correct package
// without filesystem paths and things like that.
func TestSizeFull(t *testing.T) {
tests := []string{
"microbit",
"wasip1",
}
libMatch := regexp.MustCompile(`^C [a-z -]+$`) // example: "C interrupt vector"
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
for _, target := range tests {
target := target
t.Run(target, func(t *testing.T) {
t.Parallel()
// Build the binary.
result := buildBinary(t, target, "examples/serial")
// Check whether the binary doesn't contain any unexpected package
// names.
sizes, err := loadProgramSize(result.Executable, result.PackagePathMap)
if err != nil {
t.Fatal("could not read program size:", err)
}
for _, pkg := range sizes.sortedPackageNames() {
if pkg == "(padding)" || pkg == "(unknown)" {
// TODO: correctly attribute all unknown binary size.
continue
}
if libMatch.MatchString(pkg) {
continue
}
if pkgMatch.MatchString(pkg) {
continue
}
t.Error("unexpected package name in size output:", pkg)
}
})
}
}
func buildBinary(t *testing.T, targetString, pkgName string) BuildResult {
options := compileopts.Options{
Target: targetString,
Opt: "z",
Semaphore: sema,
InterpTimeout: 60 * time.Second,
Debug: true,
VerifyIR: true,
}
target, err := compileopts.LoadTarget(&options)
if err != nil {
t.Fatal("could not load target:", err)
}
config := &compileopts.Config{
Options: &options,
Target: target,
}
result, err := Build(pkgName, "", t.TempDir(), config)
if err != nil {
t.Fatal("could not build:", err)
}
return result
}
+22 -169
View File
@@ -1,191 +1,44 @@
package builder package builder
import ( import (
"bytes"
"fmt"
"go/scanner"
"go/token"
"os" "os"
"os/exec" "os/exec"
"regexp"
"strconv" "github.com/tinygo-org/tinygo/goenv"
"strings"
) )
// runCCompiler invokes a C compiler with the given arguments. // runCCompiler invokes a C compiler with the given arguments.
func runCCompiler(flags ...string) error { func runCCompiler(flags ...string) error {
// Find the right command to run Clang.
var cmd *exec.Cmd
if hasBuiltinTools { if hasBuiltinTools {
// Compile this with the internal Clang compiler. // Compile this with the internal Clang compiler.
cmd = exec.Command(os.Args[0], append([]string{"clang"}, flags...)...) cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
} else { cmd.Stdout = os.Stdout
// Compile this with an external invocation of the Clang compiler. cmd.Stderr = os.Stderr
name, err := LookupCommand("clang") return cmd.Run()
if err != nil {
return err
}
cmd = exec.Command(name, flags...)
} }
cmd.Stdout = os.Stdout // Compile this with an external invocation of the Clang compiler.
cmd.Stderr = os.Stderr return execCommand("clang", flags...)
// Make sure the command doesn't use any environmental variables.
// Most importantly, it should not use C_INCLUDE_PATH and the like.
cmd.Env = []string{}
// Let some environment variables through. One important one is the
// temporary directory, especially on Windows it looks like Clang breaks if
// the temporary directory has not been set.
// See: https://github.com/tinygo-org/tinygo/issues/4557
// Also see: https://github.com/llvm/llvm-project/blob/release/18.x/llvm/lib/Support/Unix/Path.inc#L1435
for _, env := range os.Environ() {
// We could parse the key and look it up in a map, but since there are
// only a few keys iterating through them is easier and maybe even
// faster.
for _, prefix := range []string{"TMPDIR=", "TMP=", "TEMP=", "TEMPDIR="} {
if strings.HasPrefix(env, prefix) {
cmd.Env = append(cmd.Env, env)
break
}
}
}
return cmd.Run()
} }
// link invokes a linker with the given name and flags. // link invokes a linker with the given name and flags.
func link(linker string, flags ...string) error { func link(linker string, flags ...string) error {
// We only support LLD. if hasBuiltinTools && (linker == "ld.lld" || linker == "wasm-ld") {
if linker != "ld.lld" && linker != "wasm-ld" { // Run command with internal linker.
return fmt.Errorf("unexpected: linker %s should be ld.lld or wasm-ld", linker) cmd := exec.Command(os.Args[0], append([]string{linker}, flags...)...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
} }
var cmd *exec.Cmd // Fall back to external command.
if hasBuiltinTools { if _, ok := commands[linker]; ok {
cmd = exec.Command(os.Args[0], append([]string{linker}, flags...)...) return execCommand(linker, flags...)
} else {
name, err := LookupCommand(linker)
if err != nil {
return err
}
cmd = exec.Command(name, flags...)
} }
var buf bytes.Buffer
cmd := exec.Command(linker, flags...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = &buf cmd.Stderr = os.Stderr
err := cmd.Run() cmd.Dir = goenv.Get("TINYGOROOT")
if err != nil { return cmd.Run()
if buf.Len() == 0 {
// The linker failed but there was no output.
// Therefore, show some output anyway.
return fmt.Errorf("failed to run linker: %w", err)
}
return parseLLDErrors(buf.String())
}
return nil
}
// Split LLD errors into individual erros (including errors that continue on the
// next line, using a ">>>" prefix). If possible, replace the raw errors with a
// more user-friendly version (and one that's more in a Go style).
func parseLLDErrors(text string) error {
// Split linker output in separate error messages.
lines := strings.Split(text, "\n")
var errorLines []string // one or more line (belonging to a single error) per line
for _, line := range lines {
line = strings.TrimRight(line, "\r") // needed for Windows
if len(errorLines) != 0 && strings.HasPrefix(line, ">>> ") {
errorLines[len(errorLines)-1] += "\n" + line
continue
}
if line == "" {
continue
}
errorLines = append(errorLines, line)
}
// Parse error messages.
var linkErrors []error
var flashOverflow, ramOverflow uint64
for _, message := range errorLines {
parsedError := false
// Check for undefined symbols.
// This can happen in some cases like with CGo and //go:linkname tricker.
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
symbolName := matches[2]
for _, line := range strings.Split(message, "\n") {
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
if matches != nil {
parsedError = true
line, _ := strconv.Atoi(matches[3])
// TODO: detect common mistakes like -gc=none?
linkErrors = append(linkErrors, scanner.Error{
Pos: token.Position{
Filename: matches[2],
Line: line,
},
Msg: "linker could not find symbol " + symbolName,
})
}
}
}
// Check for flash/RAM overflow.
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: section '(.*?)' will not fit in region '(.*?)': overflowed by ([0-9]+) bytes$`).FindStringSubmatch(message); matches != nil {
region := matches[3]
n, err := strconv.ParseUint(matches[4], 10, 64)
if err != nil {
// Should not happen at all (unless it overflows an uint64 for some reason).
continue
}
// Check which area overflowed.
// Some chips use differently named memory areas, but these are by
// far the most common.
switch region {
case "FLASH_TEXT":
if n > flashOverflow {
flashOverflow = n
}
parsedError = true
case "RAM":
if n > ramOverflow {
ramOverflow = n
}
parsedError = true
}
}
// If we couldn't parse the linker error: show the error as-is to
// the user.
if !parsedError {
linkErrors = append(linkErrors, LinkerError{message})
}
}
if flashOverflow > 0 {
linkErrors = append(linkErrors, LinkerError{
Msg: fmt.Sprintf("program too large for this chip (flash overflowed by %d bytes)\n\toptimization guide: https://tinygo.org/docs/guides/optimizing-binaries/", flashOverflow),
})
}
if ramOverflow > 0 {
linkErrors = append(linkErrors, LinkerError{
Msg: fmt.Sprintf("program uses too much static RAM on this chip (RAM overflowed by %d bytes)", ramOverflow),
})
}
return newMultiError(linkErrors, "")
}
// LLD linker error that could not be parsed or doesn't refer to a source
// location.
type LinkerError struct {
Msg string
}
func (e LinkerError) Error() string {
return e.Msg
} }
-284
View File
@@ -1,284 +0,0 @@
package builder
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/goenv"
)
var libWasiLibc = Library{
name: "wasi-libc",
makeHeaders: func(target, includeDir string) error {
bits := filepath.Join(includeDir, "bits")
err := os.Mkdir(bits, 0777)
if err != nil {
return err
}
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "wasi-libc/libc-top-half/musl")
err = buildMuslAllTypes("wasm32", muslDir, bits)
if err != nil {
return err
}
// See MUSL_OMIT_HEADERS in the Makefile.
omitHeaders := map[string]struct{}{
"syslog.h": {},
"wait.h": {},
"ucontext.h": {},
"paths.h": {},
"utmp.h": {},
"utmpx.h": {},
"lastlog.h": {},
"elf.h": {},
"link.h": {},
"pwd.h": {},
"shadow.h": {},
"grp.h": {},
"mntent.h": {},
"netdb.h": {},
"resolv.h": {},
"pty.h": {},
"dlfcn.h": {},
"setjmp.h": {},
"ulimit.h": {},
"wordexp.h": {},
"spawn.h": {},
"termios.h": {},
"libintl.h": {},
"aio.h": {},
"stdarg.h": {},
"stddef.h": {},
"pthread.h": {},
}
for _, glob := range [][2]string{
{"libc-bottom-half/headers/public/*.h", ""},
{"libc-bottom-half/headers/public/wasi/*.h", "wasi"},
{"libc-top-half/musl/arch/wasm32/bits/*.h", "bits"},
{"libc-top-half/musl/include/*.h", ""},
{"libc-top-half/musl/include/netinet/*.h", "netinet"},
{"libc-top-half/musl/include/sys/*.h", "sys"},
} {
matches, _ := filepath.Glob(filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc", glob[0]))
outDir := filepath.Join(includeDir, glob[1])
os.MkdirAll(outDir, 0o777)
for _, match := range matches {
name := filepath.Base(match)
if _, ok := omitHeaders[name]; ok {
continue
}
data, err := os.ReadFile(match)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(outDir, name), data, 0o666)
if err != nil {
return err
}
}
}
return nil
},
cflags: func(target, headerPath string) []string {
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
return []string{
"-Werror",
"-Wall",
"-std=gnu11",
"-nostdlibinc",
"-mnontrapping-fptoint", "-msign-ext", "-mbulk-memory",
"-Wno-null-pointer-arithmetic", "-Wno-unused-parameter", "-Wno-sign-compare", "-Wno-unused-variable", "-Wno-unused-function", "-Wno-ignored-attributes", "-Wno-missing-braces", "-Wno-ignored-pragmas", "-Wno-unused-but-set-variable", "-Wno-unknown-warning-option",
"-Wno-parentheses", "-Wno-shift-op-parentheses", "-Wno-bitwise-op-parentheses", "-Wno-logical-op-parentheses", "-Wno-string-plus-int", "-Wno-dangling-else", "-Wno-unknown-pragmas",
"-DNDEBUG",
"-D__wasilibc_printscan_no_long_double",
"-D__wasilibc_printscan_full_support_option=\"long double support is disabled\"",
"-DBULK_MEMORY_THRESHOLD=32", // default threshold in wasi-libc
"-isystem", headerPath,
"-I" + libcDir + "/libc-top-half/musl/src/include",
"-I" + libcDir + "/libc-top-half/musl/src/internal",
"-I" + libcDir + "/libc-top-half/musl/arch/wasm32",
"-I" + libcDir + "/libc-top-half/musl/arch/generic",
"-I" + libcDir + "/libc-top-half/headers/private",
}
},
cflagsForFile: func(path string) []string {
if strings.HasPrefix(path, "libc-bottom-half"+string(os.PathSeparator)) {
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
return []string{
"-I" + libcDir + "/libc-bottom-half/headers/private",
"-I" + libcDir + "/libc-bottom-half/cloudlibc/src/include",
"-I" + libcDir + "/libc-bottom-half/cloudlibc/src",
}
}
return nil
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
librarySources: func(target string, libcNeedsMalloc bool) ([]string, error) {
type filePattern struct {
glob string
exclude []string
}
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
globs := []filePattern{
// Top half: mostly musl sources.
{glob: "libc-top-half/sources/*.c"},
{glob: "libc-top-half/musl/src/conf/*.c"},
{glob: "libc-top-half/musl/src/internal/*.c", exclude: []string{
"procfdname.c", "syscall.c", "syscall_ret.c", "vdso.c", "version.c",
}},
{glob: "libc-top-half/musl/src/locale/*.c", exclude: []string{
"dcngettext.c", "textdomain.c", "bind_textdomain_codeset.c"}},
{glob: "libc-top-half/musl/src/math/*.c", exclude: []string{
"__signbit.c", "__signbitf.c", "__signbitl.c",
"__fpclassify.c", "__fpclassifyf.c", "__fpclassifyl.c",
"ceilf.c", "ceil.c",
"floorf.c", "floor.c",
"truncf.c", "trunc.c",
"rintf.c", "rint.c",
"nearbyintf.c", "nearbyint.c",
"sqrtf.c", "sqrt.c",
"fabsf.c", "fabs.c",
"copysignf.c", "copysign.c",
"fminf.c", "fmaxf.c",
"fmin.c", "fmax.c,",
}},
{glob: "libc-top-half/musl/src/multibyte/*.c"},
{glob: "libc-top-half/musl/src/stdio/*.c", exclude: []string{
"vfwscanf.c", "vfwprintf.c", // long double is unsupported
"__lockfile.c", "flockfile.c", "funlockfile.c", "ftrylockfile.c",
"rename.c",
"tmpnam.c", "tmpfile.c", "tempnam.c",
"popen.c", "pclose.c",
"remove.c",
"gets.c"}},
{glob: "libc-top-half/musl/src/stdlib/*.c"},
{glob: "libc-top-half/musl/src/string/*.c", exclude: []string{
"strsignal.c"}},
// Bottom half: connect top half to WASI equivalents.
{glob: "libc-bottom-half/cloudlibc/src/libc/*/*.c"},
{glob: "libc-bottom-half/cloudlibc/src/libc/sys/*/*.c"},
{glob: "libc-bottom-half/sources/*.c"},
}
// We're using the Boehm GC, so we need a heap implementation in the libc.
if libcNeedsMalloc {
globs = append(globs, filePattern{glob: "dlmalloc/src/dlmalloc.c"})
}
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
sources := []string{
"libc-top-half/musl/src/misc/a64l.c",
"libc-top-half/musl/src/misc/basename.c",
"libc-top-half/musl/src/misc/dirname.c",
"libc-top-half/musl/src/misc/ffs.c",
"libc-top-half/musl/src/misc/ffsl.c",
"libc-top-half/musl/src/misc/ffsll.c",
"libc-top-half/musl/src/misc/fmtmsg.c",
"libc-top-half/musl/src/misc/getdomainname.c",
"libc-top-half/musl/src/misc/gethostid.c",
"libc-top-half/musl/src/misc/getopt.c",
"libc-top-half/musl/src/misc/getopt_long.c",
"libc-top-half/musl/src/misc/getsubopt.c",
"libc-top-half/musl/src/misc/uname.c",
"libc-top-half/musl/src/misc/nftw.c",
"libc-top-half/musl/src/errno/strerror.c",
"libc-top-half/musl/src/network/htonl.c",
"libc-top-half/musl/src/network/htons.c",
"libc-top-half/musl/src/network/ntohl.c",
"libc-top-half/musl/src/network/ntohs.c",
"libc-top-half/musl/src/network/inet_ntop.c",
"libc-top-half/musl/src/network/inet_pton.c",
"libc-top-half/musl/src/network/inet_aton.c",
"libc-top-half/musl/src/network/in6addr_any.c",
"libc-top-half/musl/src/network/in6addr_loopback.c",
"libc-top-half/musl/src/fenv/fenv.c",
"libc-top-half/musl/src/fenv/fesetround.c",
"libc-top-half/musl/src/fenv/feupdateenv.c",
"libc-top-half/musl/src/fenv/fesetexceptflag.c",
"libc-top-half/musl/src/fenv/fegetexceptflag.c",
"libc-top-half/musl/src/fenv/feholdexcept.c",
"libc-top-half/musl/src/exit/exit.c",
"libc-top-half/musl/src/exit/atexit.c",
"libc-top-half/musl/src/exit/assert.c",
"libc-top-half/musl/src/exit/quick_exit.c",
"libc-top-half/musl/src/exit/at_quick_exit.c",
"libc-top-half/musl/src/time/strftime.c",
"libc-top-half/musl/src/time/asctime.c",
"libc-top-half/musl/src/time/asctime_r.c",
"libc-top-half/musl/src/time/ctime.c",
"libc-top-half/musl/src/time/ctime_r.c",
"libc-top-half/musl/src/time/wcsftime.c",
"libc-top-half/musl/src/time/strptime.c",
"libc-top-half/musl/src/time/difftime.c",
"libc-top-half/musl/src/time/timegm.c",
"libc-top-half/musl/src/time/ftime.c",
"libc-top-half/musl/src/time/gmtime.c",
"libc-top-half/musl/src/time/gmtime_r.c",
"libc-top-half/musl/src/time/timespec_get.c",
"libc-top-half/musl/src/time/getdate.c",
"libc-top-half/musl/src/time/localtime.c",
"libc-top-half/musl/src/time/localtime_r.c",
"libc-top-half/musl/src/time/mktime.c",
"libc-top-half/musl/src/time/__tm_to_secs.c",
"libc-top-half/musl/src/time/__month_to_secs.c",
"libc-top-half/musl/src/time/__secs_to_tm.c",
"libc-top-half/musl/src/time/__year_to_secs.c",
"libc-top-half/musl/src/time/__tz.c",
"libc-top-half/musl/src/fcntl/creat.c",
"libc-top-half/musl/src/dirent/alphasort.c",
"libc-top-half/musl/src/dirent/versionsort.c",
"libc-top-half/musl/src/env/__stack_chk_fail.c",
"libc-top-half/musl/src/env/clearenv.c",
"libc-top-half/musl/src/env/getenv.c",
"libc-top-half/musl/src/env/putenv.c",
"libc-top-half/musl/src/env/setenv.c",
"libc-top-half/musl/src/env/unsetenv.c",
"libc-top-half/musl/src/unistd/posix_close.c",
"libc-top-half/musl/src/stat/futimesat.c",
"libc-top-half/musl/src/legacy/getpagesize.c",
"libc-top-half/musl/src/thread/thrd_sleep.c",
}
basepath := goenv.Get("TINYGOROOT") + "/lib/wasi-libc/"
for _, pattern := range globs {
matches, err := filepath.Glob(basepath + pattern.glob)
if err != nil {
// From the documentation:
// > Glob ignores file system errors such as I/O errors reading
// > directories. The only possible returned error is
// > ErrBadPattern, when pattern is malformed.
// So the only possible error is when the (statically defined)
// pattern is wrong. In other words, a programming bug.
return nil, fmt.Errorf("wasi-libc: could not glob source dirs: %w", err)
}
if len(matches) == 0 {
return nil, fmt.Errorf("wasi-libc: did not find any files for pattern %#v", pattern)
}
excludeSet := map[string]struct{}{}
for _, exclude := range pattern.exclude {
excludeSet[exclude] = struct{}{}
}
for _, match := range matches {
if _, ok := excludeSet[filepath.Base(match)]; ok {
continue
}
relpath, err := filepath.Rel(basepath, match)
if err != nil {
// Not sure if this is even possible.
return nil, err
}
sources = append(sources, relpath)
}
}
return sources, nil
},
}
+2 -4
View File
@@ -7,7 +7,7 @@ import (
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
var libWasmBuiltins = Library{ var WasmBuiltins = Library{
name: "wasmbuiltins", name: "wasmbuiltins",
makeHeaders: func(target, includeDir string) error { makeHeaders: func(target, includeDir string) error {
if err := os.Mkdir(includeDir+"/bits", 0o777); err != nil { if err := os.Mkdir(includeDir+"/bits", 0o777); err != nil {
@@ -29,8 +29,6 @@ var libWasmBuiltins = Library{
"-Wall", "-Wall",
"-std=gnu11", "-std=gnu11",
"-nostdlibinc", "-nostdlibinc",
"-mnontrapping-fptoint", // match wasm-unknown (default on in LLVM 20)
"-mno-bulk-memory", // same here
"-isystem", libcDir + "/libc-top-half/musl/arch/wasm32", "-isystem", libcDir + "/libc-top-half/musl/arch/wasm32",
"-isystem", libcDir + "/libc-top-half/musl/arch/generic", "-isystem", libcDir + "/libc-top-half/musl/arch/generic",
"-isystem", libcDir + "/libc-top-half/musl/src/internal", "-isystem", libcDir + "/libc-top-half/musl/src/internal",
@@ -41,7 +39,7 @@ var libWasmBuiltins = Library{
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
librarySources: func(target string, _ bool) ([]string, error) { librarySources: func(target string) ([]string, error) {
return []string{ return []string{
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and // memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
// llvm.memset.* LLVM intrinsics. // llvm.memset.* LLVM intrinsics.
+55 -209
View File
@@ -18,7 +18,6 @@ import (
"go/scanner" "go/scanner"
"go/token" "go/token"
"path/filepath" "path/filepath"
"sort"
"strconv" "strconv"
"strings" "strings"
@@ -43,7 +42,6 @@ type cgoPackage struct {
fset *token.FileSet fset *token.FileSet
tokenFiles map[string]*token.File tokenFiles map[string]*token.File
definedGlobally map[string]ast.Node definedGlobally map[string]ast.Node
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
anonDecls map[interface{}]string anonDecls map[interface{}]string
cflags []string // CFlags from #cgo lines cflags []string // CFlags from #cgo lines
ldflags []string // LDFlags from #cgo lines ldflags []string // LDFlags from #cgo lines
@@ -82,28 +80,21 @@ type bitfieldInfo struct {
endBit int64 // may be 0 meaning "until the end of the field" endBit int64 // may be 0 meaning "until the end of the field"
} }
// Information about a #cgo noescape line in the source code.
type noescapingFunc struct {
name string
pos token.Pos
used bool // true if used somewhere in the source (for proper error reporting)
}
// cgoAliases list type aliases between Go and C, for types that are equivalent // cgoAliases list type aliases between Go and C, for types that are equivalent
// in both languages. See addTypeAliases. // in both languages. See addTypeAliases.
var cgoAliases = map[string]string{ var cgoAliases = map[string]string{
"_Cgo_int8_t": "int8", "C.int8_t": "int8",
"_Cgo_int16_t": "int16", "C.int16_t": "int16",
"_Cgo_int32_t": "int32", "C.int32_t": "int32",
"_Cgo_int64_t": "int64", "C.int64_t": "int64",
"_Cgo_uint8_t": "uint8", "C.uint8_t": "uint8",
"_Cgo_uint16_t": "uint16", "C.uint16_t": "uint16",
"_Cgo_uint32_t": "uint32", "C.uint32_t": "uint32",
"_Cgo_uint64_t": "uint64", "C.uint64_t": "uint64",
"_Cgo_uintptr_t": "uintptr", "C.uintptr_t": "uintptr",
"_Cgo_float": "float32", "C.float": "float32",
"_Cgo_double": "float64", "C.double": "float64",
"_Cgo__Bool": "bool", "C._Bool": "bool",
} }
// builtinAliases are handled specially because they only exist on the Go side // builtinAliases are handled specially because they only exist on the Go side
@@ -145,105 +136,31 @@ typedef unsigned long long _Cgo_ulonglong;
// The string/bytes functions below implement C.CString etc. To make sure the // The string/bytes functions below implement C.CString etc. To make sure the
// runtime doesn't need to know the C int type, lengths are converted to uintptr // runtime doesn't need to know the C int type, lengths are converted to uintptr
// first. // first.
const generatedGoFilePrefixBase = ` // These functions will be modified to get a "C." prefix, so the source below
import "syscall" // doesn't reflect the final AST.
const generatedGoFilePrefix = `
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func __GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func __GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
`
const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
}
`
// Windows uses fake errno values in the syscall package.
// See for example: https://github.com/golang/go/issues/23468
// TinyGo uses mingw-w64 though, which does have defined errno values. Since the
// syscall package is the standard library one we can't change it, but we can
// map the errno values to match the values in the syscall package.
// Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
var _Cgo___errno_mapping = [...]syscall.Errno{
1: syscall.EPERM,
2: syscall.ENOENT,
3: syscall.ESRCH,
4: syscall.EINTR,
5: syscall.EIO,
6: syscall.ENXIO,
7: syscall.E2BIG,
8: syscall.ENOEXEC,
9: syscall.EBADF,
10: syscall.ECHILD,
11: syscall.EAGAIN,
12: syscall.ENOMEM,
13: syscall.EACCES,
14: syscall.EFAULT,
16: syscall.EBUSY,
17: syscall.EEXIST,
18: syscall.EXDEV,
19: syscall.ENODEV,
20: syscall.ENOTDIR,
21: syscall.EISDIR,
22: syscall.EINVAL,
23: syscall.ENFILE,
24: syscall.EMFILE,
25: syscall.ENOTTY,
27: syscall.EFBIG,
28: syscall.ENOSPC,
29: syscall.ESPIPE,
30: syscall.EROFS,
31: syscall.EMLINK,
32: syscall.EPIPE,
33: syscall.EDOM,
34: syscall.ERANGE,
36: syscall.EDEADLK,
38: syscall.ENAMETOOLONG,
39: syscall.ENOLCK,
40: syscall.ENOSYS,
41: syscall.ENOTEMPTY,
42: syscall.EILSEQ,
}
func _Cgo___get_errno() error {
num := _Cgo___get_errno_num()
if num < uintptr(len(_Cgo___errno_mapping)) {
if mapped := _Cgo___errno_mapping[num]; mapped != 0 {
return mapped
}
}
return syscall.Errno(num)
} }
` `
@@ -254,7 +171,7 @@ func _Cgo___get_errno() error {
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file // functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
// hashes of the accessed C header files. If there is one or more error, it // hashes of the accessed C header files. If there is one or more error, it
// returns these in the []error slice but still modifies the AST. // returns these in the []error slice but still modifies the AST.
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, goos string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) { func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
p := &cgoPackage{ p := &cgoPackage{
packageName: files[0].Name.Name, packageName: files[0].Name.Name,
currentDir: dir, currentDir: dir,
@@ -262,7 +179,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
fset: fset, fset: fset,
tokenFiles: map[string]*token.File{}, tokenFiles: map[string]*token.File{},
definedGlobally: map[string]ast.Node{}, definedGlobally: map[string]ast.Node{},
noescapingFuncs: map[string]*noescapingFunc{},
anonDecls: map[interface{}]string{}, anonDecls: map[interface{}]string{},
visitedFiles: map[string][]byte{}, visitedFiles: map[string][]byte{},
} }
@@ -287,12 +203,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// Construct a new in-memory AST for CGo declarations of this package. // Construct a new in-memory AST for CGo declarations of this package.
// The first part is written as Go code that is then parsed, but more code // The first part is written as Go code that is then parsed, but more code
// is added later to the AST to declare functions, globals, etc. // is added later to the AST to declare functions, globals, etc.
goCode := "package " + files[0].Name.Name + "\n\n" goCode := "package " + files[0].Name.Name + "\n\n" + generatedGoFilePrefix
if goos == "windows" {
goCode += generatedGoFilePrefixWindows
} else {
goCode += generatedGoFilePrefixOther
}
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments) p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
if err != nil { if err != nil {
// This is always a bug in the cgo package. // This is always a bug in the cgo package.
@@ -302,6 +213,23 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// If the Comments field is not set to nil, the go/format package will get // If the Comments field is not set to nil, the go/format package will get
// confused about where comments should go. // confused about where comments should go.
p.generated.Comments = nil p.generated.Comments = nil
// Adjust some of the functions in there.
for _, decl := range p.generated.Decls {
switch decl := decl.(type) {
case *ast.FuncDecl:
switch decl.Name.Name {
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes":
// Adjust the name to have a "C." prefix so it is correctly
// resolved.
decl.Name.Name = "C." + decl.Name.Name
}
}
}
// Patch some types, for example *C.char in C.CString.
cf := p.newCGoFile(nil, -1) // dummy *cgoFile for the walker
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
return cf.walker(cursor, nil)
}, nil)
// Find `import "C"` C fragments in the file. // Find `import "C"` C fragments in the file.
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
@@ -380,7 +308,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
Tok: token.TYPE, Tok: token.TYPE,
} }
for _, name := range builtinAliases { for _, name := range builtinAliases {
typeSpec := p.getIntegerType("_Cgo_"+name, names["_Cgo_"+name]) typeSpec := p.getIntegerType("C."+name, names["_Cgo_"+name])
gen.Specs = append(gen.Specs, typeSpec) gen.Specs = append(gen.Specs, typeSpec)
} }
p.generated.Decls = append(p.generated.Decls, gen) p.generated.Decls = append(p.generated.Decls, gen)
@@ -409,22 +337,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
}) })
} }
// Show an error when a #cgo noescape line isn't used in practice.
// This matches upstream Go. I think the goal is to avoid issues with
// misspelled function names, which seems very useful.
var unusedNoescapeLines []*noescapingFunc
for _, value := range p.noescapingFuncs {
if !value.used {
unusedNoescapeLines = append(unusedNoescapeLines, value)
}
}
sort.SliceStable(unusedNoescapeLines, func(i, j int) bool {
return unusedNoescapeLines[i].pos < unusedNoescapeLines[j].pos
})
for _, value := range unusedNoescapeLines {
p.addError(value.pos, fmt.Sprintf("function %#v in #cgo noescape line is not used", value.name))
}
// Print the newly generated in-memory AST, for debugging. // Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated) //ast.Print(fset, p.generated)
@@ -490,33 +402,6 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
} }
text = text[:lineStart] + string(spaces) + text[lineEnd:] text = text[:lineStart] + string(spaces) + text[lineEnd:]
allFields := strings.Fields(line[4:])
switch allFields[0] {
case "noescape":
// The code indicates that pointer parameters will not be captured
// by the called C function.
if len(allFields) < 2 {
p.addErrorAfter(pos, text[:lineStart], "missing function name in #cgo noescape line")
continue
}
if len(allFields) > 2 {
p.addErrorAfter(pos, text[:lineStart], "multiple function names in #cgo noescape line")
continue
}
name := allFields[1]
p.noescapingFuncs[name] = &noescapingFunc{
name: name,
pos: pos,
used: false,
}
continue
case "nocallback":
// We don't do anything special when calling a C function, so there
// appears to be no optimization that we can do here.
// Accept, but ignore the parameter for compatibility.
continue
}
// Get the text before the colon in the #cgo directive. // Get the text before the colon in the #cgo directive.
colon := strings.IndexByte(line, ':') colon := strings.IndexByte(line, ':')
if colon < 0 { if colon < 0 {
@@ -1253,22 +1138,22 @@ func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string { func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
// Some types are defined in stdint.h and map directly to a particular Go // Some types are defined in stdint.h and map directly to a particular Go
// type. // type.
if alias := cgoAliases["_Cgo_"+name]; alias != "" { if alias := cgoAliases["C."+name]; alias != "" {
return alias return alias
} }
node := f.getASTDeclNode(name, found) node := f.getASTDeclNode(name, found, iscall)
if node, ok := node.(*ast.FuncDecl); ok { if node, ok := node.(*ast.FuncDecl); ok {
if !iscall { if !iscall {
return node.Name.Name + "$funcaddr" return node.Name.Name + "$funcaddr"
} }
return node.Name.Name return node.Name.Name
} }
return "_Cgo_" + name return "C." + name
} }
// getASTDeclNode will declare the given C AST node (if not already defined) and // getASTDeclNode will declare the given C AST node (if not already defined) and
// returns it. // returns it.
func (f *cgoFile) getASTDeclNode(name string, found clangCursor) ast.Node { func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) ast.Node {
if node, ok := f.defined[name]; ok { if node, ok := f.defined[name]; ok {
// Declaration was found in the current file, so return it immediately. // Declaration was found in the current file, so return it immediately.
return node return node
@@ -1363,8 +1248,8 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
case *elaboratedTypeInfo: case *elaboratedTypeInfo:
// Add struct bitfields. // Add struct bitfields.
for _, bitfield := range elaboratedType.bitfields { for _, bitfield := range elaboratedType.bitfields {
f.createBitfieldGetter(bitfield, "_Cgo_"+name) f.createBitfieldGetter(bitfield, "C."+name)
f.createBitfieldSetter(bitfield, "_Cgo_"+name) f.createBitfieldSetter(bitfield, "C."+name)
} }
if elaboratedType.unionSize != 0 { if elaboratedType.unionSize != 0 {
// Create union getters/setters. // Create union getters/setters.
@@ -1373,7 +1258,7 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names))) f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
continue continue
} }
f.createUnionAccessor(field, "_Cgo_"+name) f.createUnionAccessor(field, "C."+name)
} }
} }
} }
@@ -1387,45 +1272,6 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
// separate namespace (no _Cgo_ hacks like in gc). // separate namespace (no _Cgo_ hacks like in gc).
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool { func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
switch node := cursor.Node().(type) { switch node := cursor.Node().(type) {
case *ast.AssignStmt:
// An assign statement could be something like this:
//
// val, errno := C.some_func()
//
// Check whether it looks like that, and if so, read the errno value and
// return it as the second return value. The call will be transformed
// into something like this:
//
// val, errno := C.some_func(), C.__get_errno()
if len(node.Lhs) != 2 || len(node.Rhs) != 1 {
return true
}
rhs, ok := node.Rhs[0].(*ast.CallExpr)
if !ok {
return true
}
fun, ok := rhs.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
x, ok := fun.X.(*ast.Ident)
if !ok {
return true
}
if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
// Replace "C"."some_func" into "C.somefunc".
rhs.Fun = &ast.Ident{
NamePos: x.NamePos,
Name: f.getASTDeclName(fun.Sel.Name, found, true),
}
// Add the errno value as the second value in the statement.
node.Rhs = append(node.Rhs, &ast.CallExpr{
Fun: &ast.Ident{
NamePos: node.Lhs[1].End(),
Name: "_Cgo___get_errno",
},
})
}
case *ast.CallExpr: case *ast.CallExpr:
fun, ok := node.Fun.(*ast.SelectorExpr) fun, ok := node.Fun.(*ast.SelectorExpr)
if !ok { if !ok {
@@ -1447,7 +1293,7 @@ func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) b
return true return true
} }
if x.Name == "C" { if x.Name == "C" {
name := "_Cgo_" + node.Sel.Name name := "C." + node.Sel.Name
if found, ok := names[node.Sel.Name]; ok { if found, ok := names[node.Sel.Name]; ok {
name = f.getASTDeclName(node.Sel.Name, found, false) name = f.getASTDeclName(node.Sel.Name, found, false)
} }
+5 -31
View File
@@ -7,7 +7,6 @@ import (
"go/ast" "go/ast"
"go/format" "go/format"
"go/parser" "go/parser"
"go/scanner"
"go/token" "go/token"
"go/types" "go/types"
"os" "os"
@@ -56,7 +55,7 @@ func TestCGo(t *testing.T) {
} }
// Process the AST with CGo. // Process the AST with CGo.
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "linux") cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
// Check the AST for type errors. // Check the AST for type errors.
var typecheckErrors []error var typecheckErrors []error
@@ -64,7 +63,7 @@ func TestCGo(t *testing.T) {
Error: func(err error) { Error: func(err error) {
typecheckErrors = append(typecheckErrors, err) typecheckErrors = append(typecheckErrors, err)
}, },
Importer: newSimpleImporter(), Importer: simpleImporter{},
Sizes: types.SizesFor("gccgo", "arm"), Sizes: types.SizesFor("gccgo", "arm"),
} }
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil) _, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
@@ -202,33 +201,14 @@ func Test_cgoPackage_isEquivalentAST(t *testing.T) {
} }
// simpleImporter implements the types.Importer interface, but only allows // simpleImporter implements the types.Importer interface, but only allows
// importing the syscall and unsafe packages. // importing the unsafe package.
type simpleImporter struct { type simpleImporter struct {
syscallPkg *types.Package
}
func newSimpleImporter() *simpleImporter {
i := &simpleImporter{}
// Implement a dummy syscall package with the Errno type.
i.syscallPkg = types.NewPackage("syscall", "syscall")
obj := types.NewTypeName(token.NoPos, i.syscallPkg, "Errno", nil)
named := types.NewNamed(obj, nil, nil)
i.syscallPkg.Scope().Insert(obj)
named.SetUnderlying(types.Typ[types.Uintptr])
sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(types.NewParam(token.NoPos, i.syscallPkg, "", types.Typ[types.String])), false)
named.AddMethod(types.NewFunc(token.NoPos, i.syscallPkg, "Error", sig))
i.syscallPkg.MarkComplete()
return i
} }
// Import implements the Importer interface. For testing usage only: it only // Import implements the Importer interface. For testing usage only: it only
// supports importing the unsafe package. // supports importing the unsafe package.
func (i *simpleImporter) Import(path string) (*types.Package, error) { func (i simpleImporter) Import(path string) (*types.Package, error) {
switch path { switch path {
case "syscall":
return i.syscallPkg, nil
case "unsafe": case "unsafe":
return types.Unsafe, nil return types.Unsafe, nil
default: default:
@@ -239,13 +219,7 @@ func (i *simpleImporter) Import(path string) (*types.Package, error) {
// formatDiagnostic formats the error message to be an indented comment. It // formatDiagnostic formats the error message to be an indented comment. It
// also fixes Windows path name issues (backward slashes). // also fixes Windows path name issues (backward slashes).
func formatDiagnostic(err error) string { func formatDiagnostic(err error) string {
var msg string msg := err.Error()
switch err := err.(type) {
case scanner.Error:
msg = err.Pos.String() + ": " + err.Msg
default:
msg = err.Error()
}
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
// Fix Windows path slashes. // Fix Windows path slashes.
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/") msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
+8 -154
View File
@@ -17,8 +17,6 @@ var (
token.OR: precedenceOr, token.OR: precedenceOr,
token.XOR: precedenceXor, token.XOR: precedenceXor,
token.AND: precedenceAnd, token.AND: precedenceAnd,
token.SHL: precedenceShift,
token.SHR: precedenceShift,
token.ADD: precedenceAdd, token.ADD: precedenceAdd,
token.SUB: precedenceAdd, token.SUB: precedenceAdd,
token.MUL: precedenceMul, token.MUL: precedenceMul,
@@ -27,13 +25,11 @@ var (
} }
) )
// See: https://en.cppreference.com/w/c/language/operator_precedence
const ( const (
precedenceLowest = iota + 1 precedenceLowest = iota + 1
precedenceOr precedenceOr
precedenceXor precedenceXor
precedenceAnd precedenceAnd
precedenceShift
precedenceAdd precedenceAdd
precedenceMul precedenceMul
precedencePrefix precedencePrefix
@@ -54,72 +50,8 @@ func init() {
} }
// parseConst parses the given string as a C constant. // parseConst parses the given string as a C constant.
func parseConst(pos token.Pos, fset *token.FileSet, value string, params []ast.Expr, callerPos token.Pos, f *cgoFile) (ast.Expr, *scanner.Error) { func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) {
t := newTokenizer(pos, fset, value, f) t := newTokenizer(pos, fset, value)
// If params is non-nil (could be a zero length slice), this const is
// actually a function-call like expression from another macro.
// This means we have to parse a string like "(a, b) (a+b)".
// We do this by parsing the parameters at the start and then treating the
// following like a normal constant expression.
if params != nil {
// Parse opening paren.
if t.curToken != token.LPAREN {
return nil, unexpectedToken(t, token.LPAREN)
}
t.Next()
// Parse parameters (identifiers) and closing paren.
var paramIdents []string
for i := 0; ; i++ {
if i == 0 && t.curToken == token.RPAREN {
// No parameters, break early.
t.Next()
break
}
// Read the parameter name.
if t.curToken != token.IDENT {
return nil, unexpectedToken(t, token.IDENT)
}
paramIdents = append(paramIdents, t.curValue)
t.Next()
// Read the next token: either a continuation (comma) or end of list
// (rparen).
if t.curToken == token.RPAREN {
// End of parameter list.
t.Next()
break
} else if t.curToken == token.COMMA {
// Comma, so there will be another parameter name.
t.Next()
} else {
return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos),
Msg: "unexpected token " + t.curToken.String() + " inside macro parameters, expected ',' or ')'",
}
}
}
// Report an error if there is a mismatch in parameter length.
// The error is reported at the location of the closing paren from the
// caller location.
if len(params) != len(paramIdents) {
return nil, &scanner.Error{
Pos: t.fset.Position(callerPos),
Msg: fmt.Sprintf("unexpected number of parameters: expected %d, got %d", len(paramIdents), len(params)),
}
}
// Assign values to the parameters.
// These parameter names are closer in 'scope' than other identifiers so
// will be used first when parsing an identifier.
for i, name := range paramIdents {
t.params[name] = params[i]
}
}
expr, err := parseConstExpr(t, precedenceLowest) expr, err := parseConstExpr(t, precedenceLowest)
t.Next() t.Next()
if t.curToken != token.EOF { if t.curToken != token.EOF {
@@ -150,7 +82,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
for t.peekToken != token.EOF && precedence < precedences[t.peekToken] { for t.peekToken != token.EOF && precedence < precedences[t.peekToken] {
switch t.peekToken { switch t.peekToken {
case token.OR, token.XOR, token.AND, token.SHL, token.SHR, token.ADD, token.SUB, token.MUL, token.QUO, token.REM: case token.OR, token.XOR, token.AND, token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
t.Next() t.Next()
leftExpr, err = parseBinaryExpr(t, leftExpr) leftExpr, err = parseBinaryExpr(t, leftExpr)
} }
@@ -160,68 +92,6 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
} }
func parseIdent(t *tokenizer) (ast.Expr, *scanner.Error) { func parseIdent(t *tokenizer) (ast.Expr, *scanner.Error) {
// If the identifier is one of the parameters of this function-like macro,
// use the parameter value.
if val, ok := t.params[t.curValue]; ok {
return val, nil
}
if t.f != nil {
// Check whether this identifier is actually a macro "call" with
// parameters. In that case, we should parse the parameters and pass it
// on to a new invocation of parseConst.
if t.peekToken == token.LPAREN {
if cursor, ok := t.f.names[t.curValue]; ok && t.f.isFunctionLikeMacro(cursor) {
// We know the current and peek tokens (the peek one is the '('
// token). So skip ahead until the current token is the first
// unknown token.
t.Next()
t.Next()
// Parse the list of parameters until ')' (rparen) is found.
params := []ast.Expr{}
for i := 0; ; i++ {
if i == 0 && t.curToken == token.RPAREN {
break
}
x, err := parseConstExpr(t, precedenceLowest)
if err != nil {
return nil, err
}
params = append(params, x)
t.Next()
if t.curToken == token.COMMA {
t.Next()
} else if t.curToken == token.RPAREN {
break
} else {
return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos),
Msg: "unexpected token " + t.curToken.String() + ", ',' or ')'",
}
}
}
// Evaluate the macro value and use it as the identifier value.
rparen := t.curPos
pos, text := t.f.getMacro(cursor)
return parseConst(pos, t.fset, text, params, rparen, t.f)
}
}
// Normally the name is something defined in the file (like another
// macro) which we get the declaration from using getASTDeclName.
// This ensures that names that are only referenced inside a macro are
// still getting defined.
if cursor, ok := t.f.names[t.curValue]; ok {
return &ast.Ident{
NamePos: t.curPos,
Name: t.f.getASTDeclName(t.curValue, cursor, false),
}, nil
}
}
// t.f is nil during testing. This is a fallback.
return &ast.Ident{ return &ast.Ident{
NamePos: t.curPos, NamePos: t.curPos,
Name: "C." + t.curValue, Name: "C." + t.curValue,
@@ -290,25 +160,21 @@ func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error {
// tokenizer reads C source code and converts it to Go tokens. // tokenizer reads C source code and converts it to Go tokens.
type tokenizer struct { type tokenizer struct {
f *cgoFile
curPos, peekPos token.Pos curPos, peekPos token.Pos
fset *token.FileSet fset *token.FileSet
curToken, peekToken token.Token curToken, peekToken token.Token
curValue, peekValue string curValue, peekValue string
buf string buf string
params map[string]ast.Expr
} }
// newTokenizer initializes a new tokenizer, positioned at the first token in // newTokenizer initializes a new tokenizer, positioned at the first token in
// the string. // the string.
func newTokenizer(start token.Pos, fset *token.FileSet, buf string, f *cgoFile) *tokenizer { func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer {
t := &tokenizer{ t := &tokenizer{
f: f,
peekPos: start, peekPos: start,
fset: fset, fset: fset,
buf: buf, buf: buf,
peekToken: token.ILLEGAL, peekToken: token.ILLEGAL,
params: make(map[string]ast.Expr),
} }
// Parse the first two tokens (cur and peek). // Parse the first two tokens (cur and peek).
t.Next() t.Next()
@@ -325,9 +191,7 @@ func (t *tokenizer) Next() {
t.curValue = t.peekValue t.curValue = t.peekValue
// Parse the next peek token. // Parse the next peek token.
if t.peekPos != token.NoPos { t.peekPos += token.Pos(len(t.curValue))
t.peekPos += token.Pos(len(t.curValue))
}
for { for {
if len(t.buf) == 0 { if len(t.buf) == 0 {
t.peekToken = token.EOF t.peekToken = token.EOF
@@ -339,28 +203,20 @@ func (t *tokenizer) Next() {
// Skip whitespace. // Skip whitespace.
// Based on this source, not sure whether it represents C whitespace: // Based on this source, not sure whether it represents C whitespace:
// https://en.cppreference.com/w/cpp/string/byte/isspace // https://en.cppreference.com/w/cpp/string/byte/isspace
if t.peekPos != token.NoPos { t.peekPos++
t.peekPos++
}
t.buf = t.buf[1:] t.buf = t.buf[1:]
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&" || string(t.buf[:2]) == "<<" || string(t.buf[:2]) == ">>"): case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&"):
// Two-character tokens. // Two-character tokens.
switch c { switch c {
case '&': case '&':
t.peekToken = token.LAND t.peekToken = token.LAND
case '|': case '|':
t.peekToken = token.LOR t.peekToken = token.LOR
case '<':
t.peekToken = token.SHL
case '>':
t.peekToken = token.SHR
default:
panic("unreachable")
} }
t.peekValue = t.buf[:2] t.peekValue = t.buf[:2]
t.buf = t.buf[2:] t.buf = t.buf[2:]
return return
case c == '(' || c == ')' || c == ',' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^': case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
// Single-character tokens. // Single-character tokens.
// TODO: ++ (increment) and -- (decrement) operators. // TODO: ++ (increment) and -- (decrement) operators.
switch c { switch c {
@@ -368,8 +224,6 @@ func (t *tokenizer) Next() {
t.peekToken = token.LPAREN t.peekToken = token.LPAREN
case ')': case ')':
t.peekToken = token.RPAREN t.peekToken = token.RPAREN
case ',':
t.peekToken = token.COMMA
case '+': case '+':
t.peekToken = token.ADD t.peekToken = token.ADD
case '-': case '-':
+1 -5
View File
@@ -40,10 +40,6 @@ func TestParseConst(t *testing.T) {
{`5&5`, `5 & 5`}, {`5&5`, `5 & 5`},
{`5|5`, `5 | 5`}, {`5|5`, `5 | 5`},
{`5^5`, `5 ^ 5`}, {`5^5`, `5 ^ 5`},
{`5<<5`, `5 << 5`},
{`5>>5`, `5 >> 5`},
{`5>>5 + 3`, `5 >> (5 + 3)`},
{`5>>5 ^ 3`, `5>>5 ^ 3`},
{`5||5`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet {`5||5`, `error: 1:2: unexpected token ||, expected end of expression`}, // logical binops aren't supported yet
{`(5/5)`, `(5 / 5)`}, {`(5/5)`, `(5 / 5)`},
{`1 - 2`, `1 - 2`}, {`1 - 2`, `1 - 2`},
@@ -59,7 +55,7 @@ func TestParseConst(t *testing.T) {
} { } {
fset := token.NewFileSet() fset := token.NewFileSet()
startPos := fset.AddFile("", -1, 1000).Pos(0) startPos := fset.AddFile("", -1, 1000).Pos(0)
expr, err := parseConst(startPos, fset, tc.C, nil, token.NoPos, nil) expr, err := parseConst(startPos, fset, tc.C)
s := "<invalid>" s := "<invalid>"
if err != nil { if err != nil {
if !strings.HasPrefix(tc.Go, "error: ") { if !strings.HasPrefix(tc.Go, "error: ") {
+72 -110
View File
@@ -4,7 +4,6 @@ package cgo
// modification. It does not touch the AST itself. // modification. It does not touch the AST itself.
import ( import (
"bytes"
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
@@ -63,24 +62,10 @@ long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c); CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c); unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c); unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
unsigned tinygo_clang_Cursor_isMacroFunctionLike(GoCXCursor c);
// Fix some warnings on Windows ARM. Without the __declspec(dllexport), it gives warnings like this:
// In file included from _cgo_export.c:4:
// cgo-gcc-export-header-prolog:49:34: warning: redeclaration of 'tinygo_clang_globals_visitor' should not add 'dllexport' attribute [-Wdll-attribute-on-redeclaration]
// libclang.go:68:5: note: previous declaration is here
// See: https://github.com/golang/go/issues/49721
#if defined(_WIN32)
#define CGO_DECL __declspec(dllexport)
#else
#define CGO_DECL
#endif
CGO_DECL
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
CGO_DECL
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
CGO_DECL int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data); void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
*/ */
import "C" import "C"
@@ -219,7 +204,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Fun, Kind: ast.Fun,
Name: "_Cgo_" + name, Name: "C." + name,
} }
exportName := name exportName := name
localName := name localName := name
@@ -257,7 +242,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
}, },
Name: &ast.Ident{ Name: &ast.Ident{
NamePos: pos, NamePos: pos,
Name: "_Cgo_" + localName, Name: "C." + localName,
Obj: obj, Obj: obj,
}, },
Type: &ast.FuncType{ Type: &ast.FuncType{
@@ -269,18 +254,10 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
}, },
}, },
} }
var doc []string
if C.clang_isFunctionTypeVariadic(cursorType) != 0 { if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
doc = append(doc, "//go:variadic")
}
if _, ok := f.noescapingFuncs[name]; ok {
doc = append(doc, "//go:noescape")
f.noescapingFuncs[name].used = true
}
if len(doc) != 0 {
decl.Doc.List = append(decl.Doc.List, &ast.Comment{ decl.Doc.List = append(decl.Doc.List, &ast.Comment{
Slash: pos - 1, Slash: pos - 1,
Text: strings.Join(doc, "\n"), Text: "//go:variadic",
}) })
} }
for i := 0; i < numArgs; i++ { for i := 0; i < numArgs; i++ {
@@ -319,7 +296,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
return decl, stringSignature return decl, stringSignature
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl: case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
typ := f.makeASTRecordType(c, pos) typ := f.makeASTRecordType(c, pos)
typeName := "_Cgo_" + name typeName := "C." + name
typeExpr := typ.typeExpr typeExpr := typ.typeExpr
if typ.unionSize != 0 { if typ.unionSize != 0 {
// Convert to a single-field struct type. // Convert to a single-field struct type.
@@ -340,7 +317,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
obj.Decl = typeSpec obj.Decl = typeSpec
return typeSpec, typ return typeSpec, typ
case C.CXCursor_TypedefDecl: case C.CXCursor_TypedefDecl:
typeName := "_Cgo_" + name typeName := "C." + name
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Typ, Kind: ast.Typ,
@@ -378,12 +355,12 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
} }
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Var, Kind: ast.Var,
Name: "_Cgo_" + name, Name: "C." + name,
} }
valueSpec := &ast.ValueSpec{ valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{ Names: []*ast.Ident{{
NamePos: pos, NamePos: pos,
Name: "_Cgo_" + name, Name: "C." + name,
Obj: obj, Obj: obj,
}}, }},
Type: typeExpr, Type: typeExpr,
@@ -392,8 +369,42 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
gen.Specs = append(gen.Specs, valueSpec) gen.Specs = append(gen.Specs, valueSpec)
return gen, nil return gen, nil
case C.CXCursor_MacroDefinition: case C.CXCursor_MacroDefinition:
tokenPos, value := f.getMacro(c) sourceRange := C.tinygo_clang_getCursorExtent(c)
expr, scannerError := parseConst(tokenPos, f.fset, value, nil, token.NoPos, f) start := C.clang_getRangeStart(sourceRange)
end := C.clang_getRangeEnd(sourceRange)
var file, endFile C.CXFile
var startOffset, endOffset C.unsigned
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
if file == nil {
f.addError(pos, "internal error: could not find file where macro is defined")
return nil, nil
}
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
if file != endFile {
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
return nil, nil
}
if startOffset > endOffset {
f.addError(pos, "internal error: start offset of macro is after end offset")
return nil, nil
}
// read file contents and extract the relevant byte range
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
var size C.size_t
sourcePtr := C.clang_getFileContents(tu, file, &size)
if endOffset >= C.uint(size) {
f.addError(pos, "internal error: end offset of macro lies after end of file")
return nil, nil
}
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
if !strings.HasPrefix(source, name) {
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
return nil, nil
}
value := source[len(name):]
// Try to convert this #define into a Go constant expression.
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
if scannerError != nil { if scannerError != nil {
f.errors = append(f.errors, *scannerError) f.errors = append(f.errors, *scannerError)
return nil, nil return nil, nil
@@ -407,12 +418,12 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
} }
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Con, Kind: ast.Con,
Name: "_Cgo_" + name, Name: "C." + name,
} }
valueSpec := &ast.ValueSpec{ valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{ Names: []*ast.Ident{{
NamePos: pos, NamePos: pos,
Name: "_Cgo_" + name, Name: "C." + name,
Obj: obj, Obj: obj,
}}, }},
Values: []ast.Expr{expr}, Values: []ast.Expr{expr},
@@ -423,7 +434,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
case C.CXCursor_EnumDecl: case C.CXCursor_EnumDecl:
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Typ, Kind: ast.Typ,
Name: "_Cgo_" + name, Name: "C." + name,
} }
underlying := C.tinygo_clang_getEnumDeclIntegerType(c) underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
// TODO: gc's CGo implementation uses types such as `uint32` for enums // TODO: gc's CGo implementation uses types such as `uint32` for enums
@@ -431,7 +442,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
typeSpec := &ast.TypeSpec{ typeSpec := &ast.TypeSpec{
Name: &ast.Ident{ Name: &ast.Ident{
NamePos: pos, NamePos: pos,
Name: "_Cgo_" + name, Name: "C." + name,
Obj: obj, Obj: obj,
}, },
Assign: pos, Assign: pos,
@@ -454,12 +465,12 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
} }
obj := &ast.Object{ obj := &ast.Object{
Kind: ast.Con, Kind: ast.Con,
Name: "_Cgo_" + name, Name: "C." + name,
} }
valueSpec := &ast.ValueSpec{ valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{ Names: []*ast.Ident{{
NamePos: pos, NamePos: pos,
Name: "_Cgo_" + name, Name: "C." + name,
Obj: obj, Obj: obj,
}}, }},
Values: []ast.Expr{expr}, Values: []ast.Expr{expr},
@@ -473,62 +484,6 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
} }
} }
// Return whether this is a macro that's also function-like, like this:
//
// #define add(a, b) (a+b)
func (f *cgoFile) isFunctionLikeMacro(c clangCursor) bool {
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_MacroDefinition {
return false
}
return C.tinygo_clang_Cursor_isMacroFunctionLike(c) != 0
}
// Get the macro value: the position in the source file and the string value of
// the macro.
func (f *cgoFile) getMacro(c clangCursor) (pos token.Pos, value string) {
// Extract tokens from the Clang tokenizer.
// See: https://stackoverflow.com/a/19074846/559350
sourceRange := C.tinygo_clang_getCursorExtent(c)
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
var rawTokens *C.CXToken
var numTokens C.unsigned
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
tokens := unsafe.Slice(rawTokens, numTokens)
defer C.clang_disposeTokens(tu, rawTokens, numTokens)
// Convert this range of tokens back to source text.
// Ugly, but it works well enough.
sourceBuf := &bytes.Buffer{}
var startOffset int
for i, token := range tokens {
spelling := getString(C.clang_getTokenSpelling(tu, token))
location := C.clang_getTokenLocation(tu, token)
var tokenOffset C.unsigned
C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset)
if i == 0 {
// The first token is the macro name itself.
// Skip it (after using its location).
startOffset = int(tokenOffset)
} else {
// Later tokens are the macro contents.
for int(tokenOffset) > (startOffset + sourceBuf.Len()) {
// Pad the source text with whitespace (that must have been
// present in the original source as well).
sourceBuf.WriteByte(' ')
}
sourceBuf.WriteString(spelling)
}
}
value = sourceBuf.String()
// Obtain the position of this token. This is the position of the first
// character in the 'value' string and is used to report errors at the
// correct location in the source file.
pos = f.getCursorPosition(c)
return
}
func getString(clangString C.CXString) (s string) { func getString(clangString C.CXString) (s string) {
rawString := C.clang_getCString(clangString) rawString := C.clang_getCString(clangString)
s = C.GoString(rawString) s = C.GoString(rawString)
@@ -687,6 +642,13 @@ func (p *cgoPackage) addErrorAfter(pos token.Pos, after, msg string) {
// addErrorAt is a utility function to add an error to the list of errors. // addErrorAt is a utility function to add an error to the list of errors.
func (p *cgoPackage) addErrorAt(position token.Position, msg string) { func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
if filepath.IsAbs(position.Filename) {
// Relative paths for readability, like other Go parser errors.
relpath, err := filepath.Rel(p.currentDir, position.Filename)
if err == nil {
position.Filename = relpath
}
}
p.errors = append(p.errors, scanner.Error{ p.errors = append(p.errors, scanner.Error{
Pos: position, Pos: position,
Msg: msg, Msg: msg,
@@ -745,27 +707,27 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
var typeName string var typeName string
switch typ.kind { switch typ.kind {
case C.CXType_Char_S, C.CXType_Char_U: case C.CXType_Char_S, C.CXType_Char_U:
typeName = "_Cgo_char" typeName = "C.char"
case C.CXType_SChar: case C.CXType_SChar:
typeName = "_Cgo_schar" typeName = "C.schar"
case C.CXType_UChar: case C.CXType_UChar:
typeName = "_Cgo_uchar" typeName = "C.uchar"
case C.CXType_Short: case C.CXType_Short:
typeName = "_Cgo_short" typeName = "C.short"
case C.CXType_UShort: case C.CXType_UShort:
typeName = "_Cgo_ushort" typeName = "C.ushort"
case C.CXType_Int: case C.CXType_Int:
typeName = "_Cgo_int" typeName = "C.int"
case C.CXType_UInt: case C.CXType_UInt:
typeName = "_Cgo_uint" typeName = "C.uint"
case C.CXType_Long: case C.CXType_Long:
typeName = "_Cgo_long" typeName = "C.long"
case C.CXType_ULong: case C.CXType_ULong:
typeName = "_Cgo_ulong" typeName = "C.ulong"
case C.CXType_LongLong: case C.CXType_LongLong:
typeName = "_Cgo_longlong" typeName = "C.longlong"
case C.CXType_ULongLong: case C.CXType_ULongLong:
typeName = "_Cgo_ulonglong" typeName = "C.ulonglong"
case C.CXType_Bool: case C.CXType_Bool:
typeName = "bool" typeName = "bool"
case C.CXType_Float, C.CXType_Double, C.CXType_LongDouble: case C.CXType_Float, C.CXType_Double, C.CXType_LongDouble:
@@ -896,7 +858,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
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)) f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
typeName = "_Cgo_<unknown>" typeName = "C.<unknown>"
} }
return &ast.Ident{ return &ast.Ident{
NamePos: pos, NamePos: pos,
@@ -913,7 +875,7 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
var goName string var goName string
typeSize := C.clang_Type_getSizeOf(underlyingType) typeSize := C.clang_Type_getSizeOf(underlyingType)
switch name { switch name {
case "_Cgo_char": case "C.char":
if typeSize != 1 { if typeSize != 1 {
// This happens for some very special purpose architectures // This happens for some very special purpose architectures
// (DSPs etc.) that are not currently targeted. // (DSPs etc.) that are not currently targeted.
@@ -926,7 +888,7 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
case C.CXType_Char_U: case C.CXType_Char_U:
goName = "uint8" goName = "uint8"
} }
case "_Cgo_schar", "_Cgo_short", "_Cgo_int", "_Cgo_long", "_Cgo_longlong": case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
switch typeSize { switch typeSize {
case 1: case 1:
goName = "int8" goName = "int8"
@@ -937,7 +899,7 @@ func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSp
case 8: case 8:
goName = "int64" goName = "int64"
} }
case "_Cgo_uchar", "_Cgo_ushort", "_Cgo_uint", "_Cgo_ulong", "_Cgo_ulonglong": case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
switch typeSize { switch typeSize {
case 1: case 1:
goName = "uint8" goName = "uint8"
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !byollvm && llvm18 //go:build !byollvm && !llvm15 && !llvm16 && !llvm17
package cgo package cgo
-15
View File
@@ -1,15 +0,0 @@
//go:build !byollvm && llvm19
package cgo
/*
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include -I/usr/lib64/llvm19/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-19/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@19/lib -lclang
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@19/lib -lclang
#cgo freebsd LDFLAGS: -L/usr/local/llvm19/lib -lclang
*/
import "C"
-15
View File
@@ -1,15 +0,0 @@
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
package cgo
/*
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include -I/usr/lib64/llvm20/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-20/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@20/lib -lclang
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@20/lib -lclang
#cgo freebsd LDFLAGS: -L/usr/local/llvm20/lib -lclang
*/
import "C"
-4
View File
@@ -84,7 +84,3 @@ unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) { unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
return clang_Cursor_isBitField(c); return clang_Cursor_isBitField(c);
} }
unsigned tinygo_clang_Cursor_isMacroFunctionLike(CXCursor c) {
return clang_Cursor_isMacroFunctionLike(c);
}
-1
View File
@@ -78,7 +78,6 @@ var validCompilerFlags = []*regexp.Regexp{
re(`-f(no-)?(pic|PIC|pie|PIE)`), re(`-f(no-)?(pic|PIC|pie|PIE)`),
re(`-f(no-)?plt`), re(`-f(no-)?plt`),
re(`-f(no-)?rtti`), re(`-f(no-)?rtti`),
re(`-f(no-)?short-enums`),
re(`-f(no-)?split-stack`), re(`-f(no-)?split-stack`),
re(`-f(no-)?stack-(.+)`), re(`-f(no-)?stack-(.+)`),
re(`-f(no-)?strict-aliasing`), re(`-f(no-)?strict-aliasing`),
+23 -38
View File
@@ -1,54 +1,39 @@
package main package main
import "syscall"
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func C.CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func C.GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func C.__GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func C.GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
} }
type ( type (
_Cgo_char uint8 C.char uint8
_Cgo_schar int8 C.schar int8
_Cgo_uchar uint8 C.uchar uint8
_Cgo_short int16 C.short int16
_Cgo_ushort uint16 C.ushort uint16
_Cgo_int int32 C.int int32
_Cgo_uint uint32 C.uint uint32
_Cgo_long int32 C.long int32
_Cgo_ulong uint32 C.ulong uint32
_Cgo_longlong int64 C.longlong int64
_Cgo_ulonglong uint64 C.ulonglong uint64
) )
-16
View File
@@ -3,26 +3,10 @@ package main
/* /*
#define foo 3 #define foo 3
#define bar foo #define bar foo
#define unreferenced 4
#define referenced unreferenced
#define fnlike() 5
#define fnlike_val fnlike()
#define square(n) (n*n)
#define square_val square(20)
#define add(a, b) (a + b)
#define add_val add(3, 5)
*/ */
import "C" import "C"
const ( const (
Foo = C.foo Foo = C.foo
Bar = C.bar Bar = C.bar
Baz = C.referenced
fnlike = C.fnlike_val
square = C.square_val
add = C.add_val
) )
+25 -45
View File
@@ -1,62 +1,42 @@
package main package main
import "syscall"
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func C.CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func C.GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func C.__GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func C.GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
} }
type ( type (
_Cgo_char uint8 C.char uint8
_Cgo_schar int8 C.schar int8
_Cgo_uchar uint8 C.uchar uint8
_Cgo_short int16 C.short int16
_Cgo_ushort uint16 C.ushort uint16
_Cgo_int int32 C.int int32
_Cgo_uint uint32 C.uint uint32
_Cgo_long int32 C.long int32
_Cgo_ulong uint32 C.ulong uint32
_Cgo_longlong int64 C.longlong int64
_Cgo_ulonglong uint64 C.ulonglong uint64
) )
const _Cgo_foo = 3 const C.foo = 3
const _Cgo_bar = _Cgo_foo const C.bar = C.foo
const _Cgo_unreferenced = 4
const _Cgo_referenced = _Cgo_unreferenced
const _Cgo_fnlike_val = 5
const _Cgo_square_val = (20 * 20)
const _Cgo_add_val = (3 + 5)
-26
View File
@@ -10,35 +10,20 @@ typedef struct {
typedef someType noType; // undefined type typedef someType noType; // undefined type
// Some invalid noescape lines
#cgo noescape
#cgo noescape foo bar
#cgo noescape unusedFunction
#define SOME_CONST_1 5) // invalid const syntax #define SOME_CONST_1 5) // invalid const syntax
#define SOME_CONST_2 6) // const not used (so no error) #define SOME_CONST_2 6) // const not used (so no error)
#define SOME_CONST_3 1234 // const too large for byte #define SOME_CONST_3 1234 // const too large for byte
#define SOME_CONST_b 3 ) // const with lots of weird whitespace (to test error locations)
# define SOME_CONST_startspace 3)
*/ */
// //
// //
// #define SOME_CONST_4 8) // after some empty lines // #define SOME_CONST_4 8) // after some empty lines
// #cgo CFLAGS: -DSOME_PARAM_CONST_invalid=3/+3
// #cgo CFLAGS: -DSOME_PARAM_CONST_valid=3+4
import "C" import "C"
// #warning another warning // #warning another warning
import "C" import "C"
// #define add(a, b) (a+b)
// #define add_toomuch add(1, 2, 3)
// #define add_toolittle add(1)
import "C"
// Make sure that errors for the following lines won't change with future // Make sure that errors for the following lines won't change with future
// additions to the CGo preamble. // additions to the CGo preamble.
//
//line errors.go:100 //line errors.go:100
var ( var (
// constant too large // constant too large
@@ -53,15 +38,4 @@ var (
_ byte = C.SOME_CONST_3 _ byte = C.SOME_CONST_3
_ = C.SOME_CONST_4 _ = C.SOME_CONST_4
_ = C.SOME_CONST_b
_ = C.SOME_CONST_startspace
// constants passed by a command line parameter
_ = C.SOME_PARAM_CONST_invalid
_ = C.SOME_PARAM_CONST_valid
_ = C.add_toomuch
_ = C.add_toolittle
) )
+35 -64
View File
@@ -1,89 +1,60 @@
// CGo errors: // CGo errors:
// testdata/errors.go:14:1: missing function name in #cgo noescape line
// testdata/errors.go:15:1: multiple function names in #cgo noescape line
// testdata/errors.go:4:2: warning: some warning // testdata/errors.go:4:2: warning: some warning
// testdata/errors.go:11:9: error: unknown type name 'someType' // testdata/errors.go:11:9: error: unknown type name 'someType'
// testdata/errors.go:31:5: warning: another warning // testdata/errors.go:22:5: warning: another warning
// testdata/errors.go:18:23: unexpected token ), expected end of expression // testdata/errors.go:13:23: unexpected token ), expected end of expression
// testdata/errors.go:26:26: unexpected token ), expected end of expression // testdata/errors.go:19:26: unexpected token ), expected end of expression
// testdata/errors.go:21:33: unexpected token ), expected end of expression
// testdata/errors.go:22:34: unexpected token ), expected end of expression
// -: unexpected token INT, expected end of expression
// testdata/errors.go:35:35: unexpected number of parameters: expected 2, got 3
// testdata/errors.go:36:31: unexpected number of parameters: expected 2, got 1
// testdata/errors.go:3:1: function "unusedFunction" in #cgo noescape line is not used
// 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 _Cgo_char value in variable declaration (overflows) // testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
// testdata/errors.go:105: unknown field z in struct literal // testdata/errors.go:105: unknown field z in struct literal
// testdata/errors.go:108: undefined: _Cgo_SOME_CONST_1 // testdata/errors.go:108: undefined: C.SOME_CONST_1
// testdata/errors.go:110: cannot use _Cgo_SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows) // testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
// testdata/errors.go:112: undefined: _Cgo_SOME_CONST_4 // testdata/errors.go:112: undefined: C.SOME_CONST_4
// testdata/errors.go:114: undefined: _Cgo_SOME_CONST_b
// testdata/errors.go:116: undefined: _Cgo_SOME_CONST_startspace
// testdata/errors.go:119: undefined: _Cgo_SOME_PARAM_CONST_invalid
// testdata/errors.go:122: undefined: _Cgo_add_toomuch
// testdata/errors.go:123: undefined: _Cgo_add_toolittle
package main package main
import "syscall"
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func C.CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func C.GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func C.__GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func C.GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
} }
type ( type (
_Cgo_char uint8 C.char uint8
_Cgo_schar int8 C.schar int8
_Cgo_uchar uint8 C.uchar uint8
_Cgo_short int16 C.short int16
_Cgo_ushort uint16 C.ushort uint16
_Cgo_int int32 C.int int32
_Cgo_uint uint32 C.uint uint32
_Cgo_long int32 C.long int32
_Cgo_ulong uint32 C.ulong uint32
_Cgo_longlong int64 C.longlong int64
_Cgo_ulonglong uint64 C.ulonglong uint64
) )
type _Cgo_struct_point_t struct { type C.struct_point_t struct {
x _Cgo_int x C.int
y _Cgo_int y C.int
} }
type _Cgo_point_t = _Cgo_struct_point_t type C.point_t = C.struct_point_t
const _Cgo_SOME_CONST_3 = 1234 const C.SOME_CONST_3 = 1234
const _Cgo_SOME_PARAM_CONST_valid = 3 + 4
+25 -40
View File
@@ -5,58 +5,43 @@
package main package main
import "syscall"
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func C.CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func C.GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func C.__GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func C.GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
} }
type ( type (
_Cgo_char uint8 C.char uint8
_Cgo_schar int8 C.schar int8
_Cgo_uchar uint8 C.uchar uint8
_Cgo_short int16 C.short int16
_Cgo_ushort uint16 C.ushort uint16
_Cgo_int int32 C.int int32
_Cgo_uint uint32 C.uint uint32
_Cgo_long int32 C.long int32
_Cgo_ulong uint32 C.ulong uint32
_Cgo_longlong int64 C.longlong int64
_Cgo_ulonglong uint64 C.ulonglong uint64
) )
const _Cgo_BAR = 3 const C.BAR = 3
const _Cgo_FOO_H = 1 const C.FOO_H = 1
-5
View File
@@ -9,10 +9,6 @@ static void staticfunc(int x);
// Global variable signatures. // Global variable signatures.
extern int someValue; extern int someValue;
void notEscapingFunction(int *a);
#cgo noescape notEscapingFunction
*/ */
import "C" import "C"
@@ -22,7 +18,6 @@ func accessFunctions() {
C.variadic0() C.variadic0()
C.variadic2(3, 5) C.variadic2(3, 5)
C.staticfunc(3) C.staticfunc(3)
C.notEscapingFunction(nil)
} }
func accessGlobals() { func accessGlobals() {
+32 -52
View File
@@ -1,84 +1,64 @@
package main package main
import "syscall"
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func C.CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func C.GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func C.__GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func C.GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
} }
type ( type (
_Cgo_char uint8 C.char uint8
_Cgo_schar int8 C.schar int8
_Cgo_uchar uint8 C.uchar uint8
_Cgo_short int16 C.short int16
_Cgo_ushort uint16 C.ushort uint16
_Cgo_int int32 C.int int32
_Cgo_uint uint32 C.uint uint32
_Cgo_long int32 C.long int32
_Cgo_ulong uint32 C.ulong uint32
_Cgo_longlong int64 C.longlong int64
_Cgo_ulonglong uint64 C.ulonglong uint64
) )
//export foo //export foo
func _Cgo_foo(a _Cgo_int, b _Cgo_int) _Cgo_int func C.foo(a C.int, b C.int) C.int
var _Cgo_foo$funcaddr unsafe.Pointer var C.foo$funcaddr unsafe.Pointer
//export variadic0 //export variadic0
//go:variadic //go:variadic
func _Cgo_variadic0() func C.variadic0()
var _Cgo_variadic0$funcaddr unsafe.Pointer var C.variadic0$funcaddr unsafe.Pointer
//export variadic2 //export variadic2
//go:variadic //go:variadic
func _Cgo_variadic2(x _Cgo_int, y _Cgo_int) func C.variadic2(x C.int, y C.int)
var _Cgo_variadic2$funcaddr unsafe.Pointer var C.variadic2$funcaddr unsafe.Pointer
//export _Cgo_static_173c95a79b6df1980521_staticfunc //export _Cgo_static_173c95a79b6df1980521_staticfunc
func _Cgo_staticfunc!symbols.go(x _Cgo_int) func C.staticfunc!symbols.go(x C.int)
var _Cgo_staticfunc!symbols.go$funcaddr unsafe.Pointer var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
//export notEscapingFunction
//go:noescape
func _Cgo_notEscapingFunction(a *_Cgo_int)
var _Cgo_notEscapingFunction$funcaddr unsafe.Pointer
//go:extern someValue //go:extern someValue
var _Cgo_someValue _Cgo_int var C.someValue C.int
+91 -110
View File
@@ -1,166 +1,147 @@
package main package main
import "syscall"
import "unsafe" import "unsafe"
var _ unsafe.Pointer var _ unsafe.Pointer
//go:linkname _Cgo_CString runtime.cgo_CString //go:linkname C.CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char func C.CString(string) *C.char
//go:linkname _Cgo_GoString runtime.cgo_GoString //go:linkname C.GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string func C.GoString(*C.char) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN //go:linkname C.__GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string func C.__GoStringN(*C.char, uintptr) string
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string { func C.GoStringN(cstr *C.char, length C.int) string {
return _Cgo___GoStringN(cstr, uintptr(length)) return C.__GoStringN(cstr, uintptr(length))
} }
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes //go:linkname C.__GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte { func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
} }
type ( type (
_Cgo_char uint8 C.char uint8
_Cgo_schar int8 C.schar int8
_Cgo_uchar uint8 C.uchar uint8
_Cgo_short int16 C.short int16
_Cgo_ushort uint16 C.ushort uint16
_Cgo_int int32 C.int int32
_Cgo_uint uint32 C.uint uint32
_Cgo_long int32 C.long int32
_Cgo_ulong uint32 C.ulong uint32
_Cgo_longlong int64 C.longlong int64
_Cgo_ulonglong uint64 C.ulonglong uint64
) )
type _Cgo_myint = _Cgo_int type C.myint = C.int
type _Cgo_struct_point2d_t struct { type C.struct_point2d_t struct {
x _Cgo_int x C.int
y _Cgo_int y C.int
} }
type _Cgo_point2d_t = _Cgo_struct_point2d_t type C.point2d_t = C.struct_point2d_t
type _Cgo_struct_point3d struct { type C.struct_point3d struct {
x _Cgo_int x C.int
y _Cgo_int y C.int
z _Cgo_int z C.int
} }
type _Cgo_point3d_t = _Cgo_struct_point3d type C.point3d_t = C.struct_point3d
type _Cgo_struct_type1 struct { type C.struct_type1 struct {
_type _Cgo_int _type C.int
__type _Cgo_int __type C.int
___type _Cgo_int ___type C.int
} }
type _Cgo_struct_type2 struct{ _type _Cgo_int } type C.struct_type2 struct{ _type C.int }
type _Cgo_union_union1_t struct{ i _Cgo_int } type C.union_union1_t struct{ i C.int }
type _Cgo_union1_t = _Cgo_union_union1_t type C.union1_t = C.union_union1_t
type _Cgo_union_union3_t struct{ $union uint64 } type C.union_union3_t struct{ $union uint64 }
func (union *_Cgo_union_union3_t) unionfield_i() *_Cgo_int { func (union *C.union_union3_t) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
return (*_Cgo_int)(unsafe.Pointer(&union.$union)) func (union *C.union_union3_t) unionfield_d() *float64 {
}
func (union *_Cgo_union_union3_t) unionfield_d() *float64 {
return (*float64)(unsafe.Pointer(&union.$union)) return (*float64)(unsafe.Pointer(&union.$union))
} }
func (union *_Cgo_union_union3_t) unionfield_s() *_Cgo_short { func (union *C.union_union3_t) unionfield_s() *C.short {
return (*_Cgo_short)(unsafe.Pointer(&union.$union)) return (*C.short)(unsafe.Pointer(&union.$union))
} }
type _Cgo_union3_t = _Cgo_union_union3_t type C.union3_t = C.union_union3_t
type _Cgo_union_union2d struct{ $union [2]uint64 } type C.union_union2d struct{ $union [2]uint64 }
func (union *_Cgo_union_union2d) unionfield_i() *_Cgo_int { func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
return (*_Cgo_int)(unsafe.Pointer(&union.$union)) func (union *C.union_union2d) unionfield_d() *[2]float64 {
}
func (union *_Cgo_union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union)) return (*[2]float64)(unsafe.Pointer(&union.$union))
} }
type _Cgo_union2d_t = _Cgo_union_union2d type C.union2d_t = C.union_union2d
type _Cgo_union_unionarray_t struct{ arr [10]_Cgo_uchar } type C.union_unionarray_t struct{ arr [10]C.uchar }
type _Cgo_unionarray_t = _Cgo_union_unionarray_t type C.unionarray_t = C.union_unionarray_t
type _Cgo__Ctype_union___0 struct{ $union [3]uint32 } type C._Ctype_union___0 struct{ $union [3]uint32 }
func (union *_Cgo__Ctype_union___0) unionfield_area() *_Cgo_point2d_t { func (union *C._Ctype_union___0) unionfield_area() *C.point2d_t {
return (*_Cgo_point2d_t)(unsafe.Pointer(&union.$union)) return (*C.point2d_t)(unsafe.Pointer(&union.$union))
} }
func (union *_Cgo__Ctype_union___0) unionfield_solid() *_Cgo_point3d_t { func (union *C._Ctype_union___0) unionfield_solid() *C.point3d_t {
return (*_Cgo_point3d_t)(unsafe.Pointer(&union.$union)) return (*C.point3d_t)(unsafe.Pointer(&union.$union))
} }
type _Cgo_struct_struct_nested_t struct { type C.struct_struct_nested_t struct {
begin _Cgo_point2d_t begin C.point2d_t
end _Cgo_point2d_t end C.point2d_t
tag _Cgo_int tag C.int
coord _Cgo__Ctype_union___0 coord C._Ctype_union___0
} }
type _Cgo_struct_nested_t = _Cgo_struct_struct_nested_t type C.struct_nested_t = C.struct_struct_nested_t
type _Cgo_union_union_nested_t struct{ $union [2]uint64 } type C.union_union_nested_t struct{ $union [2]uint64 }
func (union *_Cgo_union_union_nested_t) unionfield_point() *_Cgo_point3d_t { func (union *C.union_union_nested_t) unionfield_point() *C.point3d_t {
return (*_Cgo_point3d_t)(unsafe.Pointer(&union.$union)) return (*C.point3d_t)(unsafe.Pointer(&union.$union))
} }
func (union *_Cgo_union_union_nested_t) unionfield_array() *_Cgo_unionarray_t { func (union *C.union_union_nested_t) unionfield_array() *C.unionarray_t {
return (*_Cgo_unionarray_t)(unsafe.Pointer(&union.$union)) return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
} }
func (union *_Cgo_union_union_nested_t) unionfield_thing() *_Cgo_union3_t { func (union *C.union_union_nested_t) unionfield_thing() *C.union3_t {
return (*_Cgo_union3_t)(unsafe.Pointer(&union.$union)) return (*C.union3_t)(unsafe.Pointer(&union.$union))
} }
type _Cgo_union_nested_t = _Cgo_union_union_nested_t type C.union_nested_t = C.union_union_nested_t
type _Cgo_enum_option = _Cgo_int type C.enum_option = C.int
type _Cgo_option_t = _Cgo_enum_option type C.option_t = C.enum_option
type _Cgo_enum_option2_t = _Cgo_uint type C.enum_option2_t = C.uint
type _Cgo_option2_t = _Cgo_enum_option2_t type C.option2_t = C.enum_option2_t
type _Cgo_struct_types_t struct { type C.struct_types_t struct {
f float32 f float32
d float64 d float64
ptr *_Cgo_int ptr *C.int
} }
type _Cgo_types_t = _Cgo_struct_types_t type C.types_t = C.struct_types_t
type _Cgo_myIntArray = [10]_Cgo_int type C.myIntArray = [10]C.int
type _Cgo_struct_bitfield_t struct { type C.struct_bitfield_t struct {
start _Cgo_uchar start C.uchar
__bitfield_1 _Cgo_uchar __bitfield_1 C.uchar
d _Cgo_uchar d C.uchar
e _Cgo_uchar e C.uchar
} }
func (s *_Cgo_struct_bitfield_t) bitfield_a() _Cgo_uchar { return s.__bitfield_1 & 0x1f } func (s *C.struct_bitfield_t) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *_Cgo_struct_bitfield_t) set_bitfield_a(value _Cgo_uchar) { func (s *C.struct_bitfield_t) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
} }
func (s *_Cgo_struct_bitfield_t) bitfield_b() _Cgo_uchar { func (s *C.struct_bitfield_t) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1 return s.__bitfield_1 >> 5 & 0x1
} }
func (s *_Cgo_struct_bitfield_t) set_bitfield_b(value _Cgo_uchar) { func (s *C.struct_bitfield_t) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
} }
func (s *_Cgo_struct_bitfield_t) bitfield_c() _Cgo_uchar { func (s *C.struct_bitfield_t) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6 return s.__bitfield_1 >> 6
} }
func (s *_Cgo_struct_bitfield_t) set_bitfield_c(value _Cgo_uchar, func (s *C.struct_bitfield_t) set_bitfield_c(value C.uchar,
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 } ) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
type _Cgo_bitfield_t = _Cgo_struct_bitfield_t type C.bitfield_t = C.struct_bitfield_t
+71 -161
View File
@@ -8,24 +8,12 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/google/shlex" "github.com/google/shlex"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
// Library versions. Whenever an existing library is changed, this number should
// be added/increased so that existing caches are invalidated.
//
// (This is a bit of a layering violation, this should really be part of the
// builder.Library struct but that's hard to do since we want to know the
// library path in advance in several places).
var libVersions = map[string]int{
"musl": 3,
"bdwgc": 2,
}
// Config keeps all configuration affecting the build in a single struct. // Config keeps all configuration affecting the build in a single struct.
type Config struct { type Config struct {
Options *Options Options *Options
@@ -45,17 +33,6 @@ func (c *Config) CPU() string {
return c.Target.CPU return c.Target.CPU
} }
// The current build mode (like the `-buildmode` command line flag).
func (c *Config) BuildMode() string {
if c.Options.BuildMode != "" {
return c.Options.BuildMode
}
if c.Target.BuildMode != "" {
return c.Target.BuildMode
}
return "default"
}
// Features returns a list of features this CPU supports. For example, for a // Features returns a list of features this CPU supports. For example, for a
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list // RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
// will be returned. // will be returned.
@@ -83,7 +60,7 @@ func (c *Config) GOOS() string {
} }
// GOARCH returns the GOARCH of the target. This might not always be the actual // GOARCH returns the GOARCH of the target. This might not always be the actual
// architecture: for example, the AVR target is not supported by the Go standard // archtecture: for example, the AVR target is not supported by the Go standard
// library so such targets will usually pretend to be linux/arm. // library so such targets will usually pretend to be linux/arm.
func (c *Config) GOARCH() string { func (c *Config) GOARCH() string {
return c.Target.GOARCH return c.Target.GOARCH
@@ -95,27 +72,15 @@ func (c *Config) GOARM() string {
return c.Options.GOARM return c.Options.GOARM
} }
// GOMIPS will return the GOMIPS environment variable given to the compiler when
// building a program.
func (c *Config) GOMIPS() string {
return c.Options.GOMIPS
}
// BuildTags returns the complete list of build tags used during this build. // BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string { func (c *Config) BuildTags() []string {
tags := append([]string(nil), c.Target.BuildTags...) // copy slice (avoid a race) tags := append([]string(nil), c.Target.BuildTags...) // copy slice (avoid a race)
tags = append(tags, []string{ tags = append(tags, []string{
"tinygo", // that's the compiler "tinygo", // that's the compiler
"purego", // to get various crypto packages to work "purego", // to get various crypto packages to work
"osusergo", // to get os/user to work
"math_big_pure_go", // to get math/big to work "math_big_pure_go", // to get math/big to work
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package "gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
"serial." + c.Serial()}...) // used inside the machine package "serial." + c.Serial()}...) // used inside the machine package
switch c.Scheduler() {
case "threads", "cores":
default:
tags = append(tags, "tinygo.unicore")
}
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))
} }
@@ -139,7 +104,7 @@ func (c *Config) GC() string {
// that can be traced by the garbage collector. // that can be traced by the garbage collector.
func (c *Config) NeedsStackObjects() bool { func (c *Config) NeedsStackObjects() bool {
switch c.GC() { switch c.GC() {
case "conservative", "custom", "precise", "boehm": case "conservative", "custom", "precise":
for _, tag := range c.BuildTags() { for _, tag := range c.BuildTags() {
if tag == "tinygo.wasm" { if tag == "tinygo.wasm" {
return true return true
@@ -226,7 +191,7 @@ func (c *Config) StackSize() uint64 {
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap. // MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
func (c *Config) MaxStackAlloc() uint64 { func (c *Config) MaxStackAlloc() uint64 {
if c.StackSize() >= 16*1024 { if c.StackSize() > 32*1024 {
return 1024 return 1024
} }
@@ -242,40 +207,20 @@ func (c *Config) RP2040BootPatch() bool {
return false return false
} }
// Return a canonicalized architecture name, so we don't have to deal with arm* // MuslArchitecture returns the architecture name as used in musl libc. It is
// vs thumb* vs arm64. // usually the same as the first part of the LLVM triple, but not always.
func CanonicalArchName(triple string) string { func MuslArchitecture(triple string) string {
arch := strings.Split(triple, "-")[0] arch := strings.Split(triple, "-")[0]
if arch == "arm64" {
return "aarch64"
}
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") { if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
return "arm" arch = "arm"
}
if arch == "mipsel" {
return "mips"
} }
return arch return arch
} }
// MuslArchitecture returns the architecture name as used in musl libc. It is // LibcPath returns the path to the libc directory. The libc path will be either
// usually the same as the first part of the LLVM triple, but not always. // a precompiled libc shipped with a TinyGo build, or a libc path in the cache
func MuslArchitecture(triple string) string { // directory (which might not yet be built).
return CanonicalArchName(triple) func (c *Config) LibcPath(name string) (path string, precompiled bool) {
}
// Returns true if the libc needs to include malloc, for the libcs where this
// matters.
func (c *Config) LibcNeedsMalloc() bool {
if c.GC() == "boehm" && c.Target.Libc == "wasi-libc" {
return true
}
return false
}
// LibraryPath returns the path to the library build directory. The path will be
// a library path in the cache directory (which might not yet be built).
func (c *Config) LibraryPath(name string) string {
archname := c.Triple() archname := c.Triple()
if c.CPU() != "" { if c.CPU() != "" {
archname += "-" + c.CPU() archname += "-" + c.CPU()
@@ -283,27 +228,18 @@ func (c *Config) LibraryPath(name string) string {
if c.ABI() != "" { if c.ABI() != "" {
archname += "-" + c.ABI() archname += "-" + c.ABI()
} }
if c.Target.SoftFloat {
archname += "-softfloat"
}
if name == "bdwgc" {
// Boehm GC is compiled against a particular libc.
archname += "-" + c.Target.Libc
}
// Append a version string, if this library has a version. // Try to load a precompiled library.
if v, ok := libVersions[name]; ok { precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
archname += "-v" + strconv.Itoa(v) if _, err := os.Stat(precompiledDir); err == nil {
} // Found a precompiled library for this OS/architecture. Return the path
// directly.
options := "" return precompiledDir, true
if c.LibcNeedsMalloc() {
options += "+malloc"
} }
// No precompiled library found. Determine the path name that will be used // No precompiled library found. Determine the path name that will be used
// in the build cache. // in the build cache.
return filepath.Join(goenv.Get("GOCACHE"), name+options+"-"+archname) return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
} }
// DefaultBinaryExtension returns the default extension for binaries, such as // DefaultBinaryExtension returns the default extension for binaries, such as
@@ -346,7 +282,57 @@ func (c *Config) CFlags(libclang bool) []string {
"-resource-dir="+resourceDir, "-resource-dir="+resourceDir,
) )
} }
cflags = append(cflags, c.LibcCFlags()...) switch c.Target.Libc {
case "darwin-libSystem":
root := goenv.Get("TINYGOROOT")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
)
case "picolibc":
root := goenv.Get("TINYGOROOT")
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
path, _ := c.LibcPath("picolibc")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
)
case "musl":
root := goenv.Get("TINYGOROOT")
path, _ := c.LibcPath("musl")
arch := MuslArchitecture(c.Triple())
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
"-isystem", filepath.Join(root, "lib", "musl", "include"),
)
case "wasi-libc":
root := goenv.Get("TINYGOROOT")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", root+"/lib/wasi-libc/sysroot/include")
case "wasmbuiltins":
// nothing to add (library is purely for builtins)
case "mingw-w64":
root := goenv.Get("TINYGOROOT")
path, _ := c.LibcPath("mingw-w64")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
"-D_UCRT",
)
case "":
// No libc specified, nothing to add.
default:
// Incorrect configuration. This could be handled in a better way, but
// usually this will be found by developers (not by TinyGo users).
panic("unknown libc: " + c.Target.Libc)
}
// Always emit debug information. It is optionally stripped at link time. // Always emit debug information. It is optionally stripped at link time.
cflags = append(cflags, "-gdwarf-4") cflags = append(cflags, "-gdwarf-4")
// Use the same optimization level as TinyGo. // Use the same optimization level as TinyGo.
@@ -373,80 +359,6 @@ func (c *Config) CFlags(libclang bool) []string {
return cflags return cflags
} }
// LibcCFlags returns the C compiler flags for the configured libc.
// It only uses flags that are part of the libc path (triple, cpu, abi, libc
// name) so it can safely be used to compile another C library.
func (c *Config) LibcCFlags() []string {
switch c.Target.Libc {
case "darwin-libSystem":
root := goenv.Get("TINYGOROOT")
return []string{
"-nostdlibinc",
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
}
case "picolibc":
root := goenv.Get("TINYGOROOT")
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
path := c.LibraryPath("picolibc")
return []string{
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
}
case "musl":
root := goenv.Get("TINYGOROOT")
path := c.LibraryPath("musl")
arch := MuslArchitecture(c.Triple())
return []string{
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
"-isystem", filepath.Join(root, "lib", "musl", "arch", "generic"),
"-isystem", filepath.Join(root, "lib", "musl", "include"),
}
case "wasi-libc":
path := c.LibraryPath("wasi-libc")
return []string{
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
}
case "wasmbuiltins":
// nothing to add (library is purely for builtins)
return nil
case "mingw-w64":
root := goenv.Get("TINYGOROOT")
path := c.LibraryPath("mingw-w64")
cflags := []string{
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
}
if c.GOARCH() == "386" {
cflags = append(cflags,
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
"-D_WIN32_WINNT=0x0501", // target Windows XP
)
} else {
cflags = append(cflags,
"-D_UCRT",
"-D_WIN32_WINNT=0x0a00", // target Windows 10
)
}
return cflags
case "":
// No libc specified, nothing to add.
return nil
default:
// Incorrect configuration. This could be handled in a better way, but
// usually this will be found by developers (not by TinyGo users).
panic("unknown libc: " + c.Target.Libc)
}
}
// LDFlags returns the flags to pass to the linker. A few more flags are needed // LDFlags returns the flags to pass to the linker. A few more flags are needed
// (like the one for the compiler runtime), but this represents the majority of // (like the one for the compiler runtime), but this represents the majority of
// the flags. // the flags.
@@ -461,8 +373,6 @@ func (c *Config) LDFlags() []string {
if c.Target.LinkerScript != "" { if c.Target.LinkerScript != "" {
ldflags = append(ldflags, "-T", c.Target.LinkerScript) ldflags = append(ldflags, "-T", c.Target.LinkerScript)
} }
ldflags = append(ldflags, c.Options.ExtLDFlags...)
return ldflags return ldflags
} }
@@ -530,13 +440,13 @@ func (c *Config) BinaryFormat(ext string) string {
// Programmer returns the flash method and OpenOCD interface name given a // Programmer returns the flash method and OpenOCD interface name given a
// particular configuration. It may either be all configured in the target JSON // particular configuration. It may either be all configured in the target JSON
// file or be modified using the -programmer command-line option. // file or be modified using the -programmmer command-line option.
func (c *Config) Programmer() (method, openocdInterface string) { func (c *Config) Programmer() (method, openocdInterface string) {
switch c.Options.Programmer { switch c.Options.Programmer {
case "": case "":
// No configuration supplied. // No configuration supplied.
return c.Target.FlashMethod, c.Target.OpenOCDInterface return c.Target.FlashMethod, c.Target.OpenOCDInterface
case "openocd", "msd", "command", "adb": case "openocd", "msd", "command":
// The -programmer flag only specifies the flash method. // The -programmer flag only specifies the flash method.
return c.Options.Programmer, c.Target.OpenOCDInterface return c.Options.Programmer, c.Target.OpenOCDInterface
case "bmp": case "bmp":
+4 -19
View File
@@ -8,11 +8,10 @@ import (
) )
var ( var (
validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"} validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise", "boehm"} validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSchedulerOptions = []string{"none", "tasks", "asyncify", "threads", "cores"}
validSerialOptions = []string{"none", "uart", "usb", "rtt"} validSerialOptions = []string{"none", "uart", "usb", "rtt"}
validPrintSizeOptions = []string{"none", "short", "full", "html"} validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"} validPanicStrategyOptions = []string{"print", "trap"}
validOptOptions = []string{"none", "0", "1", "2", "s", "z"} validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
) )
@@ -24,10 +23,8 @@ type Options struct {
GOOS string // environment variable GOOS string // environment variable
GOARCH string // environment variable GOARCH string // environment variable
GOARM string // environment variable (only used with GOARCH=arm) GOARM string // environment variable (only used with GOARCH=arm)
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
Directory string // working dir, leave it unset to use the current working dir Directory string // working dir, leave it unset to use the current working dir
Target string Target string
BuildMode string // -buildmode flag
Opt string Opt string
GC string GC string
PanicStrategy string PanicStrategy string
@@ -43,7 +40,6 @@ type Options struct {
PrintCommands func(cmd string, args ...string) `json:"-"` PrintCommands func(cmd string, args ...string) `json:"-"`
Semaphore chan struct{} `json:"-"` // -p flag controls cap Semaphore chan struct{} `json:"-"` // -p flag controls cap
Debug bool Debug bool
Nobounds bool
PrintSizes string PrintSizes string
PrintAllocs *regexp.Regexp // regexp string PrintAllocs *regexp.Regexp // regexp string
PrintStacks bool PrintStacks bool
@@ -53,25 +49,14 @@ type Options struct {
Programmer string Programmer string
OpenOCDCommands []string OpenOCDCommands []string
LLVMFeatures string LLVMFeatures string
PrintJSON bool
Monitor bool Monitor bool
BaudRate int BaudRate int
Timeout time.Duration Timeout time.Duration
WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option
ExtLDFlags []string
GoCompatibility bool // enable to check for Go version compatibility
} }
// Verify performs a validation on the given options, raising an error if options are not valid. // Verify performs a validation on the given options, raising an error if options are not valid.
func (o *Options) Verify() error { func (o *Options) Verify() error {
if o.BuildMode != "" {
valid := isInArray(validBuildModeOptions, o.BuildMode)
if !valid {
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
o.BuildMode,
strings.Join(validBuildModeOptions, ", "))
}
}
if o.GC != "" { if o.GC != "" {
valid := isInArray(validGCOptions, o.GC) valid := isInArray(validGCOptions, o.GC)
if !valid { if !valid {
+3 -3
View File
@@ -9,9 +9,9 @@ import (
func TestVerifyOptions(t *testing.T) { func TestVerifyOptions(t *testing.T) {
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise, boehm`) expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify, threads, cores`) expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`) expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`) expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
testCases := []struct { testCases := []struct {
+124 -239
View File
@@ -24,16 +24,13 @@ import (
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json // https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
type TargetSpec struct { type TargetSpec struct {
Inherits []string `json:"inherits,omitempty"` Inherits []string `json:"inherits,omitempty"`
InheritableOnly bool `json:"inheritable-only"` // this target is only meant to be inherited from, not used directly
Triple string `json:"llvm-target,omitempty"` Triple string `json:"llvm-target,omitempty"`
CPU string `json:"cpu,omitempty"` CPU string `json:"cpu,omitempty"`
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
Features string `json:"features,omitempty"` Features string `json:"features,omitempty"`
GOOS string `json:"goos,omitempty"` GOOS string `json:"goos,omitempty"`
GOARCH string `json:"goarch,omitempty"` GOARCH string `json:"goarch,omitempty"`
SoftFloat bool // used for non-baremetal systems (GOMIPS=softfloat etc)
BuildTags []string `json:"build-tags,omitempty"` BuildTags []string `json:"build-tags,omitempty"`
BuildMode string `json:"buildmode,omitempty"` // default build mode (if nothing specified)
GC string `json:"gc,omitempty"` GC string `json:"gc,omitempty"`
Scheduler string `json:"scheduler,omitempty"` Scheduler string `json:"scheduler,omitempty"`
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none) Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
@@ -47,7 +44,6 @@ type TargetSpec struct {
LinkerScript string `json:"linkerscript,omitempty"` LinkerScript string `json:"linkerscript,omitempty"`
ExtraFiles []string `json:"extra-files,omitempty"` ExtraFiles []string `json:"extra-files,omitempty"`
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
BootPatches []string `json:"boot-patches,omitempty"` // Bootloader patches to be applied in the order they appear.
Emulator string `json:"emulator,omitempty"` Emulator string `json:"emulator,omitempty"`
FlashCommand string `json:"flash-command,omitempty"` FlashCommand string `json:"flash-command,omitempty"`
GDB []string `json:"gdb,omitempty"` GDB []string `json:"gdb,omitempty"`
@@ -64,13 +60,8 @@ type TargetSpec struct {
OpenOCDCommands []string `json:"openocd-commands,omitempty"` OpenOCDCommands []string `json:"openocd-commands,omitempty"`
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device,omitempty"` JLinkDevice string `json:"jlink-device,omitempty"`
ADBPreCommands []string `json:"adb-pre-commands,omitempty"`
ADBPushRemote string `json:"adb-push-remote,omitempty"`
ADBPostCommands []string `json:"adb-post-commands,omitempty"`
CodeModel string `json:"code-model,omitempty"` CodeModel string `json:"code-model,omitempty"`
RelocationModel string `json:"relocation-model,omitempty"` RelocationModel string `json:"relocation-model,omitempty"`
WITPackage string `json:"wit-package,omitempty"`
WITWorld string `json:"wit-world,omitempty"`
} }
// 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.
@@ -93,10 +84,6 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
if src.Uint() != 0 { if src.Uint() != 0 {
dst.Set(src) dst.Set(src)
} }
case reflect.Bool:
if src.Bool() {
dst.Set(src)
}
case reflect.Ptr: // for pointers, copy if not nil case reflect.Ptr: // for pointers, copy if not nil
if !src.IsNil() { if !src.IsNil() {
dst.Set(src) dst.Set(src)
@@ -152,11 +139,6 @@ func (spec *TargetSpec) loadFromGivenStr(str string) error {
// resolveInherits loads inherited targets, recursively. // resolveInherits loads inherited targets, recursively.
func (spec *TargetSpec) resolveInherits() error { func (spec *TargetSpec) resolveInherits() error {
// Save InheritableOnly before resolving, since it must not propagate
// from parent to child (a board target should not become inheritable-only
// just because its parent processor target is).
inheritableOnly := spec.InheritableOnly
// First create a new spec with all the inherited properties. // First create a new spec with all the inherited properties.
newSpec := &TargetSpec{} newSpec := &TargetSpec{}
for _, name := range spec.Inherits { for _, name := range spec.Inherits {
@@ -182,31 +164,65 @@ func (spec *TargetSpec) resolveInherits() error {
} }
*spec = *newSpec *spec = *newSpec
// Restore InheritableOnly from the original spec, not from parents.
spec.InheritableOnly = inheritableOnly
return nil return nil
} }
// Load a target specification. // Load a target specification.
func LoadTarget(options *Options) (*TargetSpec, error) { func LoadTarget(options *Options) (*TargetSpec, error) {
if options.Target == "" && options.GOARCH == "wasm" {
// Set a specific target if we're building from a known GOOS/GOARCH
// combination that is defined in a target JSON file.
switch options.GOOS {
case "js":
options.Target = "wasm"
case "wasip1":
options.Target = "wasip1"
case "wasip2":
options.Target = "wasip2"
default:
return nil, errors.New("GOARCH=wasm but GOOS is not set correctly. Please set GOOS to js, wasip1, or wasip2.")
}
}
if options.Target == "" { if options.Target == "" {
return defaultTarget(options) // Configure based on GOOS/GOARCH environment variables (falling back to
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
var llvmarch string
switch options.GOARCH {
case "386":
llvmarch = "i386"
case "amd64":
llvmarch = "x86_64"
case "arm64":
llvmarch = "aarch64"
case "arm":
switch options.GOARM {
case "5":
llvmarch = "armv5"
case "6":
llvmarch = "armv6"
case "7":
llvmarch = "armv7"
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
}
case "wasm":
llvmarch = "wasm32"
default:
llvmarch = options.GOARCH
}
llvmvendor := "unknown"
llvmos := options.GOOS
switch llvmos {
case "darwin":
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
// to iOS!
llvmos = "macosx10.12.0"
if llvmarch == "aarch64" {
// Looks like Apple prefers to call this architecture ARM64
// instead of AArch64.
llvmarch = "arm64"
llvmos = "macosx11.0.0"
}
llvmvendor = "apple"
case "wasip1":
llvmos = "wasi"
}
// Target triples (which actually have four components, but are called
// triples for historical reasons) have the form:
// arch-vendor-os-environment
target := llvmarch + "-" + llvmvendor + "-" + llvmos
if options.GOOS == "windows" {
target += "-gnu"
} else if options.GOARCH == "arm" {
target += "-gnueabihf"
}
return defaultTarget(options.GOOS, options.GOARCH, target)
} }
// See whether there is a target specification for this target (e.g. // See whether there is a target specification for this target (e.g.
@@ -251,17 +267,10 @@ func GetTargetSpecs() (map[string]*TargetSpec, error) {
continue continue
} }
path := filepath.Join(dir, entry.Name()) path := filepath.Join(dir, entry.Name())
spec, err := LoadTarget(&Options{Target: path}) spec, err := LoadTarget(&Options{Target: path})
if err != nil { if err != nil {
return nil, fmt.Errorf("could not list target: %w", err) return nil, fmt.Errorf("could not list target: %w", err)
} }
if spec.InheritableOnly {
// Skip targets that are only meant to be inherited from, not used directly.
continue
}
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" { if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
// This doesn't look like a regular target file, but rather like // This doesn't look like a regular target file, but rather like
// a parent target (such as targets/cortex-m.json). // a parent target (such as targets/cortex-m.json).
@@ -274,162 +283,75 @@ func GetTargetSpecs() (map[string]*TargetSpec, error) {
return maps, nil return maps, nil
} }
// Load a target from environment variables (which default to func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// runtime.GOOS/runtime.GOARCH). // No target spec available. Use the default one, useful on most systems
func defaultTarget(options *Options) (*TargetSpec, error) { // with a regular OS.
spec := TargetSpec{ spec := TargetSpec{
GOOS: options.GOOS, Triple: triple,
GOARCH: options.GOARCH, GOOS: goos,
BuildTags: []string{options.GOOS, options.GOARCH}, GOARCH: goarch,
BuildTags: []string{goos, goarch},
GC: "precise",
Scheduler: "tasks",
Linker: "cc", Linker: "cc",
DefaultStackSize: 1024 * 64, // 64kB DefaultStackSize: 1024 * 64, // 64kB
GDB: []string{"gdb"}, GDB: []string{"gdb"},
PortReset: "false", PortReset: "false",
} }
switch goarch {
// Configure target based on GOARCH.
var llvmarch string
switch options.GOARCH {
case "386": case "386":
llvmarch = "i386"
spec.CPU = "pentium4" spec.CPU = "pentium4"
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "amd64": case "amd64":
llvmarch = "x86_64"
spec.CPU = "x86-64" spec.CPU = "x86-64"
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" spec.Features = "+cmov,+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", "-fno-asynchronous-unwind-tables")
subarch := strings.Split(options.GOARM, ",") switch strings.Split(triple, "-")[0] {
if len(subarch) > 2 { case "armv5":
return nil, fmt.Errorf("invalid GOARM=%s, must be of form <num>,[hardfloat|softfloat]", options.GOARM) 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"
} case "armv6":
archLevel := subarch[0] spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
var fpu string case "armv7":
if len(subarch) >= 2 { spec.Features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
fpu = subarch[1]
} else {
// Pick the default fpu value: softfloat for armv5 and hardfloat
// above that.
if archLevel == "5" {
fpu = "softfloat"
} else {
fpu = "hardfloat"
}
}
switch fpu {
case "softfloat":
spec.CFlags = append(spec.CFlags, "-msoft-float")
spec.SoftFloat = true
case "hardfloat":
// Hardware floating point support is the default everywhere except
// on ARMv5 where it needs to be enabled explicitly.
if archLevel == "5" {
spec.CFlags = append(spec.CFlags, "-mfpu=vfpv2")
}
default:
return nil, fmt.Errorf("invalid extension GOARM=%s, must be softfloat or hardfloat", options.GOARM)
}
switch archLevel {
case "5":
llvmarch = "armv5"
if spec.SoftFloat {
spec.Features = "+armv5t,+soft-float,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
} else {
spec.Features = "+armv5t,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
}
case "6":
llvmarch = "armv6"
if spec.SoftFloat {
spec.Features = "+armv6,+dsp,+soft-float,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
} else {
spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
}
case "7":
llvmarch = "armv7"
if spec.SoftFloat {
spec.Features = "+armv7-a,+dsp,+soft-float,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
} else {
spec.Features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
}
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be of form <num>,[hardfloat|softfloat] where num is 5, 6, or 7", options.GOARM)
} }
case "arm64": case "arm64":
spec.CPU = "generic" spec.CPU = "generic"
llvmarch = "aarch64" if goos == "darwin" {
if options.GOOS == "darwin" { spec.Features = "+fp-armv8,+neon"
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a" } else if goos == "windows" {
// Looks like Apple prefers to call this architecture ARM64 spec.Features = "+fp-armv8,+neon,-fmv"
// instead of AArch64.
llvmarch = "arm64"
} else if options.GOOS == "windows" {
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv"
} else { // linux } else { // linux
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv,-outline-atomics" spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
}
case "mips", "mipsle":
spec.CPU = "mips32"
spec.CFlags = append(spec.CFlags, "-fno-pic")
if options.GOARCH == "mips" {
llvmarch = "mips" // big endian
} else {
llvmarch = "mipsel" // little endian
}
switch options.GOMIPS {
case "hardfloat":
spec.Features = "+fpxx,+mips32,+nooddspreg,-noabicalls"
case "softfloat":
spec.SoftFloat = true
spec.Features = "+mips32,+soft-float,-noabicalls"
spec.CFlags = append(spec.CFlags, "-msoft-float")
default:
return nil, fmt.Errorf("invalid GOMIPS=%s: must be hardfloat or softfloat", options.GOMIPS)
} }
case "wasm": case "wasm":
return nil, fmt.Errorf("GOARCH=wasm but GOOS is unset. Please set GOOS to js, wasip1, or wasip2.") spec.CPU = "generic"
default: spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
return nil, fmt.Errorf("unknown GOARCH=%s", options.GOARCH) spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
spec.CFlags = append(spec.CFlags,
"-mbulk-memory",
"-mnontrapping-fptoint",
"-msign-ext",
)
} }
if goos == "darwin" {
// Configure target based on GOOS.
llvmos := options.GOOS
llvmvendor := "unknown"
switch options.GOOS {
case "darwin":
spec.GC = "boehm"
platformVersion := "10.12.0"
if options.GOARCH == "arm64" {
platformVersion = "11.0.0" // first macosx platform with arm64 support
}
llvmvendor = "apple"
spec.Scheduler = "threads"
spec.Linker = "ld.lld" spec.Linker = "ld.lld"
spec.Libc = "darwin-libSystem" spec.Libc = "darwin-libSystem"
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to arch := strings.Split(triple, "-")[0]
// iOS! platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
llvmos = "macosx" + platformVersion
spec.LDFlags = append(spec.LDFlags, spec.LDFlags = append(spec.LDFlags,
"-flavor", "darwin", "-flavor", "darwin",
"-dead_strip", "-dead_strip",
"-arch", llvmarch, "-arch", arch,
"-platform_version", "macos", platformVersion, platformVersion, "-platform_version", "macos", platformVersion, platformVersion,
) )
spec.ExtraFiles = append(spec.ExtraFiles, } else if goos == "linux" {
"src/internal/futex/futex_darwin.c",
"src/internal/task/task_threads.c",
"src/runtime/os_darwin.c",
"src/runtime/runtime_unix.c",
"src/runtime/signal.c")
case "linux":
spec.GC = "boehm"
spec.Scheduler = "threads"
spec.Linker = "ld.lld" spec.Linker = "ld.lld"
spec.RTLib = "compiler-rt" spec.RTLib = "compiler-rt"
spec.Libc = "musl" spec.Libc = "musl"
spec.LDFlags = append(spec.LDFlags, "--gc-sections") spec.LDFlags = append(spec.LDFlags, "--gc-sections")
if options.GOARCH == "arm64" { if goarch == "arm64" {
// Disable outline atomics. For details, see: // Disable outline atomics. For details, see:
// https://cpufun.substack.com/p/atomics-in-aarch64 // https://cpufun.substack.com/p/atomics-in-aarch64
// A better way would be to fully support outline atomics, which // A better way would be to fully support outline atomics, which
@@ -443,32 +365,17 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
// proper threading. // proper threading.
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics") spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
} }
spec.ExtraFiles = append(spec.ExtraFiles, } else if goos == "windows" {
"src/internal/futex/futex_linux.c",
"src/internal/task/task_threads.c",
"src/runtime/runtime_unix.c",
"src/runtime/signal.c")
case "windows":
spec.GC = "boehm"
spec.Scheduler = "tasks"
spec.Linker = "ld.lld" spec.Linker = "ld.lld"
spec.Libc = "mingw-w64" spec.Libc = "mingw-w64"
switch options.GOARCH { // Note: using a medium code model, low image base and no ASLR
case "386": // because Go doesn't really need those features. ASLR patches
spec.LDFlags = append(spec.LDFlags, // around issues for unsafe languages like C/C++ that are not
"-m", "i386pe", // normally present in Go (without explicitly opting in).
"--major-os-version", "4", // For more discussion:
"--major-subsystem-version", "4", // https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
) switch goarch {
// __udivdi3 is not present in ucrt it seems.
spec.RTLib = "compiler-rt"
case "amd64": case "amd64":
// Note: using a medium code model, low image base and no ASLR
// because Go doesn't really need those features. ASLR patches
// around issues for unsafe languages like C/C++ that are not
// normally present in Go (without explicitly opting in).
// For more discussion:
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
spec.LDFlags = append(spec.LDFlags, spec.LDFlags = append(spec.LDFlags,
"-m", "i386pep", "-m", "i386pep",
"--image-base", "0x400000", "--image-base", "0x400000",
@@ -484,57 +391,40 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
"--no-insert-timestamp", "--no-insert-timestamp",
"--no-dynamicbase", "--no-dynamicbase",
) )
case "wasm", "wasip1", "wasip2": } else if goos == "wasip1" {
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS) spec.GC = "" // use default GC
default: spec.Scheduler = "asyncify"
return nil, fmt.Errorf("unknown GOOS=%s", options.GOOS) spec.Linker = "wasm-ld"
spec.RTLib = "compiler-rt"
spec.Libc = "wasi-libc"
spec.DefaultStackSize = 1024 * 64 // 64kB
spec.LDFlags = append(spec.LDFlags,
"--stack-first",
"--no-demangle",
)
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/asm_tinygowasm.S",
"src/internal/task/task_asyncify_wasm.S",
)
} else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
} }
if goarch != "wasm" {
if spec.GC == "boehm" {
// Add this file only when needed. This fixes a build failure on
// Windows.
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_boehm.c")
}
// Target triples (which actually have four components, but are called
// triples for historical reasons) have the form:
// arch-vendor-os-environment
spec.Triple = llvmarch + "-" + llvmvendor + "-" + llvmos
if options.GOOS == "windows" {
spec.Triple += "-gnu"
} else if options.GOOS == "linux" {
// We use musl on Linux (not glibc) so we should use -musleabi* instead
// of -gnueabi*.
// The *hf suffix selects between soft/hard floating point ABI.
if spec.SoftFloat {
spec.Triple += "-musleabi"
} else {
spec.Triple += "-musleabihf"
}
}
// Add extra assembly files (needed for the scheduler etc).
if options.GOARCH != "wasm" {
suffix := "" suffix := ""
if options.GOOS == "windows" && options.GOARCH == "amd64" { if goos == "windows" && goarch == "amd64" {
// Windows uses a different calling convention on amd64 from other // Windows uses a different calling convention on amd64 from other
// operating systems so we need separate assembly files. // operating systems so we need separate assembly files.
suffix = "_windows" suffix = "_windows"
} }
asmGoarch := options.GOARCH spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
if options.GOARCH == "mips" || options.GOARCH == "mipsle" { spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
asmGoarch = "mipsx"
}
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
} }
if goarch != runtime.GOARCH {
// Configure the emulator.
if options.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 options.GOOS == "linux" { if goos == "linux" {
switch options.GOARCH { switch goarch {
case "386": case "386":
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case. // amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
if runtime.GOARCH != "amd64" { if runtime.GOARCH != "amd64" {
@@ -546,19 +436,14 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
spec.Emulator = "qemu-arm {}" spec.Emulator = "qemu-arm {}"
case "arm64": case "arm64":
spec.Emulator = "qemu-aarch64 {}" spec.Emulator = "qemu-aarch64 {}"
case "mips":
spec.Emulator = "qemu-mips {}"
case "mipsle":
spec.Emulator = "qemu-mipsel {}"
} }
} }
} }
if options.GOOS != runtime.GOOS { if goos != runtime.GOOS {
if options.GOOS == "windows" { if goos == "windows" {
spec.Emulator = "wine {}" spec.Emulator = "wine {}"
} }
} }
return &spec, nil return &spec, nil
} }
-31
View File
@@ -23,37 +23,6 @@ func TestLoadTarget(t *testing.T) {
} }
} }
func TestGetTargetSpecs_InheritableOnlyTargetsExcluded(t *testing.T) {
specs, err := GetTargetSpecs()
if err != nil {
t.Fatal("GetTargetSpecs failed:", err)
}
// Inheritable-only processor-level targets should not appear in the listing.
inheritableOnlyTargets := []string{"esp32", "esp32c3", "esp32s3", "esp8266", "rp2040", "rp2350", "rp2350b"}
for _, name := range inheritableOnlyTargets {
if _, ok := specs[name]; ok {
t.Errorf("inheritable-only target %q should not appear in GetTargetSpecs", name)
}
}
// Board targets that inherit from inheritable-only targets should still appear.
boardTargets := []string{"esp32-coreboard-v2", "pico"}
for _, name := range boardTargets {
if _, ok := specs[name]; !ok {
t.Errorf("board target %q should appear in GetTargetSpecs", name)
}
}
}
func TestLoadTarget_InheritableOnlyTargetStillLoadable(t *testing.T) {
// Inheritable-only targets should still be loadable directly (for building).
_, err := LoadTarget(&Options{Target: "esp32"})
if err != nil {
t.Errorf("LoadTarget should still load inheritable-only target esp32: %v", err)
}
}
func TestOverrideProperties(t *testing.T) { func TestOverrideProperties(t *testing.T) {
baseAutoStackSize := true baseAutoStackSize := true
base := &TargetSpec{ base := &TargetSpec{
+5 -6
View File
@@ -18,12 +18,11 @@ 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.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
"crypto/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric", "crypto/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",
"crypto/sha256.block": "crypto/sha256.blockGeneric", "crypto/sha256.block": "crypto/sha256.blockGeneric",
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric", "crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
"internal/chacha8rand.block": "internal/chacha8rand.block_generic",
// AES // AES
"crypto/aes.decryptBlockAsm": "crypto/aes.decryptBlock", "crypto/aes.decryptBlockAsm": "crypto/aes.decryptBlock",
+2 -2
View File
@@ -99,7 +99,7 @@ func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value,
// However, in practice, it is also necessary to check that the length is // However, in practice, it is also necessary to check that the length is
// not too big that a GEP wouldn't be possible without wrapping the pointer. // not too big that a GEP wouldn't be possible without wrapping the pointer.
// These two checks (non-negative and not too big) can be merged into one // These two checks (non-negative and not too big) can be merged into one
// using an unsigned greater than. // using an unsiged greater than.
// Make sure the len value is at least as big as a uintptr. // Make sure the len value is at least as big as a uintptr.
len = b.extendInteger(len, lenType, b.uintptrType) len = b.extendInteger(len, lenType, b.uintptrType)
@@ -245,7 +245,7 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
// current insert position. // 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.insertBasicBlock(blockPrefix + ".next")
b.currentBlockInfo.exit = 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.
b.CreateCondBr(assert, faultBlock, nextBlock) b.CreateCondBr(assert, faultBlock, nextBlock)
+17 -10
View File
@@ -1,6 +1,9 @@
package compiler package compiler
import ( import (
"fmt"
"strings"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -12,19 +15,23 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn)) val := b.getValue(b.fn.Params[1], getPos(b.fn))
if strings.HasPrefix(b.Triple, "avr") {
// 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)
// - The result is the new value instead of the old value
vType := val.Type()
name := fmt.Sprintf("__sync_fetch_and_add_%d", vType.IntTypeWidth()/8)
fn := b.mod.NamedFunction(name)
if fn.IsNil() {
fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false))
}
oldVal := b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val}, "")
// Return the new value, not the original value returned.
return b.CreateAdd(oldVal, val, "")
}
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true) oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
// Return the new value, not the original value returned by atomicrmw. // Return the new value, not the original value returned by atomicrmw.
return b.CreateAdd(oldVal, val, "") return b.CreateAdd(oldVal, val, "")
case "AndInt32", "AndInt64", "AndUint32", "AndUint64", "AndUintptr":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn))
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAnd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
return oldVal
case "OrInt32", "OrInt64", "OrUint32", "OrUint64", "OrUintptr":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn))
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpOr, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
return oldVal
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer": case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn)) val := b.getValue(b.fn.Params[1], getPos(b.fn))
+6 -24
View File
@@ -19,9 +19,8 @@ const maxFieldsPerParam = 3
// useful while declaring or defining a function. // useful while declaring or defining a function.
type paramInfo struct { type paramInfo struct {
llvmType llvm.Type llvmType llvm.Type
name string // name, possibly with suffixes for e.g. struct fields name string // name, possibly with suffixes for e.g. struct fields
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
flags paramFlags // extra flags for this parameter
} }
// paramFlags identifies parameter attributes for flags. Most importantly, it // paramFlags identifies parameter attributes for flags. Most importantly, it
@@ -29,12 +28,9 @@ type paramInfo struct {
type paramFlags uint8 type paramFlags uint8
const ( const (
// Whether this is a full or partial Go parameter (int, slice, etc). // Parameter may have the deferenceable_or_null attribute. This attribute
// The extra context parameter is not a Go parameter. // cannot be applied to unsafe.Pointer and to the data pointer of slices.
paramIsGoParam = 1 << iota paramIsDeferenceableOrNull = 1 << iota
// Whether this is a readonly parameter (for example, a string pointer).
paramIsReadonly
) )
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or // createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
@@ -79,15 +75,7 @@ func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value,
fragments := b.expandFormalParam(arg) fragments := b.expandFormalParam(arg)
expanded = append(expanded, fragments...) expanded = append(expanded, fragments...)
} }
call := b.CreateCall(fnType, fn, expanded, name) return b.CreateCall(fnType, fn, expanded, name)
if !fn.IsAFunction().IsNil() {
if cc := fn.FunctionCallConv(); cc != llvm.CCallConv {
// Set a different calling convention if needed.
// This is needed for GetModuleHandleExA on Windows, for example.
call.SetInstructionCallConv(cc)
}
}
return call
} }
// createInvoke is like createCall but continues execution at the landing pad if // createInvoke is like createCall but continues execution at the landing pad if
@@ -170,7 +158,6 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
continue continue
} }
suffix := strconv.Itoa(i) suffix := strconv.Itoa(i)
isString := false
if goType != nil { if goType != nil {
// Try to come up with a good suffix for this struct field, // Try to come up with a good suffix for this struct field,
// depending on which Go type it's based on. // depending on which Go type it's based on.
@@ -187,16 +174,12 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
suffix = []string{"r", "i"}[i] suffix = []string{"r", "i"}[i]
case types.String: case types.String:
suffix = []string{"data", "len"}[i] suffix = []string{"data", "len"}[i]
isString = true
} }
case *types.Signature: case *types.Signature:
suffix = []string{"context", "funcptr"}[i] suffix = []string{"context", "funcptr"}[i]
} }
} }
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i)) subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
if isString {
subInfos[0].flags |= paramIsReadonly
}
paramInfos = append(paramInfos, subInfos...) paramInfos = append(paramInfos, subInfos...)
} }
return paramInfos return paramInfos
@@ -212,7 +195,6 @@ func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Ty
info := paramInfo{ info := paramInfo{
llvmType: t, llvmType: t,
name: name, name: name,
flags: paramIsGoParam,
} }
if goType != nil { if goType != nil {
switch underlying := goType.Underlying().(type) { switch underlying := goType.Underlying().(type) {
+17 -36
View File
@@ -4,9 +4,7 @@ package compiler
// or pseudo-operations that are lowered during goroutine lowering. // or pseudo-operations that are lowered during goroutine lowering.
import ( import (
"fmt"
"go/types" "go/types"
"math"
"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"
@@ -43,17 +41,17 @@ func (b *builder) createChanSend(instr *ssa.Send) {
b.CreateStore(chanValue, valueAlloca) b.CreateStore(chanValue, valueAlloca)
} }
// Allocate buffer for the channel operation. // Allocate blockedlist buffer.
channelOp := b.getLLVMRuntimeType("channelOp") channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op") channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the send. // Do the send.
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "") b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
// End the lifetime of the allocas. // End the lifetime of the allocas.
// This also works around a bug in CoroSplit, at least in LLVM 8: // This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742 // https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize) b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
if !isZeroSize { if !isZeroSize {
b.emitLifetimeEnd(valueAlloca, valueAllocaSize) b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
} }
@@ -74,12 +72,12 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
} }
// Allocate buffer for the channel operation. // Allocate blockedlist buffer.
channelOp := b.getLLVMRuntimeType("channelOp") channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op") channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the receive. // Do the receive.
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "") commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
var received llvm.Value var received llvm.Value
if isZeroSize { if isZeroSize {
received = llvm.ConstNull(valueType) received = llvm.ConstNull(valueType)
@@ -87,7 +85,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
received = b.CreateLoad(valueType, valueAlloca, "chan.received") received = b.CreateLoad(valueType, valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAlloca, valueAllocaSize) b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
} }
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize) b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
if unop.CommaOk { if unop.CommaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)) tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
@@ -126,20 +124,6 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
} }
} }
const maxSelectStates = math.MaxUint32 >> 2
if len(expr.States) > maxSelectStates {
// The runtime code assumes that the number of state must fit in 30 bits
// (so the select index can be stored in a uint32 with two bits reserved
// for other purposes). It seems unlikely that a real program would have
// that many states, but we check for this case anyway to be sure.
// We use a uint32 (and not a uintptr or uint64) to avoid 64-bit atomic
// operations which aren't available everywhere.
b.addError(expr.Pos(), fmt.Sprintf("too many select states: got %d but the maximum supported number is %d", len(expr.States), maxSelectStates))
// Continue as usual (we'll generate broken code but the error will
// prevent the compilation to complete).
}
// This code create a (stack-allocated) slice containing all the select // This code create a (stack-allocated) slice containing all the select
// cases and then calls runtime.chanSelect to perform the actual select // cases and then calls runtime.chanSelect to perform the actual select
// statement. // statement.
@@ -214,10 +198,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
if expr.Blocking { if expr.Blocking {
// Stack-allocate operation structures. // Stack-allocate operation structures.
// If these were simply created as a slice, they would heap-allocate. // If these were simply created as a slice, they would heap-allocate.
opsAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelOp"), len(selectStates)) chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
opsAlloca, opsSize := b.createTemporaryAlloca(opsAllocaType, "select.block.alloca") chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
opsLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false) chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
opsPtr := b.CreateGEP(opsAllocaType, opsAlloca, []llvm.Value{ chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 0, false),
}, "select.block") }, "select.block")
@@ -225,18 +209,15 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
results = b.createRuntimeCall("chanSelect", []llvm.Value{ results = b.createRuntimeCall("chanSelect", []llvm.Value{
recvbuf, recvbuf,
statesPtr, statesLen, statesLen, // []chanSelectState statesPtr, statesLen, statesLen, // []chanSelectState
opsPtr, opsLen, opsLen, // []channelOp chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList
}, "select.result") }, "select.result")
// Terminate the lifetime of the operation structures. // Terminate the lifetime of the operation structures.
b.emitLifetimeEnd(opsAlloca, opsSize) b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
} else { } else {
opsPtr := llvm.ConstNull(b.dataPtrType) results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
opsLen := llvm.ConstInt(b.uintptrType, 0, false)
results = b.createRuntimeCall("chanSelect", []llvm.Value{
recvbuf, recvbuf,
statesPtr, statesLen, statesLen, // []chanSelectState statesPtr, statesLen, statesLen, // []chanSelectState
opsPtr, opsLen, opsLen, // []channelOp (nil slice)
}, "select.result") }, "select.result")
} }
+46 -210
View File
@@ -17,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"
"github.com/tinygo-org/tinygo/src/tinygo"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@@ -45,7 +44,6 @@ type Config struct {
ABI string ABI string
GOOS string GOOS string
GOARCH string GOARCH string
BuildMode string
CodeModel string CodeModel string
RelocationModel string RelocationModel string
SizeLevel int SizeLevel int
@@ -58,7 +56,6 @@ type Config struct {
MaxStackAlloc uint64 MaxStackAlloc uint64
NeedsStackObjects bool NeedsStackObjects bool
Debug bool // Whether to emit debug information in the LLVM module. Debug bool // Whether to emit debug information in the LLVM module.
Nobounds bool // Whether to skip bounds checks
PanicStrategy string PanicStrategy string
} }
@@ -152,12 +149,10 @@ type builder struct {
llvmFnType llvm.Type llvmFnType llvm.Type
llvmFn llvm.Value llvmFn llvm.Value
info functionInfo info functionInfo
locals map[ssa.Value]llvm.Value // local variables locals map[ssa.Value]llvm.Value // local variables
blockInfo []blockInfo blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
currentBlock *ssa.BasicBlock currentBlock *ssa.BasicBlock
currentBlockInfo *blockInfo
tarjanStack []uint
tarjanIndex uint
phis []phiNode phis []phiNode
deferPtr llvm.Value deferPtr llvm.Value
deferFrame llvm.Value deferFrame llvm.Value
@@ -189,22 +184,11 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
info: c.getFunctionInfo(f), info: c.getFunctionInfo(f),
locals: make(map[ssa.Value]llvm.Value), locals: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata), dilocals: make(map[*types.Var]llvm.Metadata),
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
} }
} }
type blockInfo struct {
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
entry llvm.BasicBlock
// exit is the LLVM basic block corresponding to the end of this *ssa.Block.
// It will be different than entry if any of the block's instructions contain internal branches.
exit llvm.BasicBlock
// tarjan holds state for applying Tarjan's strongly connected components algorithm to the CFG.
// This is used by defer.go to determine whether to stack- or heap-allocate defer data.
tarjan tarjanNode
}
type deferBuiltin struct { type deferBuiltin struct {
callName string callName string
pos token.Pos pos token.Pos
@@ -258,7 +242,7 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
} }
// Sizes returns a types.Sizes appropriate for the given target machine. It // Sizes returns a types.Sizes appropriate for the given target machine. It
// includes the correct int size and alignment as is necessary for the Go // includes the correct int size and aligment as is necessary for the Go
// typechecker. // typechecker.
func Sizes(machine llvm.TargetMachine) types.Sizes { func Sizes(machine llvm.TargetMachine) types.Sizes {
targetData := machine.CreateTargetData() targetData := machine.CreateTargetData()
@@ -402,7 +386,7 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use // makeLLVMType creates a LLVM type for a Go type. Don't call this, use
// getLLVMType instead. // getLLVMType instead.
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type { func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
switch typ := types.Unalias(goType).(type) { switch typ := goType.(type) {
case *types.Array: case *types.Array:
elemType := c.getLLVMType(typ.Elem()) elemType := c.getLLVMType(typ.Elem())
return llvm.ArrayType(elemType, int(typ.Len())) return llvm.ArrayType(elemType, int(typ.Len()))
@@ -510,21 +494,6 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
llvmType := c.getLLVMType(typ) llvmType := c.getLLVMType(typ)
sizeInBytes := c.targetData.TypeAllocSize(llvmType) sizeInBytes := c.targetData.TypeAllocSize(llvmType)
switch typ := typ.(type) { switch typ := typ.(type) {
case *types.Alias:
// Implement types.Alias just like types.Named: by treating them like a
// C typedef.
temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
Tag: dwarf.TagTypedef,
SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
})
c.ditypes[typ] = temporaryMDNode
md := c.dibuilder.CreateTypedef(llvm.DITypedef{
Type: c.getDIType(types.Unalias(typ)), // TODO: use typ.Rhs in Go 1.23
Name: typ.String(),
})
temporaryMDNode.ReplaceAllUsesWith(md)
return md
case *types.Array: case *types.Array:
return c.dibuilder.CreateArrayType(llvm.DIArrayType{ return c.dibuilder.CreateArrayType(llvm.DIArrayType{
SizeInBits: sizeInBytes * 8, SizeInBits: sizeInBytes * 8,
@@ -873,10 +842,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
// with a LLVM intrinsic. // with a LLVM intrinsic.
continue continue
} }
if ok := b.defineCryptoIntrinsic(); ok {
// Body of this function was replaced
continue
}
if member.Blocks == nil { if member.Blocks == nil {
// Try to define this as an intrinsic function. // Try to define this as an intrinsic function.
b.defineIntrinsicFunction() b.defineIntrinsicFunction()
@@ -891,11 +856,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
// Interfaces don't have concrete methods. // Interfaces don't have concrete methods.
continue continue
} }
if _, isalias := member.Type().(*types.Alias); isalias {
// Aliases don't need to be redefined, since they just refer to
// an already existing type whose methods will be defined.
continue
}
// Named type. We should make sure all methods are created. // Named type. We should make sure all methods are created.
// This includes both functions with pointer receivers and those // This includes both functions with pointer receivers and those
@@ -1237,29 +1197,14 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// intrinsic (like an atomic operation). Create the entry block // intrinsic (like an atomic operation). Create the entry block
// manually. // manually.
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry") entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
// Intrinsics may create internal branches (e.g. nil checks).
// They will attempt to access b.currentBlockInfo to update the exit block.
// Create some fake block info for them to access.
blockInfo := []blockInfo{
{
entry: entryBlock,
exit: entryBlock,
},
}
b.blockInfo = blockInfo
b.currentBlockInfo = &blockInfo[0]
} else { } else {
blocks := b.fn.Blocks
blockInfo := make([]blockInfo, len(blocks))
for _, block := range b.fn.DomPreorder() { for _, block := range b.fn.DomPreorder() {
info := &blockInfo[block.Index]
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment) llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
info.entry = llvmBlock b.blockEntries[block] = llvmBlock
info.exit = llvmBlock b.blockExits[block] = llvmBlock
} }
b.blockInfo = blockInfo
// Normal functions have an entry block. // Normal functions have an entry block.
entryBlock = blockInfo[0].entry entryBlock = b.blockEntries[b.fn.Blocks[0]]
} }
b.SetInsertPointAtEnd(entryBlock) b.SetInsertPointAtEnd(entryBlock)
@@ -1355,9 +1300,8 @@ func (b *builder) createFunction() {
if b.DumpSSA { if b.DumpSSA {
fmt.Printf("%d: %s:\n", block.Index, block.Comment) fmt.Printf("%d: %s:\n", block.Index, block.Comment)
} }
b.SetInsertPointAtEnd(b.blockEntries[block])
b.currentBlock = block b.currentBlock = block
b.currentBlockInfo = &b.blockInfo[block.Index]
b.SetInsertPointAtEnd(b.currentBlockInfo.entry)
for _, instr := range block.Instrs { for _, instr := range block.Instrs {
if instr, ok := instr.(*ssa.DebugRef); ok { if instr, ok := instr.(*ssa.DebugRef); ok {
if !b.Debug { if !b.Debug {
@@ -1417,7 +1361,7 @@ func (b *builder) createFunction() {
block := phi.ssa.Block() block := phi.ssa.Block()
for i, edge := range phi.ssa.Edges { for i, edge := range phi.ssa.Edges {
llvmVal := b.getValue(edge, getPos(phi.ssa)) llvmVal := b.getValue(edge, getPos(phi.ssa))
llvmBlock := b.blockInfo[block.Preds[i].Index].exit llvmBlock := b.blockExits[block.Preds[i]]
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock}) phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
} }
} }
@@ -1440,11 +1384,6 @@ func (b *builder) createFunction() {
b.llvmFn.SetLinkage(llvm.InternalLinkage) b.llvmFn.SetLinkage(llvm.InternalLinkage)
b.createFunction() b.createFunction()
} }
// Create wrapper function that can be called externally.
if b.info.wasmExport != "" {
b.createWasmExport()
}
} }
// posser is an interface that's implemented by both ssa.Value and // posser is an interface that's implemented by both ssa.Value and
@@ -1531,11 +1470,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
case *ssa.If: case *ssa.If:
cond := b.getValue(instr.Cond, getPos(instr)) cond := b.getValue(instr.Cond, getPos(instr))
block := instr.Block() block := instr.Block()
blockThen := b.blockInfo[block.Succs[0].Index].entry blockThen := b.blockEntries[block.Succs[0]]
blockElse := b.blockInfo[block.Succs[1].Index].entry blockElse := b.blockEntries[block.Succs[1]]
b.CreateCondBr(cond, blockThen, blockElse) b.CreateCondBr(cond, blockThen, blockElse)
case *ssa.Jump: case *ssa.Jump:
blockJump := b.blockInfo[instr.Block().Succs[0].Index].entry blockJump := b.blockEntries[instr.Block().Succs[0]]
b.CreateBr(blockJump) b.CreateBr(blockJump)
case *ssa.MapUpdate: case *ssa.MapUpdate:
m := b.getValue(instr.Map, getPos(instr)) m := b.getValue(instr.Map, getPos(instr))
@@ -1603,8 +1542,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
elemLayout := b.createObjectLayout(elemType, pos) result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize, elemLayout}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr") newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
newLen := b.CreateExtractValue(result, 1, "append.newLen") newLen := b.CreateExtractValue(result, 1, "append.newLen")
newCap := b.CreateExtractValue(result, 2, "append.newCap") newCap := b.CreateExtractValue(result, 2, "append.newCap")
@@ -1686,41 +1624,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case "copy": case "copy":
dst := argValues[0] dst := argValues[0]
src := argValues[1] src := argValues[1]
// Fetch the lengths.
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen") dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen") srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
// Find the minimum of the lengths. dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
minFuncName := "llvm.umin.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
minFunc := b.mod.NamedFunction(minFuncName)
if minFunc.IsNil() {
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType, b.uintptrType}, false)
minFunc = llvm.AddFunction(b.mod, minFuncName, fnType)
}
minLen := b.CreateCall(minFunc.GlobalValueType(), minFunc, []llvm.Value{dstLen, srcLen}, "copy.n")
// Multiply the length by the element size.
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
// NOTE: This is also NSW when uintptr is int, but we can only choose one through the C API? return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
size := b.CreateNUWMul(minLen, elemSize, "copy.size")
// Fetch the pointers.
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstPtr")
srcBuf := b.CreateExtractValue(src, 0, "copy.srcPtr")
// Create a memcpy.
call := b.createMemCopy("memmove", dstBuf, srcBuf, size)
align := b.targetData.ABITypeAlignment(elemType)
if align > 1 {
// Apply the type's alignment to the arguments.
// LLVM sometimes turns constant-length moves into loads and stores.
// It may use this alignment for the created loads and stores.
alignAttr := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align))
call.AddCallSiteAttribute(1, alignAttr)
call.AddCallSiteAttribute(2, alignAttr)
}
// Extend and return the copied length.
if b.targetData.TypeAllocSize(minLen.Type()) < b.targetData.TypeAllocSize(b.intType) {
minLen = b.CreateZExt(minLen, b.intType, "copy.n.zext")
}
return minLen, nil
case "delete": case "delete":
m := argValues[0] m := argValues[0]
key := argValues[1] key := argValues[1]
@@ -1748,74 +1658,23 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
return llvmLen, nil return llvmLen, nil
case "min", "max": case "min", "max":
// min and max builtins, added in Go 1.21. // min and max builtins, added in Go 1.21.
// Find the corresponding intrinsic name. // We can simply reuse the existing binop comparison code, which has all
ty := argTypes[0].Underlying().(*types.Basic) // the edge cases figured out already.
llvmType := b.getLLVMType(ty) tok := token.LSS
info := ty.Info() if callName == "max" {
var prefix, delimeter, typeName string tok = token.GTR
if info&types.IsInteger != 0 {
// This is an integer value.
// Use the LLVM int min/max intrinsics.
prefix = "llvm.s"
if info&types.IsUnsigned != 0 {
prefix = "llvm.u"
}
delimeter = ".i"
typeName = strconv.Itoa(llvmType.IntTypeWidth())
} else {
switch ty.Kind() {
case types.String:
// Strings do not have an equivalent intrinsic.
// Implement with compares and selects.
tok := token.LSS
if callName == "max" {
tok = token.GTR
}
result := argValues[0]
typ := argTypes[0]
for _, arg := range argValues[1:] {
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
if err != nil {
return result, err
}
result = b.CreateSelect(cmp, result, arg, "")
}
return result, nil
case types.Float32:
typeName = "f32"
case types.Float64:
typeName = "f64"
default:
return llvm.Value{}, b.makeError(pos, "todo: min/max: unknown type")
}
// There are a few edge cases with floating point min/max:
// min(-0.0, +0.0) = -0.0
// min(NaN, number) = NaN
// The llvm.minimum.*/llvm.maximum.* intrinsics match this behavior.
// Neither Go nor LLVM defines the bit representation of resulting NaNs.
prefix = "llvm."
delimeter = "imum."
} }
intrinsicName := prefix + callName + delimeter + typeName
// Find or create the intrinsic.
llvmFn := b.mod.NamedFunction(intrinsicName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(llvmType, []llvm.Type{llvmType, llvmType}, false)
llvmFn = llvm.AddFunction(b.mod, intrinsicName, fnType)
}
// Call the intrinsic repeatedly to merge the arguments.
callType := llvmFn.GlobalValueType()
result := argValues[0] result := argValues[0]
typ := argTypes[0]
for _, arg := range argValues[1:] { for _, arg := range argValues[1:] {
result = b.CreateCall(callType, llvmFn, []llvm.Value{result, arg}, "") cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
if err != nil {
return result, err
}
result = b.CreateSelect(cmp, result, arg, "")
} }
return result, nil return result, nil
case "panic":
// This is rare, but happens in "defer panic()".
b.createRuntimeInvoke("_panic", argValues, "")
return llvm.Value{}, nil
case "print", "println": case "print", "println":
b.createRuntimeCall("printlock", nil, "")
for i, value := range argValues { for i, value := range argValues {
if i >= 1 && callName == "println" { if i >= 1 && callName == "println" {
b.createRuntimeCall("printspace", nil, "") b.createRuntimeCall("printspace", nil, "")
@@ -1876,7 +1735,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
if callName == "println" { if callName == "println" {
b.createRuntimeCall("printnl", nil, "") b.createRuntimeCall("printnl", nil, "")
} }
b.createRuntimeCall("printunlock", nil, "")
return llvm.Value{}, nil // print() or println() returns void return llvm.Value{}, nil // print() or println() returns void
case "real": case "real":
cplx := argValues[0] cplx := argValues[0]
@@ -1964,7 +1822,15 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// //
// This is also where compiler intrinsics are implemented. // This is also where compiler intrinsics are implemented.
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) { func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
// See if this is an intrinsic function that is handled specially. var params []llvm.Value
for _, param := range instr.Args {
params = append(params, b.getValue(param, getPos(instr)))
}
// Try to call the function directly for trivially static calls.
var callee, context llvm.Value
var calleeType llvm.Type
exported := false
if fn := instr.StaticCallee(); fn != nil { if fn := instr.StaticCallee(); fn != nil {
// Direct function call, either to a named or anonymous (directly // Direct function call, either to a named or anonymous (directly
// applied) function call. If it is anonymous, it may be a closure. // applied) function call. If it is anonymous, it may be a closure.
@@ -1980,15 +1846,9 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitSV64Call(instr.Args, getPos(instr)) return b.emitSV64Call(instr.Args, getPos(instr))
case strings.HasPrefix(name, "(device/riscv.CSR)."): case strings.HasPrefix(name, "(device/riscv.CSR)."):
return b.emitCSROperation(instr) return b.emitCSROperation(instr)
case (strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall")) && name != "syscall.SyscallN": case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"):
if b.GOOS != "darwin" { return b.createSyscall(instr)
return b.createSyscall(instr) case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
}
case name == "syscall.syscalln":
if b.GOOS == "windows" {
return b.createSyscalln(instr)
}
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
return b.createRawSyscallNoError(instr) return b.createRawSyscallNoError(instr)
case name == "runtime.supportsRecover": case name == "runtime.supportsRecover":
supportsRecover := uint64(0) supportsRecover := uint64(0)
@@ -1997,36 +1857,16 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
} }
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
case name == "runtime.panicStrategy": case name == "runtime.panicStrategy":
// These constants are defined in src/runtime/panic.go.
panicStrategy := map[string]uint64{ panicStrategy := map[string]uint64{
"print": tinygo.PanicStrategyPrint, "print": 1, // panicStrategyPrint
"trap": tinygo.PanicStrategyTrap, "trap": 2, // panicStrategyTrap
}[b.Config.PanicStrategy] }[b.Config.PanicStrategy]
return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil
case name == "runtime/interrupt.New": case name == "runtime/interrupt.New":
return b.createInterruptGlobal(instr) return b.createInterruptGlobal(instr)
case name == "runtime.exportedFuncPtr":
_, ptr := b.getFunction(instr.Args[0].(*ssa.Function))
return b.CreatePtrToInt(ptr, b.uintptrType, ""), nil
case name == "(*runtime/interrupt.Checkpoint).Save":
return b.createInterruptCheckpoint(instr.Args[0]), nil
case name == "internal/abi.FuncPCABI0":
retval := b.createDarwinFuncPCABI0Call(instr)
if !retval.IsNil() {
return retval, nil
}
} }
}
var params []llvm.Value
for _, param := range instr.Args {
params = append(params, b.getValue(param, getPos(instr)))
}
// Try to call the function directly for trivially static calls.
var callee, context llvm.Value
var calleeType llvm.Type
exported := false
if fn := instr.StaticCallee(); fn != nil {
calleeType, callee = b.getFunction(fn) calleeType, callee = b.getFunction(fn)
info := b.getFunctionInfo(fn) info := b.getFunctionInfo(fn)
if callee.IsNil() { if callee.IsNil() {
@@ -2123,7 +1963,7 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
return value return value
} else { } else {
// indicates a compiler bug // indicates a compiler bug
panic("SSA value not previously found in function: " + expr.String()) panic("local has not been parsed: " + expr.String())
} }
} }
} }
@@ -2177,8 +2017,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sizeValue := llvm.ConstInt(b.uintptrType, size, false) sizeValue := llvm.ConstInt(b.uintptrType, size, false)
layoutValue := b.createObjectLayout(typ, expr.Pos()) layoutValue := b.createObjectLayout(typ, expr.Pos())
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment) buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
align := b.targetData.ABITypeAlignment(typ)
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
return buf, nil return buf, nil
} else { } else {
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment) buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
@@ -2385,7 +2223,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceType := expr.Type().Underlying().(*types.Slice) sliceType := expr.Type().Underlying().(*types.Slice)
llvmElemType := b.getLLVMType(sliceType.Elem()) llvmElemType := b.getLLVMType(sliceType.Elem())
elemSize := b.targetData.TypeAllocSize(llvmElemType) elemSize := b.targetData.TypeAllocSize(llvmElemType)
elemAlign := b.targetData.ABITypeAlignment(llvmElemType)
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false) elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
maxSize := b.maxSliceSize(llvmElemType) maxSize := b.maxSliceSize(llvmElemType)
@@ -2409,7 +2246,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos()) layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf") slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
// Extend or truncate if necessary. This is safe as we've already done // Extend or truncate if necessary. This is safe as we've already done
// the bounds check. // the bounds check.
+36 -134
View File
@@ -16,7 +16,6 @@ package compiler
import ( import (
"go/types" "go/types"
"strconv" "strconv"
"strings"
"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"
@@ -100,14 +99,13 @@ func (b *builder) createLandingPad() {
// Continue at the 'recover' block, which returns to the parent in an // Continue at the 'recover' block, which returns to the parent in an
// appropriate way. // appropriate way.
b.CreateBr(b.blockInfo[b.fn.Recover.Index].entry) b.CreateBr(b.blockEntries[b.fn.Recover])
} }
// Create a checkpoint (similar to setjmp). This emits inline assembly that // createInvokeCheckpoint saves the function state at the given point, to
// stores the current program counter inside the ptr address (actually // continue at the landing pad if a panic happened. This is implemented using a
// ptr+sizeof(ptr)) and then returns a boolean indicating whether this is the // setjmp-like construct.
// normal flow (false) or we jumped here from somewhere else (true). func (b *builder) createInvokeCheckpoint() {
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
// Construct inline assembly equivalents of setjmp. // Construct inline assembly equivalents of setjmp.
// The assembly works as follows: // The assembly works as follows:
// * All registers (both callee-saved and caller saved) are clobbered // * All registers (both callee-saved and caller saved) are clobbered
@@ -162,7 +160,7 @@ str x2, [x1, #8]
mov x0, #0 mov x0, #0
1: 1:
` `
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{memory}" constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
if b.GOOS != "darwin" && b.GOOS != "windows" { if b.GOOS != "darwin" && b.GOOS != "windows" {
// These registers cause the following warning when compiling for // These registers cause the following warning when compiling for
// MacOS and Windows: // MacOS and Windows:
@@ -189,24 +187,6 @@ std z+5, r29
ldi r24, 0 ldi r24, 0
1:` 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}" 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 "mips":
// $4 flag (zero or non-zero)
// $5 defer frame
asmString = `
.set noat
move $$4, $$zero
jal 1f
1:
addiu $$ra, 8
sw $$ra, 4($$5)
.set at`
constraints = "={$4},{$5},~{$1},~{$2},~{$3},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$29},~{$30},~{$31},~{memory}"
if !strings.Contains(b.Features, "+soft-float") {
// Using floating point registers together with GOMIPS=softfloat
// results in a crash: "This value type is not natively supported!"
// So only add them when using hardfloat.
constraints += ",~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31}"
}
case "riscv32": case "riscv32":
asmString = ` asmString = `
la a2, 1f la a2, 1f
@@ -218,124 +198,49 @@ li a0, 0
// This case should have been handled by b.supportsRecover(). // This case should have been handled by b.supportsRecover().
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily()) b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
} }
asmType := llvm.FunctionType(resultType, []llvm.Type{b.dataPtrType}, false) asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false) asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
result := b.CreateCall(asmType, asm, []llvm.Value{ptr}, "setjmp") result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0)) result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result") isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
return isZero
}
// 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() {
isZero := b.createCheckpoint(b.deferFrame)
continueBB := b.insertBasicBlock("") continueBB := b.insertBasicBlock("")
b.CreateCondBr(isZero, continueBB, b.landingpad) b.CreateCondBr(isZero, continueBB, b.landingpad)
b.SetInsertPointAtEnd(continueBB) b.SetInsertPointAtEnd(continueBB)
b.currentBlockInfo.exit = continueBB b.blockExits[b.currentBlock] = continueBB
} }
// isInLoop checks if there is a path from the current block to itself. // isInLoop checks if there is a path from a basic block to itself.
// Use Tarjan's strongly connected components algorithm to search for cycles. func isInLoop(start *ssa.BasicBlock) bool {
// A one-node SCC is a cycle iff there is an edge from the node to itself. // Use a breadth-first search to scan backwards through the block graph.
// A multi-node SCC is always a cycle. queue := []*ssa.BasicBlock{start}
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm checked := map[*ssa.BasicBlock]struct{}{}
func (b *builder) isInLoop() bool {
if b.currentBlockInfo.tarjan.lowLink == 0 {
b.strongConnect(b.currentBlock)
}
return b.currentBlockInfo.tarjan.cyclic
}
func (b *builder) strongConnect(block *ssa.BasicBlock) { for len(queue) > 0 {
// Assign a new index. // pop a block off of the queue
// Indices start from 1 so that 0 can be used as a sentinel. block := queue[len(queue)-1]
assignedIndex := b.tarjanIndex + 1 queue = queue[:len(queue)-1]
b.tarjanIndex = assignedIndex
// Apply the new index. // Search through predecessors.
blockIndex := block.Index // Searching backwards means that this is pretty fast when the block is close to the start of the function.
node := &b.blockInfo[blockIndex].tarjan // Defers are often placed near the start of the function.
node.lowLink = assignedIndex for _, pred := range block.Preds {
if pred == start {
// Push the node onto the stack. // cycle found
node.onStack = true return true
b.tarjanStack = append(b.tarjanStack, uint(blockIndex))
// Process the successors.
for _, successor := range block.Succs {
// Look up the successor's state.
successorIndex := successor.Index
if successorIndex == blockIndex {
// Handle a self-cycle specially.
node.cyclic = true
continue
}
successorNode := &b.blockInfo[successorIndex].tarjan
switch {
case successorNode.lowLink == 0:
// This node has not yet been visisted.
b.strongConnect(successor)
case !successorNode.onStack:
// This node has been visited, but is in a different SCC.
// Ignore it, and do not update lowLink.
continue
}
// Update the lowLink index.
// This always uses the min-of-lowlink instead of using index in the on-stack case.
// This is done for two reasons:
// 1. The lowLink update can be shared between the new-node and on-stack cases.
// 2. The assigned index does not need to be saved - it is only needed for root node detection.
if successorNode.lowLink < node.lowLink {
node.lowLink = successorNode.lowLink
}
}
if node.lowLink == assignedIndex {
// This is a root node.
// Pop the SCC off the stack.
stack := b.tarjanStack
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
blocks := b.blockInfo
topNode := &blocks[top].tarjan
topNode.onStack = false
if top != uint(blockIndex) {
// The root node is not the only node in the SCC.
// Mark all nodes in this SCC as cyclic.
topNode.cyclic = true
for top != uint(blockIndex) {
top = stack[len(stack)-1]
stack = stack[:len(stack)-1]
topNode = &blocks[top].tarjan
topNode.onStack = false
topNode.cyclic = true
} }
if _, ok := checked[pred]; ok {
// block already checked
continue
}
// add to queue and checked map
queue = append(queue, pred)
checked[pred] = struct{}{}
} }
b.tarjanStack = stack
} }
}
// tarjanNode holds per-block state for isInLoop and strongConnect. return false
type tarjanNode struct {
// lowLink is the index of the first visited node that is reachable from this block.
// The lowLink indices are assigned by the SCC search, and do not correspond to b.Index.
// A lowLink of 0 is used as a sentinel to mark a node which has not yet been visited.
lowLink uint
// onStack tracks whether this node is currently on the SCC search stack.
onStack bool
// cyclic indicates whether this block is in a loop.
// If lowLink is 0, strongConnect must be called before reading this field.
cyclic bool
} }
// createDefer emits a single defer instruction, to be run when this function // createDefer emits a single defer instruction, to be run when this function
@@ -477,10 +382,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// Put this struct in an allocation. // Put this struct in an allocation.
var alloca llvm.Value var alloca llvm.Value
if instr.Block() != b.currentBlock { if !isInLoop(instr.Block()) {
panic("block mismatch")
}
if !b.isInLoop() {
// This can safely use a stack allocation. // This can safely use a stack allocation.
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca") alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
} else { } else {
+1 -4
View File
@@ -78,7 +78,7 @@ func (b *builder) trackValue(value llvm.Value) {
} }
} }
// trackPointer creates a call to runtime.trackPointer, bitcasting the pointer // trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
// first if needed. The input value must be of LLVM pointer type. // first if needed. The input value must be of LLVM pointer type.
func (b *builder) trackPointer(value llvm.Value) { func (b *builder) trackPointer(value llvm.Value) {
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "") b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
@@ -99,9 +99,6 @@ func typeHasPointers(t llvm.Type) bool {
} }
return false return false
case llvm.ArrayTypeKind: case llvm.ArrayTypeKind:
if t.ArrayLength() == 0 {
return false
}
if typeHasPointers(t.ElementType()) { if typeHasPointers(t.ElementType()) {
return true return true
} }
+51 -286
View File
@@ -7,14 +7,43 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// createGo emits code to start a new goroutine. // createGo emits code to start a new goroutine.
func (b *builder) createGo(instr *ssa.Go) { func (b *builder) createGo(instr *ssa.Go) {
if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { // Get all function parameters to pass to the goroutine.
var params []llvm.Value
for _, param := range instr.Call.Args {
params = append(params, b.getValue(param, getPos(instr)))
}
var prefix string
var funcPtr llvm.Value
var funcType llvm.Type
hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new
// goroutine.
var context llvm.Value
switch value := instr.Call.Value.(type) {
case *ssa.Function:
// Goroutine call is regular function call. No context is necessary.
case *ssa.MakeClosure:
// A goroutine call on a func value, but the callee is trivial to find. For
// example: immediately applied functions.
funcValue := b.getValue(value, getPos(instr))
context = b.extractFuncContext(funcValue)
default:
panic("StaticCallee returned an unexpected value")
}
if !context.IsNil() {
params = append(params, context) // context parameter
hasContext = true
}
funcType, funcPtr = b.getFunction(callee)
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
// We cheat. None of the builtins do any long or blocking operation, so // We cheat. None of the builtins do any long or blocking operation, so
// we might as well run these builtins right away without the program // we might as well run these builtins right away without the program
// noticing the difference. // noticing the difference.
@@ -45,38 +74,6 @@ func (b *builder) createGo(instr *ssa.Go) {
} }
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos()) b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
return return
}
// Get all function parameters to pass to the goroutine.
var params []llvm.Value
for _, param := range instr.Call.Args {
params = append(params, b.expandFormalParam(b.getValue(param, getPos(instr)))...)
}
var prefix string
var funcPtr llvm.Value
var funcType llvm.Type
hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new
// goroutine.
var context llvm.Value
switch value := instr.Call.Value.(type) {
case *ssa.Function:
// Goroutine call is regular function call. No context is necessary.
case *ssa.MakeClosure:
// A goroutine call on a func value, but the callee is trivial to find. For
// example: immediately applied functions.
funcValue := b.getValue(value, getPos(instr))
context = b.extractFuncContext(funcValue)
default:
panic("StaticCallee returned an unexpected value")
}
if !context.IsNil() {
params = append(params, context) // context parameter
hasContext = true
}
funcType, funcPtr = b.getFunction(callee)
} else if instr.Call.IsInvoke() { } else if instr.Call.IsInvoke() {
// This is a method call on an interface value. // This is a method call on an interface value.
itf := b.getValue(instr.Call.Value, getPos(instr)) itf := b.getValue(instr.Call.Value, getPos(instr))
@@ -102,7 +99,7 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params) paramBundle := b.emitPointerPack(params)
var stackSize llvm.Value var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos()) callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos())
if b.AutomaticStackSize { if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy // The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF // function that will be replaced with a load from a special ELF
@@ -122,147 +119,6 @@ func (b *builder) createGo(instr *ssa.Go) {
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "") b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
} }
// Create an exported wrapper function for functions with the //go:wasmexport
// pragma. This wrapper function is quite complex when the scheduler is enabled:
// it needs to start a new goroutine each time the exported function is called.
func (b *builder) createWasmExport() {
pos := b.info.wasmExportPos
if b.info.exported {
// //export really shouldn't be used anymore when //go:wasmexport is
// available, because //go:wasmexport is much better defined.
b.addError(pos, "cannot use //export and //go:wasmexport at the same time")
return
}
const suffix = "#wasmexport"
// Declare the exported function.
paramTypes := b.llvmFnType.ParamTypes()
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
exportedFn := llvm.AddFunction(b.mod, b.fn.RelString(nil)+suffix, exportedFnType)
b.addStandardAttributes(exportedFn)
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
// Create a builder for this wrapper function.
builder := newBuilder(b.compilerContext, b.ctx.NewBuilder(), b.fn)
defer builder.Dispose()
// Define this function as a separate function in DWARF
if b.Debug {
if b.fn.Syntax() != nil {
// Create debug info file if needed.
pos := b.program.Fset.Position(pos)
builder.difunc = builder.attachDebugInfoRaw(b.fn, exportedFn, suffix, pos.Filename, pos.Line)
}
builder.setDebugLocation(pos)
}
// Create a single basic block inside of it.
bb := llvm.AddBasicBlock(exportedFn, "entry")
builder.SetInsertPointAtEnd(bb)
// Insert an assertion to make sure this //go:wasmexport function is not
// called at a time when it is not allowed (for example, before the runtime
// is initialized).
builder.createRuntimeCall("wasmExportCheckRun", nil, "")
if b.Scheduler == "none" {
// When the scheduler has been disabled, this is really trivial: just
// call the function.
params := exportedFn.Params()
params = append(params, llvm.ConstNull(b.dataPtrType)) // context parameter
retval := builder.CreateCall(b.llvmFnType, b.llvmFn, params, "")
if b.fn.Signature.Results() == nil {
builder.CreateRetVoid()
} else {
builder.CreateRet(retval)
}
} else {
// The scheduler is enabled, so we need to start a new goroutine, wait
// for it to complete, and read the result value.
// Build a function that looks like this:
//
// func foo#wasmexport(param0, param1, ..., paramN) {
// var state *stateStruct
//
// // 'done' must be explicitly initialized ('state' is not zeroed)
// state.done = false
//
// // store the parameters in the state object
// state.param0 = param0
// state.param1 = param1
// ...
// state.paramN = paramN
//
// // create a goroutine and push it to the runqueue
// task.start(uintptr(gowrapper), &state)
//
// // run the scheduler
// runtime.wasmExportRun(&state.done)
//
// // if there is a return value, load it and return
// return state.result
// }
hasReturn := b.fn.Signature.Results() != nil
// Build the state struct type.
// It stores the function parameters, the 'done' flag, and reserves
// space for a return value if needed.
stateFields := exportedFnType.ParamTypes()
numParams := len(stateFields)
stateFields = append(stateFields, b.ctx.Int1Type()) // 'done' field
if hasReturn {
stateFields = append(stateFields, b.llvmFnType.ReturnType())
}
stateStruct := b.ctx.StructType(stateFields, false)
// Allocate the state struct on the stack.
statePtr := builder.CreateAlloca(stateStruct, "status")
// Initialize the 'done' field.
doneGEP := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(numParams), false),
}, "done.gep")
builder.CreateStore(llvm.ConstNull(b.ctx.Int1Type()), doneGEP)
// Store all parameters in the state object.
for i, param := range exportedFn.Params() {
gep := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}, "")
builder.CreateStore(param, gep)
}
// Create a new goroutine and add it to the runqueue.
wrapper := b.createGoroutineStartWrapper(b.llvmFnType, b.llvmFn, "", false, true, pos)
stackSize := llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
taskStartFnType, taskStartFn := builder.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
builder.createCall(taskStartFnType, taskStartFn, []llvm.Value{wrapper, statePtr, stackSize, llvm.Undef(b.dataPtrType)}, "")
// Run the scheduler.
builder.createRuntimeCall("wasmExportRun", []llvm.Value{doneGEP}, "")
// Read the return value (if any) and return to the caller of the
// //go:wasmexport function.
if hasReturn {
gep := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(numParams)+1, false),
}, "")
retval := builder.CreateLoad(b.llvmFnType.ReturnType(), gep, "retval")
builder.CreateRet(retval)
} else {
builder.CreateRetVoid()
}
}
}
// 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:
// //
@@ -286,7 +142,7 @@ func (b *builder) createWasmExport() {
// to last parameter of the function) is used for this wrapper. If hasContext is // to last parameter of the function) is used for this wrapper. If hasContext is
// false, the parameter bundle is assumed to have no context parameter and undef // false, the parameter bundle is assumed to have no context parameter and undef
// is passed instead. // is passed instead.
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext, isWasmExport bool, pos token.Pos) llvm.Value { func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
var wrapper llvm.Value var wrapper llvm.Value
b := &builder{ b := &builder{
@@ -304,18 +160,14 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if !fn.IsAFunction().IsNil() { if !fn.IsAFunction().IsNil() {
// See whether this wrapper has already been created. If so, return it. // See whether this wrapper has already been created. If so, return it.
name := fn.Name() name := fn.Name()
wrapperName := name + "$gowrapper" wrapper = c.mod.NamedFunction(name + "$gowrapper")
if isWasmExport {
wrapperName += "-wasmexport"
}
wrapper = c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() { if !wrapper.IsNil() {
return llvm.ConstPtrToInt(wrapper, c.uintptrType) return llvm.ConstPtrToInt(wrapper, c.uintptrType)
} }
// Create the wrapper. // Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false) wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapperType) wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
c.addStandardAttributes(wrapper) c.addStandardAttributes(wrapper)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage) wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
wrapper.SetUnnamedAddr(true) wrapper.SetUnnamedAddr(true)
@@ -345,110 +197,23 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
} }
if !isWasmExport { // Create the list of params for the call.
// Regular 'go' instruction. paramTypes := fnType.ParamTypes()
if !hasContext {
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
}
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
if !hasContext {
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
}
// Create the list of params for the call. // Create the call.
paramTypes := fnType.ParamTypes() b.CreateCall(fnType, fn, params, "")
if !hasContext {
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
}
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes) if c.Scheduler == "asyncify" {
if !hasContext { b.CreateCall(deadlockType, deadlock, []llvm.Value{
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter llvm.Undef(c.dataPtrType),
} }, "")
// Create the call.
b.CreateCall(fnType, fn, params, "")
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.dataPtrType),
}, "")
}
} else {
// Goroutine started from a //go:wasmexport pragma.
// The function looks like this:
//
// func foo$gowrapper-wasmexport(state *stateStruct) {
// // load values
// param0 := state.params[0]
// param1 := state.params[1]
//
// // call wrapped functions
// result := foo(param0, param1, ...)
//
// // store result value (if there is any)
// state.result = result
//
// // finish exported function
// state.done = true
// runtime.wasmExportExit()
// }
//
// The state object here looks like:
//
// struct state {
// param0
// param1
// param* // etc
// done bool
// result returnType
// }
returnType := fnType.ReturnType()
hasReturn := returnType != b.ctx.VoidType()
statePtr := wrapper.Param(0)
// Create the state struct (it must match the type in createWasmExport).
stateFields := fnType.ParamTypes()
numParams := len(stateFields) - 1
stateFields = stateFields[:numParams:numParams] // strip 'context' parameter
stateFields = append(stateFields, c.ctx.Int1Type()) // 'done' bool
if hasReturn {
stateFields = append(stateFields, returnType)
}
stateStruct := b.ctx.StructType(stateFields, false)
// Extract parameters from the state object, and call the function
// that's being wrapped.
var callParams []llvm.Value
for i := 0; i < numParams; i++ {
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}, "")
param := b.CreateLoad(stateFields[i], gep, "")
callParams = append(callParams, param)
}
callParams = append(callParams, llvm.ConstNull(c.dataPtrType)) // add 'context' parameter
result := b.CreateCall(fnType, fn, callParams, "")
// Store the return value back into the shared state.
// Unlike regular goroutines, these special //go:wasmexport
// goroutines can return a value.
if hasReturn {
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), uint64(numParams)+1, false),
}, "result.ptr")
b.CreateStore(result, gep)
}
// Mark this function as having finished executing.
// This is important so the runtime knows the exported function
// didn't block.
doneGEP := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), uint64(numParams), false),
}, "done.gep")
b.CreateStore(llvm.ConstInt(b.ctx.Int1Type(), 1, false), doneGEP)
// Call back into the runtime. This will exit the goroutine, switch
// back to the scheduler, which will in turn return from the
// //go:wasmexport function.
b.createRuntimeCall("wasmExportExit", nil, "")
} }
} else { } else {
@@ -530,5 +295,5 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
} }
// Return a ptrtoint of the wrapper, not the function itself. // Return a ptrtoint of the wrapper, not the function itself.
return llvm.ConstPtrToInt(wrapper, c.uintptrType) return b.CreatePtrToInt(wrapper, c.uintptrType, "")
} }
+3 -15
View File
@@ -38,10 +38,10 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
// provided immediately. For example: // provided immediately. For example:
// //
// arm.AsmFull( // arm.AsmFull(
// "str {value}, [{result}]", // "str {value}, {result}",
// map[string]interface{}{ // map[string]interface{}{
// "value": 1, // "value": 1
// "result": uintptr(unsafe.Pointer(&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)
@@ -249,15 +249,3 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name) return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
} }
} }
// Implement runtime/interrupt.Checkpoint.Save. It needs to be implemented
// directly at the call site. If it isn't implemented directly at the call site
// (but instead through a function call), it might result in an overwritten
// stack in the non-jump return case.
func (b *builder) createInterruptCheckpoint(ptr ssa.Value) llvm.Value {
addr := b.getValue(ptr, ptr.Pos())
b.createNilCheck(ptr, addr, "deref")
stackPointer := b.readStackPointer()
b.CreateStore(stackPointer, addr)
return b.createCheckpoint(addr)
}
+42 -160
View File
@@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"go/token" "go/token"
"go/types" "go/types"
"sort"
"strconv" "strconv"
"strings" "strings"
@@ -18,12 +17,6 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// numMethodHasMethodSet is a flag in bit 15 of the numMethod field (uint16) in
// Named, Pointer, and Struct type descriptors. When set, an inline method set
// is present in the type descriptor. Must match the constant in
// src/internal/reflectlite/type.go.
const numMethodHasMethodSet = 0x8000
// Type kinds for basic types. // Type kinds for basic types.
// They must match the constants for the Kind type in src/reflect/type.go. // They must match the constants for the Kind type in src/reflect/type.go.
var basicTypes = [...]uint8{ var basicTypes = [...]uint8{
@@ -93,7 +86,7 @@ func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.
// extractValueFromInterface extract the value from an interface value // extractValueFromInterface extract the value from an interface value
// (runtime._interface) under the assumption that it is of the type given in // (runtime._interface) under the assumption that it is of the type given in
// llvmType. The behavior is undefined if the interface is nil or llvmType // llvmType. The behavior is undefied if the interface is nil or llvmType
// doesn't match the underlying type of the interface. // doesn't match the underlying type of the interface.
func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value { func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value {
valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr") valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
@@ -129,9 +122,6 @@ func (c *compilerContext) pkgPathPtr(pkgpath string) llvm.Value {
// This function returns a pointer to the 'kind' field (which might not be the // This function returns a pointer to the 'kind' field (which might not be the
// first field in the struct). // first field in the struct).
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
// Resolve alias types: alias types are resolved at compile time.
typ = types.Unalias(typ)
ms := c.program.MethodSets.MethodSet(typ) ms := c.program.MethodSets.MethodSet(typ)
hasMethodSet := ms.Len() != 0 hasMethodSet := ms.Len() != 0
_, isInterface := typ.Underlying().(*types.Interface) _, isInterface := typ.Underlying().(*types.Interface)
@@ -190,16 +180,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFieldTypes := []*types.Var{ typeFieldTypes := []*types.Var{
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]), types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
} }
// Compute the method set value for types that support methods.
var methods []*types.Func
for i := 0; i < ms.Len(); i++ {
methods = append(methods, ms.At(i).Obj().(*types.Func))
}
methodSetType := types.NewStruct([]*types.Var{
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
types.NewVar(token.NoPos, nil, "methods", types.NewArray(types.Typ[types.UnsafePointer], int64(len(methods)))),
}, nil)
methodSetValue := c.getMethodSetValue(methods)
switch typ := typ.(type) { switch typ := typ.(type) {
case *types.Basic: case *types.Basic:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
@@ -216,13 +196,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
)
if len(methods) > 0 {
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
}
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))), types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
) )
case *types.Chan: case *types.Chan:
@@ -242,11 +215,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]), types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
) )
if len(methods) > 0 {
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
}
case *types.Array: case *types.Array:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]), types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
@@ -271,16 +239,11 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]), types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))), types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
) )
if len(methods) > 0 {
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
}
case *types.Interface: case *types.Interface:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
) )
// TODO: methods
case *types.Signature: case *types.Signature:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
@@ -326,24 +289,14 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
pkgname = pkg.Name() pkgname = pkg.Name()
} }
pkgPathPtr := c.pkgPathPtr(pkgpath) pkgPathPtr := c.pkgPathPtr(pkgpath)
namedNumMethods := uint64(numMethods)
if namedNumMethods&numMethodHasMethodSet != 0 {
panic("numMethods overflow: too many exported methods on named type " + name)
}
if len(methods) > 0 {
namedNumMethods |= numMethodHasMethodSet
}
typeFields = []llvm.Value{ typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), namedNumMethods, false), // numMethods llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying c.getTypeCode(typ.Underlying()), // underlying
pkgPathPtr, // pkgpath pointer pkgPathPtr, // pkgpath pointer
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
} }
if len(methods) > 0 { metabyte |= 1 << 5 // "named" flag
typeFields = append(typeFields, methodSetValue) // methods
}
typeFields = append(typeFields, c.ctx.ConstString(pkgname+"."+name+"\x00", false)) // name
metabyte |= 1 << 5 // "named" flag
case *types.Chan: case *types.Chan:
var dir reflectChanDir var dir reflectChanDir
switch typ.Dir() { switch typ.Dir() {
@@ -367,20 +320,10 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(typ.Elem()), // elementType c.getTypeCode(typ.Elem()), // elementType
} }
case *types.Pointer: case *types.Pointer:
ptrNumMethods := uint64(numMethods)
if ptrNumMethods&numMethodHasMethodSet != 0 {
panic("numMethods overflow: too many exported methods on pointer type")
}
if len(methods) > 0 {
ptrNumMethods |= numMethodHasMethodSet
}
typeFields = []llvm.Value{ typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), ptrNumMethods, false), // numMethods llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(typ.Elem()), c.getTypeCode(typ.Elem()),
} }
if len(methods) > 0 {
typeFields = append(typeFields, methodSetValue)
}
case *types.Array: case *types.Array:
typeFields = []llvm.Value{ typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), 0, false), // numMethods llvm.ConstInt(c.ctx.Int16Type(), 0, false), // numMethods
@@ -407,16 +350,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
llvmStructType := c.getLLVMType(typ) llvmStructType := c.getLLVMType(typ)
size := c.targetData.TypeStoreSize(llvmStructType) size := c.targetData.TypeStoreSize(llvmStructType)
structNumMethods := uint64(numMethods)
if structNumMethods&numMethodHasMethodSet != 0 {
panic("numMethods overflow: too many exported methods on struct type")
}
if len(methods) > 0 {
structNumMethods |= numMethodHasMethodSet
}
typeFields = []llvm.Value{ typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), structNumMethods, false), // numMethods llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo c.getTypeCode(types.NewPointer(typ)), // ptrTo
pkgPathPtr, pkgPathPtr,
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
@@ -468,14 +404,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
})) }))
} }
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields)) typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
if len(methods) > 0 {
typeFields = append(typeFields, methodSetValue)
}
case *types.Interface: case *types.Interface:
typeFields = []llvm.Value{ typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
c.getTypeCode(types.NewPointer(typ)), // TODO: methods
methodSetValue,
}
case *types.Signature: case *types.Signature:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))} typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc // TODO: params, return values, etc
@@ -581,9 +512,10 @@ var basicTypeNames = [...]string{
// 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.
func getTypeCodeName(t types.Type) (string, bool) { func getTypeCodeName(t types.Type) (string, bool) {
switch t := types.Unalias(t).(type) { switch t := t.(type) {
case *types.Named: case *types.Named:
if t.Obj().Parent() != t.Obj().Pkg().Scope() { // Note: check for `t.Obj().Pkg() != nil` for Go 1.18 only.
if t.Obj().Pkg() != nil && t.Obj().Parent() != t.Obj().Pkg().Scope() {
return "named:" + t.String() + "$local", true return "named:" + t.String() + "$local", true
} }
return "named:" + t.String(), false return "named:" + t.String(), false
@@ -762,11 +694,17 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// This type assertion always succeeds, so we can just set commaOk to true. // This type assertion always succeeds, so we can just set commaOk to true.
commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true) commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
} else { } else {
// Type assert on an interface type with methods. // Type assert on interface type with methods.
// Create a call to a declared-but-not-defined function that will // This is a call to an interface type assert function.
// be lowered by the interface lowering pass into a type-ID // The interface lowering pass will define this function by filling it
// comparison chain. // with a type switch over all concrete types that implement this
commaOk = b.createInterfaceTypeAssert(intf, actualTypeNum) // interface, and returning whether it's one of the matched types.
// This is very different from how interface asserts are implemented in
// the main Go compiler, where the runtime checks whether the type
// implements each method of the interface. See:
// https://research.swtch.com/interfaces
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
} }
} else { } else {
name, _ := getTypeCodeName(expr.AssertedType) name, _ := getTypeCodeName(expr.AssertedType)
@@ -797,7 +735,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
prevBlock := b.GetInsertBlock() prevBlock := b.GetInsertBlock()
okBlock := b.insertBasicBlock("typeassert.ok") okBlock := b.insertBasicBlock("typeassert.ok")
nextBlock := b.insertBasicBlock("typeassert.next") nextBlock := b.insertBasicBlock("typeassert.next")
b.currentBlockInfo.exit = 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)
// Retrieve the value from the interface if the type assert was // Retrieve the value from the interface if the type assert was
@@ -843,58 +781,20 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
return strings.Join(methods, "; ") return strings.Join(methods, "; ")
} }
// getMethodSetValue creates the method set struct value for a list of methods. // getInterfaceImplementsFunc returns a declared function that works as a type
// The struct contains a length and a sorted array of method signature pointers. // switch. The interface lowering pass will define this function.
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value { func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
// Create a sorted list of method signature global names. s, _ := getTypeCodeName(assertedType.Underlying())
type methodRef struct { fnName := s + ".$typeassert"
name string llvmFn := c.mod.NamedFunction(fnName)
value llvm.Value if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn)
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
} }
var refs []methodRef return llvmFn
for _, method := range methods {
name := method.Name()
if !token.IsExported(name) {
name = method.Pkg().Path() + "." + name
}
s, _ := getTypeCodeName(method.Type())
globalName := "reflect/types.signature:" + name + ":" + s
value := c.mod.NamedGlobal(globalName)
if value.IsNil() {
value = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
value.SetInitializer(llvm.ConstNull(c.ctx.Int8Type()))
value.SetGlobalConstant(true)
value.SetLinkage(llvm.LinkOnceODRLinkage)
value.SetAlignment(1)
if c.Debug {
file := c.getDIFile("<Go type>")
diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
Name: globalName,
File: file,
Line: 1,
Type: c.getDIType(types.Typ[types.Uint8]),
LocalToUnit: false,
Expr: c.dibuilder.CreateExpression(nil),
AlignInBits: 8,
})
value.AddMetadata(0, diglobal)
}
}
refs = append(refs, methodRef{globalName, value})
}
sort.Slice(refs, func(i, j int) bool {
return refs[i].name < refs[j].name
})
var values []llvm.Value
for _, ref := range refs {
values = append(values, ref.value)
}
return c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(len(values)), false),
llvm.ConstArray(c.dataPtrType, values),
}, false)
} }
// getInvokeFunction returns the thunk to call the given interface method. The // getInvokeFunction returns the thunk to call the given interface method. The
@@ -921,24 +821,6 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
return llvmFn return llvmFn
} }
// createInterfaceTypeAssert creates a call to a declared-but-not-defined
// $typeassert function for the given interface. This function will be defined
// by the interface lowering pass as a type-ID comparison chain, avoiding the
// need for runtime.typeImplementsMethodSet at compile time.
func (b *builder) createInterfaceTypeAssert(intf *types.Interface, actualType llvm.Value) llvm.Value {
s, _ := getTypeCodeName(intf)
fnName := s + ".$typeassert"
llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(b.ctx.Int1Type(), []llvm.Type{b.dataPtrType}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, llvmFnType)
b.addStandardDeclaredAttributes(llvmFn)
methods := b.getMethodsString(intf)
llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("tinygo-methods", methods))
}
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{actualType}, "")
}
// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be // getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
// invoked from an interface. The wrapper takes in a pointer to the underlying // invoked from an interface. The wrapper takes in a pointer to the underlying
// value, dereferences or unpacks it if necessary, and calls the real method. // value, dereferences or unpacks it if necessary, and calls the real method.
@@ -1061,7 +943,7 @@ func signature(sig *types.Signature) string {
// normalization around `byte` vs `uint8` for example. // normalization around `byte` vs `uint8` for example.
func typestring(t types.Type) string { func typestring(t types.Type) string {
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go // See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
switch t := types.Unalias(t).(type) { switch t := t.(type) {
case *types.Array: case *types.Array:
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem()) return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
case *types.Basic: case *types.Basic:
-3
View File
@@ -41,8 +41,6 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Create a new global of type runtime/interrupt.handle. Globals of this // Create a new global of type runtime/interrupt.handle. Globals of this
// type are lowered in the interrupt lowering pass. // type are lowered in the interrupt lowering pass.
// It must have an alignment of 1, otherwise LLVM thinks a ptrtoint of the
// global has the lower bits unset.
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type() globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalLLVMType := b.getLLVMType(globalType) globalLLVMType := b.getLLVMType(globalType)
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10) globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
@@ -50,7 +48,6 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
global.SetVisibility(llvm.HiddenVisibility) global.SetVisibility(llvm.HiddenVisibility)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
global.SetAlignment(1)
initializer := llvm.ConstNull(globalLLVMType) initializer := llvm.ConstNull(globalLLVMType)
initializer = b.CreateInsertValue(initializer, funcContext, 0, "") initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "") initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
+8 -69
View File
@@ -27,8 +27,6 @@ func (b *builder) defineIntrinsicFunction() {
b.createStackSaveImpl() b.createStackSaveImpl()
case name == "runtime.KeepAlive": case name == "runtime.KeepAlive":
b.createKeepAliveImpl() b.createKeepAliveImpl()
case name == "machine.keepAliveNoEscape":
b.createMachineKeepAliveImpl()
case strings.HasPrefix(name, "runtime/volatile.Load"): case strings.HasPrefix(name, "runtime/volatile.Load"):
b.createVolatileLoad() b.createVolatileLoad()
case strings.HasPrefix(name, "runtime/volatile.Store"): case strings.HasPrefix(name, "runtime/volatile.Store"):
@@ -50,24 +48,19 @@ func (b *builder) defineIntrinsicFunction() {
// and will otherwise be lowered to regular libc memcpy/memmove calls. // and will otherwise be lowered to regular libc memcpy/memmove calls.
func (b *builder) createMemoryCopyImpl() { func (b *builder) createMemoryCopyImpl() {
b.createFunctionStart(true) b.createFunctionStart(true)
params := b.fn.Params[0:3] fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
b.createMemCopy(
b.fn.Name(),
b.getValue(params[0], getPos(b.fn)),
b.getValue(params[1], getPos(b.fn)),
b.getValue(params[2], getPos(b.fn)),
)
b.CreateRetVoid()
}
func (b *builder) createMemCopy(kind string, dst, src, len llvm.Value) llvm.Value {
fnName := "llvm." + kind + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType) llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
} }
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{dst, src, len, llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "") var params []llvm.Value
for _, param := range b.fn.Params {
params = append(params, b.getValue(param, getPos(b.fn)))
}
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
b.CreateRetVoid()
} }
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of // createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
@@ -128,43 +121,6 @@ func (b *builder) createKeepAliveImpl() {
b.CreateRetVoid() b.CreateRetVoid()
} }
// createAbiEscapeImpl implements the generic internal/abi.Escape function. It
// currently only supports pointer types.
func (b *builder) createAbiEscapeImpl() {
b.createFunctionStart(true)
// The first parameter is assumed to be a pointer. This is checked at the
// call site of createAbiEscapeImpl.
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
// Create an equivalent of the following C code, which is basically just a
// nop but ensures the pointerValue is kept alive:
//
// __asm__ __volatile__("" : : "r"(pointerValue))
//
// It should be portable to basically everything as the "r" register type
// exists basically everywhere.
asmType := llvm.FunctionType(b.dataPtrType, []llvm.Type{b.dataPtrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "=r,0", true, false, 0, false)
result := b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
b.CreateRet(result)
}
// Implement machine.keepAliveNoEscape, which makes sure the compiler keeps the
// pointer parameter alive until this point (for GC).
func (b *builder) createMachineKeepAliveImpl() {
b.createFunctionStart(true)
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
// See createKeepAliveImpl for details.
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
b.CreateRetVoid()
}
var mathToLLVMMapping = map[string]string{ var mathToLLVMMapping = map[string]string{
"math.Ceil": "llvm.ceil.f64", "math.Ceil": "llvm.ceil.f64",
"math.Exp": "llvm.exp.f64", "math.Exp": "llvm.exp.f64",
@@ -208,23 +164,6 @@ func (b *builder) defineMathOp() {
b.CreateRet(result) b.CreateRet(result)
} }
func (b *builder) defineCryptoIntrinsic() bool {
if b.fn.Pkg.Pkg.Path() != "crypto/internal/constanttime" {
return false
}
switch b.fn.Name() {
case "boolToUint8":
b.createFunctionStart(true)
param := b.getValue(b.fn.Params[0], b.fn.Pos())
result := b.CreateZExt(param, b.ctx.Int8Type(), "")
b.CreateRet(result)
return true
}
return false
}
// Implement most math/bits functions. // Implement most math/bits functions.
// //
// This implements all the functions that operate on bits. It does not yet // This implements all the functions that operate on bits. It does not yet
+109 -113
View File
@@ -1,13 +1,12 @@
package compiler package compiler
import ( import (
"encoding/binary"
"fmt" "fmt"
"go/token" "go/token"
"go/types" "go/types"
"math/big"
"strings" "strings"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -128,14 +127,12 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
// Packed data is bigger than a pointer, so allocate it on the heap. // Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(b.uintptrType, size, false) sizeValue := llvm.ConstInt(b.uintptrType, size, false)
align := b.targetData.ABITypeAlignment(packedType)
alloc := b.mod.NamedFunction("runtime.alloc") alloc := b.mod.NamedFunction("runtime.alloc")
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{ packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
sizeValue, sizeValue,
llvm.ConstNull(b.dataPtrType), llvm.ConstNull(b.dataPtrType),
llvm.Undef(b.dataPtrType), // unused context parameter llvm.Undef(b.dataPtrType), // unused context parameter
}, "") }, "")
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
if b.NeedsStackObjects { if b.NeedsStackObjects {
b.trackPointer(packedAlloc) b.trackPointer(packedAlloc)
} }
@@ -231,12 +228,6 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
// //
// For details on what's in this value, see src/runtime/gc_precise.go. // For details on what's in this value, see src/runtime/gc_precise.go.
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value { func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
if !typeHasPointers(t) {
// There are no pointers in this type, so we can simplify the layout.
layout := (uint64(1) << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
}
// Use the element type for arrays. This works even for nested arrays. // Use the element type for arrays. This works even for nested arrays.
for { for {
kind := t.TypeKind() kind := t.TypeKind()
@@ -254,29 +245,54 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
break break
} }
// Create the pointer bitmap. // Do a few checks to see whether we need to generate any object layout
// information at all.
objectSizeBytes := c.targetData.TypeAllocSize(t) objectSizeBytes := c.targetData.TypeAllocSize(t)
pointerAlignment := uint64(c.targetData.PrefTypeAlignment(c.dataPtrType))
bitmapLen := objectSizeBytes / pointerAlignment
bitmapBytes := (bitmapLen + 7) / 8
bitmap := make([]byte, bitmapBytes, max(bitmapBytes, 8))
c.buildPointerBitmap(bitmap, pointerAlignment, pos, t, 0)
// Try to encode the layout inline.
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType) pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
pointerBits := pointerSize * 8 pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
if bitmapLen < pointerBits { if objectSizeBytes < pointerSize {
rawMask := binary.LittleEndian.Uint64(bitmap[0:8]) // Too small to contain a pointer.
layout := rawMask*pointerBits + bitmapLen layout := (uint64(1) << 1) | 1
layout <<= 1 return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
layout |= 1 }
bitmap := c.getPointerBitmap(t, pos)
if bitmap.BitLen() == 0 {
// There are no pointers in this type, so we can simplify the layout.
// TODO: this can be done in many other cases, e.g. when allocating an
// array (like [4][]byte, which repeats a slice 4 times).
layout := (uint64(1) << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
}
if objectSizeBytes%uint64(pointerAlignment) != 0 {
// This shouldn't happen except for packed structs, which aren't
// currently used.
c.addError(pos, "internal error: unexpected object size for object with pointer field")
return llvm.ConstNull(c.dataPtrType)
}
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
// Check if the layout fits. pointerBits := pointerSize * 8
layout &= 1<<pointerBits - 1 var sizeFieldBits uint64
if (layout>>1)/pointerBits == rawMask { switch pointerBits {
// No set bits were shifted off. case 16:
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType) sizeFieldBits = 4
} case 32:
sizeFieldBits = 5
case 64:
sizeFieldBits = 6
default:
panic("unknown pointer size")
}
layoutFieldBits := pointerBits - 1 - sizeFieldBits
// Try to emit the value as an inline integer. This is possible in most
// cases.
if objectSizeWords < layoutFieldBits {
// If it can be stored directly in the pointer value, do so.
// The runtime knows that if the least significant bit of the pointer is
// set, the pointer contains the value itself.
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
} }
// Unfortunately, the object layout is too big to fit in a pointer-sized // Unfortunately, the object layout is too big to fit in a pointer-sized
@@ -284,24 +300,25 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// Try first whether the global already exists. All objects with a // Try first whether the global already exists. All objects with a
// particular name have the same type, so this is possible. // particular name have the same type, so this is possible.
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", bitmapLen, (bitmapLen+15)/16, bitmap) globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
global := c.mod.NamedGlobal(globalName) global := c.mod.NamedGlobal(globalName)
if !global.IsNil() { if !global.IsNil() {
return global return global
} }
// Create the global initializer. // Create the global initializer.
bitmapByteValues := make([]llvm.Value, bitmapBytes) bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
i8 := c.ctx.Int8Type() bitmap.FillBytes(bitmapBytes)
for i, b := range bitmap { reverseBytes(bitmapBytes) // big-endian to little-endian
bitmapByteValues[i] = llvm.ConstInt(i8, uint64(b), false) var bitmapByteValues []llvm.Value
for _, b := range bitmapBytes {
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
} }
initializer := c.ctx.ConstStruct([]llvm.Value{ initializer := c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, bitmapLen, false), llvm.ConstInt(c.uintptrType, objectSizeWords, false),
llvm.ConstArray(i8, bitmapByteValues), llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
}, false) }, false)
// Create the actual global.
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName) global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
global.SetInitializer(initializer) global.SetInitializer(initializer)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
@@ -309,7 +326,6 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
global.SetLinkage(llvm.LinkOnceODRLinkage) global.SetLinkage(llvm.LinkOnceODRLinkage)
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 { if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
// AVR doesn't have alignment by default. // AVR doesn't have alignment by default.
// The lowest bit must be unset to distinguish this from an inline layout.
global.SetAlignment(2) global.SetAlignment(2)
} }
if c.Debug && pos != token.NoPos { if c.Debug && pos != token.NoPos {
@@ -341,82 +357,77 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
return global return global
} }
// buildPointerBitmap scans the given LLVM type for pointers and sets bits in a // getPointerBitmap scans the given LLVM type for pointers and sets bits in a
// bitmap at the word offset that contains a pointer. This scan is recursive. // bigint at the word offset that contains a pointer. This scan is recursive.
func (c *compilerContext) buildPointerBitmap( func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
dst []byte, alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
ptrAlign uint64, switch typ.TypeKind() {
pos token.Pos,
t llvm.Type,
offset uint64,
) {
switch t.TypeKind() {
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind: case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
// These types do not contain pointers. return big.NewInt(0)
case llvm.PointerTypeKind: case llvm.PointerTypeKind:
// Set the corresponding position in the bitmap. return big.NewInt(1)
dst[offset/8] |= 1 << (offset % 8)
case llvm.StructTypeKind: case llvm.StructTypeKind:
// Recurse over struct elements. ptrs := big.NewInt(0)
for i, et := range t.StructElementTypes() { if typ.StructName() == "runtime.funcValue" {
eo := c.targetData.ElementOffset(t, i) // Hack: the type runtime.funcValue contains an 'id' field which is
if eo%uint64(ptrAlign) != 0 { // of type uintptr, but before the LowerFuncValues pass it actually
if typeHasPointers(et) { // contains a pointer (ptrtoint) to a global. This trips up the
// This error will let the compilation fail, but by continuing // interp package. Therefore, make the id field a pointer for now.
// the error can still easily be shown. typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
c.addError(pos, "internal error: allocated struct contains unaligned pointer") }
} for i, subtyp := range typ.StructElementTypes() {
subptrs := c.getPointerBitmap(subtyp, pos)
if subptrs.BitLen() == 0 {
continue continue
} }
c.buildPointerBitmap( offset := c.targetData.ElementOffset(typ, i)
dst, if offset%uint64(alignment) != 0 {
ptrAlign, // This error will let the compilation fail, but by continuing
pos, // the error can still easily be shown.
et, c.addError(pos, "internal error: allocated struct contains unaligned pointer")
offset+(eo/ptrAlign), continue
)
}
case llvm.ArrayTypeKind:
// Recurse over array elements.
len := t.ArrayLength()
if len <= 0 {
return
}
et := t.ElementType()
elementSize := c.targetData.TypeAllocSize(et)
if elementSize%ptrAlign != 0 {
if typeHasPointers(et) {
// This error will let the compilation fail (but continues so that
// other errors can be shown).
c.addError(pos, "internal error: allocated array contains unaligned pointer")
} }
return subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
ptrs.Or(ptrs, subptrs)
} }
elementSize /= ptrAlign return ptrs
for i := 0; i < len; i++ { case llvm.ArrayTypeKind:
c.buildPointerBitmap( subtyp := typ.ElementType()
dst, subptrs := c.getPointerBitmap(subtyp, pos)
ptrAlign, ptrs := big.NewInt(0)
pos, if subptrs.BitLen() == 0 {
et, return ptrs
offset+uint64(i)*elementSize,
)
} }
elementSize := c.targetData.TypeAllocSize(subtyp)
if elementSize%uint64(alignment) != 0 {
// This error will let the compilation fail (but continues so that
// other errors can be shown).
c.addError(pos, "internal error: allocated array contains unaligned pointer")
return ptrs
}
for i := 0; i < typ.ArrayLength(); i++ {
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
ptrs.Or(ptrs, subptrs)
}
return ptrs
default: default:
// Should not happen. // Should not happen.
panic("unknown LLVM type") panic("unknown LLVM type")
} }
} }
// archFamily returns the architecture from the LLVM triple but with some // archFamily returns the archtecture from the LLVM triple but with some
// architecture names ("armv6", "thumbv7m", etc) merged into a single // architecture names ("armv6", "thumbv7m", etc) merged into a single
// architecture name ("arm"). // architecture name ("arm").
func (c *compilerContext) archFamily() string { func (c *compilerContext) archFamily() string {
return compileopts.CanonicalArchName(c.Triple) 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 // isThumb returns whether we're in ARM or in Thumb mode. It panics if the
@@ -452,21 +463,6 @@ func (b *builder) readStackPointer() llvm.Value {
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "") return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
} }
// writeStackPointer emits a LLVM intrinsic call that updates the current stack
// pointer.
func (b *builder) writeStackPointer(sp llvm.Value) {
name := "llvm.stackrestore.p0"
if llvmutil.Version() < 18 {
name = "llvm.stackrestore" // backwards compatibility with LLVM 17 and below
}
stackrestore := b.mod.NamedFunction(name)
if stackrestore.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
stackrestore = llvm.AddFunction(b.mod, name, fnType)
}
b.CreateCall(stackrestore.GlobalValueType(), stackrestore, []llvm.Value{sp}, "")
}
// createZExtOrTrunc lets the input value fit in the output type bits, by zero // createZExtOrTrunc lets the input value fit in the output type bits, by zero
// extending or truncating the integer. // extending or truncating the integer.
func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value { func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value {
+1 -12
View File
@@ -8,7 +8,6 @@
package llvmutil package llvmutil
import ( import (
"encoding/binary"
"strconv" "strconv"
"strings" "strings"
@@ -208,7 +207,7 @@ func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
used.SetLinkage(llvm.AppendingLinkage) used.SetLinkage(llvm.AppendingLinkage)
} }
// Version returns the LLVM major version. // Return the LLVM major version.
func Version() int { func Version() int {
majorStr := strings.Split(llvm.Version, ".")[0] majorStr := strings.Split(llvm.Version, ".")[0]
major, err := strconv.Atoi(majorStr) major, err := strconv.Atoi(majorStr)
@@ -217,13 +216,3 @@ func Version() int {
} }
return major return major
} }
// ByteOrder returns the byte order for the given target triple. Most targets are little
// endian, but for example MIPS can be big-endian.
func ByteOrder(target string) binary.ByteOrder {
if strings.HasPrefix(target, "mips-") {
return binary.BigEndian
} else {
return binary.LittleEndian
}
}
+15 -10
View File
@@ -6,11 +6,17 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"github.com/tinygo-org/tinygo/src/tinygo"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"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) {
@@ -18,20 +24,20 @@ 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 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 = uint64(tinygo.HashmapAlgorithmString) 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 = uint64(tinygo.HashmapAlgorithmBinary) 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 = uint64(tinygo.HashmapAlgorithmInterface) alg = hashmapAlgorithmInterface
} }
keySize := b.targetData.TypeAllocSize(llvmKeyType) keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType) valueSize := b.targetData.TypeAllocSize(llvmValueType)
@@ -248,11 +254,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
// can be compared with runtime.memequal. Note that padding bytes are undef // can be compared with runtime.memequal. Note that padding bytes are undef
// and can alter two "equal" structs being equal when compared with memequal. // and can alter two "equal" structs being equal when compared with memequal.
func hashmapIsBinaryKey(keyType types.Type) bool { func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.Underlying().(type) { switch keyType := keyType.(type) {
case *types.Basic: case *types.Basic:
// TODO: unsafe.Pointer is also a binary key, but to support that we
// need to fix an issue with interp first (see
// https://github.com/tinygo-org/tinygo/pull/4898).
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
case *types.Pointer: case *types.Pointer:
return true return true
@@ -266,6 +269,8 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
return true return true
case *types.Array: case *types.Array:
return hashmapIsBinaryKey(keyType.Elem()) return hashmapIsBinaryKey(keyType.Elem())
case *types.Named:
return hashmapIsBinaryKey(keyType.Underlying())
default: default:
return false return false
} }
@@ -321,7 +326,7 @@ func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
if i < numFields-1 { if i < numFields-1 {
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1) nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
} else { } else {
// Last field? Next offset is the total size of the allocate struct. // Last field? Next offset is the total size of the allcoate struct.
nextOffset = b.targetData.TypeAllocSize(llvmStructType) nextOffset = b.targetData.TypeAllocSize(llvmStructType)
} }
+122 -304
View File
@@ -12,7 +12,6 @@ import (
"strings" "strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader" "github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@@ -24,18 +23,15 @@ import (
// The linkName value contains a valid link name, even if //go:linkname is not // The linkName value contains a valid link name, even if //go:linkname is not
// present. // present.
type functionInfo struct { type functionInfo struct {
wasmModule string // go:wasm-module wasmModule string // go:wasm-module
wasmName string // wasm-export-name or wasm-import-name in the IR wasmName string // wasm-export-name or wasm-import-name in the IR
wasmExport string // go:wasmexport is defined (export is unset, this adds an exported wrapper) linkName string // go:linkname, go:export - the IR function name
wasmExportPos token.Pos // position of //go:wasmexport comment section string // go:section - object file section name
linkName string // go:linkname, go:export - the IR function name exported bool // go:export, CGo
section string // go:section - object file section name interrupt bool // go:interrupt
exported bool // go:export, CGo nobounds bool // go:nobounds
interrupt bool // go:interrupt variadic bool // go:variadic (CGo only)
nobounds bool // go:nobounds inline inlineType // go:inline
noescape bool // go:noescape
variadic bool // go:variadic (CGo only)
inline inlineType // go:inline
} }
type inlineType int type inlineType int
@@ -128,25 +124,11 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null") dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
for i, paramInfo := range paramInfos { for i, info := range paramInfos {
if paramInfo.elemSize != 0 { if info.elemSize != 0 {
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, paramInfo.elemSize) dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize)
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull) llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
} }
if info.noescape && paramInfo.flags&paramIsGoParam != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
// Parameters to functions with a //go:noescape parameter should get
// the nocapture attribute. However, the context parameter should
// not.
// (It may be safe to add the nocapture parameter to the context
// parameter, but I'd like to stay on the safe side here).
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
llvmFn.AddAttributeAtIndex(i+1, nocapture)
}
if paramInfo.flags&paramIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
llvmFn.AddAttributeAtIndex(i+1, readonly)
}
} }
// Set a number of function or parameter attributes, depending on the // Set a number of function or parameter attributes, depending on the
@@ -157,10 +139,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// On *nix systems, the "abort" functuion in libc is used to handle fatal panics. // On *nix systems, the "abort" functuion in libc is used to handle fatal panics.
// Mark it as noreturn so LLVM can optimize away code. // Mark it as noreturn so LLVM can optimize away code.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0)) llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
case "internal/abi.NoEscape":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "runtime.alloc": case "runtime.alloc":
// Tell the optimizer that runtime.alloc is an allocator, meaning that it // Tell the optimizer that runtime.alloc is an allocator, meaning that it
// returns values that are never null and never alias to an existing value. // returns values that are never null and never alias to an existing value.
@@ -184,12 +162,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// be modified. // be modified.
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.stringFromBytes": case "runtime.sliceCopy":
// Copying a slice won't capture any of the parameters.
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.stringFromRunes": llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.trackPointer": case "runtime.trackPointer":
// This function is necessary for tracking pointers on the stack in a // This function is necessary for tracking pointers on the stack in a
// portable way (see gc_stack_portable.go). Indicate to the optimizer // portable way (see gc_stack_portable.go). Indicate to the optimizer
@@ -209,12 +187,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// > circumstances, and should not be exposed to source languages. // > circumstances, and should not be exposed to source languages.
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn) llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
} }
case "GetModuleHandleExA", "GetProcAddress", "GetSystemInfo", "GetSystemTimeAsFileTime", "LoadLibraryExW", "QueryPerformanceCounter", "QueryPerformanceFrequency", "QueryUnbiasedInterruptTime", "SetEnvironmentVariableA", "Sleep", "SystemFunction036", "VirtualAlloc":
// On Windows we need to use a special calling convention for some
// external calls.
if c.GOOS == "windows" && c.GOARCH == "386" {
llvmFn.SetFunctionCallConv(llvm.X86StdcallCallConv)
}
} }
// External/exported functions may not retain pointer values. // External/exported functions may not retain pointer values.
@@ -238,43 +210,13 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
} }
} }
// Build the function if needed.
c.maybeCreateSyntheticFunction(fn, llvmFn)
return fnType, llvmFn
}
// If this is a synthetic function (such as a generic function or a wrapper),
// create it now.
func (c *compilerContext) maybeCreateSyntheticFunction(fn *ssa.Function, llvmFn llvm.Value) {
// Synthetic functions are functions that do not appear in the source code, // Synthetic functions are functions that do not appear in the source code,
// they are artificially constructed. Usually they are wrapper functions // they are artificially constructed. Usually they are wrapper functions
// that are not referenced anywhere except in a SSA call instruction so // that are not referenced anywhere except in a SSA call instruction so
// 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" && fn.Synthetic != "range-over-func yield" { if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
if origin := fn.Origin(); origin != nil && origin.RelString(nil) == "internal/abi.Escape" {
// This is a special implementation or internal/abi.Escape, which
// can only really be implemented in the compiler.
// For simplicity we'll only implement pointer parameters for now.
if _, ok := fn.Params[0].Type().Underlying().(*types.Pointer); ok {
irbuilder := c.ctx.NewBuilder()
defer irbuilder.Dispose()
b := newBuilder(c, irbuilder, fn)
b.createAbiEscapeImpl()
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
llvmFn.SetUnnamedAddr(true)
}
// If the parameter is not of a pointer type, it will be left
// unimplemented. This will result in a linker error if the function
// is really called, making it clear it needs to be implemented.
return
}
if len(fn.Blocks) == 0 {
c.addError(fn.Pos(), "missing function body")
return
}
irbuilder := c.ctx.NewBuilder() irbuilder := c.ctx.NewBuilder()
b := newBuilder(c, irbuilder, fn) b := newBuilder(c, irbuilder, fn)
b.createFunction() b.createFunction()
@@ -282,6 +224,8 @@ func (c *compilerContext) maybeCreateSyntheticFunction(fn *ssa.Function, llvmFn
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage) llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
llvmFn.SetUnnamedAddr(true) llvmFn.SetUnnamedAddr(true)
} }
return fnType, llvmFn
} }
// getFunctionInfo returns information about a function that is not directly // getFunctionInfo returns information about a function that is not directly
@@ -295,27 +239,8 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
// Pick the default linkName. // Pick the default linkName.
linkName: f.RelString(nil), linkName: f.RelString(nil),
} }
// Check for a few runtime functions that are treated specially.
if info.linkName == "runtime.wasmEntryReactor" && c.BuildMode == "c-shared" {
info.linkName = "_initialize"
info.wasmName = "_initialize"
info.exported = true
}
if info.linkName == "runtime.wasmEntryCommand" && c.BuildMode == "default" {
info.linkName = "_start"
info.wasmName = "_start"
info.exported = true
}
if info.linkName == "runtime.wasmEntryLegacy" && c.BuildMode == "wasi-legacy" {
info.linkName = "_start"
info.wasmName = "_start"
info.exported = true
}
// Check for //go: pragmas, which may change the link name (among others). // Check for //go: pragmas, which may change the link name (among others).
c.parsePragmas(&info, f) c.parsePragmas(&info, f)
c.functionInfos[f] = info c.functionInfos[f] = info
return info return info
} }
@@ -323,246 +248,150 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
// parsePragmas is used by getFunctionInfo to parse function pragmas such as // parsePragmas is used by getFunctionInfo to parse function pragmas such as
// //export or //go:noinline. // //export or //go:noinline.
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) { func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
syntax := f.Syntax() if f.Syntax() == nil {
if f.Origin() != nil {
syntax = f.Origin().Syntax()
}
if syntax == nil {
return return
} }
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
// Read all pragmas of this function.
var pragmas []*ast.Comment
hasWasmExport := false
if decl, ok := syntax.(*ast.FuncDecl); ok && decl.Doc != nil {
for _, comment := range decl.Doc.List { for _, comment := range decl.Doc.List {
text := comment.Text text := comment.Text
if strings.HasPrefix(text, "//go:") || strings.HasPrefix(text, "//export ") { if strings.HasPrefix(text, "//export ") {
pragmas = append(pragmas, comment) // Rewrite '//export' to '//go:export' for compatibility with
if strings.HasPrefix(comment.Text, "//go:wasmexport ") { // gc.
hasWasmExport = true text = "//go:" + text[2:]
}
if !strings.HasPrefix(text, "//go:") {
continue
}
parts := strings.Fields(text)
switch parts[0] {
case "//go:export":
if len(parts) != 2 {
continue
}
info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
c.checkWasmImport(f, comment.Text)
info.exported = true
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:section":
// Only enable go:section when the package imports "unsafe".
// go:section also implies go:noinline since inlining could
// move the code to a different section than that requested.
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
info.inline = inlineNone
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "C.") {
// This prefix cannot naturally be created, it must have
// been created as a result of CGo preprocessing.
info.variadic = true
} }
} }
} }
} }
// Parse each pragma.
for _, comment := range pragmas {
parts := strings.Fields(comment.Text)
switch parts[0] {
case "//export", "//go:export":
if len(parts) != 2 {
continue
}
if hasWasmExport {
// //go:wasmexport overrides //export.
continue
}
info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
continue
}
c.checkWasmImportExport(f, comment.Text)
info.exported = true
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:wasmexport":
if f.Blocks == nil {
c.addError(f.Pos(), "can only use //go:wasmexport on definitions")
continue
}
if len(parts) != 2 {
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmexport, not %d", len(parts)-1))
continue
}
name := parts[1]
if name == "_start" || name == "_initialize" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow %#v", name))
continue
}
if c.BuildMode != "c-shared" && f.RelString(nil) == "main.main" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow main.main to be exported with -buildmode=%s", c.BuildMode))
continue
}
if c.archFamily() != "wasm32" {
c.addError(f.Pos(), "//go:wasmexport is only supported on wasm")
}
c.checkWasmImportExport(f, comment.Text)
info.wasmExport = name
info.wasmExportPos = comment.Slash
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:section":
// Only enable go:section when the package imports "unsafe".
// go:section also implies go:noinline since inlining could
// move the code to a different section than that requested.
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
info.inline = inlineNone
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:noescape":
// Don't let pointer parameters escape.
// Following the upstream Go implementation, we only do this for
// declarations, not definitions.
if len(f.Blocks) == 0 {
info.noescape = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "_Cgo_") {
// This prefix was created as a result of CGo preprocessing.
info.variadic = true
}
}
}
if c.Nobounds {
info.nobounds = true
}
} }
// Check whether this function can be used in //go:wasmimport or // Check whether this function cannot be used in //go:wasmimport. It will add an
// //go:wasmexport. It will add an error if this is not the case. // error if this is the case.
// //
// The list of allowed types is based on this proposal: // The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149 // https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string) { func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" || c.pkg.Path() == "crypto/internal/sysrand" { if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
// The runtime is a special case. Allow all kinds of parameters // The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers). // (importantly, including pointers).
return return
} }
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), fmt.Sprintf("can only use //go:wasmimport on declarations"))
return
}
if f.Signature.Results().Len() > 1 { if f.Signature.Results().Len() > 1 {
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma)) c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
} else if f.Signature.Results().Len() == 1 { } else if f.Signature.Results().Len() == 1 {
result := f.Signature.Results().At(0) result := f.Signature.Results().At(0)
if !c.isValidWasmType(result.Type(), siteResult) { if !isValidWasmType(result.Type(), true) {
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String())) c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
} }
} }
for _, param := range f.Params { for _, param := range f.Params {
// Check whether the type is allowed. // Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly. // Only a very limited number of types can be mapped to WebAssembly.
if !c.isValidWasmType(param.Type(), siteParam) { if !isValidWasmType(param.Type(), false) {
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String())) c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
} }
} }
} }
// Check whether the type maps directly to a WebAssembly type. // Check whether the type maps directly to a WebAssembly type, according to:
//
// This reflects the relaxed type restrictions proposed here (except for structs.HostLayout):
// https://github.com/golang/go/issues/66984
//
// This previously reflected the additional restrictions documented here:
// https://github.com/golang/go/issues/59149 // https://github.com/golang/go/issues/59149
func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool { func isValidWasmType(typ types.Type, isReturn bool) bool {
switch typ := typ.Underlying().(type) { switch typ := typ.Underlying().(type) {
case *types.Basic: case *types.Basic:
switch typ.Kind() { switch typ.Kind() {
case types.Bool:
return true
case types.Int8, types.Uint8, types.Int16, types.Uint16:
return site == siteIndirect
case types.Int32, types.Uint32, types.Int64, types.Uint64: case types.Int32, types.Uint32, types.Int64, types.Uint64:
return true return true
case types.Float32, types.Float64: case types.Float32, types.Float64:
return true return true
case types.Uintptr, types.UnsafePointer: case types.UnsafePointer:
return true if !isReturn {
case types.String: return true
// string flattens to two values, so disallowed as a result
return site == siteParam || site == siteIndirect
}
case *types.Array:
return site == siteIndirect && c.isValidWasmType(typ.Elem(), siteIndirect)
case *types.Struct:
if site != siteIndirect {
return false
}
// Structs with no fields do not need structs.HostLayout
if typ.NumFields() == 0 {
return true
}
hasHostLayout := true // default to true before detecting Go version
// (*types.Package).GoVersion added in go1.21
if gv, ok := any(c.pkg).(interface{ GoVersion() string }); ok {
if goenv.Compare(gv.GoVersion(), "go1.23") >= 0 {
hasHostLayout = false // package structs added in go1.23
} }
} }
for i := 0; i < typ.NumFields(); i++ {
ftyp := typ.Field(i).Type()
if types.Unalias(ftyp).String() == "structs.HostLayout" {
hasHostLayout = true
continue
}
if !c.isValidWasmType(ftyp, siteIndirect) {
return false
}
}
return hasHostLayout
case *types.Pointer:
return c.isValidWasmType(typ.Elem(), siteIndirect)
} }
return false return false
} }
type wasmSite int
const (
siteParam wasmSite = iota
siteResult
siteIndirect // pointer or field
)
// getParams returns the function parameters, including the receiver at the // getParams returns the function parameters, including the receiver at the
// start. This is an alternative to the Params member of *ssa.Function, which is // start. This is an alternative to the Params member of *ssa.Function, which is
// not yet populated when the package has not yet been built. // not yet populated when the package has not yet been built.
@@ -631,7 +460,7 @@ func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) {
// linkName is equal to .RelString(nil) on a global and extern is false, but for // linkName is equal to .RelString(nil) on a global and extern is false, but for
// some symbols this is different (due to //go:extern for example). // some symbols this is different (due to //go:extern for example).
type globalInfo struct { type globalInfo struct {
linkName string // go:extern, go:linkname linkName string // go:extern
extern bool // go:extern extern bool // go:extern
align int // go:align align int // go:align
section string // go:section section string // go:section
@@ -716,14 +545,14 @@ func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
// Check for //go: pragmas, which may change the link name (among others). // Check for //go: pragmas, which may change the link name (among others).
doc := c.astComments[info.linkName] doc := c.astComments[info.linkName]
if doc != nil { if doc != nil {
info.parsePragmas(doc, c, g) info.parsePragmas(doc)
} }
return info return info
} }
// Parse //go: pragma comments from the source. In particular, it parses the // Parse //go: pragma comments from the source. In particular, it parses the
// //go:extern and //go:linkname pragmas on globals. // //go:extern pragma on globals.
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup, c *compilerContext, g *ssa.Global) { func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
for _, comment := range doc.List { for _, comment := range doc.List {
if !strings.HasPrefix(comment.Text, "//go:") { if !strings.HasPrefix(comment.Text, "//go:") {
continue continue
@@ -744,17 +573,6 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup, c *compilerContext,
if len(parts) == 2 { if len(parts) == 2 {
info.section = parts[1] info.section = parts[1]
} }
case "//go:linkname":
if len(parts) != 3 || parts[1] != g.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(g.Pkg.Pkg) {
info.linkName = parts[2]
}
} }
} }
} }
+5 -317
View File
@@ -5,7 +5,6 @@ package compiler
import ( import (
"strconv" "strconv"
"strings"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@@ -13,8 +12,7 @@ import (
// createRawSyscall creates a system call with the provided system call number // createRawSyscall creates a system call with the provided system call number
// and returns the result as a single integer (the system call result). The // and returns the result as a single integer (the system call result). The
// result is not further interpreted (with the exception of MIPS to use the same // result is not further interpreted.
// return value everywhere).
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], getPos(call)) num := b.getValue(call.Args[0], getPos(call))
switch { switch {
@@ -35,17 +33,18 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
"{r10}", "{r10}",
"{r8}", "{r8}",
"{r9}", "{r9}",
"{r11}",
"{r12}",
"{r13}",
}[i] }[i]
llvmValue := b.getValue(arg, getPos(call)) llvmValue := b.getValue(arg, getPos(call))
args = append(args, llvmValue) args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type()) argTypes = append(argTypes, llvmValue.Type())
} }
// rcx and r11 are clobbered by the syscall, so make sure they are not used
constraints += ",~{rcx},~{r11}" constraints += ",~{rcx},~{r11}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "386" && b.GOOS == "linux": case b.GOARCH == "386" && b.GOOS == "linux":
// Sources: // Sources:
// syscall(2) man page // syscall(2) man page
@@ -72,16 +71,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "arm" && b.GOOS == "linux": case b.GOARCH == "arm" && b.GOOS == "linux":
if arch := b.archFamily(); arch != "arm" {
// Some targets pretend to be linux/arm for compatibility but aren't
// actually such a system. Make sure we emit an error instead of
// creating inline assembly that will fail to compile.
// See: https://github.com/tinygo-org/tinygo/issues/4959
return llvm.Value{}, b.makeError(call.Pos(), "system calls are not supported: target emulates a linux/arm system on "+arch)
}
// Implement the EABI system call convention for Linux. // Implement the EABI system call convention for Linux.
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
@@ -113,7 +103,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "arm64" && b.GOOS == "linux": case b.GOARCH == "arm64" && b.GOOS == "linux":
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
@@ -146,98 +135,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(fnType, target, args, ""), nil
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
// Implement the system call convention for Linux.
// Source: syscall(2) man page and musl:
// https://git.musl-libc.org/cgit/musl/tree/arch/mips/syscall_arch.h
// Also useful:
// https://web.archive.org/web/20220529105937/https://www.linux-mips.org/wiki/Syscall
// The syscall number goes in r2, the result also in r2.
// Register r7 is both an input parameter and an output parameter: if it
// is non-zero, the system call failed and r2 is the error code.
// The code below implements the O32 syscall ABI, not the N32 ABI. It
// could implement both at the same time if needed (like what appears to
// be done in musl) by forcing arg5-arg7 into the right registers but
// letting the compiler decide the registers should result in _slightly_
// faster and smaller code.
args := []llvm.Value{num}
argTypes := []llvm.Type{b.uintptrType}
constraints := "={$2},={$7},0"
syscallParams := call.Args[1:]
if len(syscallParams) > 7 {
// There is one syscall that uses 7 parameters: sync_file_range.
// But only 7, not more. Go however only has Syscall6 and Syscall9.
// Therefore, we can ignore the remaining parameters.
syscallParams = syscallParams[:7]
}
for i, arg := range syscallParams {
constraints += "," + [...]string{
"{$4}", // arg1
"{$5}", // arg2
"{$6}", // arg3
"1", // arg4, error return
"r", // arg5 on the stack
"r", // arg6 on the stack
"r", // arg7 on the stack
}[i]
llvmValue := b.getValue(arg, getPos(call))
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
// Create assembly code.
// Parameters beyond the first 4 are passed on the stack instead of in
// registers in the O32 syscall ABI.
// We need ".set noat" because LLVM might pick register $1 ($at) as the
// register for a parameter and apparently this is not allowed on MIPS
// unless you use this specific pragma.
asm := "syscall"
switch len(syscallParams) {
case 5:
asm = "" +
".set noat\n" +
"subu $$sp, $$sp, 32\n" +
"sw $7, 16($$sp)\n" + // arg5
"syscall\n" +
"addu $$sp, $$sp, 32\n" +
".set at\n"
case 6:
asm = "" +
".set noat\n" +
"subu $$sp, $$sp, 32\n" +
"sw $7, 16($$sp)\n" + // arg5
"sw $8, 20($$sp)\n" + // arg6
"syscall\n" +
"addu $$sp, $$sp, 32\n" +
".set at\n"
case 7:
asm = "" +
".set noat\n" +
"subu $$sp, $$sp, 32\n" +
"sw $7, 16($$sp)\n" + // arg5
"sw $8, 20($$sp)\n" + // arg6
"sw $9, 24($$sp)\n" + // arg7
"syscall\n" +
"addu $$sp, $$sp, 32\n" +
".set at\n"
}
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
fnType := llvm.FunctionType(returnType, argTypes, false)
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
call := b.CreateCall(fnType, target, args, "")
resultCode := b.CreateExtractValue(call, 0, "") // r2
errorFlag := b.CreateExtractValue(call, 1, "") // r7
// Pseudocode to return the result with the same convention as other
// archs:
// return (errorFlag != 0) ? -resultCode : resultCode;
// At least on QEMU with the O32 ABI, the error code is always positive.
zero := llvm.ConstInt(b.uintptrType, 0, false)
isError := b.CreateICmp(llvm.IntNE, errorFlag, zero, "")
negativeResult := b.CreateSub(zero, resultCode, "")
result := b.CreateSelect(isError, negativeResult, resultCode, "")
return result, nil
default: default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH) return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
} }
@@ -276,8 +173,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
// The signature looks like this: // The signature looks like this:
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) // func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
isI386 := strings.HasPrefix(b.Triple, "i386-")
// Prepare input values. // Prepare input values.
var paramTypes []llvm.Type var paramTypes []llvm.Type
var params []llvm.Value var params []llvm.Value
@@ -295,17 +190,11 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
if setLastError.IsNil() { if setLastError.IsNil() {
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false) llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType) setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
if isI386 {
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
}
} }
getLastError := b.mod.NamedFunction("GetLastError") getLastError := b.mod.NamedFunction("GetLastError")
if getLastError.IsNil() { if getLastError.IsNil() {
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false) llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType) getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
if isI386 {
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
}
} }
// Now do the actual call. Pseudocode: // Now do the actual call. Pseudocode:
@@ -316,24 +205,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
// Note that SetLastError/GetLastError could be replaced with direct // Note that SetLastError/GetLastError could be replaced with direct
// access to the thread control block, which is probably smaller and // access to the thread control block, which is probably smaller and
// faster. The Go runtime does this in assembly. // faster. The Go runtime does this in assembly.
// On windows/386, we also need to save/restore the stack pointer. I'm b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
// not entirely sure why this is needed, but without it these calls
// change the stack pointer leading to a crash soon after.
setLastErrorCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
var sp llvm.Value
if isI386 {
setLastErrorCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
sp = b.readStackPointer()
}
syscallResult := b.CreateCall(llvmType, fnPtr, params, "") syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
if isI386 {
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
b.writeStackPointer(sp)
}
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err") errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
if isI386 {
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
}
if b.uintptrType != b.ctx.Int32Type() { if b.uintptrType != b.ctx.Int32Type() {
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr") errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
} }
@@ -343,136 +217,11 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, syscallResult, 0, "") retval = b.CreateInsertValue(retval, syscallResult, 0, "")
retval = b.CreateInsertValue(retval, errResult, 2, "") retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil return retval, nil
default: default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH) return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
} }
} }
// createSyscalln emits instructions for the syscall.syscalln function on
// Windows. This handles the variadic calling convention used in Go 1.26+:
//
// func syscalln(fn, n uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
//
// The function generates a switch on n to dispatch to the correct fixed-argument
// function pointer call with SetLastError(0)/GetLastError() wrapping.
func (b *builder) createSyscalln(call *ssa.CallCommon) (llvm.Value, error) {
const maxArgs = 18 // Windows syscalls support up to 18 args
isI386 := strings.HasPrefix(b.Triple, "i386-")
// Get the function pointer (call.Args[0]) and n (call.Args[1]).
fn := b.getValue(call.Args[0], getPos(call))
fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
n := b.getValue(call.Args[1], getPos(call))
// Get the variadic args slice (call.Args[2]).
// In SSA, the variadic slice is the third argument.
var argsPtr llvm.Value
if len(call.Args) > 2 {
argsSlice := b.getValue(call.Args[2], getPos(call))
argsPtr = b.CreateExtractValue(argsSlice, 0, "args.data")
} else {
argsPtr = llvm.ConstNull(b.dataPtrType)
}
// Prepare SetLastError and GetLastError.
setLastError := b.mod.NamedFunction("SetLastError")
if setLastError.IsNil() {
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
if isI386 {
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
}
}
getLastError := b.mod.NamedFunction("GetLastError")
if getLastError.IsNil() {
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
if isI386 {
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
}
}
retType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)
// Create the merge block where all cases converge.
mergeBB := b.insertBasicBlock("syscalln.merge")
// Create the default (panic) block.
panicBB := b.insertBasicBlock("syscalln.panic")
// Create the switch on n.
sw := b.CreateSwitch(n, panicBB, maxArgs+1)
// We'll collect blocks and values for the PHI node.
var incomingVals []llvm.Value
var incomingBlocks []llvm.BasicBlock
// Generate a case for each arg count 0..maxArgs.
for i := 0; i <= maxArgs; i++ {
caseBB := b.insertBasicBlock("syscalln.case" + strconv.Itoa(i))
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), caseBB)
b.SetInsertPointAtEnd(caseBB)
// Load args[0] through args[i-1] from the slice data pointer.
var params []llvm.Value
var paramTypes []llvm.Type
for j := 0; j < i; j++ {
gep := b.CreateInBoundsGEP(b.uintptrType, argsPtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), uint64(j), false),
}, "")
arg := b.CreateLoad(b.uintptrType, gep, "")
params = append(params, arg)
paramTypes = append(paramTypes, b.uintptrType)
}
// SetLastError(0)
setCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
var sp llvm.Value
if isI386 {
setCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
sp = b.readStackPointer()
}
// Call fn(args...)
fnType := llvm.FunctionType(b.uintptrType, paramTypes, false)
syscallResult := b.CreateCall(fnType, fnPtr, params, "")
if isI386 {
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
b.writeStackPointer(sp)
}
// err = GetLastError()
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
if isI386 {
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
}
if b.uintptrType != b.ctx.Int32Type() {
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
}
// Build {r1, 0, err}
result := llvm.ConstNull(retType)
result = b.CreateInsertValue(result, syscallResult, 0, "")
result = b.CreateInsertValue(result, errResult, 2, "")
incomingVals = append(incomingVals, result)
incomingBlocks = append(incomingBlocks, b.Builder.GetInsertBlock())
b.CreateBr(mergeBB)
}
// Panic block for n > maxArgs.
b.SetInsertPointAtEnd(panicBB)
b.CreateUnreachable()
// Merge block: PHI node to select the result.
b.SetInsertPointAtEnd(mergeBB)
phi := b.CreatePHI(retType, "syscalln.result")
phi.AddIncoming(incomingVals, incomingBlocks)
return phi, nil
}
// createRawSyscallNoError emits instructions for the Linux-specific // createRawSyscallNoError emits instructions for the Linux-specific
// syscall.rawSyscallNoError function. // syscall.rawSyscallNoError function.
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) { func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
@@ -485,64 +234,3 @@ func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, err
retval = b.CreateInsertValue(retval, llvm.ConstInt(b.uintptrType, 0, false), 1, "") retval = b.CreateInsertValue(retval, llvm.ConstInt(b.uintptrType, 0, false), 1, "")
return retval, nil return retval, nil
} }
// Lower a call to internal/abi.FuncPCABI0 on MacOS.
// This function is called like this:
//
// syscall(abi.FuncPCABI0(libc_mkdir_trampoline), uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
//
// So we'll want to return a function pointer (as uintptr) that points to the
// libc function. Specifically, we _don't_ want to point to the trampoline
// function (which is implemented in Go assembly which we can't read), but
// rather to the actually intended function. For this we're going to assume that
// all the functions follow a specific pattern: libc_<functionname>_trampoline.
//
// The return value is the function pointer as an uintptr, or a nil value if
// this isn't possible (and a regular call should be made as fallback).
func (b *builder) createDarwinFuncPCABI0Call(instr *ssa.CallCommon) llvm.Value {
if b.GOOS != "darwin" {
// This has only been tested on MacOS (and only seems to be used there).
return llvm.Value{}
}
// Check that it uses a function call like syscall.libc_*_trampoline
itf := instr.Args[0].(*ssa.MakeInterface)
calledFn := itf.X.(*ssa.Function)
if pkgName := calledFn.Pkg.Pkg.Path(); pkgName != "syscall" && pkgName != "internal/syscall/unix" {
return llvm.Value{}
}
if !strings.HasPrefix(calledFn.Name(), "libc_") || !strings.HasSuffix(calledFn.Name(), "_trampoline") {
return llvm.Value{}
}
// Extract the libc function name.
name := strings.TrimPrefix(strings.TrimSuffix(calledFn.Name(), "_trampoline"), "libc_")
if name == "open" {
// Special case: open() is a variadic function and can't be called like
// a regular function. Therefore, we need to use a wrapper implemented
// in C.
name = "syscall_libc_open"
}
if b.GOARCH == "amd64" {
if name == "fdopendir" || name == "readdir_r" {
// Hack to support amd64, which needs the $INODE64 suffix.
// This is also done in upstream Go:
// https://github.com/golang/go/commit/096ab3c21b88ccc7d411379d09fe6274e3159467
name += "$INODE64"
}
}
// Obtain the C function.
// Use a simple function (no parameters or return value) because all we need
// is the address of the function.
llvmFn := b.mod.NamedFunction(name)
if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(b.ctx.VoidType(), nil, false)
llvmFn = llvm.AddFunction(b.mod, name, llvmFnType)
}
// Cast the function pointer to a uintptr (because that's what
// abi.FuncPCABI0 returns).
return b.CreatePtrToInt(llvmFn, b.uintptrType, "")
}
+4 -4
View File
@@ -1,6 +1,6 @@
; ModuleID = 'basic.go' ; ModuleID = 'basic.go'
source_filename = "basic.go" source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%main.kv = type { float, i32, i32, i32 } %main.kv = type { float, i32, i32, i32 }
@@ -206,7 +206,7 @@ entry:
ret void ret void
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+33 -33
View File
@@ -1,9 +1,9 @@
; 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-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.channelOp = type { ptr, ptr, i32, ptr } %runtime.channelBlockedList = type { ptr, ptr, ptr, { ptr, i32, i32 } }
%runtime.chanSelectState = type { ptr, ptr } %runtime.chanSelectState = type { ptr, ptr }
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
@@ -18,15 +18,15 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
%chan.op = alloca %runtime.channelOp, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4 %chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value) call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
store i32 3, ptr %chan.value, align 4 store i32 3, ptr %chan.value, align 4
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4 call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value) call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
ret void ret void
} }
@@ -34,61 +34,61 @@ entry:
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3 declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1 declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3 declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
%chan.op = alloca %runtime.channelOp, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
%chan.value = alloca i32, align 4 %chan.value = alloca i32, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value) call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4 %0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value) call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
ret void ret void
} }
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1 declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
%chan.op = alloca %runtime.channelOp, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4 call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
%chan.op = alloca %runtime.channelOp, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4 %0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op) call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 { define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %ch2, ptr %context) unnamed_addr #2 {
entry: entry:
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8 %select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.send.value = alloca i32, align 4 %select.send.value = alloca i32, align 4
store i32 1, ptr %select.send.value, align 4 store i32 1, ptr %select.send.value, align 4
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca) call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
store ptr %ch1, ptr %select.states.alloca, align 4 store ptr %ch1, ptr %select.states.alloca, align 4
%select.states.alloca.repack1 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4 %select.states.alloca.repack1 = getelementptr inbounds %runtime.chanSelectState, ptr %select.states.alloca, i32 0, i32 1
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4 store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
%0 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 8 %0 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1
store ptr %ch2, ptr %0, align 4 store ptr %ch2, ptr %0, align 4
%.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12 %.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1
store ptr null, ptr %.repack3, align 4 store ptr null, ptr %.repack3, align 4
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4 %select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca) call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
%1 = extractvalue { i32, i1 } %select.result, 0 %1 = extractvalue { i32, i1 } %select.result, 0
%2 = icmp eq i32 %1, 0 %2 = icmp eq i32 %1, 0
@@ -105,10 +105,10 @@ select.body: ; preds = %select.next
br label %select.done br label %select.done
} }
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1 declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { nounwind } attributes #4 = { nounwind }
+12 -256
View File
@@ -3,8 +3,9 @@ source_filename = "defer.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-unknown-eabi" target triple = "thumbv7m-unknown-unknown-eabi"
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface } %runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
%runtime._interface = type { ptr, ptr } %runtime._interface = type { ptr, ptr }
%runtime._defer = type { i32, ptr }
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -27,7 +28,7 @@ entry:
%0 = call ptr @llvm.stacksave.p0() %0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4 call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
store i32 0, ptr %defer.alloca, align 4 store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4 %defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
store ptr null, ptr %defer.alloca.repack15, align 4 store ptr null, ptr %defer.alloca.repack15, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4 store ptr %defer.alloca, ptr %deferPtr, align 4
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5 %setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
@@ -51,7 +52,7 @@ rundefers.loophead: ; preds = %3, %rundefers.block
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %2, i32 4 %stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
%stack.next = load ptr, ptr %stack.next.gep, align 4 %stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4 store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %2, align 4 %callback = load i32, ptr %2, align 4
@@ -87,7 +88,7 @@ rundefers.loophead6: ; preds = %5, %lpad
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5 br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
rundefers.loop5: ; preds = %rundefers.loophead6 rundefers.loop5: ; preds = %rundefers.loophead6
%stack.next.gep8 = getelementptr inbounds nuw i8, ptr %4, i32 4 %stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %4, i32 0, i32 1
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4 %stack.next9 = load ptr, ptr %stack.next.gep8, align 4
store ptr %stack.next9, ptr %deferPtr, align 4 store ptr %stack.next9, ptr %deferPtr, align 4
%callback11 = load i32, ptr %4, align 4 %callback11 = load i32, ptr %4, align 4
@@ -121,18 +122,12 @@ declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 { define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
entry: entry:
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 3, ptr undef) #4 call void @runtime.printint32(i32 3, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
ret void ret void
} }
declare void @runtime.printlock(ptr) #2
declare void @runtime.printint32(i32, ptr) #2 declare void @runtime.printint32(i32, ptr) #2
declare void @runtime.printunlock(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 { define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
entry: entry:
@@ -144,11 +139,11 @@ entry:
%0 = call ptr @llvm.stacksave.p0() %0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4 call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
store i32 0, ptr %defer.alloca, align 4 store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack22 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4 %defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
store ptr null, ptr %defer.alloca.repack22, align 4 store ptr null, ptr %defer.alloca.repack22, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4 store ptr %defer.alloca, ptr %deferPtr, align 4
store i32 1, ptr %defer.alloca2, align 4 store i32 1, ptr %defer.alloca2, align 4
%defer.alloca2.repack23 = getelementptr inbounds nuw i8, ptr %defer.alloca2, i32 4 %defer.alloca2.repack23 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4 store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
store ptr %defer.alloca2, ptr %deferPtr, align 4 store ptr %defer.alloca2, ptr %deferPtr, align 4
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5 %setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
@@ -172,7 +167,7 @@ rundefers.loophead: ; preds = %4, %3, %rundefers.b
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %2, i32 4 %stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
%stack.next = load ptr, ptr %stack.next.gep, align 4 %stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4 store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %2, align 4 %callback = load i32, ptr %2, align 4
@@ -218,7 +213,7 @@ rundefers.loophead10: ; preds = %7, %6, %lpad
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9 br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
rundefers.loop9: ; preds = %rundefers.loophead10 rundefers.loop9: ; preds = %rundefers.loophead10
%stack.next.gep12 = getelementptr inbounds nuw i8, ptr %5, i32 4 %stack.next.gep12 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4 %stack.next13 = load ptr, ptr %stack.next.gep12, align 4
store ptr %stack.next13, ptr %deferPtr, align 4 store ptr %stack.next13, ptr %deferPtr, align 4
%callback15 = load i32, ptr %5, align 4 %callback15 = load i32, ptr %5, align 4
@@ -255,259 +250,20 @@ rundefers.end7: ; preds = %rundefers.loophead1
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 { define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
entry: entry:
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 3, ptr undef) #4 call void @runtime.printint32(i32 3, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
ret void ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 { define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
entry: entry:
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 5, ptr undef) #4 call void @runtime.printint32(i32 5, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
ret void ret void
} }
; Function Attrs: nounwind attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "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" }
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 { 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" }
entry: attributes #2 = { "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" }
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
br label %for.body
for.body: ; preds = %for.body, %entry
%defer.next = load ptr, ptr %deferPtr, align 4
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
store i32 0, ptr %defer.alloc.call, align 4
%defer.alloc.call.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
store ptr %defer.next, ptr %defer.alloc.call.repack1, align 4
%defer.alloc.call.repack3 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
store i32 8, ptr %defer.alloc.call.repack3, align 4
store ptr %defer.alloc.call, ptr %deferPtr, align 4
br label %for.body
recover: ; preds = %rundefers.end
ret void
lpad: ; No predecessors!
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %lpad
br i1 poison, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
switch i32 poison, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
br label %recover
}
; Function Attrs: nounwind
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
entry:
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
br label %for.loop
for.loop: ; preds = %for.body, %entry
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
%2 = icmp slt i32 %1, 10
br i1 %2, label %for.body, label %for.done
for.body: ; preds = %for.loop
%defer.next = load ptr, ptr %deferPtr, align 4
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
store i32 0, ptr %defer.alloc.call, align 4
%defer.alloc.call.repack13 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
store ptr %defer.next, ptr %defer.alloc.call.repack13, align 4
%defer.alloc.call.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
store i32 %1, ptr %defer.alloc.call.repack15, align 4
store ptr %defer.alloc.call, ptr %deferPtr, align 4
%3 = add i32 %1, 1
br label %for.loop
for.done: ; preds = %for.loop
br label %rundefers.block
rundefers.after: ; preds = %rundefers.end
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
ret void
rundefers.block: ; preds = %for.done
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
%4 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %4, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %4, i32 4
%stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %4, align 4
switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
%gep = getelementptr inbounds nuw i8, ptr %4, i32 8
%param = load i32, ptr %gep, align 4
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 %param, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
br label %rundefers.after
recover: ; preds = %rundefers.end1
ret void
lpad: ; No predecessors!
br label %rundefers.loophead4
rundefers.loophead4: ; preds = %rundefers.callback010, %lpad
br i1 poison, label %rundefers.end1, label %rundefers.loop3
rundefers.loop3: ; preds = %rundefers.loophead4
switch i32 poison, label %rundefers.default2 [
i32 0, label %rundefers.callback010
]
rundefers.callback010: ; preds = %rundefers.loop3
br label %rundefers.loophead4
rundefers.default2: ; preds = %rundefers.loop3
unreachable
rundefers.end1: ; preds = %rundefers.loophead4
br label %recover
}
; Function Attrs: nounwind
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
entry:
%defer.alloca = alloca { i32, ptr, i32 }, align 4
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
br label %for.loop
for.loop: ; preds = %for.body, %entry
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
%2 = icmp slt i32 %1, 10
br i1 %2, label %for.body, label %for.done
for.body: ; preds = %for.loop
%3 = add i32 %1, 1
br label %for.loop
for.done: ; preds = %for.loop
%defer.next = load ptr, ptr %deferPtr, align 4
store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack16 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
store ptr %defer.next, ptr %defer.alloca.repack16, align 4
%defer.alloca.repack18 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 8
store i32 1, ptr %defer.alloca.repack18, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4
br label %for.loop1
for.loop1: ; preds = %for.body2, %for.done
%4 = phi i32 [ 0, %for.done ], [ %6, %for.body2 ]
%5 = icmp slt i32 %4, 10
br i1 %5, label %for.body2, label %for.done3
for.body2: ; preds = %for.loop1
%6 = add i32 %4, 1
br label %for.loop1
for.done3: ; preds = %for.loop1
br label %rundefers.block
rundefers.after: ; preds = %rundefers.end
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
ret void
rundefers.block: ; preds = %for.done3
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
%7 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %7, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %7, i32 4
%stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %7, align 4
switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
%gep = getelementptr inbounds nuw i8, ptr %7, i32 8
%param = load i32, ptr %gep, align 4
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 %param, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
br label %rundefers.after
recover: ; preds = %rundefers.end4
ret void
lpad: ; No predecessors!
br label %rundefers.loophead7
rundefers.loophead7: ; preds = %rundefers.callback013, %lpad
br i1 poison, label %rundefers.end4, label %rundefers.loop6
rundefers.loop6: ; preds = %rundefers.loophead7
switch i32 poison, label %rundefers.default5 [
i32 0, label %rundefers.callback013
]
rundefers.callback013: ; preds = %rundefers.loop6
br label %rundefers.loophead7
rundefers.default5: ; preds = %rundefers.loop6
unreachable
rundefers.end4: ; preds = %rundefers.loophead7
br label %recover
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #3 = { nocallback nofree nosync nounwind willreturn } attributes #3 = { nocallback nofree nosync nounwind willreturn }
attributes #4 = { nounwind } attributes #4 = { nounwind }
attributes #5 = { nounwind returns_twice } attributes #5 = { nounwind returns_twice }
-20
View File
@@ -18,23 +18,3 @@ func deferMultiple() {
}() }()
external() external()
} }
func deferInfiniteLoop() {
for {
defer print(8)
}
}
func deferLoop() {
for i := 0; i < 10; i++ {
defer print(i)
}
}
func deferBetweenLoops() {
for i := 0; i < 10; i++ {
}
defer print(1)
for i := 0; i < 10; i++ {
}
}
+14 -78
View File
@@ -1,9 +1,6 @@
package main package main
import ( import "unsafe"
"structs"
"unsafe"
)
//go:wasmimport modulename empty //go:wasmimport modulename empty
func empty() func empty()
@@ -16,92 +13,31 @@ func implementation() {
type Uint uint32 type Uint uint32
type S struct {
_ structs.HostLayout
a [4]uint32
b uintptr
d float32
e float64
}
//go:wasmimport modulename validparam //go:wasmimport modulename validparam
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S, j *struct{}, k *[8]uint8) func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint)
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [4]uint32
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type struct{a int}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type func()
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type uint // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [8]int // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32
// //
//go:wasmimport modulename invalidparam //go:wasmimport modulename invalidparam
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func(), f int, g uint, h [8]int) func invalidparam(a int, b string, c []byte, d *int32)
// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{int} //go:wasmimport modulename validreturn
// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{string} func validreturn() int32
//
//go:wasmimport modulename invalidparam_no_hostlayout
func invalidparam_no_hostlayout(a *struct{ int }, b *struct{ string })
//go:wasmimport modulename validreturn_int32
func validreturn_int32() int32
//go:wasmimport modulename validreturn_ptr_int32
func validreturn_ptr_int32() *int32
//go:wasmimport modulename validreturn_ptr_string
func validreturn_ptr_string() *string
//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_struct() *S
//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_empty_struct() *struct{}
//go:wasmimport modulename validreturn_ptr_array
func validreturn_ptr_array() *[8]uint8
//go:wasmimport modulename validreturn_unsafe_pointer
func validreturn_unsafe_pointer() unsafe.Pointer
// ERROR: //go:wasmimport modulename manyreturns: too many return values // ERROR: //go:wasmimport modulename manyreturns: too many return values
// //
//go:wasmimport modulename manyreturns //go:wasmimport modulename manyreturns
func manyreturns() (int32, int32) func manyreturns() (int32, int32)
// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type int // ERROR: //go:wasmimport modulename invalidreturn: unsupported result type int
// //
//go:wasmimport modulename invalidreturn_int //go:wasmimport modulename invalidreturn
func invalidreturn_int() int func invalidreturn() int
// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type uint // ERROR: //go:wasmimport modulename invalidUnsafePointerReturn: unsupported result type unsafe.Pointer
// //
//go:wasmimport modulename invalidreturn_int //go:wasmimport modulename invalidUnsafePointerReturn
func invalidreturn_uint() uint func invalidUnsafePointerReturn() unsafe.Pointer
// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
//
//go:wasmimport modulename invalidreturn_func
func invalidreturn_func() func()
// ERROR: //go:wasmimport modulename invalidreturn_pointer_array_int: unsupported result type *[8]int
//
//go:wasmimport modulename invalidreturn_pointer_array_int
func invalidreturn_pointer_array_int() *[8]int
// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
//
//go:wasmimport modulename invalidreturn_slice_byte
func invalidreturn_slice_byte() []byte
// ERROR: //go:wasmimport modulename invalidreturn_chan_int: unsupported result type chan int
//
//go:wasmimport modulename invalidreturn_chan_int
func invalidreturn_chan_int() chan int
// ERROR: //go:wasmimport modulename invalidreturn_string: unsupported result type string
//
//go:wasmimport modulename invalidreturn_string
func invalidreturn_string() string
+4 -4
View File
@@ -1,6 +1,6 @@
; 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-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
@@ -93,6 +93,6 @@ entry:
ret i8 %0 ret i8 %0
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
+4 -4
View File
@@ -1,6 +1,6 @@
; 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-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
@@ -44,7 +44,7 @@ entry:
ret void ret void
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
-8
View File
@@ -24,10 +24,6 @@ var (
x *byte x *byte
y [61]uintptr y [61]uintptr
} }
struct5 *struct {
x *byte
y [30]uintptr
}
slice1 []byte slice1 []byte
slice2 []*int slice2 []*int
@@ -62,10 +58,6 @@ func newStruct() {
x *byte x *byte
y [61]uintptr y [61]uintptr
}) })
struct5 = new(struct {
x *byte
y [30]uintptr
})
} }
func newFuncValue() *func() { func newFuncValue() *func() {
+29 -33
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-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._interface = type { ptr, ptr } %runtime._interface = type { ptr, ptr }
@@ -16,12 +16,11 @@ target triple = "wasm32-unknown-wasi"
@main.struct2 = hidden global ptr null, align 4 @main.struct2 = hidden global ptr null, align 4
@main.struct3 = hidden global ptr null, align 4 @main.struct3 = hidden global ptr null, align 4
@main.struct4 = hidden global ptr null, align 4 @main.struct4 = hidden global ptr null, align 4
@main.struct5 = hidden global ptr null, align 4
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4 @main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4 @main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4 @main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
@"runtime/gc.layout:62-0100000000000020" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " } @"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
@"runtime/gc.layout:62-0100000000000000" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" } @"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4 @"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4 @"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
@@ -40,16 +39,16 @@ entry:
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 { define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new = call dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new, ptr @main.scalar1, align 4 store ptr %new, ptr @main.scalar1, align 4
%new1 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new1 = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new1, ptr @main.scalar2, align 4 store ptr %new1, ptr @main.scalar2, align 4
%new2 = call align 8 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new2 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new2, ptr @main.scalar3, align 4 store ptr %new2, ptr @main.scalar3, align 4
%new3 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new3 = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new3, ptr @main.scalar4, align 4 store ptr %new3, ptr @main.scalar4, align 4
ret void ret void
@@ -59,13 +58,13 @@ entry:
define hidden void @main.newArray(ptr %context) unnamed_addr #2 { define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new = call dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new, ptr @main.array1, align 4 store ptr %new, ptr @main.array1, align 4
%new1 = call align 1 dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new1 = call dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new1, ptr @main.array2, align 4 store ptr %new1, ptr @main.array2, align 4
%new2 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3 %new2 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new2, ptr @main.array3, align 4 store ptr %new2, ptr @main.array3, align 4
ret void ret void
@@ -75,21 +74,18 @@ entry:
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 { define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new, ptr @main.struct1, align 4 store ptr %new, ptr @main.struct1, align 4
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %new1 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new1, ptr @main.struct2, align 4 store ptr %new1, ptr @main.struct2, align 4
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #3 %new2 = call dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new2, ptr @main.struct3, align 4 store ptr %new2, ptr @main.struct3, align 4
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #3 %new3 = call dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new3, ptr @main.struct4, align 4 store ptr %new3, ptr @main.struct4, align 4
%new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new4, ptr @main.struct5, align 4
ret void ret void
} }
@@ -97,7 +93,7 @@ entry:
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 { define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3 %new = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %new ret ptr %new
} }
@@ -106,21 +102,21 @@ entry:
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 { define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 %makeslice = call dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
store ptr %makeslice, ptr @main.slice1, align 4 store ptr %makeslice, ptr @main.slice1, align 4
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice1, i32 4), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice1, i32 8), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 4
%makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3 %makeslice1 = call dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %makeslice1, ptr @main.slice2, align 4 store ptr %makeslice1, ptr @main.slice2, align 4
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice2, i32 4), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice2, i32 8), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 4
%makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3 %makeslice3 = call dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3
store ptr %makeslice3, ptr @main.slice3, align 4 store ptr %makeslice3, ptr @main.slice3, align 4
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice3, i32 4), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds nuw (i8, ptr @main.slice3, i32 8), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 4
ret void ret void
} }
@@ -128,10 +124,10 @@ entry:
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 { define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3 %0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
store double %v.r, ptr %0, align 8 store double %v.r, ptr %0, align 8
%.repack1 = getelementptr inbounds nuw i8, ptr %0, i32 8 %.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
store double %v.i, ptr %.repack1, align 8 store double %v.i, ptr %.repack1, align 8
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1 %1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3
@@ -139,7 +135,7 @@ entry:
ret %runtime._interface %1 ret %runtime._interface %1
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+6 -6
View File
@@ -1,6 +1,6 @@
; ModuleID = 'go1.20.go' ; ModuleID = 'go1.20.go'
source_filename = "go1.20.go" source_filename = "go1.20.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
@@ -36,7 +36,7 @@ entry:
br i1 %4, label %unsafe.String.throw, label %unsafe.String.next br i1 %4, label %unsafe.String.throw, label %unsafe.String.next
unsafe.String.next: ; preds = %entry unsafe.String.next: ; preds = %entry
%5 = zext nneg i16 %len to i32 %5 = zext i16 %len to i32
%6 = insertvalue %runtime._string undef, ptr %ptr, 0 %6 = insertvalue %runtime._string undef, ptr %ptr, 0
%7 = insertvalue %runtime._string %6, i32 %5, 1 %7 = insertvalue %runtime._string %6, i32 %5, 1
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
@@ -50,14 +50,14 @@ unsafe.String.throw: ; preds = %entry
declare void @runtime.unsafeSlicePanic(ptr) #1 declare void @runtime.unsafeSlicePanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 { define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %s.data ret ptr %s.data
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind } attributes #3 = { nounwind }
+36 -42
View File
@@ -1,6 +1,6 @@
; ModuleID = 'go1.21.go' ; ModuleID = 'go1.21.go'
source_filename = "go1.21.go" source_filename = "go1.21.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
@@ -22,9 +22,6 @@ entry:
ret i32 %a ret i32 %a
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry: entry:
@@ -56,9 +53,6 @@ entry:
ret i8 %0 ret i8 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.umin.i8(i8, i8) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry: entry:
@@ -66,31 +60,24 @@ entry:
ret i32 %0 ret i32 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 { define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call float @llvm.minimum.f32(float %a, float %b) %0 = fcmp olt float %a, %b
ret float %0 %1 = select i1 %0, float %a, float %b
ret float %1
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.minimum.f32(float, float) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 { define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call double @llvm.minimum.f64(double %a, double %b) %0 = fcmp olt double %a, %b
ret double %0 %1 = select i1 %0, double %a, double %b
ret double %1
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare double @llvm.minimum.f64(double, double) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 { define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0 %0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1 %1 = insertvalue %runtime._string %0, i32 %a.len, 1
@@ -99,12 +86,12 @@ entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5 %4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
%5 = select i1 %4, %runtime._string %1, %runtime._string %3 %5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = select i1 %4, ptr %a.data, ptr %b.data %6 = extractvalue %runtime._string %5, 0
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._string %5 ret %runtime._string %5
} }
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1 declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
@@ -113,9 +100,6 @@ entry:
ret i32 %0 ret i32 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry: entry:
@@ -123,21 +107,16 @@ entry:
ret i32 %0 ret i32 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 { define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call float @llvm.maximum.f32(float %a, float %b) %0 = fcmp ogt float %a, %b
ret float %0 %1 = select i1 %0, float %a, float %b
ret float %1
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.maximum.f32(float, float) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 { define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0 %0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1 %1 = insertvalue %runtime._string %0, i32 %a.len, 1
@@ -146,7 +125,7 @@ entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5 %4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
%5 = select i1 %4, %runtime._string %1, %runtime._string %3 %5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = select i1 %4, ptr %a.data, ptr %b.data %6 = extractvalue %runtime._string %5, 0
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._string %5 ret %runtime._string %5
} }
@@ -160,7 +139,7 @@ entry:
} }
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #4 declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 { define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
@@ -177,9 +156,24 @@ entry:
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1 declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } declare i32 @llvm.smin.i32(i32, i32) #4
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) } declare i8 @llvm.umin.i8(i8, i8) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #4
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #5 = { nounwind } attributes #5 = { nounwind }
+51 -62
View File
@@ -3,6 +3,8 @@ source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-unknown-eabi" target triple = "thumbv7m-unknown-unknown-eabi"
%runtime._string = type { ptr, i32 }
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1 @"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
@@ -17,8 +19,8 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
ret void ret void
} }
@@ -28,7 +30,7 @@ declare void @main.regularFunction(i32, ptr) #2
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11 call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
ret void ret void
} }
@@ -39,8 +41,8 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
ret void ret void
} }
@@ -61,18 +63,16 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry: entry:
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 %n = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
store i32 3, ptr %n, align 4 store i32 3, ptr %n, align 4
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11 %0 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4 store ptr %n, ptr %1, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
%2 = load i32, ptr %n, align 4 %2 = load i32, ptr %n, align 4
call void @runtime.printlock(ptr undef) #11 call void @runtime.printint32(i32 %2, ptr undef) #9
call void @runtime.printint32(i32 %2, ptr undef) #11
call void @runtime.printunlock(ptr undef) #11
ret void ret void
} }
@@ -87,29 +87,25 @@ entry:
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 { define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3) call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
ret void ret void
} }
declare void @runtime.printlock(ptr) #2
declare void @runtime.printint32(i32, ptr) #2 declare void @runtime.printint32(i32, ptr) #2
declare void @runtime.printunlock(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11 %0 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4 store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8 %2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
store ptr %fn.funcptr, ptr %2, align 4 store ptr %fn.funcptr, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
ret void ret void
} }
@@ -117,11 +113,11 @@ entry:
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 { define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8 %4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
%5 = load ptr, ptr %4, align 4 %5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #11 call void %5(i32 %1, ptr %3) #9
ret void ret void
} }
@@ -134,67 +130,60 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
entry: entry:
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len) %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
ret void ret void
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
declare i32 @llvm.umin.i32(i32, i32) #7
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
entry: entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11 call void @runtime.chanClose(ptr %ch, ptr undef) #9
ret void ret void
} }
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2 declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11 %0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
store ptr %itf.value, ptr %0, align 4 store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4 store ptr @"main$string", ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8 %.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
store i32 4, ptr %2, align 4 store i32 4, ptr %.repack1, align 4
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12 %2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %3, align 4 store ptr %itf.typecode, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
entry: entry:
%1 = load ptr, ptr %0, align 4 %1 = load ptr, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8 %4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
%5 = load i32, ptr %4, align 4 %5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12 %6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
%7 = load ptr, ptr %6, align 4 %7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
ret void ret void
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "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,+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 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+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 #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" } attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+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 #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } attributes #4 = { 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 #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+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 #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" } attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+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 #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #7 = { "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 #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #8 = { 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 #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #9 = { nounwind }
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #11 = { nounwind }
+58 -69
View File
@@ -1,8 +1,10 @@
; 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-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1 @"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
@@ -19,7 +21,7 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
ret void ret void
} }
@@ -31,8 +33,8 @@ declare void @runtime.deadlock(ptr) #1
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11 call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -41,7 +43,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
ret void ret void
} }
@@ -56,7 +58,7 @@ define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unn
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint ptr %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef) call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -64,21 +66,19 @@ entry:
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 %n = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
store i32 3, ptr %n, align 4 store i32 3, ptr %n, align 4
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #9
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11 %0 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4 store ptr %n, ptr %1, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
%2 = load i32, ptr %n, align 4 %2 = load i32, ptr %n, align 4
call void @runtime.printlock(ptr undef) #11 call void @runtime.printint32(i32 %2, ptr undef) #9
call void @runtime.printint32(i32 %2, ptr undef) #11
call void @runtime.printunlock(ptr undef) #11
ret void ret void
} }
@@ -93,31 +93,27 @@ entry:
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 { define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3) call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
declare void @runtime.printlock(ptr) #1
declare void @runtime.printint32(i32, ptr) #1 declare void @runtime.printint32(i32, ptr) #1
declare void @runtime.printunlock(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 { define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11 %0 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4 store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8 %2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
store ptr %fn.funcptr, ptr %2, align 4 store ptr %fn.funcptr, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
ret void ret void
} }
@@ -125,12 +121,12 @@ entry:
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 { define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
entry: entry:
%1 = load i32, ptr %0, align 4 %1 = load i32, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8 %4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
%5 = load ptr, ptr %4, align 4 %5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #11 call void %5(i32 %1, ptr %3) #9
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -143,69 +139,62 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 { define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len) %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
ret void ret void
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
declare i32 @llvm.umin.i32(i32, i32) #7
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11 call void @runtime.chanClose(ptr %ch, ptr undef) #9
ret void ret void
} }
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1 declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11 %0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store ptr %itf.value, ptr %0, align 4 store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4 store ptr @"main$string", ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8 %.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1
store i32 4, ptr %2, align 4 store i32 4, ptr %.repack1, align 4
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12 %2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %3, align 4 store ptr %itf.typecode, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
entry: entry:
%1 = load ptr, ptr %0, align 4 %1 = load ptr, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8 %4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
%5 = load i32, ptr %4, align 4 %5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12 %6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
%7 = load ptr, ptr %6, align 4 %7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" } attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" } attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #9 = { nounwind }
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #11 = { nounwind }
+11 -13
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-i128:128-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._interface = type { ptr, ptr } %runtime._interface = type { ptr, ptr }
@@ -9,14 +9,12 @@ target triple = "wasm32-unknown-wasi"
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4 @"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4 @"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4 @"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1 @"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 -32767, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1 @"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4 @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1 @"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
@"reflect/types.typeid:basic:int" = external constant i8 @"reflect/types.typeid:basic:int" = external constant i8
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
@@ -132,11 +130,11 @@ entry:
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6 declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" } attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" } attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" } attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #6 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" } attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #7 = { nounwind } attributes #7 = { nounwind }

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