Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem 3ecefd6a0a cgo: refactor Go types a little bit
Code like this is not allowed by the upstream Go CGo implementation, but
was allowed by TinyGo:

    var _ int8 = C.int8_t(5)

The reason it shouldn't be allowed is a little bit complicated. While
it is true that C.int8_t is always the same underlying data type as Go
int8 (signed 8-bit integer), the C type is actually a typedef of one of
the base C types (usually unsigned char or signed char) which in turn do
_not_ map cleanly to Go types: the 'char' type is ambiguous (it may be
either signed or unsigned depending on the ABI) and types like 'int'
vary in size by ABI as well.

To make code more portable, I think it's better to match the upstream
implementation.
2023-09-22 17:51:15 +02:00
543 changed files with 6599 additions and 19888 deletions
+15 -24
View File
@@ -10,12 +10,12 @@ commands:
steps:
- restore_cache:
keys:
- llvm-source-18-v1
- llvm-source-16-v3
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-18-v1
key: llvm-source-16-v3
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
@@ -33,13 +33,13 @@ commands:
steps:
- restore_cache:
keys:
- binaryen-linux-v3
- binaryen-linux-v2
- run:
name: "Build Binaryen"
command: |
make binaryen
- save_cache:
key: binaryen-linux-v3
key: binaryen-linux-v2
paths:
- build/wasm-opt
test-linux:
@@ -55,7 +55,7 @@ commands:
- run:
name: "Install apt dependencies"
command: |
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
apt-get update
apt-get install --no-install-recommends -y \
@@ -69,16 +69,16 @@ commands:
- build-binaryen-linux
- restore_cache:
keys:
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v4-{{ checksum "go.mod" }}
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v3-{{ checksum "go.mod" }}
- llvm-source-linux
- run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache:
keys:
- wasi-libc-sysroot-systemclang-v7
- wasi-libc-sysroot-systemclang-v6
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v7
key: wasi-libc-sysroot-systemclang-v6
paths:
- lib/wasi-libc/sysroot
- when:
@@ -88,29 +88,22 @@ commands:
# Do this before gen-device so that it doesn't check the
# formatting of generated files.
name: Check Go code formatting
command: make fmt-check lint
command: make fmt-check
- run: make gen-device -j4
- run: make smoketest XTENSA=0
- save_cache:
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
jobs:
test-llvm15-go119:
test-llvm14-go118:
docker:
- image: golang:1.19-bullseye
- image: golang:1.18-buster
steps:
- test-linux:
llvm: "15"
resource_class: large
test-llvm18-go122:
docker:
- image: golang:1.22-bullseye
steps:
- test-linux:
llvm: "18"
llvm: "14"
resource_class: large
workflows:
@@ -118,6 +111,4 @@ workflows:
jobs:
# This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass.
- test-llvm15-go119
# This tests LLVM 18 support when linking against system libraries.
- test-llvm18-go122
- test-llvm14-go118
+22 -47
View File
@@ -14,36 +14,26 @@ concurrency:
jobs:
build-macos:
name: build-macos
strategy:
matrix:
# macos-12: amd64 (oldest supported version as of 05-02-2024)
# macos-14: arm64 (oldest arm64 version)
os: [macos-12, macos-14]
include:
- os: macos-12
goarch: amd64
- os: macos-14
goarch: arm64
runs-on: ${{ matrix.os }}
runs-on: macos-11
steps:
- name: Install Dependencies
shell: bash
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: true
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-18-${{ matrix.os }}-v2
key: llvm-source-16-macos-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -54,7 +44,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -65,10 +55,10 @@ jobs:
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-18-${{ matrix.os }}-v3
key: llvm-build-16-macos-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -83,16 +73,16 @@ jobs:
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache wasi-libc sysroot
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-${{ matrix.os }}-v1
key: wasi-libc-sysroot-v4
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -108,7 +98,7 @@ jobs:
run: make tinygo-test
- name: Make release artifact
shell: bash
run: cp -p build/release.tar.gz build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz
- name: Publish release artifact
# Note: this release artifact is double-zipped, see:
# https://github.com/actions/upload-artifact/issues/39
@@ -116,44 +106,29 @@ jobs:
# - 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
uses: actions/upload-artifact@v2
with:
name: darwin-${{ matrix.goarch }}-double-zipped
path: build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
name: darwin-amd64-double-zipped
path: build/tinygo.darwin-amd64.tar.gz
- name: Smoke tests
shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo
test-macos-homebrew:
name: homebrew-install
runs-on: macos-latest
strategy:
matrix:
version: [16, 17, 18]
steps:
- name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Fix Python symlinks
run: |
# Github runners have broken symlinks, so relink
# see: https://github.com/actions/setup-python/issues/577
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
- name: Install LLVM
shell: bash
run: |
brew install llvm@${{ matrix.version }}
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Build TinyGo (LLVM ${{ matrix.version }})
run: go install -tags=llvm${{ matrix.version }}
- name: Check binary
run: tinygo version
- name: Build TinyGo (default LLVM)
if: matrix.version == 18
- name: Build TinyGo
run: go install
- name: Check binary
if: matrix.version == 18
run: tinygo version
+7 -17
View File
@@ -19,26 +19,15 @@ jobs:
packages: write
contents: read
steps:
- name: Free Disk space
shell: bash
run: |
df -h
sudo rm -rf /opt/hostedtoolcache
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/graalvm
sudo rm -rf /usr/local/share/boost
df -h
- name: Check out the repo
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: |
tinygo/tinygo-dev
@@ -47,23 +36,24 @@ jobs:
type=sha,format=long
type=raw,value=latest
- name: Log in to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to Github Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-16
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trigger Drivers repo build on Github Actions
+184 -84
View File
@@ -18,32 +18,32 @@ jobs:
# statically linked binary.
runs-on: ubuntu-latest
container:
image: golang:1.22-alpine
image: golang:1.21-alpine
steps:
- name: Install apk dependencies
# tar: needed for actions/cache@v4
# tar: needed for actions/cache@v3
# git+openssh: needed for checkout (I think?)
# ruby: needed to install fpm
run: apk add tar git openssh make g++ ruby-dev
run: apk add tar git openssh make g++ ruby
- name: Work around CVE-2022-24765
# We're not on a multi-user machine, so this is safe.
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: true
- name: Cache Go
uses: actions/cache@v4
uses: actions/cache@v3
with:
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-18-linux-alpine-v1
key: llvm-source-16-linux-alpine-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -54,7 +54,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -65,10 +65,10 @@ jobs:
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-18-linux-alpine-v2
key: llvm-build-16-linux-alpine-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -83,13 +83,13 @@ jobs:
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-alpine-v1
@@ -100,10 +100,10 @@ jobs:
apk add cmake samurai python3
make binaryen STATIC=1
- name: Cache wasi-libc
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-alpine-v2
key: wasi-libc-sysroot-linux-alpine-v1
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -113,15 +113,13 @@ jobs:
gem install --version 4.0.7 public_suffix
gem install --version 2.7.6 dotenv
gem install --no-document fpm
- name: Run linter
run: make lint
- name: Build TinyGo release
run: |
make release deb -j3 STATIC=1
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: linux-amd64-double-zipped
path: |
@@ -133,22 +131,20 @@ jobs:
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "19.0.1"
- name: Install wasm-tools
uses: bytecodealliance/actions/wasm-tools/setup@v1
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
curl https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Download release artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: linux-amd64-double-zipped
- name: Extract release tarball
@@ -156,8 +152,8 @@ jobs:
mkdir -p ~/lib
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
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-wasip2-fast
- run: make smoketest
assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
@@ -165,7 +161,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: true
- name: Install apt dependencies
@@ -180,25 +176,25 @@ jobs:
simavr \
ninja-build
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Install Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: '18'
node-version: '16'
- name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "19.0.1"
- name: Setup `wasm-tools`
uses: bytecodealliance/actions/wasm-tools/setup@v1
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-18-linux-asserts-v1
key: llvm-source-16-linux-asserts-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -209,7 +205,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -220,10 +216,10 @@ jobs:
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-18-linux-asserts-v2
key: llvm-build-16-linux-asserts-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -236,13 +232,13 @@ jobs:
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-asserts-v1
@@ -251,10 +247,10 @@ jobs:
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen
- name: Cache wasi-libc
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v6
key: wasi-libc-sysroot-linux-asserts-v5
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -271,7 +267,7 @@ jobs:
- run: make smoketest
- run: make wasmtest
- run: make tinygo-baremetal
build-linux-cross:
build-linux-arm:
# Build ARM Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of
@@ -280,38 +276,28 @@ jobs:
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
strategy:
matrix:
goarch: [ arm, arm64 ]
include:
- goarch: arm64
toolchain: aarch64-linux-gnu
libc: arm64
- goarch: arm
toolchain: arm-linux-gnueabihf
libc: armhf
runs-on: ubuntu-20.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
qemu-user \
g++-${{ matrix.toolchain }} \
libc6-dev-${{ matrix.libc }}-cross
g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-18-linux-v1
key: llvm-source-16-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -322,7 +308,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -333,10 +319,10 @@ jobs:
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
key: llvm-build-16-linux-arm-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -347,27 +333,27 @@ jobs:
# Install build dependencies.
sudo apt-get install --no-install-recommends ninja-build
# build!
make llvm-build CROSS=${{ matrix.toolchain }}
make llvm-build CROSS=arm-linux-gnueabihf
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-${{ matrix.goarch }}-v4
key: binaryen-linux-arm-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
sudo apt-get install --no-install-recommends ninja-build
git submodule update --init lib/binaryen
make CROSS=${{ matrix.toolchain }} binaryen
make CROSS=arm-linux-gnueabihf binaryen
- name: Install fpm
run: |
sudo gem install --version 4.0.7 public_suffix
@@ -375,9 +361,9 @@ jobs:
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=${{ matrix.toolchain }}
make CROSS=arm-linux-gnueabihf
- name: Download amd64 release
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
@@ -388,15 +374,129 @@ jobs:
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create ${{ matrix.goarch }} release
- name: Create arm release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
make release deb RELEASEONLY=1 DEB_ARCH=armhf
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb
- name: Publish release artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: linux-${{ matrix.goarch }}-double-zipped
name: linux-arm-double-zipped
path: |
/tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
/tmp/tinygo_${{ matrix.libc }}.deb
/tmp/tinygo.linux-arm.tar.gz
/tmp/tinygo_armhf.deb
build-linux-arm64:
# Build ARM64 Linux binaries, ready for release.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
runs-on: ubuntu-20.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
qemu-user \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
ninja-build
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-16-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-16-linux-arm64-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# build!
make llvm-build CROSS=aarch64-linux-gnu
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-arm64-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
git submodule update --init lib/binaryen
make CROSS=aarch64-linux-gnu binaryen
- name: Install fpm
run: |
sudo gem install --version 4.0.7 public_suffix
sudo gem install --version 2.7.6 dotenv
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=aarch64-linux-gnu
- name: Download amd64 release
uses: actions/download-artifact@v3
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm64 release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=arm64
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
cp -p build/release.deb /tmp/tinygo_arm64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v3
with:
name: linux-arm64-double-zipped
path: |
/tmp/tinygo.linux-arm64.tar.gz
/tmp/tinygo_arm64.deb
+7 -7
View File
@@ -25,18 +25,18 @@ jobs:
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: |
tinygo/llvm-18
ghcr.io/${{ github.repository_owner }}/llvm-18
tinygo/llvm-16
ghcr.io/${{ github.repository_owner }}/llvm-16
tags: |
type=sha,format=long
type=raw,value=latest
@@ -46,13 +46,13 @@ jobs:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to Github Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v4
with:
target: tinygo-llvm-build
context: .
-43
View File
@@ -1,43 +0,0 @@
name: Nix
on:
pull_request:
push:
branches:
- dev
- release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
nix-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Pull musl
run: |
git submodule update --init lib/musl
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
id: cache-llvm-source
with:
key: llvm-source-18-linux-nix-v1
path: |
llvm-project/compiler-rt
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save LLVM source cache
uses: actions/cache/save@v4
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/compiler-rt
- uses: cachix/install-nix-action@v22
- name: Test
run: |
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
@@ -1,12 +0,0 @@
# Command that's part of sizediff.yml. This is put in a separate file so that it
# 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).
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 -
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
llvm-18-dev \
clang-18 \
libclang-18-dev \
lld-18
+24 -19
View File
@@ -9,7 +9,7 @@ concurrency:
jobs:
sizediff:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
permissions:
pull-requests: write
steps:
@@ -18,26 +18,34 @@ jobs:
run: |
echo "$HOME/go/bin" >> $GITHUB_PATH
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0 # fetch all history (no sparse checkout)
submodules: true
- name: Install apt dependencies
run: ./.github/workflows/sizediff-install-pkgs.sh
run: |
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
llvm-16-dev \
clang-16 \
libclang-16-dev \
lld-16
- name: Restore LLVM source cache
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-llvm-source
with:
key: llvm-source-18-sizediff-v1
key: llvm-source-16-sizediff-v1
path: |
llvm-project/compiler-rt
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache Go
uses: actions/cache@v4
uses: actions/cache@v3
with:
key: go-cache-linux-sizediff-v2-${{ hashFiles('go.mod') }}
key: go-cache-linux-sizediff-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
@@ -47,25 +55,22 @@ jobs:
- name: Save HEAD
run: git branch github-actions-saved-HEAD HEAD
# Compute sizes for the PR branch
- name: Build tinygo binary for the PR branch
run: go install
- name: Determine binary sizes on the PR branch
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-pr.txt)
# Compute sizes for the dev branch
- name: Checkout dev branch
run: |
git reset --hard origin/dev
git checkout --no-recurse-submodules `git merge-base HEAD origin/dev`
- name: Install apt dependencies on the dev branch
# this is only needed on a PR that changes the LLVM version
run: ./.github/workflows/sizediff-install-pkgs.sh
run: git checkout --no-recurse-submodules `git merge-base HEAD origin/dev`
- name: Build tinygo binary for the dev branch
run: go install
- name: Determine binary sizes on the dev branch
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-dev.txt)
# Compute sizes for the PR branch
- name: Checkout PR branch
run: git checkout --no-recurse-submodules github-actions-saved-HEAD
- name: Build tinygo binary for the PR branch
run: go install
- name: Determine binary sizes on the PR branch
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-pr.txt)
# Create comment
# TODO: add a summary, something like:
# - overall size difference (percent)
+32 -32
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: windows-2022
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.4
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
@@ -29,19 +29,19 @@ jobs:
run: |
scoop install ninja binaryen
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: true
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Restore cached LLVM source
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-18-windows-v1
key: llvm-source-16-windows-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -52,7 +52,7 @@ jobs:
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Save cached LLVM source
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
@@ -63,10 +63,10 @@ jobs:
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore cached LLVM build
uses: actions/cache/restore@v4
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-18-windows-v2
key: llvm-build-16-windows-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -80,23 +80,23 @@ jobs:
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save cached LLVM build
uses: actions/cache/save@v4
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache wasi-libc sysroot
uses: actions/cache@v4
uses: actions/cache@v3
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-v5
key: wasi-libc-sysroot-v4
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install wasmtime
run: |
scoop install wasmtime@14.0.4
scoop install wasmtime
- name: make gen-device
run: make -j3 gen-device
- name: Test TinyGo
@@ -116,7 +116,7 @@ jobs:
# - 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
uses: actions/upload-artifact@v3
with:
name: windows-amd64-double-zipped
path: build/release/release.zip
@@ -126,7 +126,7 @@ jobs:
needs: build-windows
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.4
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
@@ -139,14 +139,14 @@ jobs:
run: |
scoop install binaryen
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v2
with:
name: windows-amd64-double-zipped
path: build/
@@ -163,20 +163,20 @@ jobs:
needs: build-windows
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.4
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v2
with:
name: windows-amd64-double-zipped
path: build/
@@ -192,7 +192,7 @@ jobs:
needs: build-windows
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.4
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
@@ -203,16 +203,16 @@ jobs:
- name: Install Dependencies
shell: bash
run: |
scoop install binaryen && scoop install wasmtime@14.0.4
scoop install binaryen wasmtime
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: '1.22'
go-version: '1.21'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v2
with:
name: windows-amd64-double-zipped
path: build/
@@ -220,5 +220,5 @@ jobs:
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Test stdlib packages on wasip1
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
+1 -6
View File
@@ -1,8 +1,3 @@
.DS_Store
.vscode
go.work
go.work.sum
docs/_build
src/device/avr/*.go
src/device/avr/*.ld
@@ -22,7 +17,7 @@ src/device/kendryte/*.go
src/device/kendryte/*.s
src/device/rp/*.go
src/device/rp/*.s
./vendor
vendor
llvm-build
llvm-project
build/*
+2 -10
View File
@@ -9,11 +9,10 @@
url = https://github.com/avr-rust/avr-mcu.git
[submodule "lib/cmsis-svd"]
path = lib/cmsis-svd
url = https://github.com/cmsis-svd/cmsis-svd-data.git
branch = main
url = https://github.com/tinygo-org/cmsis-svd
[submodule "lib/wasi-libc"]
path = lib/wasi-libc
url = https://github.com/WebAssembly/wasi-libc
url = https://github.com/CraneStation/wasi-libc
[submodule "lib/picolibc"]
path = lib/picolibc
url = https://github.com/keith-packard/picolibc.git
@@ -35,10 +34,3 @@
[submodule "lib/renesas-svd"]
path = lib/renesas-svd
url = https://github.com/tinygo-org/renesas-svd.git
[submodule "src/net"]
path = src/net
url = https://github.com/tinygo-org/net.git
branch = dev
[submodule "lib/wasi-cli"]
path = lib/wasi-cli
url = https://github.com/WebAssembly/wasi-cli
+1 -2
View File
@@ -18,8 +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
build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.19+)
* GNU Make
* Go (1.18+)
* Standard build tools (gcc/clang)
* git
* CMake
-160
View File
@@ -1,163 +1,3 @@
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 existance
* **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
---
* **general**
* update the `net` submodule to updated version with `Buffers` implementation
* **compiler**
* `syscall`: add wasm_unknown tag to some additional files so it can compile more code
* **standard library**
* `runtime`: add Frame.Entry field
0.31.1
---
* **general**
* fix Binaryen build in make task
* update final build stage of Docker `dev` image to go1.22
* only use GHA cache for building Docker `dev` image
* update the `net` submodule to latest version
* **compiler**
* `interp`: make getelementptr offsets signed
* `interp`: return a proper error message when indexing out of range
0.31.0
---
* **general**
* remove LLVM 14 support
* add LLVM 17 support, and use it by default
* add Nix flake support
* update bundled Binaryen to version 116
* add `ports` subcommand that lists available serial ports for `-port` and `-monitor`
* support wasmtime version 14
* add `-serial=rtt` for serial output over SWD
* add Go 1.22 support and use it by default
* change minimum Node.js version from 16 to 18
* **compiler**
* use the new LLVM pass manager
* allow systems with more stack space to allocate larger values on the stack
* `build`: fix a crash due to sharing GlobalValues between build instances
* `cgo`: add `C._Bool` type
* `cgo`: fix calling CGo callback inside generic function
* `compileopts`: set `purego` build tag by default so that more packages can be built
* `compileopts`: force-enable CGo to avoid build issues
* `compiler`: fix crash on type assert on interfaces with no methods
* `interp`: print LLVM instruction in traceback
* `interp`: support runtime times by running them at runtime
* `loader`: enforce Go language version in the type checker (this may break existing programs with an incorrect Go version in go.mod)
* `transform`: fix bug in StringToBytes optimization pass
* **standard library**
* `crypto/tls`: stub out a lot of functions
* `internal/task`, `machine`: make TinyGo code usable with "big Go" CGo
* `machine`: implement `I2C.SetBaudRate` consistently across chips
* `machine`: implement `SPI.Configure` consistently across chips
* `machine`: add `DeviceID` for nrf, rp2040, sam, stm32
* `machine`: use smaller UART buffer size on atmega chips
* `machine/usb`: allow setting a serial number using a linker flag
* `math`: support more math functions on baremetal (picolibc) systems
* `net`: replace entire net package with a new one based on the netdev driver
* `os/user`: add bare-bones implementation of this package
* `reflect`: stub `CallSlice` and `FuncOf`
* `reflect`: add `TypeFor[T]`
* `reflect`: update `IsZero` to Go 1.22 semantics
* `reflect`: move indirect values into interface when setting interfaces
* `runtime`: stub `Breakpoint`
* `sync`: implement trylock
* **targets**
* `atmega`: use UART double speed mode for fewer errors and higher throughput
* `atmega328pb`: refactor to enable extra uart
* `avr`: don't compile large parts of picolibc (math, stdio) for LLVM 17 support
* `esp32`: switch over to the official SVD file
* `esp32c3`: implement USB_SERIAL for USBCDC communication
* `esp32c3`: implement I2C
* `esp32c3`: implement RNG
* `esp32c3`: add more ROM functions and update linker script for the in-progress wifi support
* `esp32c3`: update to newer SVD files
* `rp2040`: add support for UART hardware flow control
* `rp2040`: add definition for `machine.PinToggle`
* `rp2040`: set XOSC startup delay multiplier
* `samd21`: add support for UART hardware flow control
* `samd51`: add support for UART hardware flow control
* `wasm`: increase default stack size to 64k for wasi/wasm targets
* `wasm`: bump wasi-libc version to SDK 20
* `wasm`: remove line of dead code in wasm_exec.js
* **new targets/boards**
* `qtpy-esp32c3`: add Adafruit QT Py ESP32-C3 board
* `mksnanov3`: add support for the MKS Robin Nano V3.x
* `nrf52840-generic`: add generic nrf52840 chip support
* `thumby`: add support for Thumby
* `wasm`: add new `wasm-unknown` target that doesn't depend on WASI or a browser
* **boards**
* `arduino-mkrwifi1010`, `arduino-nano33`, `nano-rp2040`, `matrixportal-m4`, `metro-m4-airlift`, `pybadge`, `pyportal`: add `ninafw` build tag and some constants for BLE support
* `gopher-badge`: fix typo in USB product name
* `nano-rp2040`: add UART1 and correct mappings for NINA via UART
* `pico`: bump default stack size from 2kB to 8kB
* `wioterminal`: expose UART4
0.30.0
---
+19 -22
View File
@@ -1,15 +1,10 @@
# tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.22 AS tinygo-llvm
FROM golang:1.21 AS tinygo-llvm
RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-15 ninja-build && \
rm -rf \
/var/lib/apt/lists/* \
/var/log/* \
/var/tmp/* \
/tmp/*
apt-get install -y apt-utils make cmake clang-15 ninja-build
COPY ./GNUmakefile /tinygo/GNUmakefile
COPY ./Makefile /tinygo/Makefile
RUN cd /tinygo/ && \
make llvm-source
@@ -20,24 +15,26 @@ FROM tinygo-llvm AS tinygo-llvm-build
RUN cd /tinygo/ && \
make llvm-build
# tinygo-compiler-build stage builds the compiler itself
FROM tinygo-llvm-build AS tinygo-compiler-build
# tinygo-compiler stage builds the compiler itself
FROM tinygo-llvm-build AS tinygo-compiler
COPY . /tinygo
# build the compiler and tools
# update submodules
RUN cd /tinygo/ && \
git submodule update --init && \
rm -rf ./lib/*/ && \
git submodule sync && \
git submodule update --init --recursive --force
RUN cd /tinygo/ && \
make
# tinygo-tools stage installs the needed dependencies to compile TinyGo programs for all platforms.
FROM tinygo-compiler AS tinygo-tools
RUN cd /tinygo/ && \
make wasi-libc binaryen && \
make gen-device -j4 && \
make build/release
cp build/* $GOPATH/bin/
# tinygo-compiler copies the compiler build over to a base Go container (without
# all the build tools etc).
FROM golang:1.22 AS tinygo-compiler
# Copy tinygo build.
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
# Configure the container.
ENV PATH="${PATH}:/tinygo/bin"
CMD ["tinygo"]
+43 -131
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order.
LLVM_VERSIONS = 18 17 16 15
LLVM_VERSIONS = 16 15 14 13 12 11
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)))
toolSearchPathsVersion = $(1)-$(2)
@@ -31,7 +31,9 @@ export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test.
GOTESTFLAGS ?=
GOTESTPKGS ?= ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# md5sum binary
MD5SUM = md5sum
# tinygo binary for tests
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
@@ -111,7 +113,7 @@ endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontenddriver frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
ifeq ($(OS),Windows_NT)
EXE = .exe
@@ -128,14 +130,14 @@ ifeq ($(OS),Windows_NT)
USE_SYSTEM_BINARYEN ?= 1
else ifeq ($(shell uname -s),Darwin)
MD5SUM ?= md5
MD5SUM = md5
CGO_LDFLAGS += -lxar
USE_SYSTEM_BINARYEN ?= 1
else ifeq ($(shell uname -s),FreeBSD)
MD5SUM ?= md5
MD5SUM = md5
START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group
else
@@ -143,11 +145,8 @@ else
END_GROUP = -Wl,--end-group
endif
# md5sum binary default, can be overridden by an environment variable
MD5SUM ?= md5sum
# 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 clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD.
@@ -166,7 +165,7 @@ LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
# library path (for ninja).
# This list also includes a few tools that are necessary as part of the full
# TinyGo build.
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm lld $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES)))
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES)))
# For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
@@ -239,13 +238,13 @@ gen-device-renesas: build/gen-device-svd
# Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm:
git clone -b tinygo_xtensa_release_18.1.2 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
git clone -b xtensa_release_16.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(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)
# Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -257,7 +256,7 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
binaryen: build/wasm-opt$(EXE)
build/wasm-opt$(EXE):
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 $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
endif
@@ -266,37 +265,25 @@ endif
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
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)
# Generate WASI syscall bindings
WASM_TOOLS_MODULE=github.com/ydnar/wasm-tools-go
.PHONY: wasi-syscall
wasi-syscall: wasi-cm
go run -modfile ./internal/wasm-tools/go.mod $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
# Copy package cm into src/internal/cm
.PHONY: wasi-cm
wasi-cm:
# rm -rf ./src/internal/cm
rsync -rv --delete --exclude '*_test.go' $(shell go list -modfile ./internal/wasm-tools/go.mod -m -f {{.Dir}} $(WASM_TOOLS_MODULE))/cm ./src/internal/
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)
# 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=16
.PHONY: check-nodejs-version
check-nodejs-version:
ifeq (, $(shell which node))
@echo "Install NodeJS version 18+ to run tests."; exit 1;
@echo "Install NodeJS version 16+ 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
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 16+ to run tests."; exit 1; fi
# Build the Go compiler.
tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc check-nodejs-version
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -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" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
TEST_PACKAGES_SLOW = \
@@ -439,38 +426,17 @@ tinygo-bench-fast:
# Same thing, except for wasi rather than the current platform.
tinygo-test-wasi:
$(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasip1:
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasi-fast:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip1-fast:
$(TINYGO) test -target=wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip2-slow:
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_SLOW)
tinygo-test-wasip2-fast:
$(TINYGO) test -target=wasip2 $(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-bench-wasip1-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)
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-bench-wasi:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasi-fast:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST)
# Test external packages in a large corpus.
test-corpus:
@@ -478,7 +444,7 @@ test-corpus:
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
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=wasi
tinygo-baremetal:
# Regression tests that run on a baremetal target and don't fit in either main_test.go or smoketest.
@@ -524,7 +490,7 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
@$(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
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
@$(MD5SUM) test.hex
@@ -540,8 +506,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org
ifneq ($(WASM), 0)
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@@ -556,9 +520,7 @@ ifneq ($(WASM), 0)
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/machinetest
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1
$(TINYGO) build -size short -o test.wasm -tags=mch2022 examples/serial
@$(MD5SUM) test.wasm
endif
# test all targets/boards
@@ -634,8 +596,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10059-s140v7 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=reelboard-s140v7 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/blinky1
@@ -654,7 +614,7 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
@$(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
$(TINYGO) build -size short -o test.hex -target=teensy41 examples/blinky1
@$(MD5SUM) test.hex
@@ -692,8 +652,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=badger2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=badger2040-w examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=tufty2040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=thingplus-rp2040 examples/blinky1
@@ -710,8 +668,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo
@$(MD5SUM) test.hex
# test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex
@@ -726,8 +682,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
@$(MD5SUM) test.hex
ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
@$(MD5SUM) test.hex
@@ -743,8 +697,6 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-l476rg examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-wl55jc examples/blinky1
@@ -763,12 +715,8 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
@$(MD5SUM) test.hex
endif
$(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
@$(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
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
@$(MD5SUM) test.hex
@@ -795,22 +743,22 @@ ifneq ($(XTENSA), 0)
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
@$(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
$(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
$(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
$(TINYGO) build -size short -o test.bin -target m5paper examples/machinetest
@$(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
endif
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/machinetest
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/machinetest
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
@@ -819,7 +767,6 @@ endif
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/main
$(TINYGO) build -size short -o wasm.wasm -target=wasm-unknown examples/hello-wasm-unknown
endif
# test various compiler flags
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
@@ -828,14 +775,11 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
@$(MD5SUM) test.nro
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
@$(MD5SUM) test.hex
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=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
@@ -863,10 +807,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/lib/nrfx
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
@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-cli/
@mkdir -p build/release/tinygo/lib/wasi-libc
@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
@@ -883,7 +824,6 @@ endif
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
@@ -894,7 +834,6 @@ endif
@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/legacy 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/mman build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
@@ -918,16 +857,7 @@ endif
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
@cp -rp lib/wasi-libc/libc-bottom-half/headers/public build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
@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/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/math 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/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
@cp -rp src build/release/tinygo/src
@@ -948,27 +878,9 @@ deb:
@mkdir -p build/release-deb/usr/local/lib
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
ifneq ($(RELEASEONLY), 1)
release: build/release
deb: build/release
endif
.PHONY: tools
tools:
go generate -C ./internal/tools -tags tools ./
.PHONY: lint
lint:
go run github.com/mgechev/revive -version
# 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.
# Can't use grep with friendly formatter. Plain output isn't too bad, though.
# Use 'grep .' to get rid of stray blank line
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}'
.PHONY: spell
spell:
# Check for typos in comments. Skip git submodules etc.
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' )
+4 -4
View File
@@ -1,6 +1,6 @@
# TinyGo - Go compiler for small places
[![Linux](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [![macOS](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [![Docker](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [![Nix](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
[![Linux](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [![macOS](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [![Docker](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
@@ -41,7 +41,7 @@ tinygo flash -target arduino examples/blinky1
TinyGo is very useful for compiling programs both for use in browsers (WASM) as well as for use on servers and other edge devices (WASI).
TinyGo programs can run in [Fastly Compute](https://www.fastly.com/documentation/guides/compute/go/), [Fermyon Spin](https://developer.fermyon.com/spin/go-components), [wazero](https://wazero.io/languages/tinygo/) and many other WebAssembly runtimes.
TinyGo programs can run in Fastly Compute@Edge (https://developer.fastly.com/learning/compute/go/), Fermyon Spin (https://developer.fermyon.com/spin/go-components), wazero (https://wazero.io/languages/tinygo/) and many other WebAssembly runtimes.
Here is a small TinyGo program for use by a WASI host application:
@@ -54,14 +54,14 @@ func add(x, y uint32) uint32 {
return x + y
}
// main is required for the `wasip1` target, even if it isn't used.
// main is required for the `wasi` target, even if it isn't used.
func main() {}
```
This compiles the above TinyGo program for use on any WASI runtime:
```shell
tinygo build -o main.wasm -target=wasip1 main.go
tinygo build -o main.wasm -target=wasi main.go
```
## Installation
+6 -16
View File
@@ -78,27 +78,17 @@ func makeArchive(arfile *os.File, objs []string) error {
} else if dbg, err := wasm.Parse(objfile); err == nil {
for _, s := range dbg.Sections {
switch section := s.(type) {
case *wasm.SectionLinking:
for _, symbol := range section.Symbols {
if symbol.Flags&wasm.LinkingSymbolFlagUndefined != 0 {
// Don't list undefined functions.
case *wasm.SectionImport:
for _, ln := range section.Entries {
if ln.Kind != wasm.ExtKindFunction {
// Not a function
continue
}
if symbol.Flags&wasm.LinkingSymbolFlagBindingLocal != 0 {
// Don't include local symbols.
continue
}
if symbol.Kind != wasm.LinkingSymbolKindFunction && symbol.Kind != wasm.LinkingSymbolKindData {
// Link functions and data symbols.
// Some data symbols need to be included, such as
// __log_data.
continue
}
// Include in the archive.
symbolTable = append(symbolTable, struct {
name string
fileIndex int
}{symbol.Name, i})
}{ln.Field, i})
}
}
}
+66 -125
View File
@@ -83,7 +83,8 @@ type packageAction struct {
FileHashes map[string]string // hash of every file that's part of the package
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
Imports map[string]string // map from imported package to action ID hash
OptLevel string // LLVM optimization level (O0, O1, O2, Os, Oz)
OptLevel int // LLVM optimization level (0-3)
SizeLevel int // LLVM optimization for size level (0-2)
UndefinedGlobals []string // globals that are left as external globals (no initializer)
}
@@ -114,30 +115,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
cacheDir = tmpdir
}
// Create default global values.
globalValues := map[string]map[string]string{
"runtime": {
"buildVersion": goenv.Version(),
},
"testing": {},
}
if config.TestConfig.CompileTestBinary {
// The testing.testBinary is set to "1" when in a test.
// This is needed for testing.Testing() to work correctly.
globalValues["testing"]["testBinary"] = "1"
}
// Copy over explicitly set global values, like
// -ldflags="-X main.Version="1.0"
for pkgPath, vals := range config.Options.GlobalValues {
if _, ok := globalValues[pkgPath]; !ok {
globalValues[pkgPath] = map[string]string{}
}
for k, v := range vals {
globalValues[pkgPath][k] = v
}
}
// Check for a libc dependency.
// As a side effect, this also creates the headers for the given libc, if
// the libc needs them.
@@ -168,13 +145,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
}
libcDependencies = append(libcDependencies, dummyCompileJob(path))
case "wasmbuiltins":
libcJob, unlock, err := WasmBuiltins.load(config, tmpdir)
if err != nil {
return BuildResult{}, err
}
defer unlock()
libcDependencies = append(libcDependencies, libcJob)
case "mingw-w64":
_, unlock, err := MinGW.load(config, tmpdir)
if err != nil {
@@ -188,7 +158,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
}
optLevel, speedLevel, sizeLevel := config.OptLevel()
optLevel, sizeLevel, _ := config.OptLevels()
compilerConfig := &compiler.Config{
Triple: config.Triple(),
CPU: config.CPU(),
@@ -199,15 +169,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
SizeLevel: sizeLevel,
TinyGoVersion: goenv.Version(),
TinyGoVersion: goenv.Version,
Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.StackSize(),
MaxStackAlloc: config.MaxStackAlloc(),
NeedsStackObjects: config.NeedsStackObjects(),
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
PanicStrategy: config.PanicStrategy(),
}
// Load the target machine, which is the LLVM object that contains all
@@ -220,7 +188,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
defer machine.Dispose()
// Load entire program AST into memory.
lprogram, err := loader.Load(config, pkgName, types.Config{
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {
@@ -249,12 +217,34 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
var packageJobs []*compileJob
packageActionIDJobs := make(map[string]*compileJob)
if config.Options.GlobalValues == nil {
config.Options.GlobalValues = make(map[string]map[string]string)
}
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
version += "-" + goenv.GitSha1
}
if config.Options.GlobalValues["runtime"] == nil {
config.Options.GlobalValues["runtime"] = make(map[string]string)
}
config.Options.GlobalValues["runtime"]["buildVersion"] = version
}
if config.TestConfig.CompileTestBinary {
// The testing.testBinary is set to "1" when in a test.
// This is needed for testing.Testing() to work correctly.
if config.Options.GlobalValues["testing"] == nil {
config.Options.GlobalValues["testing"] = make(map[string]string)
}
config.Options.GlobalValues["testing"]["testBinary"] = "1"
}
var embedFileObjects []*compileJob
for _, pkg := range lprogram.Sorted() {
pkg := pkg // necessary to avoid a race condition
var undefinedGlobals []string
for name := range globalValues[pkg.Pkg.Path()] {
for name := range config.Options.GlobalValues[pkg.Pkg.Path()] {
undefinedGlobals = append(undefinedGlobals, name)
}
sort.Strings(undefinedGlobals)
@@ -331,6 +321,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
EmbeddedFiles: make(map[string]string, len(allFiles)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
}
for filePath, hash := range pkg.FileHashes {
@@ -353,6 +344,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
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
// if the package is already compiled.
job := &compileJob{
@@ -375,7 +370,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
defer mod.Context().Dispose()
defer mod.Dispose()
if errs != nil {
return newMultiError(errs, pkg.ImportPath)
return newMultiError(errs)
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after compiling package " + pkg.ImportPath)
@@ -537,13 +532,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
irbuilder := mod.Context().NewBuilder()
defer irbuilder.Dispose()
irbuilder.SetInsertPointAtEnd(block)
ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, pkg := range lprogram.Sorted() {
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path())
}
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(ptrType)}, "")
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
}
irbuilder.CreateRetVoid()
@@ -578,7 +573,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run all optimization passes, which are much more effective now
// that the optimizer can see the whole program at once.
err := optimizeProgram(mod, config, globalValues)
err := optimizeProgram(mod, config)
if err != nil {
return err
}
@@ -667,7 +662,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{
description: "compile extra file " + path,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(false), config.Options.PrintCommands)
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
job.result = result
return err
},
@@ -748,17 +743,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(speedLevel),
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(speedLevel),
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
@@ -769,7 +764,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if sizeLevel >= 2 {
// Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 19.
// Can hopefully be removed in LLVM 15.
ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
}
@@ -812,8 +807,21 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Run wasm-opt for wasm binaries
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
optLevel, _, _ := config.OptLevel()
opt := "-" + optLevel
var opt string
switch config.Options.Opt {
case "none", "0":
opt = "-O0"
case "1":
opt = "-O1"
case "2":
opt = "-O2"
case "s":
opt = "-Os"
case "z":
opt = "-Oz"
default:
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
}
var args []string
@@ -821,26 +829,14 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
args = append(args, "--asyncify")
}
exeunopt := result.Executable
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,
opt,
"-g",
exeunopt,
result.Executable,
"--output", result.Executable,
)
wasmopt := goenv.Get("WASMOPT")
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(wasmopt, args...)
}
cmd := exec.Command(wasmopt, args...)
cmd := exec.Command(goenv.Get("WASMOPT"), args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -850,62 +846,6 @@ 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
args := []string{
"component",
"embed",
"-w", witWorld,
witPackage,
result.Executable,
"-o", result.Executable,
}
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 failed: %w", err)
}
// wasm-tools component new embedded.wasm -o component.wasm
args = []string{
"component",
"new",
result.Executable,
"-o", result.Executable,
}
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 failed: %w", err)
}
}
// Print code size if requested.
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
packagePathMap := make(map[string]string, len(lprogram.Packages))
@@ -1102,7 +1042,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
// optimizeProgram runs a series of optimizations and transformations that are
// 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.
func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues map[string]map[string]string) error {
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
if err != nil {
return err
@@ -1121,16 +1061,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
}
// Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, globalValues)
err = setGlobalValues(mod, config.Options.GlobalValues)
if err != nil {
return err
}
// Run most of the whole-program optimizations (including the whole
// O0/O1/O2/Os/Oz optimization pipeline).
errs := transform.Optimize(mod, config)
// Optimization levels here are roughly the same as Clang, but probably not
// exactly.
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
if len(errs) > 0 {
return newMultiError(errs, "")
return newMultiError(errs)
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification failure after LLVM optimization passes")
+7 -9
View File
@@ -8,6 +8,7 @@ import (
"testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
@@ -33,10 +34,8 @@ func TestClangAttributes(t *testing.T) {
"k210",
"nintendoswitch",
"riscv-qemu",
"wasip1",
"wasip2",
"wasi",
"wasm",
"wasm-unknown",
}
if hasBuiltinTools {
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
@@ -57,14 +56,11 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
{GOOS: "linux", GOARCH: "arm64"},
{GOOS: "linux", GOARCH: "mips"},
{GOOS: "linux", GOARCH: "mipsle"},
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"},
{GOOS: "wasip1", GOARCH: "wasm"},
{GOOS: "wasip2", GOARCH: "wasm"},
} {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" {
@@ -78,6 +74,7 @@ func TestClangAttributes(t *testing.T) {
func testClangAttributes(t *testing.T, options *compileopts.Options) {
testDir := t.TempDir()
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
ctx := llvm.NewContext()
defer ctx.Dispose()
@@ -87,8 +84,9 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
t.Fatalf("could not load target: %s", err)
}
config := compileopts.Config{
Options: options,
Target: target,
Options: options,
Target: target,
ClangHeaders: clangHeaderPath,
}
// Create a very simple C input file.
@@ -100,7 +98,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
// Compile this file using Clang.
outpath := filepath.Join(testDir, "test.bc")
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags(false)...)
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
if config.GOOS() == "darwin" {
// Silence some warnings that happen when testing GOOS=darwin on
// something other than MacOS.
+10 -34
View File
@@ -77,15 +77,6 @@ func ReadBuildID() ([]byte, error) {
}
return raw[4:], nil
}
// Normally we would have found a build ID by now. But not on Nix,
// unfortunately, because Nix adds -no_uuid for some reason:
// https://github.com/NixOS/nixpkgs/issues/178366
// Fall back to the same implementation that we use for Windows.
id, err := readRawGoBuildID(f, 32*1024)
if len(id) != 0 || err != nil {
return id, err
}
default:
// On other platforms (such as Windows) there isn't such a convenient
// build ID. Luckily, Go does have an equivalent of the build ID, which
@@ -97,31 +88,16 @@ func ReadBuildID() ([]byte, error) {
// directly. Luckily the build ID is always at the start of the file.
// For details, see:
// https://github.com/golang/go/blob/master/src/cmd/internal/buildid/buildid.go
id, err := readRawGoBuildID(f, 4096)
if len(id) != 0 || err != nil {
return id, err
fileStart := make([]byte, 4096)
_, err := io.ReadFull(f, fileStart)
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
if index < 0 || index > len(fileStart)-103 {
return nil, fmt.Errorf("could not find build id in %s", err)
}
buf := fileStart[index : index+103]
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
}
}
return nil, fmt.Errorf("could not find build ID in %v", executable)
}
// The Go toolchain stores a build ID in the binary that we can use, as a
// fallback if binary file specific build IDs can't be obtained.
// This function reads that build ID from the binary.
func readRawGoBuildID(f *os.File, prefixSize int) ([]byte, error) {
fileStart := make([]byte, prefixSize)
_, err := io.ReadFull(f, fileStart)
if err != nil {
return nil, fmt.Errorf("could not read build id from %s: %v", f.Name(), err)
}
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
if index < 0 || index > len(fileStart)-103 {
return nil, fmt.Errorf("could not find build id in %s", f.Name())
}
buf := fileStart[index : index+103]
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
}
return nil, nil
return nil, fmt.Errorf("could not find build ID in %s", executable)
}
+7 -5
View File
@@ -8,14 +8,14 @@ import (
"github.com/tinygo-org/tinygo/goenv"
)
// These are the GENERIC_SOURCES according to CMakeList.txt except for
// divmodsi4.c and udivmodsi4.c.
// These are the GENERIC_SOURCES according to CMakeList.txt.
var genericBuiltins = []string{
"absvdi2.c",
"absvsi2.c",
"absvti2.c",
"adddf3.c",
"addsf3.c",
"addtf3.c",
"addvdi3.c",
"addvsi3.c",
"addvti3.c",
@@ -40,12 +40,12 @@ var genericBuiltins = []string{
"divdf3.c",
"divdi3.c",
"divmoddi4.c",
//"divmodsi4.c",
"divmodti4.c",
"divsc3.c",
"divsf3.c",
"divsi3.c",
"divtc3.c",
"divti3.c",
"divtf3.c",
"extendsfdf2.c",
"extendhfsf2.c",
"ffsdi2.c",
@@ -91,6 +91,7 @@ var genericBuiltins = []string{
"mulsc3.c",
"mulsf3.c",
"multi3.c",
"multf3.c",
"mulvdi3.c",
"mulvsi3.c",
"mulvti3.c",
@@ -110,11 +111,13 @@ var genericBuiltins = []string{
"popcountti2.c",
"powidf2.c",
"powisf2.c",
"powitf2.c",
"subdf3.c",
"subsf3.c",
"subvdi3.c",
"subvsi3.c",
"subvti3.c",
"subtf3.c",
"trampoline_setup.c",
"truncdfhf2.c",
"truncdfsf2.c",
@@ -123,7 +126,6 @@ var genericBuiltins = []string{
"ucmpti2.c",
"udivdi3.c",
"udivmoddi4.c",
//"udivmodsi4.c",
"udivmodti4.c",
"udivsi3.c",
"udivti3.c",
+13 -25
View File
@@ -1,12 +1,5 @@
//go:build byollvm
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
// This file needs to be updated each LLVM release.
// There are a few small modifications to make, like:
// * ExecuteAssembler is made non-static.
// * The struct AssemblerImplementation is moved to cc1as.h so it can be
// included elsewhere.
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -28,8 +21,8 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
@@ -53,6 +46,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
@@ -61,8 +55,6 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include <memory>
#include <optional>
#include <system_error>
@@ -82,10 +74,10 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
// Parse the arguments.
const OptTable &OptTbl = getDriverOptTable();
llvm::opt::Visibility VisibilityMask(options::CC1AsOption);
const unsigned IncludedFlagsBitmask = options::CC1AsOption;
unsigned MissingArgIndex, MissingArgCount;
InputArgList Args =
OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount, VisibilityMask);
InputArgList Args = OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount,
IncludedFlagsBitmask);
// Check for missing argument error.
if (MissingArgCount) {
@@ -98,7 +90,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
auto ArgString = A->getAsString(Args);
std::string Nearest;
if (OptTbl.findNearest(ArgString, Nearest, VisibilityMask) > 1)
if (OptTbl.findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
else
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
@@ -159,7 +151,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
auto Split = StringRef(Arg).split('=');
Opts.DebugPrefixMap.emplace_back(Split.first, Split.second);
Opts.DebugPrefixMap.insert(
{std::string(Split.first), std::string(Split.second)});
}
// Frontend Options
@@ -232,9 +225,6 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.Case("default", EmitDwarfUnwindType::Default);
}
Opts.EmitCompactUnwindNonCanonical =
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
return Success;
@@ -270,8 +260,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
if (std::error_code EC = Buffer.getError()) {
return Diags.Report(diag::err_fe_error_reading)
<< Opts.InputFile << EC.message();
Error = EC.message();
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
}
SourceMgr SrcMgr;
@@ -288,7 +278,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCTargetOptions MCOptions;
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
std::unique_ptr<MCAsmInfo> MAI(
@@ -521,10 +510,9 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (Asm.ShowHelp) {
getDriverOptTable().printHelp(
llvm::outs(), "clang -cc1as [options] file...",
"Clang Integrated Assembler", /*ShowHidden=*/false,
/*ShowAllAliases=*/false,
llvm::opt::Visibility(driver::options::CC1AsOption));
"Clang Integrated Assembler",
/*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
/*ShowAllAliases=*/false);
return 0;
}
+1 -9
View File
@@ -1,6 +1,3 @@
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
// See cc1as.cpp for details.
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -47,7 +44,7 @@ struct AssemblerInvocation {
std::string DwarfDebugFlags;
std::string DwarfDebugProducer;
std::string DebugCompilationDir;
llvm::SmallVector<std::pair<std::string, std::string>, 0> DebugPrefixMap;
std::map<const std::string, const std::string> DebugPrefixMap;
llvm::DebugCompressionType CompressDebugSections =
llvm::DebugCompressionType::None;
std::string MainFileName;
@@ -92,10 +89,6 @@ struct AssemblerInvocation {
/// Whether to emit DWARF unwind info.
EmitDwarfUnwindType EmitDwarfUnwind;
// Whether to emit compact-unwind for non-canonical entries.
// Note: maybe overriden by other constraints.
unsigned EmitCompactUnwindNonCanonical : 1;
/// The name of the relocation model to use.
std::string RelocationModel;
@@ -135,7 +128,6 @@ public:
DwarfVersion = 0;
EmbedBitcode = 0;
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
EmitCompactUnwindNonCanonical = false;
}
static bool CreateFromArgs(AssemblerInvocation &Res,
+1 -1
View File
@@ -11,7 +11,7 @@
#include <clang/FrontendTool/Utils.h>
#include <llvm/ADT/IntrusiveRefCntPtr.h>
#include <llvm/Option/Option.h>
#include <llvm/TargetParser/Host.h>
#include <llvm/Support/Host.h>
using namespace llvm;
using namespace clang;
+5 -2
View File
@@ -27,16 +27,19 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, err
}
if major != 1 || minor < 19 || minor > 22 {
if major != 1 || minor < 18 || minor > 21 {
// Note: when this gets updated, also update the Go compatibility matrix:
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
return nil, fmt.Errorf("requires go version 1.19 through 1.22, got go%d.%d", major, minor)
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
return &compileopts.Config{
Options: options,
Target: spec,
GoMinorVersion: minor,
ClangHeaders: clangHeaderPath,
TestConfig: options.TestConfig,
}, nil
}
+105
View File
@@ -0,0 +1,105 @@
package builder
import (
"errors"
"io/fs"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"tinygo.org/x/go-llvm"
)
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
// multiple locations, which should make it find the directory when installed in
// various ways.
func getClangHeaderPath(TINYGOROOT string) string {
// Check whether we're running from the source directory.
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
return path
}
// Check whether we're running from the installation directory.
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
return path
}
// It looks like we are built with a system-installed LLVM. Do a last
// attempt: try to use Clang headers relative to the clang binary.
llvmMajor := strings.Split(llvm.Version, ".")[0]
for _, cmdName := range commands["clang"] {
binpath, err := exec.LookPath(cmdName)
if err == nil {
// This should be the command that will also be used by
// execCommand. To avoid inconsistencies, make sure we use the
// headers relative to this command.
binpath, err = filepath.EvalSymlinks(binpath)
if err != nil {
// Unexpected.
return ""
}
// Example executable:
// /usr/lib/llvm-9/bin/clang
// Example include path:
// /usr/lib/llvm-9/lib64/clang/9.0.1/include/
llvmRoot := filepath.Dir(filepath.Dir(binpath))
clangVersionRoot := filepath.Join(llvmRoot, "lib64", "clang")
dirs64, err64 := ioutil.ReadDir(clangVersionRoot)
// Example include path:
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
clangVersionRoot = filepath.Join(llvmRoot, "lib", "clang")
dirs32, err32 := ioutil.ReadDir(clangVersionRoot)
if err64 != nil && err32 != nil {
// Unexpected.
continue
}
dirnames := make([]string, len(dirs64)+len(dirs32))
dirCount := 0
for _, d := range dirs32 {
name := d.Name()
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
dirnames[dirCount] = filepath.Join(llvmRoot, "lib", "clang", name)
dirCount++
}
}
for _, d := range dirs64 {
name := d.Name()
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
dirnames[dirCount] = filepath.Join(llvmRoot, "lib64", "clang", name)
dirCount++
}
}
sort.Strings(dirnames)
// Check for the highest version first.
for i := dirCount - 1; i >= 0; i-- {
path := filepath.Join(dirnames[i], "include")
_, err := os.Stat(filepath.Join(path, "stdint.h"))
if err == nil {
return path
}
}
}
}
// On Arch Linux, the clang executable is stored in /usr/bin rather than being symlinked from there.
// Search directly in /usr/lib for clang.
if matches, err := filepath.Glob("/usr/lib/clang/" + llvmMajor + ".*.*"); err == nil {
// Check for the highest version first.
sort.Strings(matches)
for i := len(matches) - 1; i >= 0; i-- {
path := filepath.Join(matches[i], "include")
_, err := os.Stat(filepath.Join(path, "stdint.h"))
if err == nil {
return path
}
}
}
// Could not find it.
return ""
}
+3 -5
View File
@@ -3,8 +3,7 @@ package builder
// MultiError is a list of multiple errors (actually: diagnostics) returned
// during LLVM IR generation.
type MultiError struct {
ImportPath string
Errs []error
Errs []error
}
func (e *MultiError) Error() string {
@@ -16,15 +15,14 @@ func (e *MultiError) Error() string {
// 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
// will lead to a panic.
// The importPath may be passed if this error is for a single package.
func newMultiError(errs []error, importPath string) error {
func newMultiError(errs []error) error {
switch len(errs) {
case 0:
panic("attempted to create empty MultiError")
case 1:
return errs[0]
default:
return &MultiError{importPath, errs}
return &MultiError{errs}
}
}
+5 -6
View File
@@ -143,10 +143,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// reproducible. Otherwise the temporary directory is stored in the archive
// itself, which varies each run.
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
resourceDir := goenv.ClangResourceDir(false)
if resourceDir != "" {
args = append(args, "-resource-dir="+resourceDir)
}
cpu := config.CPU()
if cpu != "" {
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
@@ -182,8 +178,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc")
}
if strings.HasPrefix(target, "mips") {
args = append(args, "-fno-pic")
if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port:
// https://github.com/espressif/llvm-project/issues/52
// Hopefully this will be fixed soon (LLVM 14).
args = append(args, "-D__ELF__")
}
var once sync.Once
+21 -10
View File
@@ -5,11 +5,7 @@
#include <lld/Common/Driver.h>
#include <llvm/Support/Parallel.h>
LLD_HAS_DRIVER(coff)
LLD_HAS_DRIVER(elf)
LLD_HAS_DRIVER(mingw)
LLD_HAS_DRIVER(macho)
LLD_HAS_DRIVER(wasm)
extern "C" {
static void configure() {
#if _WIN64
@@ -20,13 +16,28 @@ static void configure() {
#endif
}
extern "C" {
bool tinygo_link(int argc, char **argv) {
bool tinygo_link_elf(int argc, char **argv) {
configure();
std::vector<const char*> args(argv, argv + argc);
lld::Result r = lld::lldMain(args, llvm::outs(), llvm::errs(), LLD_ALL_DRIVERS);
return !r.retCode;
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
}
bool tinygo_link_macho(int argc, char **argv) {
configure();
std::vector<const char*> args(argv, argv + argc);
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
}
bool tinygo_link_mingw(int argc, char **argv) {
configure();
std::vector<const char*> args(argv, argv + argc);
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false);
}
bool tinygo_link_wasm(int argc, char **argv) {
configure();
std::vector<const char*> args(argv, argv + argc);
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false);
}
} // external "C"
+7 -2
View File
@@ -6,10 +6,12 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
var Musl = Library{
@@ -91,7 +93,6 @@ var Musl = Library{
"-Wno-string-plus-int",
"-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare",
"-Wno-deprecated-non-prototype",
"-Qunused-arguments",
// Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications),
@@ -105,6 +106,11 @@ var Musl = Library{
"-I" + muslDir + "/include",
"-fno-stack-protector",
}
llvmMajor, _ := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if llvmMajor >= 15 {
// This flag was added in Clang 15. It is not present in LLVM 14.
cflags = append(cflags, "-Wno-deprecated-non-prototype")
}
return cflags
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
@@ -119,7 +125,6 @@ var Musl = Library{
"internal/syscall_ret.c",
"internal/vdso.c",
"legacy/*.c",
"linux/*.c",
"malloc/*.c",
"malloc/mallocng/*.c",
"mman/*.c",
+79 -99
View File
@@ -3,7 +3,6 @@ package builder
import (
"os"
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -42,23 +41,91 @@ var Picolibc = Library{
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
librarySources: func(target string) ([]string, error) {
sources := append([]string(nil), picolibcSources...)
if !strings.HasPrefix(target, "avr") {
// Small chips without long jumps can't compile many files (printf,
// pow, etc). Therefore exclude those source files for those chips.
// Unfortunately it's difficult to exclude only some chips, so this
// excludes those files on all AVR chips for now.
// More information:
// https://github.com/llvm/llvm-project/issues/67042
sources = append(sources, picolibcSourcesLarge...)
}
return sources, nil
return picolibcSources, nil
},
}
var picolibcSources = []string{
"../../picolibc-stdio.c",
// srcs_tinystdio
"libc/tinystdio/asprintf.c",
"libc/tinystdio/bufio.c",
"libc/tinystdio/clearerr.c",
"libc/tinystdio/ecvt_r.c",
"libc/tinystdio/ecvt.c",
"libc/tinystdio/ecvtf_r.c",
"libc/tinystdio/ecvtf.c",
"libc/tinystdio/fcvt.c",
"libc/tinystdio/fcvt_r.c",
"libc/tinystdio/fcvtf.c",
"libc/tinystdio/fcvtf_r.c",
"libc/tinystdio/gcvt.c",
"libc/tinystdio/gcvtf.c",
"libc/tinystdio/fclose.c",
"libc/tinystdio/fdevopen.c",
"libc/tinystdio/feof.c",
"libc/tinystdio/ferror.c",
"libc/tinystdio/fflush.c",
"libc/tinystdio/fgetc.c",
"libc/tinystdio/fgets.c",
"libc/tinystdio/fileno.c",
"libc/tinystdio/filestrget.c",
"libc/tinystdio/filestrput.c",
"libc/tinystdio/filestrputalloc.c",
"libc/tinystdio/fmemopen.c",
"libc/tinystdio/fprintf.c",
"libc/tinystdio/fputc.c",
"libc/tinystdio/fputs.c",
"libc/tinystdio/fread.c",
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
"libc/tinystdio/fscanf.c",
"libc/tinystdio/fseek.c",
"libc/tinystdio/fseeko.c",
"libc/tinystdio/ftell.c",
"libc/tinystdio/ftello.c",
"libc/tinystdio/fwrite.c",
"libc/tinystdio/getchar.c",
"libc/tinystdio/gets.c",
"libc/tinystdio/matchcaseprefix.c",
"libc/tinystdio/mktemp.c",
"libc/tinystdio/perror.c",
"libc/tinystdio/printf.c",
"libc/tinystdio/putchar.c",
"libc/tinystdio/puts.c",
"libc/tinystdio/rewind.c",
"libc/tinystdio/scanf.c",
"libc/tinystdio/setbuf.c",
"libc/tinystdio/setbuffer.c",
"libc/tinystdio/setlinebuf.c",
"libc/tinystdio/setvbuf.c",
"libc/tinystdio/snprintf.c",
"libc/tinystdio/sprintf.c",
"libc/tinystdio/snprintfd.c",
"libc/tinystdio/snprintff.c",
"libc/tinystdio/sprintff.c",
"libc/tinystdio/sprintfd.c",
"libc/tinystdio/sscanf.c",
"libc/tinystdio/strfromf.c",
"libc/tinystdio/strfromd.c",
"libc/tinystdio/strtof.c",
"libc/tinystdio/strtof_l.c",
"libc/tinystdio/strtod.c",
"libc/tinystdio/strtod_l.c",
"libc/tinystdio/ungetc.c",
"libc/tinystdio/vasprintf.c",
"libc/tinystdio/vfiprintf.c",
"libc/tinystdio/vfprintf.c",
"libc/tinystdio/vfprintff.c",
"libc/tinystdio/vfscanf.c",
"libc/tinystdio/vfiscanf.c",
"libc/tinystdio/vfscanff.c",
"libc/tinystdio/vprintf.c",
"libc/tinystdio/vscanf.c",
"libc/tinystdio/vsscanf.c",
"libc/tinystdio/vsnprintf.c",
"libc/tinystdio/vsprintf.c",
"libc/string/bcmp.c",
"libc/string/bcopy.c",
"libc/string/bzero.c",
@@ -162,87 +229,6 @@ var picolibcSources = []string{
"libc/string/wmempcpy.c",
"libc/string/wmemset.c",
"libc/string/xpg_strerror_r.c",
}
// Parts of picolibc that are too large for small AVRs.
var picolibcSourcesLarge = []string{
// srcs_tinystdio
"libc/tinystdio/asprintf.c",
"libc/tinystdio/bufio.c",
"libc/tinystdio/clearerr.c",
"libc/tinystdio/ecvt_r.c",
"libc/tinystdio/ecvt.c",
"libc/tinystdio/ecvtf_r.c",
"libc/tinystdio/ecvtf.c",
"libc/tinystdio/fcvt.c",
"libc/tinystdio/fcvt_r.c",
"libc/tinystdio/fcvtf.c",
"libc/tinystdio/fcvtf_r.c",
"libc/tinystdio/gcvt.c",
"libc/tinystdio/gcvtf.c",
"libc/tinystdio/fclose.c",
"libc/tinystdio/fdevopen.c",
"libc/tinystdio/feof.c",
"libc/tinystdio/ferror.c",
"libc/tinystdio/fflush.c",
"libc/tinystdio/fgetc.c",
"libc/tinystdio/fgets.c",
"libc/tinystdio/fileno.c",
"libc/tinystdio/filestrget.c",
"libc/tinystdio/filestrput.c",
"libc/tinystdio/filestrputalloc.c",
"libc/tinystdio/fmemopen.c",
"libc/tinystdio/fprintf.c",
"libc/tinystdio/fputc.c",
"libc/tinystdio/fputs.c",
"libc/tinystdio/fread.c",
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
"libc/tinystdio/fscanf.c",
"libc/tinystdio/fseek.c",
"libc/tinystdio/fseeko.c",
"libc/tinystdio/ftell.c",
"libc/tinystdio/ftello.c",
"libc/tinystdio/fwrite.c",
"libc/tinystdio/getchar.c",
"libc/tinystdio/gets.c",
"libc/tinystdio/matchcaseprefix.c",
"libc/tinystdio/mktemp.c",
"libc/tinystdio/perror.c",
"libc/tinystdio/printf.c",
"libc/tinystdio/putchar.c",
"libc/tinystdio/puts.c",
"libc/tinystdio/rewind.c",
"libc/tinystdio/scanf.c",
"libc/tinystdio/setbuf.c",
"libc/tinystdio/setbuffer.c",
"libc/tinystdio/setlinebuf.c",
"libc/tinystdio/setvbuf.c",
"libc/tinystdio/snprintf.c",
"libc/tinystdio/sprintf.c",
"libc/tinystdio/snprintfd.c",
"libc/tinystdio/snprintff.c",
"libc/tinystdio/sprintff.c",
"libc/tinystdio/sprintfd.c",
"libc/tinystdio/sscanf.c",
"libc/tinystdio/strfromf.c",
"libc/tinystdio/strfromd.c",
"libc/tinystdio/strtof.c",
"libc/tinystdio/strtof_l.c",
"libc/tinystdio/strtod.c",
"libc/tinystdio/strtod_l.c",
"libc/tinystdio/ungetc.c",
"libc/tinystdio/vasprintf.c",
"libc/tinystdio/vfiprintf.c",
"libc/tinystdio/vfprintf.c",
"libc/tinystdio/vfprintff.c",
"libc/tinystdio/vfscanf.c",
"libc/tinystdio/vfiscanf.c",
"libc/tinystdio/vfscanff.c",
"libc/tinystdio/vprintf.c",
"libc/tinystdio/vscanf.c",
"libc/tinystdio/vsscanf.c",
"libc/tinystdio/vsnprintf.c",
"libc/tinystdio/vsprintf.c",
"libm/common/sf_finite.c",
"libm/common/sf_copysign.c",
@@ -337,12 +323,6 @@ var picolibcSourcesLarge = []string{
"libm/common/math_err_may_uflow.c",
"libm/common/math_err_check_uflow.c",
"libm/common/math_err_check_oflow.c",
"libm/common/math_errf_divzerof.c",
"libm/common/math_errf_invalidf.c",
"libm/common/math_errf_may_uflowf.c",
"libm/common/math_errf_oflowf.c",
"libm/common/math_errf_uflowf.c",
"libm/common/math_errf_with_errnof.c",
"libm/common/math_inexact.c",
"libm/common/math_inexactf.c",
"libm/common/log.c",
+3 -3
View File
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test.
tests := []sizeTest{
// microcontrollers
{"hifive1b", "examples/echo", 4484, 280, 0, 2252},
{"microbit", "examples/serial", 2732, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 6016, 1484, 116, 6816},
{"hifive1b", "examples/echo", 4568, 280, 0, 2252},
{"microbit", "examples/serial", 2728, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 5996, 1484, 116, 6816},
// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
+27 -4
View File
@@ -12,7 +12,10 @@ import (
#include <stdbool.h>
#include <stdlib.h>
bool tinygo_clang_driver(int argc, char **argv);
bool tinygo_link(int argc, char **argv);
bool tinygo_link_elf(int argc, char **argv);
bool tinygo_link_macho(int argc, char **argv);
bool tinygo_link_mingw(int argc, char **argv);
bool tinygo_link_wasm(int argc, char **argv);
*/
import "C"
@@ -23,7 +26,16 @@ const hasBuiltinTools = true
// This version actually runs the tools because TinyGo was compiled while
// linking statically with LLVM (with the byollvm build tag).
func RunTool(tool string, args ...string) error {
args = append([]string{tool}, args...)
linker := "elf"
if tool == "ld.lld" && len(args) >= 2 {
if args[0] == "-m" && (args[1] == "i386pep" || args[1] == "arm64pe") {
linker = "mingw"
} else if args[0] == "-flavor" {
linker = args[1]
args = args[2:]
}
}
args = append([]string{"tinygo:" + tool}, args...)
var cflag *C.char
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
@@ -39,8 +51,19 @@ func RunTool(tool string, args ...string) error {
switch tool {
case "clang":
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
case "ld.lld", "wasm-ld":
ok = C.tinygo_link(C.int(len(args)), (**C.char)(buf))
case "ld.lld":
switch linker {
case "darwin":
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
case "elf":
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
case "mingw":
ok = C.tinygo_link_mingw(C.int(len(args)), (**C.char)(buf))
default:
return errors.New("unknown linker: " + linker)
}
case "wasm-ld":
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
default:
return errors.New("unknown tool: " + tool)
}
+6
View File
@@ -1,6 +1,7 @@
package builder
import (
"errors"
"os"
"os/exec"
@@ -11,6 +12,11 @@ import (
func runCCompiler(flags ...string) error {
if hasBuiltinTools {
// Compile this with the internal Clang compiler.
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
if headerPath == "" {
return errors.New("could not locate Clang headers")
}
flags = append(flags, "-I"+headerPath)
cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
-81
View File
@@ -1,81 +0,0 @@
package builder
import (
"os"
"path/filepath"
"github.com/tinygo-org/tinygo/goenv"
)
var WasmBuiltins = Library{
name: "wasmbuiltins",
makeHeaders: func(target, includeDir string) error {
if err := os.Mkdir(includeDir+"/bits", 0o777); err != nil {
return err
}
f, err := os.Create(includeDir + "/bits/alltypes.h")
if err != nil {
return err
}
if _, err := f.Write([]byte(wasmAllTypes)); err != nil {
return err
}
return f.Close()
},
cflags: func(target, headerPath string) []string {
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
return []string{
"-Werror",
"-Wall",
"-std=gnu11",
"-nostdlibinc",
"-isystem", libcDir + "/libc-top-half/musl/arch/wasm32",
"-isystem", libcDir + "/libc-top-half/musl/arch/generic",
"-isystem", libcDir + "/libc-top-half/musl/src/internal",
"-isystem", libcDir + "/libc-top-half/musl/src/include",
"-isystem", libcDir + "/libc-top-half/musl/include",
"-isystem", libcDir + "/libc-bottom-half/headers/public",
"-I" + headerPath,
}
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
librarySources: func(target string) ([]string, error) {
return []string{
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
// llvm.memset.* LLVM intrinsics.
"libc-top-half/musl/src/string/memcpy.c",
"libc-top-half/musl/src/string/memmove.c",
"libc-top-half/musl/src/string/memset.c",
// exp, exp2, and log are needed for LLVM math builtin functions
// like llvm.exp.*.
"libc-top-half/musl/src/math/__math_divzero.c",
"libc-top-half/musl/src/math/__math_invalid.c",
"libc-top-half/musl/src/math/__math_oflow.c",
"libc-top-half/musl/src/math/__math_uflow.c",
"libc-top-half/musl/src/math/__math_xflow.c",
"libc-top-half/musl/src/math/exp.c",
"libc-top-half/musl/src/math/exp_data.c",
"libc-top-half/musl/src/math/exp2.c",
"libc-top-half/musl/src/math/log.c",
"libc-top-half/musl/src/math/log_data.c",
}, nil
},
}
// alltypes.h for wasm-libc, using the types as defined inside Clang.
const wasmAllTypes = `
typedef __SIZE_TYPE__ size_t;
typedef __INT8_TYPE__ int8_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __INT64_TYPE__ int64_t;
typedef __UINT8_TYPE__ uint8_t;
typedef __UINT16_TYPE__ uint16_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
typedef __UINTPTR_TYPE__ uintptr_t;
// This type is used internally in wasi-libc.
typedef double double_t;
`
+12 -29
View File
@@ -25,15 +25,9 @@ import (
"golang.org/x/tools/go/ast/astutil"
)
// Function that's only defined in Go 1.22.
var setASTFileFields = func(f *ast.File, start, end token.Pos) {
}
// cgoPackage holds all CGo-related information of a package.
type cgoPackage struct {
generated *ast.File
packageName string
cgoFiles []*ast.File
generatedPos token.Pos
errors []error
currentDir string // current working directory
@@ -81,20 +75,10 @@ type bitfieldInfo struct {
}
// cgoAliases list type aliases between Go and C, for types that are equivalent
// in both languages. See addTypeAliases.
// in both languages.
var cgoAliases = map[string]string{
"C.int8_t": "int8",
"C.int16_t": "int16",
"C.int32_t": "int32",
"C.int64_t": "int64",
"C.uint8_t": "uint8",
"C.uint16_t": "uint16",
"C.uint32_t": "uint32",
"C.uint64_t": "uint64",
"C.uintptr_t": "uintptr",
"C.float": "float32",
"C.double": "float64",
"C._Bool": "bool",
"float": "float32",
"double": "float64",
}
// builtinAliases are handled specially because they only exist on the Go side
@@ -171,9 +155,8 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
// 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
// returns these in the []error slice but still modifies the AST.
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
p := &cgoPackage{
packageName: files[0].Name.Name,
currentDir: dir,
importPath: importPath,
fset: fset,
@@ -209,7 +192,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// This is always a bug in the cgo package.
panic("unexpected error: " + err.Error())
}
p.cgoFiles = append(p.cgoFiles, p.generated)
// If the Comments field is not set to nil, the go/format package will get
// confused about where comments should go.
p.generated.Comments = nil
@@ -300,6 +282,9 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// have better alternatives anyway.
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
cflagsForCGo = append(cflagsForCGo, p.cflags...)
if clangHeaders != "" {
cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders)
}
// Retrieve types such as C.int, C.longlong, etc from C.
p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
@@ -319,9 +304,9 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
cf := p.newCGoFile(f, i)
// These types are aliased with the corresponding types in C. For
// example, float in C is always float32 in Go.
cf.names["float"] = clangCursor{}
cf.names["double"] = clangCursor{}
cf.names["_Bool"] = clangCursor{}
for name := range cgoAliases {
cf.names[name] = clangCursor{}
}
// Now read all the names (identifies) that C defines in the header
// snippet.
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
@@ -340,7 +325,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated)
return p.cgoFiles, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
}
func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
@@ -1136,9 +1121,7 @@ func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
// getASTDeclName will declare the given C AST node (if not already defined) and
// will return its name, in the form of C.foo.
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
// type.
if alias := cgoAliases["C."+name]; alias != "" {
if alias := cgoAliases[name]; alias != "" {
return alias
}
node := f.getASTDeclNode(name, found, iscall)
-17
View File
@@ -1,17 +0,0 @@
//go:build go1.22
package cgo
// Code specifically for Go 1.22.
import (
"go/ast"
"go/token"
)
func init() {
setASTFileFields = func(f *ast.File, start, end token.Pos) {
f.FileStart = start
f.FileEnd = end
}
}
+5 -12
View File
@@ -7,7 +7,6 @@ import (
"go/ast"
"go/format"
"go/parser"
"go/scanner"
"go/token"
"go/types"
"os"
@@ -56,7 +55,7 @@ func TestCGo(t *testing.T) {
}
// Process the AST with CGo.
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "")
// Check the AST for type errors.
var typecheckErrors []error
@@ -67,7 +66,7 @@ func TestCGo(t *testing.T) {
Importer: simpleImporter{},
Sizes: types.SizesFor("gccgo", "arm"),
}
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
_, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil)
if err != nil && len(typecheckErrors) == 0 {
// Only report errors when no type errors are found (an
// unexpected condition).
@@ -92,7 +91,7 @@ func TestCGo(t *testing.T) {
}
buf.WriteString("\n")
}
err = format.Node(buf, fset, cgoFiles[0])
err = format.Node(buf, fset, cgoAST)
if err != nil {
t.Errorf("could not write out CGo AST: %v", err)
}
@@ -217,16 +216,10 @@ func (i simpleImporter) Import(path string) (*types.Package, error) {
}
}
// formatDiagnostic formats the error message to be an indented comment. It
// formatDiagnostics formats the error message to be an indented comment. It
// also fixes Windows path name issues (backward slashes).
func formatDiagnostic(err error) string {
var msg string
switch err := err.(type) {
case scanner.Error:
msg = err.Pos.String() + ": " + err.Msg
default:
msg = err.Error()
}
msg := err.Error()
if runtime.GOOS == "windows" {
// Fix Windows path slashes.
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
+4 -18
View File
@@ -17,8 +17,6 @@ var (
token.OR: precedenceOr,
token.XOR: precedenceXor,
token.AND: precedenceAnd,
token.SHL: precedenceShift,
token.SHR: precedenceShift,
token.ADD: precedenceAdd,
token.SUB: precedenceAdd,
token.MUL: precedenceMul,
@@ -27,13 +25,11 @@ var (
}
)
// See: https://en.cppreference.com/w/c/language/operator_precedence
const (
precedenceLowest = iota + 1
precedenceOr
precedenceXor
precedenceAnd
precedenceShift
precedenceAdd
precedenceMul
precedencePrefix
@@ -86,7 +82,7 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
for t.peekToken != token.EOF && precedence < precedences[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()
leftExpr, err = parseBinaryExpr(t, leftExpr)
}
@@ -195,9 +191,7 @@ func (t *tokenizer) Next() {
t.curValue = t.peekValue
// 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 {
if len(t.buf) == 0 {
t.peekToken = token.EOF
@@ -209,23 +203,15 @@ func (t *tokenizer) Next() {
// Skip whitespace.
// Based on this source, not sure whether it represents C whitespace:
// https://en.cppreference.com/w/cpp/string/byte/isspace
if t.peekPos != token.NoPos {
t.peekPos++
}
t.peekPos++
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.
switch c {
case '&':
t.peekToken = token.LAND
case '|':
t.peekToken = token.LOR
case '<':
t.peekToken = token.SHL
case '>':
t.peekToken = token.SHR
default:
panic("unreachable")
}
t.peekValue = t.buf[:2]
t.buf = t.buf[2:]
-4
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 + 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)`, `(5 / 5)`},
{`1 - 2`, `1 - 2`},
+39 -43
View File
@@ -4,7 +4,6 @@ package cgo
// modification. It does not touch the AST itself.
import (
"bytes"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
@@ -370,45 +369,42 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_MacroDefinition:
// Extract tokens from the Clang tokenizer.
// See: https://stackoverflow.com/a/19074846/559350
sourceRange := C.tinygo_clang_getCursorExtent(c)
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 rawTokens *C.CXToken
var numTokens C.unsigned
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
tokens := unsafe.Slice(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) + len(name)
} 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)
}
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
}
C.clang_disposeTokens(tu, rawTokens, numTokens)
value := sourceBuf.String()
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.
tokenPos := token.NoPos
if pos != token.NoPos {
tokenPos = pos + token.Pos(len(name))
}
expr, scannerError := parseConst(tokenPos, f.fset, value)
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
if scannerError != nil {
f.errors = append(f.errors, *scannerError)
return nil, nil
@@ -593,13 +589,6 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
f := p.fset.AddFile(filename, -1, int(size))
f.SetLines(lines)
p.tokenFiles[filename] = f
// Add dummy file AST, to satisfy the type checker.
astFile := &ast.File{
Package: f.Pos(0),
Name: ast.NewIdent(p.packageName),
}
setASTFileFields(astFile, f.Pos(0), f.Pos(int(size)))
p.cgoFiles = append(p.cgoFiles, astFile)
}
positionFile := p.tokenFiles[filename]
@@ -646,6 +635,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.
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{
Pos: position,
Msg: msg,
+15
View File
@@ -0,0 +1,15 @@
//go:build !byollvm && llvm14
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
*/
import "C"
+2 -2
View File
@@ -8,8 +8,8 @@ package cgo
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
*/
import "C"
+4 -4
View File
@@ -1,8 +1,8 @@
//go:build !byollvm && llvm16
//go:build !byollvm && !llvm14 && !llvm15
package cgo
// As of 2023-05-05, there is a packaging issue with LLVM 16 on Debian:
// As of 2023-05-05, there is a packaging issue on Debian:
// https://github.com/llvm/llvm-project/issues/62199
// A workaround is to fix this locally, using something like this:
//
@@ -14,8 +14,8 @@ package cgo
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
*/
import "C"
-15
View File
@@ -1,15 +0,0 @@
//go:build !byollvm && llvm17
package cgo
/*
#cgo linux CFLAGS: -I/usr/include/llvm-17 -I/usr/include/llvm-c-17 -I/usr/lib/llvm-17/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@17/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@17/include
#cgo freebsd CFLAGS: -I/usr/local/llvm17/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-17/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@17/lib -lclang
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@17/lib -lclang
#cgo freebsd LDFLAGS: -L/usr/local/llvm17/lib -lclang
*/
import "C"
-15
View File
@@ -1,15 +0,0 @@
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17
package cgo
/*
#cgo linux CFLAGS: -I/usr/include/llvm-18 -I/usr/include/llvm-c-18 -I/usr/lib/llvm-18/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@18/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@18/include
#cgo freebsd CFLAGS: -I/usr/local/llvm18/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-18/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@18/lib -lclang
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@18/lib -lclang
#cgo freebsd LDFLAGS: -L/usr/local/llvm18/lib -lclang
*/
import "C"
+6 -11
View File
@@ -13,19 +13,18 @@ typedef someType noType; // undefined type
#define SOME_CONST_1 5) // invalid const syntax
#define SOME_CONST_2 6) // const not used (so no error)
#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
// #cgo CFLAGS: -DSOME_PARAM_CONST_invalid=3/+3
// #cgo CFLAGS: -DSOME_PARAM_CONST_valid=3+4
import "C"
// #warning another warning
import "C"
// #include <stdint.h>
import "C"
// Make sure that errors for the following lines won't change with future
// additions to the CGo preamble.
//
@@ -44,11 +43,7 @@ var (
_ = 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
// This must result in a type error. Previously, TinyGo would allow this
// code (which is not allowed by upstream Go).
_ int8 = C.int8_t(5)
)
+5 -9
View File
@@ -1,12 +1,9 @@
// CGo errors:
// testdata/errors.go:4:2: warning: some warning
// testdata/errors.go:11:9: error: unknown type name 'someType'
// testdata/errors.go:26:5: warning: another warning
// testdata/errors.go:22:5: warning: another warning
// testdata/errors.go:13:23: unexpected token ), expected end of expression
// testdata/errors.go:21:26: unexpected token ), expected end of expression
// testdata/errors.go:16:33: unexpected token ), expected end of expression
// testdata/errors.go:17:34: unexpected token ), expected end of expression
// -: unexpected token INT, expected end of expression
// testdata/errors.go:19:26: unexpected token ), expected end of expression
// Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
@@ -14,9 +11,7 @@
// testdata/errors.go:108: undefined: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
// testdata/errors.go:112: undefined: C.SOME_CONST_4
// testdata/errors.go:114: undefined: C.SOME_CONST_b
// testdata/errors.go:116: undefined: C.SOME_CONST_startspace
// testdata/errors.go:119: undefined: C.SOME_PARAM_CONST_invalid
// testdata/errors.go:116: cannot use C.int8_t(5) (constant 5 of type C.schar) as int8 value in variable declaration
package main
@@ -64,4 +59,5 @@ type C.struct_point_t struct {
type C.point_t = C.struct_point_t
const C.SOME_CONST_3 = 1234
const C.SOME_PARAM_CONST_valid = 3 + 4
type C.int8_t = C.schar
+24 -51
View File
@@ -19,6 +19,7 @@ type Config struct {
Options *Options
Target *TargetSpec
GoMinorVersion int
ClangHeaders string // Clang built-in header include path
TestConfig TestConfig
}
@@ -74,13 +75,7 @@ func (c *Config) GOARM() string {
// BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string {
tags := append([]string(nil), c.Target.BuildTags...) // copy slice (avoid a race)
tags = append(tags, []string{
"tinygo", // that's the compiler
"purego", // to get various crypto packages to work
"math_big_pure_go", // to get math/big to work
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
"serial." + c.Serial()}...) // used inside the machine package
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
for i := 1; i <= c.GoMinorVersion; i++ {
tags = append(tags, fmt.Sprintf("go1.%d", i))
}
@@ -88,6 +83,12 @@ func (c *Config) BuildTags() []string {
return tags
}
// CgoEnabled returns true if (and only if) CGo is enabled. It is true by
// default and false if CGO_ENABLED is set to "0".
func (c *Config) CgoEnabled() bool {
return goenv.Get("CGO_ENABLED") == "1"
}
// GC returns the garbage collection strategy in use on this platform. Valid
// values are "none", "leaking", "conservative" and "precise".
func (c *Config) GC() string {
@@ -144,18 +145,18 @@ func (c *Config) Serial() string {
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
// threshold as used in the LLVM optimization pipeline.
func (c *Config) OptLevel() (level string, speedLevel, sizeLevel int) {
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
switch c.Options.Opt {
case "none", "0":
return "O0", 0, 0
return 0, 0, 0 // -O0
case "1":
return "O1", 1, 0
return 1, 0, 0 // -O1
case "2":
return "O2", 2, 0
return 2, 0, 225 // -O2
case "s":
return "Os", 2, 1
return 2, 1, 225 // -Os
case "z":
return "Oz", 2, 2 // default
return 2, 2, 5 // -Oz, default
default:
// This is not shown to the user: valid choices are already checked as
// part of Options.Verify(). It is here as a sanity check.
@@ -189,15 +190,6 @@ func (c *Config) StackSize() uint64 {
return c.Target.DefaultStackSize
}
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
func (c *Config) MaxStackAlloc() uint64 {
if c.StackSize() > 32*1024 {
return 1024
}
return 256
}
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
// calculates and patches in the checksum for the 2nd stage bootloader.
func (c *Config) RP2040BootPatch() bool {
@@ -212,10 +204,7 @@ func (c *Config) RP2040BootPatch() bool {
func MuslArchitecture(triple string) string {
arch := strings.Split(triple, "-")[0]
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
return "arm"
}
if arch == "mipsel" {
return "mips"
arch = "arm"
}
return arch
}
@@ -270,35 +259,24 @@ func (c *Config) DefaultBinaryExtension() string {
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
// preprocessing.
func (c *Config) CFlags(libclang bool) []string {
func (c *Config) CFlags() []string {
var cflags []string
for _, flag := range c.Target.CFlags {
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
}
resourceDir := goenv.ClangResourceDir(libclang)
if resourceDir != "" {
// The resource directory contains the built-in clang headers like
// stdbool.h, stdint.h, float.h, etc.
// It is left empty if we're using an external compiler (that already
// knows these headers).
cflags = append(cflags,
"-resource-dir="+resourceDir,
)
}
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"),
"--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"),
)
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"),
"--sysroot="+path,
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
"-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
)
@@ -314,17 +292,12 @@ func (c *Config) CFlags(libclang bool) []string {
)
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)
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
case "mingw-w64":
root := goenv.Get("TINYGOROOT")
path, _ := c.LibcPath("mingw-w64")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"--sysroot="+path,
"-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",
@@ -483,6 +456,9 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport)
}
args = []string{"-f", "interface/" + openocdInterface + ".cfg"}
for _, cmd := range c.Target.OpenOCDCommands {
args = append(args, "-c", cmd)
}
if c.Target.OpenOCDTransport != "" {
transport := c.Target.OpenOCDTransport
if transport == "swd" {
@@ -494,9 +470,6 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
args = append(args, "-c", "transport select "+transport)
}
args = append(args, "-f", "target/"+c.Target.OpenOCDTarget+".cfg")
for _, cmd := range c.Target.OpenOCDCommands {
args = append(args, "-c", cmd)
}
return args, nil
}
+2 -4
View File
@@ -10,7 +10,7 @@ import (
var (
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
validSerialOptions = []string{"none", "uart", "usb"}
validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"}
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
@@ -23,7 +23,6 @@ type Options struct {
GOOS string // environment variable
GOARCH string // environment variable
GOARM string // environment variable (only used with GOARCH=arm)
Directory string // working dir, leave it unset to use the current working dir
Target string
Opt string
GC string
@@ -49,12 +48,11 @@ type Options struct {
Programmer string
OpenOCDCommands []string
LLVMFeatures string
Directory string
PrintJSON bool
Monitor bool
BaudRate int
Timeout time.Duration
WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option
}
// Verify performs a validation on the given options, raising an error if options are not valid.
+10 -81
View File
@@ -26,7 +26,7 @@ type TargetSpec struct {
Inherits []string `json:"inherits,omitempty"`
Triple string `json:"llvm-target,omitempty"`
CPU string `json:"cpu,omitempty"`
ABI string `json:"target-abi,omitempty"` // roughly equivalent to -mabi= flag
ABI string `json:"target-abi,omitempty"` // rougly equivalent to -mabi= flag
Features string `json:"features,omitempty"`
GOOS string `json:"goos,omitempty"`
GOARCH string `json:"goarch,omitempty"`
@@ -62,8 +62,6 @@ type TargetSpec struct {
JLinkDevice string `json:"jlink-device,omitempty"`
CodeModel string `json:"code-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.
@@ -193,10 +191,6 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
}
case "mips":
llvmarch = "mips"
case "mipsle":
llvmarch = "mipsel"
case "wasm":
llvmarch = "wasm32"
default:
@@ -252,43 +246,6 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
return spec, nil
}
// GetTargetSpecs retrieves target specifications from the TINYGOROOT targets
// directory. Only valid target JSON files are considered, and the function
// returns a map of target names to their respective TargetSpec.
func GetTargetSpecs() (map[string]*TargetSpec, error) {
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("could not list targets: %w", err)
}
maps := map[string]*TargetSpec{}
for _, entry := range entries {
entryInfo, err := entry.Info()
if err != nil {
return nil, fmt.Errorf("could not get entry info: %w", err)
}
if !entryInfo.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
// Only inspect JSON files.
continue
}
path := filepath.Join(dir, entry.Name())
spec, err := LoadTarget(&Options{Target: path})
if err != nil {
return nil, fmt.Errorf("could not list target: %w", err)
}
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
// This doesn't look like a regular target file, but rather like
// a parent target (such as targets/cortex-m.json).
continue
}
name := entry.Name()
name = name[:len(name)-5]
maps[name] = spec
}
return maps, nil
}
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// No target spec available. Use the default one, useful on most systems
// with a regular OS.
@@ -307,10 +264,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
switch goarch {
case "386":
spec.CPU = "pentium4"
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "amd64":
spec.CPU = "x86-64"
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "arm":
spec.CPU = "generic"
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
@@ -325,16 +282,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
case "arm64":
spec.CPU = "generic"
if goos == "darwin" {
spec.Features = "+fp-armv8,+neon"
} else if goos == "windows" {
spec.Features = "+fp-armv8,+neon,-fmv"
} else { // linux
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
spec.Features = "+neon"
} else { // windows, linux
spec.Features = "+neon,-fmv"
}
case "mips", "mipsle":
spec.CPU = "mips32r2"
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
spec.CFlags = append(spec.CFlags, "-fno-pic")
case "wasm":
spec.CPU = "generic"
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
@@ -361,20 +312,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.RTLib = "compiler-rt"
spec.Libc = "musl"
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
if goarch == "arm64" {
// Disable outline atomics. For details, see:
// https://cpufun.substack.com/p/atomics-in-aarch64
// A better way would be to fully support outline atomics, which
// makes atomics slightly more efficient on systems with many cores.
// But the instructions are only supported on newer aarch64 CPUs, so
// this feature is normally put in a system library which does
// feature detection for you.
// We take the lazy way out and simply disable this feature, instead
// of enabling it in compiler-rt (which is a bit more complicated).
// We don't really need this feature anyway as we don't even support
// proper threading.
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
}
} else if goos == "windows" {
spec.Linker = "ld.lld"
spec.Libc = "mingw-w64"
@@ -407,12 +344,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Linker = "wasm-ld"
spec.RTLib = "compiler-rt"
spec.Libc = "wasi-libc"
spec.DefaultStackSize = 1024 * 64 // 64kB
spec.DefaultStackSize = 1024 * 32 // 32kB
spec.LDFlags = append(spec.LDFlags,
"--stack-first",
"--no-demangle",
)
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/asm_tinygowasm.S",
"src/internal/task/task_asyncify_wasm.S",
@@ -427,12 +364,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// operating systems so we need separate assembly files.
suffix = "_windows"
}
asmGoarch := goarch
if goarch == "mips" || goarch == "mipsle" {
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")
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
}
if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs.
@@ -450,10 +383,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Emulator = "qemu-arm {}"
case "arm64":
spec.Emulator = "qemu-aarch64 {}"
case "mips":
spec.Emulator = "qemu-mips {}"
case "mipsle":
spec.Emulator = "qemu-mipsel {}"
}
}
}
+2 -6
View File
@@ -16,18 +16,14 @@ import "tinygo.org/x/go-llvm"
var stdlibAliases = map[string]string{
// crypto packages
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
"crypto/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
"crypto/ed25519/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
"crypto/md5.block": "crypto/md5.blockGeneric",
"crypto/sha1.block": "crypto/sha1.blockGeneric",
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
"crypto/sha256.block": "crypto/sha256.blockGeneric",
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
// AES
"crypto/aes.decryptBlockAsm": "crypto/aes.decryptBlock",
"crypto/aes.encryptBlockAsm": "crypto/aes.encryptBlock",
// math package
"math.archHypot": "math.hypot",
"math.archMax": "math.max",
+2 -2
View File
@@ -135,7 +135,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in an
// uintptr if uintptrs were signed.
maxBufSize := b.CreateLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false), "")
maxBufSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
if elementSize > maxBufSize.ZExtValue() {
b.addError(pos, fmt.Sprintf("channel element type is too big (%v bytes)", elementSize))
return
@@ -150,7 +150,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
// Make sure maxBufSize has the same type as bufSize.
if maxBufSize.Type() != bufSize.Type() {
maxBufSize = b.CreateZExt(maxBufSize, bufSize.Type(), "")
maxBufSize = llvm.ConstZExt(maxBufSize, bufSize.Type())
}
// Do the check for a too large (or negative) buffer size.
+45
View File
@@ -1,6 +1,9 @@
package compiler
import (
"fmt"
"strings"
"tinygo.org/x/go-llvm"
)
@@ -12,13 +15,37 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
ptr := b.getValue(b.fn.Params[0], 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)
// Return the new value, not the original value returned by atomicrmw.
return b.CreateAdd(oldVal, val, "")
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn))
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
if isPointer {
// atomicrmw only supports integers, so cast to an integer.
// TODO: this is fixed in LLVM 15.
val = b.CreatePtrToInt(val, b.uintptrType, "")
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
}
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
if isPointer {
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
}
return oldVal
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
@@ -36,6 +63,24 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
val := b.getValue(b.fn.Params[1], getPos(b.fn))
if strings.HasPrefix(b.Triple, "avr") {
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
vType := val.Type()
isPointer := vType.TypeKind() == llvm.PointerTypeKind
if isPointer {
// libcalls only supports integers, so cast to an integer.
vType = b.uintptrType
val = b.CreatePtrToInt(val, vType, "")
ptr = b.CreateBitCast(ptr, llvm.PointerType(vType, 0), "")
}
name := fmt.Sprintf("__atomic_store_%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, b.uintptrType}, false))
}
b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "")
return llvm.Value{}
}
store := b.CreateStore(val, ptr)
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
+1 -1
View File
@@ -45,7 +45,7 @@ func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name
if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil))
}
args = append(args, llvm.Undef(b.dataPtrType)) // unused context parameter
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
if isInvoke {
return b.createInvoke(fnType, llvmFn, args, name)
}
+26 -22
View File
@@ -33,27 +33,28 @@ func (b *builder) createChanSend(instr *ssa.Send) {
// store value-to-send
valueType := b.getLLVMType(instr.X.Type())
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaSize llvm.Value
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
if isZeroSize {
valueAlloca = llvm.ConstNull(b.dataPtrType)
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
} else {
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
b.CreateStore(chanValue, valueAlloca)
}
// Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the send.
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
// End the lifetime of the allocas.
// This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
if !isZeroSize {
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
}
@@ -65,27 +66,28 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
// Allocate memory to receive into.
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaSize llvm.Value
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
if isZeroSize {
valueAlloca = llvm.ConstNull(b.dataPtrType)
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
} else {
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
}
// Allocate blockedlist buffer.
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the receive.
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
var received llvm.Value
if isZeroSize {
received = llvm.ConstNull(valueType)
} else {
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
if unop.CommaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
@@ -157,7 +159,8 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
sendValue := b.getValue(state.Send, state.Pos)
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
b.CreateStore(sendValue, alloca)
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
ptr := b.CreateBitCast(alloca, b.i8ptrType, "")
selectState = b.CreateInsertValue(selectState, ptr, 1, "")
default:
panic("unreachable")
}
@@ -165,10 +168,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
}
// Create a receive buffer, where the received value will be stored.
recvbuf := llvm.Undef(b.dataPtrType)
recvbuf := llvm.Undef(b.i8ptrType)
if recvbufSize != 0 {
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
recvbufAlloca, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
recvbufAlloca.SetAlignment(recvbufAlign)
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
@@ -178,7 +181,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
// Create the states slice (allocated on the stack).
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
statesAlloca, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
for i, state := range selectStates {
// Set each slice element to the appropriate channel.
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
@@ -199,7 +202,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
// Stack-allocate operation structures.
// If these were simply created as a slice, they would heap-allocate.
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
@@ -213,7 +216,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
}, "select.result")
// Terminate the lifetime of the operation structures.
b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize)
} else {
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
recvbuf,
@@ -222,7 +225,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
}
// Terminate the lifetime of the states alloca.
b.emitLifetimeEnd(statesAlloca, statesSize)
b.emitLifetimeEnd(statesI8, statesSize)
// The result value does not include all the possible received values,
// because we can't load them in advance. Instead, the *ssa.Extract
@@ -262,6 +265,7 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
// it to the correct type, and dereference it.
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
typ := b.getLLVMType(expr.Type())
return b.CreateLoad(typ, recvbuf, "")
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "")
return b.CreateLoad(typ, ptr, "")
}
}
+64 -51
View File
@@ -53,10 +53,8 @@ type Config struct {
Scheduler string
AutomaticStackSize bool
DefaultStackSize uint64
MaxStackAlloc uint64
NeedsStackObjects bool
Debug bool // Whether to emit debug information in the LLVM module.
PanicStrategy string
}
// compilerContext contains function-independent data that should still be
@@ -77,9 +75,10 @@ type compilerContext struct {
machine llvm.TargetMachine
targetData llvm.TargetData
intType llvm.Type
dataPtrType llvm.Type // pointer in address space 0
funcPtrType llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere)
i8ptrType llvm.Type // for convenience
rawVoidFuncType llvm.Type // for convenience
funcPtrAddrSpace int
hasTypedPointers bool // for LLVM 14 backwards compatibility
uintptrType llvm.Type
program *ssa.Program
diagnostics []error
@@ -124,12 +123,13 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
} else {
panic("unknown pointer size")
}
c.dataPtrType = llvm.PointerType(c.ctx.Int8Type(), 0)
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
c.funcPtrType = dummyFunc.Type()
c.hasTypedPointers = c.i8ptrType != llvm.PointerType(c.ctx.Int16Type(), 0) // with opaque pointers, all pointers are the same type (LLVM 15+)
c.rawVoidFuncType = dummyFunc.Type()
dummyFunc.EraseFromParentAsFunction()
return c
@@ -417,14 +417,16 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
case types.Uintptr:
return c.uintptrType
case types.UnsafePointer:
return c.dataPtrType
return c.i8ptrType
default:
panic("unknown basic type: " + typ.String())
}
case *types.Chan, *types.Map, *types.Pointer:
return c.dataPtrType // all pointers are the same
case *types.Chan:
return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0)
case *types.Interface:
return c.getLLVMRuntimeType("_interface")
case *types.Map:
return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0)
case *types.Named:
if st, ok := typ.Underlying().(*types.Struct); ok {
// Structs are a special case. While other named types are ignored
@@ -439,11 +441,21 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
return llvmType
}
return c.getLLVMType(typ.Underlying())
case *types.Pointer:
if c.hasTypedPointers {
ptrTo := c.getLLVMType(typ.Elem())
return llvm.PointerType(ptrTo, 0)
}
return c.i8ptrType // all pointers are the same
case *types.Signature: // function value
return c.getFuncType(typ)
case *types.Slice:
ptrType := c.i8ptrType
if c.hasTypedPointers {
ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0)
}
members := []llvm.Type{
c.dataPtrType,
ptrType,
c.uintptrType, // len
c.uintptrType, // cap
}
@@ -533,8 +545,8 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
Elements: []llvm.Metadata{
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "ptr",
SizeInBits: c.targetData.TypeAllocSize(c.dataPtrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.dataPtrType)) * 8,
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8,
OffsetInBits: 0,
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
}),
@@ -1536,18 +1548,21 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
src := argValues[0]
elems := argValues[1]
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
srcCap := b.CreateExtractValue(src, 2, "append.srcCap")
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
newBuf := b.CreateBitCast(newPtr, srcBuf.Type(), "append.newBuf")
newLen := b.CreateExtractValue(result, 1, "append.newLen")
newCap := b.CreateExtractValue(result, 2, "append.newCap")
newSlice := llvm.Undef(src.Type())
newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "")
newSlice = b.CreateInsertValue(newSlice, newBuf, 0, "")
newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
return newSlice, nil
@@ -1595,6 +1610,9 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
// The pointer to the data to be cleared.
llvmBuf := b.CreateExtractValue(value, 0, "buf")
if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14
llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "")
}
// The length (in bytes) to be cleared.
llvmLen := b.CreateExtractValue(value, 1, "len")
@@ -1629,6 +1647,8 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr")
srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr")
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
case "delete":
@@ -1846,9 +1866,9 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitSV64Call(instr.Args, getPos(instr))
case strings.HasPrefix(name, "(device/riscv.CSR)."):
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"):
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"):
return b.createSyscall(instr)
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
return b.createRawSyscallNoError(instr)
case name == "runtime.supportsRecover":
supportsRecover := uint64(0)
@@ -1856,13 +1876,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
supportsRecover = 1
}
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
case name == "runtime.panicStrategy":
// These constants are defined in src/runtime/panic.go.
panicStrategy := map[string]uint64{
"print": 1, // panicStrategyPrint
"trap": 2, // panicStrategyTrap
}[b.Config.PanicStrategy]
return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil
case name == "runtime/interrupt.New":
return b.createInterruptGlobal(instr)
}
@@ -1875,13 +1888,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
switch value := instr.Value.(type) {
case *ssa.Function:
// Regular function call. No context is necessary.
context = llvm.Undef(b.dataPtrType)
context = llvm.Undef(b.i8ptrType)
if info.variadic && len(fn.Params) == 0 {
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
// Eventually we might be able to eliminate this special case
// entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace))
}
case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For
@@ -1909,14 +1923,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
params = append(params, typecode)
callee = b.getInvokeFunction(instr)
calleeType = callee.GlobalValueType()
context = llvm.Undef(b.dataPtrType)
context = llvm.Undef(b.i8ptrType)
} else {
// Function pointer.
value := b.getValue(instr.Value, getPos(instr))
// This is a func value, which cannot be called directly. We have to
// extract the function pointer and context first from the func value.
callee, context = b.decodeFuncValue(value)
calleeType = b.getLLVMFunctionType(instr.Value.Type().Underlying().(*types.Signature))
calleeType, callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
b.createNilCheck(instr.Value, callee, "fpcall")
}
@@ -1949,7 +1962,7 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
return llvm.Undef(b.getLLVMType(expr.Type()))
}
_, fn := b.getFunction(expr)
return b.createFuncValue(fn, llvm.Undef(b.dataPtrType), expr.Signature)
return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature)
case *ssa.Global:
value := b.getGlobal(expr)
if value.IsNil() {
@@ -2006,8 +2019,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
case *ssa.Alloc:
typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
size := b.targetData.TypeAllocSize(typ)
// Move all "large" allocations to the heap.
if expr.Heap || size > b.MaxStackAlloc {
// Move all "large" allocations to the heap. This value is also transform.maxStackAlloc.
if expr.Heap || size > 256 {
// Calculate ^uintptr(0)
maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue()
if size > maxSize {
@@ -2017,8 +2030,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
layoutValue := b.createObjectLayout(typ, expr.Pos())
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)))
buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
return buf, nil
} else {
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
@@ -2064,6 +2076,10 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
value = b.CreateInsertValue(value, field, i, "changetype.struct")
}
return value, nil
case llvm.PointerTypeKind:
// This can happen with pointers to structs. This case is easy:
// simply bitcast the pointer to the destination type.
return b.CreateBitCast(x, llvmType, "changetype.pointer"), nil
default:
return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
}
@@ -2140,12 +2156,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
// Can't load directly from array (as index is non-constant), so
// have to do it using an alloca+gep+load.
arrayType := collection.Type()
alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
b.CreateStore(collection, alloca)
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
b.emitLifetimeEnd(alloca, allocaSize)
b.emitLifetimeEnd(allocaPtr, allocaSize)
return result, nil
default:
panic("unknown *ssa.Index type")
@@ -2183,7 +2199,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
}
// Make sure index is at least the size of uintptr because getelementptr
// Make sure index is at least the size of uintptr becuase getelementptr
// assumes index is a signed integer.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
@@ -2225,7 +2241,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceType := expr.Type().Underlying().(*types.Slice)
llvmElemType := b.getLLVMType(sliceType.Elem())
elemSize := b.targetData.TypeAllocSize(llvmElemType)
elemAlign := b.targetData.ABITypeAlignment(llvmElemType)
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
maxSize := b.maxSliceSize(llvmElemType)
@@ -2249,7 +2264,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array")
// Extend or truncate if necessary. This is safe as we've already done
// the bounds check.
@@ -2295,7 +2310,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
default:
panic("unknown type in range: " + typ.String())
}
it, _ := b.createTemporaryAlloca(iteratorType, "range.it")
it, _, _ := b.createTemporaryAlloca(iteratorType, "range.it")
b.CreateStore(llvm.ConstNull(iteratorType), it)
return it, nil
case *ssa.Select:
@@ -2463,6 +2478,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
ptr := b.CreateExtractValue(slice, 0, "")
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
return ptr, nil
case *ssa.TypeAssert:
return b.createTypeAssert(expr), nil
@@ -2561,7 +2577,7 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
sizeY := b.targetData.TypeAllocSize(y.Type())
// Check if the shift is bigger than the bit-width of the shifted value.
// This is UB in LLVM, so it needs to be handled separately.
// This is UB in LLVM, so it needs to be handled seperately.
// The Go spec indirectly defines the result as 0.
// Negative shifts are handled earlier, so we can treat y as unsigned.
overshifted := b.CreateICmp(llvm.IntUGE, y, llvm.ConstInt(y.Type(), 8*sizeX, false), "shift.overflow")
@@ -2928,16 +2944,16 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
} else {
strPtr = llvm.ConstNull(c.dataPtrType)
strPtr = llvm.ConstNull(c.i8ptrType)
}
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
return strObj
} else if typ.Kind() == types.UnsafePointer {
if !expr.IsNil() {
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.dataPtrType)
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
}
return llvm.ConstNull(c.dataPtrType)
return llvm.ConstNull(c.i8ptrType)
} else if typ.Info()&types.IsUnsigned != 0 {
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstInt(llvmType, n, false)
@@ -2981,7 +2997,7 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
// Create a generic nil interface with no dynamic type (typecode=0).
fields := []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstPointerNull(c.dataPtrType),
llvm.ConstPointerNull(c.i8ptrType),
}
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
case *types.Pointer:
@@ -2998,7 +3014,8 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
if expr.Value != nil {
panic("expected nil slice constant")
}
llvmPtr := llvm.ConstPointerNull(c.dataPtrType)
elemType := c.getLLVMType(typ.Elem())
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
slice := c.ctx.ConstStruct([]llvm.Value{
llvmPtr, // backing array
@@ -3039,7 +3056,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
// Conversion between pointers and unsafe.Pointer.
if isPtrFrom && isPtrTo {
return value, nil
return b.CreateBitCast(value, llvmTypeTo, ""), nil
}
switch typeTo := typeTo.Underlying().(type) {
@@ -3274,15 +3291,11 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
// Instead of a load from the global, create a bitcast of the
// function pointer itself.
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
pkg := b.fn.Pkg
if pkg == nil {
pkg = b.fn.Origin().Pkg
}
_, fn := b.getFunction(pkg.Members[name].(*ssa.Function))
_, fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
if fn.IsNil() {
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
}
return fn, nil
return b.CreateBitCast(fn, b.i8ptrType, ""), nil
} else {
b.createNilCheck(unop.X, x, "deref")
load := b.CreateLoad(valueType, x, "")
+10 -8
View File
@@ -91,12 +91,14 @@ func TestCompiler(t *testing.T) {
}
// Optimize IR a little.
passOptions := llvm.NewPassBuilderOptions()
defer passOptions.Dispose()
err = mod.RunPasses("instcombine", llvm.TargetMachine{}, passOptions)
if err != nil {
t.Error(err)
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
defer funcPasses.Dispose()
funcPasses.AddInstructionCombiningPass()
funcPasses.InitializeFunc()
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
funcPasses.RunFunc(fn)
}
funcPasses.FinalizeFunc()
outFilePrefix := tc.file[:len(tc.file)-3]
if tc.target != "" {
@@ -167,9 +169,9 @@ func filterIrrelevantIRLines(lines []string) []string {
if strings.HasPrefix(line, "source_filename = ") {
continue
}
if llvmVersion < 15 && strings.HasPrefix(line, "target datalayout = ") {
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
// The datalayout string may vary betewen LLVM versions.
// Right now test outputs are for LLVM 15 and higher.
// Right now test outputs are for LLVM 14 and higher.
continue
}
out = append(out, line)
@@ -243,7 +245,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string)
defer machine.Dispose()
// Load entire program AST into memory.
lprogram, err := loader.Load(config, "./testdata/"+file, types.Config{
lprogram, err := loader.Load(config, "./testdata/"+file, config.ClangHeaders, types.Config{
Sizes: Sizes(machine),
})
if err != nil {
+33 -36
View File
@@ -60,8 +60,9 @@ func (b *builder) deferInitFunc() {
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
// Create defer list pointer.
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
if b.hasDeferFrame() {
// Set up the defer frame with the current stack pointer.
@@ -187,18 +188,6 @@ std z+5, r29
ldi r24, 0
1:`
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
case "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},~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31},~{memory}"
case "riscv32":
asmString = `
la a2, 1f
@@ -260,7 +249,8 @@ func isInLoop(start *ssa.BasicBlock) bool {
func (b *builder) createDefer(instr *ssa.Defer) {
// The pointer to the previous defer struct, which we will replace to
// make a linked list.
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
var values []llvm.Value
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
@@ -281,7 +271,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
values = []llvm.Value{callback, next, typecode, receiverValue}
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
for _, arg := range instr.Call.Args {
val := b.getValue(arg, getPos(instr))
values = append(values, val)
@@ -401,8 +391,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// This may be hit a variable number of times, so use a heap allocation.
size := b.targetData.TypeAllocSize(deferredCallType)
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
nilPtr := llvm.ConstNull(b.dataPtrType)
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
nilPtr := llvm.ConstNull(b.i8ptrType)
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc")
}
if b.NeedsStackObjects {
b.trackPointer(alloca)
@@ -410,12 +401,14 @@ func (b *builder) createDefer(instr *ssa.Defer) {
b.CreateStore(deferredCall, alloca)
// Push it on top of the linked list by replacing deferPtr.
b.CreateStore(alloca, b.deferPtr)
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
b.CreateStore(allocaCast, b.deferPtr)
}
// createRunDefers emits code to run all deferred functions.
func (b *builder) createRunDefers() {
deferType := b.getLLVMRuntimeType("_defer")
deferPtrType := llvm.PointerType(deferType, 0)
// Add a loop like the following:
// for stack != nil {
@@ -442,7 +435,7 @@ func (b *builder) createRunDefers() {
// Create loop head:
// for stack != nil {
b.SetInsertPointAtEnd(loophead)
deferData := b.CreateLoad(b.dataPtrType, b.deferPtr, "")
deferData := b.CreateLoad(deferPtrType, b.deferPtr, "")
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
b.CreateCondBr(stackIsNil, end, loop)
@@ -455,7 +448,7 @@ func (b *builder) createRunDefers() {
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
}, "stack.next.gep")
nextStack := b.CreateLoad(b.dataPtrType, nextStackGEP, "stack.next")
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next")
b.CreateStore(nextStack, b.deferPtr)
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
@@ -476,26 +469,28 @@ func (b *builder) createRunDefers() {
// Call on an value or interface value.
// Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
if !callback.IsInvoke() {
//Expect funcValue to be passed through the deferred call.
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
} else {
//Expect typecode
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
}
for _, arg := range callback.Args {
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
}
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct (including receiver).
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
deferredCallType := b.ctx.StructType(valueTypes, false)
for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
forwardParams = append(forwardParams, forwardParam)
}
@@ -510,8 +505,7 @@ func (b *builder) createRunDefers() {
//Get function pointer and context
var context llvm.Value
fnPtr, context = b.decodeFuncValue(funcValue)
fnType = b.getLLVMFunctionType(callback.Signature())
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature())
//Pass context
forwardParams = append(forwardParams, context)
@@ -525,7 +519,7 @@ func (b *builder) createRunDefers() {
// Add the context parameter. An interface call cannot also be a
// closure but we have to supply the parameter anyway for platforms
// with a strict calling convention.
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
}
b.createCall(fnType, fnPtr, forwardParams, "")
@@ -534,17 +528,18 @@ func (b *builder) createRunDefers() {
// Direct call.
// Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
for _, param := range getParams(callback.Signature) {
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
}
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct.
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := range getParams(callback.Signature) {
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
forwardParams = append(forwardParams, forwardParam)
}
@@ -554,7 +549,7 @@ func (b *builder) createRunDefers() {
if !b.getFunctionInfo(callback).exported {
// Add the context parameter. We know it is ignored by the receiving
// function, but we have to pass one anyway.
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
}
// Call real function.
@@ -564,19 +559,20 @@ func (b *builder) createRunDefers() {
case *ssa.MakeClosure:
// Get the real defer struct type and cast to it.
fn := callback.Fn.(*ssa.Function)
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
params := fn.Signature.Params()
for i := 0; i < params.Len(); i++ {
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
}
valueTypes = append(valueTypes, b.dataPtrType) // closure
valueTypes = append(valueTypes, b.i8ptrType) // closure
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct.
forwardParams := []llvm.Value{}
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 2; i < len(valueTypes); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
forwardParams = append(forwardParams, forwardParam)
}
@@ -588,7 +584,7 @@ func (b *builder) createRunDefers() {
db := b.deferBuiltinFuncs[callback]
//Get parameter types
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
//Get signature from call results
params := callback.Type().Underlying().(*types.Signature).Params()
@@ -597,12 +593,13 @@ func (b *builder) createRunDefers() {
}
deferredCallType := b.ctx.StructType(valueTypes, false)
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
// Extract the params from the struct.
var argValues []llvm.Value
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < params.Len(); i++ {
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
argValues = append(argValues, forwardParam)
}
+39 -10
View File
@@ -13,14 +13,35 @@ import (
// createFuncValue creates a function value from a raw function pointer with no
// context.
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
return b.compilerContext.createFuncValue(b.Builder, funcPtr, context, sig)
}
// createFuncValue creates a function value from a raw function pointer with no
// context.
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
// Closure is: {context, function pointer}
funcValueType := b.getFuncType(sig)
funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
funcValueType := c.getFuncType(sig)
funcValue := llvm.Undef(funcValueType)
funcValue = b.CreateInsertValue(funcValue, context, 0, "")
funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "")
funcValue = builder.CreateInsertValue(funcValue, context, 0, "")
funcValue = builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
return funcValue
}
// getFuncSignatureID returns a new external global for a given signature. This
// global reference is not real, it is only used during func lowering to assign
// signature types to functions and will then be removed.
func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value {
s, _ := getTypeCodeName(sig)
sigGlobalName := "reflect/types.funcid:" + s
sigGlobal := c.mod.NamedGlobal(sigGlobalName)
if sigGlobal.IsNil() {
sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName)
sigGlobal.SetGlobalConstant(true)
}
return sigGlobal
}
// extractFuncScalar returns some scalar that can be used in comparisons. It is
// a cheap operation.
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
@@ -34,20 +55,28 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
}
// decodeFuncValue extracts the context and the function pointer from this func
// value.
func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) {
// value. This may be an expensive operation.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "")
funcPtr = b.CreateExtractValue(funcValue, 1, "")
if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast {
funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers)
}
if sig != nil {
funcType = b.getRawFuncType(sig)
llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace)
funcPtr = b.CreateBitCast(funcPtr, llvmSig, "")
}
return
}
// getFuncType returns the type of a func value given a signature.
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false)
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
}
// getLLVMFunctionType returns a LLVM function type for a given signature.
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
// getRawFuncType returns a LLVM function type for a given signature.
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
// Get the return type.
var returnType llvm.Type
switch typ.Results().Len() {
@@ -75,7 +104,7 @@ func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
if recv.StructName() == "runtime._interface" {
// This is a call on an interface, not a concrete type.
// The receiver is not an interface, but a i8* type.
recv = c.dataPtrType
recv = c.i8ptrType
}
for _, info := range c.expandFormalParamType(recv, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
@@ -88,7 +117,7 @@ func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
}
}
// All functions take these parameters at the end.
paramTypes = append(paramTypes, c.dataPtrType) // context
paramTypes = append(paramTypes, c.i8ptrType) // context
// Make a func type out of the signature.
return llvm.FunctionType(returnType, paramTypes, false)
+3
View File
@@ -81,6 +81,9 @@ func (b *builder) trackValue(value llvm.Value) {
// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
// first if needed. The input value must be of LLVM pointer type.
func (b *builder) trackPointer(value llvm.Value) {
if value.Type() != b.i8ptrType {
value = b.CreateBitCast(value, b.i8ptrType, "")
}
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
}
+12 -13
View File
@@ -21,7 +21,7 @@ func (b *builder) createGo(instr *ssa.Go) {
var prefix string
var funcPtr llvm.Value
var funcType llvm.Type
var funcPtrType llvm.Type
hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new
@@ -42,7 +42,7 @@ func (b *builder) createGo(instr *ssa.Go) {
params = append(params, context) // context parameter
hasContext = true
}
funcType, funcPtr = b.getFunction(callee)
funcPtrType, 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 might as well run these builtins right away without the program
@@ -80,7 +80,7 @@ func (b *builder) createGo(instr *ssa.Go) {
itfTypeCode := b.CreateExtractValue(itf, 0, "")
itfValue := b.CreateExtractValue(itf, 1, "")
funcPtr = b.getInvokeFunction(&instr.Call)
funcType = funcPtr.GlobalValueType()
funcPtrType = funcPtr.GlobalValueType()
params = append([]llvm.Value{itfValue}, params...) // start with receiver
params = append(params, itfTypeCode) // end with typecode
} else {
@@ -90,8 +90,7 @@ func (b *builder) createGo(instr *ssa.Go) {
// * The function context, for closures.
// * The function pointer (for tasks).
var context llvm.Value
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)), instr.Call.Value.Type().Underlying().(*types.Signature))
params = append(params, context, funcPtr)
hasContext = true
prefix = b.fn.RelString(nil)
@@ -99,14 +98,14 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params)
var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos())
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos())
if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF
// section that contains the stack size (and is modified after
// linking).
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.dataPtrType)}, "stacksize")
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
} else {
// The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized.
@@ -116,7 +115,7 @@ func (b *builder) createGo(instr *ssa.Go) {
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
}
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
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.i8ptrType)}, "")
}
// createGoroutineStartWrapper creates a wrapper for the task-based
@@ -166,7 +165,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
}
// Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
c.addStandardAttributes(wrapper)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
@@ -204,7 +203,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
}
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
if !hasContext {
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
}
// Create the call.
@@ -212,7 +211,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.dataPtrType),
llvm.Undef(c.i8ptrType),
}, "")
}
@@ -235,7 +234,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
// merged into one.
// Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
c.addStandardAttributes(wrapper)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
@@ -280,7 +279,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.dataPtrType),
llvm.Undef(c.i8ptrType),
}, "")
}
}
+2 -2
View File
@@ -99,8 +99,8 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
case llvm.IntegerTypeKind:
constraints = append(constraints, "r")
case llvm.PointerTypeKind:
// Memory references require a type starting with LLVM 14,
// probably as a preparation for opaque pointers.
// Memory references require a type in LLVM 14, probably as a
// preparation for opaque pointers.
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
return s
default:
+42 -113
View File
@@ -10,7 +10,6 @@ import (
"fmt"
"go/token"
"go/types"
"sort"
"strconv"
"strings"
@@ -125,19 +124,16 @@ func (c *compilerContext) pkgPathPtr(pkgpath string) llvm.Value {
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
ms := c.program.MethodSets.MethodSet(typ)
hasMethodSet := ms.Len() != 0
_, isInterface := typ.Underlying().(*types.Interface)
if isInterface {
if _, ok := typ.Underlying().(*types.Interface); ok {
hasMethodSet = false
}
// As defined in https://pkg.go.dev/reflect#Type:
// NumMethod returns the number of methods accessible using Method.
// For a non-interface type, it returns the number of exported methods.
// For an interface type, it returns the number of exported and unexported methods.
var numMethods int
for i := 0; i < ms.Len(); i++ {
if isInterface || ms.At(i).Obj().Exported() {
numMethods++
if hasMethodSet {
for i := 0; i < ms.Len(); i++ {
if ms.At(i).Obj().Exported() {
numMethods++
}
}
}
@@ -181,15 +177,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFieldTypes := []*types.Var{
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
}
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) {
case *types.Basic:
typeFieldTypes = append(typeFieldTypes,
@@ -206,7 +193,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, "underlying", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
)
case *types.Chan:
@@ -225,7 +211,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFieldTypes = append(typeFieldTypes,
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, "methods", methodSetType),
)
case *types.Array:
typeFieldTypes = append(typeFieldTypes,
@@ -250,13 +235,12 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]),
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, "methods", methodSetType),
)
case *types.Interface:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
// TODO: methods
case *types.Signature:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
@@ -307,7 +291,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying
pkgPathPtr, // pkgpath pointer
methodSetValue, // methods
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
}
metabyte |= 1 << 5 // "named" flag
@@ -337,7 +320,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(typ.Elem()),
methodSetValue, // methods
}
case *types.Array:
typeFields = []llvm.Value{
@@ -419,12 +401,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}))
}
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
typeFields = append(typeFields, methodSetValue)
case *types.Interface:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)),
methodSetValue,
}
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: methods
case *types.Signature:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc
@@ -435,10 +414,10 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}, typeFields...)
if hasMethodSet {
typeFields = append([]llvm.Value{
c.getTypeMethodSet(typ),
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType),
}, typeFields...)
}
alignment := c.targetData.TypeAllocSize(c.dataPtrType)
alignment := c.targetData.TypeAllocSize(c.i8ptrType)
if alignment < 4 {
alignment = 4
}
@@ -532,7 +511,8 @@ var basicTypeNames = [...]string{
func getTypeCodeName(t types.Type) (string, bool) {
switch t := t.(type) {
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(), false
@@ -648,7 +628,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
// Construct global value.
globalValue := c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
llvm.ConstArray(c.dataPtrType, signatures),
llvm.ConstArray(c.i8ptrType, signatures),
c.ctx.ConstStruct(wrappers, false),
}, false)
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
@@ -703,25 +683,20 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
assertedType := b.getLLVMType(expr.AssertedType)
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
var commaOk llvm.Value
commaOk := llvm.Value{}
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
// Type assert on interface type.
// This is a call to an interface type assert function.
// The interface lowering pass will define this function by filling it
// with a type switch over all concrete types that implement this
// interface, and returning whether it's one of the matched types.
// This is very different from how interface asserts are implemented in
// 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}, "")
if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
if intf.Empty() {
// intf is the empty interface => no methods
// This type assertion always succeeds, so we can just set commaOk to true.
commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
} else {
// Type assert using interface type with methods.
// This is implemented using a runtime call, which checks that the
// type implements each method of the interface.
// For comparison, here is how the Go compiler does this (which is
// very similar):
// https://research.swtch.com/interfaces
commaOk = b.createRuntimeCall("typeImplementsMethodSet", []llvm.Value{
actualTypeNum,
b.getInterfaceMethodSet(intf),
}, "")
}
} else {
name, _ := getTypeCodeName(expr.AssertedType)
globalName := "reflect/types.typeid:" + name
@@ -797,66 +772,20 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
return strings.Join(methods, "; ")
}
// Get the global that contains an interface method set, creating it if needed.
func (c *compilerContext) getInterfaceMethodSet(t *types.Interface) llvm.Value {
s, _ := getTypeCodeName(t)
methodSetName := s + "$itfmethods"
methodSet := c.mod.NamedFunction(methodSetName)
if methodSet.IsNil() {
var methods []*types.Func
for i := 0; i < t.NumMethods(); i++ {
methods = append(methods, t.Method(i))
}
if len(methods) == 0 {
// This *should* be unreachable: the caller checks whether the
// interface is empty before creating a method set.
panic("unreachable")
}
methodSetValue := c.getMethodSetValue(methods)
methodSet = llvm.AddGlobal(c.mod, methodSetValue.Type(), methodSetName)
methodSet.SetInitializer(methodSetValue)
methodSet.SetGlobalConstant(true)
methodSet.SetLinkage(llvm.LinkOnceODRLinkage)
methodSet.SetAlignment(c.targetData.ABITypeAlignment(methodSetValue.Type()))
methodSet.SetUnnamedAddr(true)
// getInterfaceImplementsFunc returns a declared function that works as a type
// switch. The interface lowering pass will define this function.
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
s, _ := getTypeCodeName(assertedType.Underlying())
fnName := s + ".$typeassert"
llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, 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))
}
return methodSet
}
// Get the method set value that is used in a number of type structs.
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
// Create a sorted list of method names.
var methodRefNames []string
for _, method := range methods {
name := method.Name()
if !token.IsExported(name) {
name = method.Pkg().Path() + "." + name
}
s, _ := getTypeCodeName(method.Type())
methodRefNames = append(methodRefNames, "reflect/types.signature:"+name+":"+s)
}
sort.Strings(methodRefNames)
// Turn this slice of strings in a slice of global variables.
var methodRefValues []llvm.Value
for _, name := range methodRefNames {
value := c.mod.NamedGlobal(name)
if value.IsNil() {
value = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), name)
value.SetInitializer(llvm.ConstNull(c.ctx.Int8Type()))
value.SetGlobalConstant(true)
value.SetLinkage(llvm.LinkOnceODRLinkage)
value.SetAlignment(1)
}
methodRefValues = append(methodRefValues, value)
}
return c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(len(methodRefValues)), false),
llvm.ConstArray(c.dataPtrType, methodRefValues),
}, false)
return llvmFn
}
// getInvokeFunction returns the thunk to call the given interface method. The
@@ -873,7 +802,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
paramTuple = append(paramTuple, sig.Params().At(i))
}
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn)
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
@@ -913,7 +842,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
}
// create wrapper function
paramTypes := append([]llvm.Type{c.dataPtrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
c.addStandardAttributes(wrapper)
+1 -1
View File
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Fall back to a generic error.
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
}
funcRawPtr, funcContext := b.decodeFuncValue(funcValue)
_, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
// Create a new global of type runtime/interrupt.handle. Globals of this
+10 -13
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm"
)
@@ -23,8 +24,6 @@ func (b *builder) defineIntrinsicFunction() {
b.createMemoryCopyImpl()
case name == "runtime.memzero":
b.createMemoryZeroImpl()
case name == "runtime.stacksave":
b.createStackSaveImpl()
case name == "runtime.KeepAlive":
b.createKeepAliveImpl()
case strings.HasPrefix(name, "runtime/volatile.Load"):
@@ -49,9 +48,12 @@ func (b *builder) defineIntrinsicFunction() {
func (b *builder) createMemoryCopyImpl() {
b.createFunctionStart(true)
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
llvmFn := b.mod.NamedFunction(fnName)
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.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
}
var params []llvm.Value
@@ -79,20 +81,15 @@ func (b *builder) createMemoryZeroImpl() {
b.CreateRetVoid()
}
// createStackSaveImpl creates a call to llvm.stacksave.p0 to read the current
// stack pointer.
func (b *builder) createStackSaveImpl() {
b.createFunctionStart(true)
sp := b.readStackPointer()
b.CreateRet(sp)
}
// Return the llvm.memset.p0.i8 function declaration.
func (c *compilerContext) getMemsetFunc() llvm.Value {
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
}
llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
}
return llvmFn
@@ -114,7 +111,7 @@ func (b *builder) createKeepAliveImpl() {
//
// It should be portable to basically everything as the "r" register type
// exists basically everywhere.
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
+46 -50
View File
@@ -20,7 +20,7 @@ import (
//
// This is useful for creating temporary allocas for intrinsics. Don't forget to
// end the lifetime using emitLifetimeEnd after you're done with it.
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, size llvm.Value) {
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
}
@@ -63,45 +63,47 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
// Allocate memory for the packed data.
size := b.targetData.TypeAllocSize(packedType)
if size == 0 {
return llvm.ConstPointerNull(b.dataPtrType)
return llvm.ConstPointerNull(b.i8ptrType)
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
return values[0]
} else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr")
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
// Packed data fits in a pointer, so store it directly inside the
// pointer.
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
// Try to keep this cast in SSA form.
return b.CreateIntToPtr(values[0], b.dataPtrType, "pack.int")
return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int")
}
// Because packedType is a struct and we have to cast it to a *i8, store
// it in a *i8 alloca first and load the *i8 value from there. This is
// effectively a bitcast.
packedAlloc, _ := b.createTemporaryAlloca(b.dataPtrType, "")
packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "")
if size < b.targetData.TypeAllocSize(b.dataPtrType) {
if size < b.targetData.TypeAllocSize(b.i8ptrType) {
// The alloca is bigger than the value that will be stored in it.
// To avoid having some bits undefined, zero the alloca first.
// Hopefully this will get optimized away.
b.CreateStore(llvm.ConstNull(b.dataPtrType), packedAlloc)
b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc)
}
// Store all values in the alloca.
packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "")
b.CreateStore(value, gep)
}
// Load value (the *i8) from the alloca.
result := b.CreateLoad(b.dataPtrType, packedAlloc, "")
result := b.CreateLoad(b.i8ptrType, packedAlloc, "")
// End the lifetime of the alloca, to help the optimizer.
packedPtr := b.CreateBitCast(packedAlloc, b.i8ptrType, "")
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
b.emitLifetimeEnd(packedAlloc, packedSize)
b.emitLifetimeEnd(packedPtr, packedSize)
return result
} else {
@@ -122,22 +124,21 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetLinkage(llvm.InternalLinkage)
return global
return llvm.ConstBitCast(global, b.i8ptrType)
}
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
align := b.targetData.ABITypeAlignment(packedType)
alloc := b.mod.NamedFunction("runtime.alloc")
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
sizeValue,
llvm.ConstNull(b.dataPtrType),
llvm.Undef(b.dataPtrType), // unused context parameter
llvm.ConstNull(b.i8ptrType),
llvm.Undef(b.i8ptrType), // unused context parameter
}, "")
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
if b.NeedsStackObjects {
b.trackPointer(packedAlloc)
b.trackPointer(packedHeapAlloc)
}
packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
// Store all values in the heap pointer.
for i, value := range values {
@@ -150,7 +151,7 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
}
// Return the original heap allocation pointer, which already is an *i8.
return packedAlloc
return packedHeapAlloc
}
}
@@ -159,27 +160,28 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll
packedType := b.ctx.StructType(valueTypes, false)
// Get a correctly-typed pointer to the packed data.
var packedAlloc llvm.Value
needsLifetimeEnd := false
var packedAlloc, packedRawAlloc llvm.Value
size := b.targetData.TypeAllocSize(packedType)
if size == 0 {
// No data to unpack.
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
// A single pointer is always stored directly.
return []llvm.Value{ptr}
} else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
// Packed data stored directly in pointer.
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
// Keep this cast in SSA form.
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
}
// Fallback: load it using an alloca.
packedAlloc, _ = b.createTemporaryAlloca(b.dataPtrType, "unpack.raw.alloc")
b.CreateStore(ptr, packedAlloc)
needsLifetimeEnd = true
packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc")
packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value")
b.CreateStore(packedRawValue, packedRawAlloc)
packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
} else {
// Packed data stored on the heap.
packedAlloc = ptr
// Packed data stored on the heap. Bitcast the passed-in pointer to the
// correct pointer type.
packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
}
// Load each value from the packed data.
values := make([]llvm.Value, len(valueTypes))
@@ -196,9 +198,10 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
values[i] = b.CreateLoad(valueType, gep, "")
}
if needsLifetimeEnd {
if !packedRawAlloc.IsNil() {
allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "")
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
b.emitLifetimeEnd(packedAlloc, allocSize)
b.emitLifetimeEnd(allocPtr, allocSize)
}
return values
}
@@ -250,12 +253,12 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// Do a few checks to see whether we need to generate any object layout
// information at all.
objectSizeBytes := c.targetData.TypeAllocSize(t)
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
pointerSize := c.targetData.TypeAllocSize(c.i8ptrType)
pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
if objectSizeBytes < pointerSize {
// Too small to contain a pointer.
layout := (uint64(1) << 1) | 1
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
}
bitmap := c.getPointerBitmap(t, pos)
if bitmap.BitLen() == 0 {
@@ -263,13 +266,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// 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)
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
}
if objectSizeBytes%uint64(pointerAlignment) != 0 {
// This shouldn't happen except for packed structs, which aren't
// currently used.
c.addError(pos, "internal error: unexpected object size for object with pointer field")
return llvm.ConstNull(c.dataPtrType)
return llvm.ConstNull(c.i8ptrType)
}
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
@@ -294,7 +297,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
// 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)
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
}
// Unfortunately, the object layout is too big to fit in a pointer-sized
@@ -305,7 +308,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
global := c.mod.NamedGlobal(globalName)
if !global.IsNil() {
return global
return llvm.ConstBitCast(global, c.i8ptrType)
}
// Create the global initializer.
@@ -356,13 +359,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
global.AddMetadata(0, diglobal)
}
return global
return llvm.ConstBitCast(global, c.i8ptrType)
}
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
// bigint at the word offset that contains a pointer. This scan is recursive.
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
switch typ.TypeKind() {
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
return big.NewInt(0)
@@ -375,7 +378,7 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
// of type uintptr, but before the LowerFuncValues pass it actually
// contains a pointer (ptrtoint) to a global. This trips up the
// interp package. Therefore, make the id field a pointer for now.
typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
typ = c.ctx.StructType([]llvm.Type{c.i8ptrType, c.i8ptrType}, false)
}
for i, subtyp := range typ.StructElementTypes() {
subptrs := c.getPointerBitmap(subtyp, pos)
@@ -429,9 +432,6 @@ func (c *compilerContext) archFamily() string {
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
return "arm"
}
if arch == "mipsel" {
return "mips"
}
return arch
}
@@ -456,14 +456,10 @@ func (c *compilerContext) isThumb() bool {
// readStackPointer emits a LLVM intrinsic call that returns the current stack
// pointer as an *i8.
func (b *builder) readStackPointer() llvm.Value {
name := "llvm.stacksave.p0"
if llvmutil.Version() < 18 {
name = "llvm.stacksave" // backwards compatibility with LLVM 17 and below
}
stacksave := b.mod.NamedFunction(name)
stacksave := b.mod.NamedFunction("llvm.stacksave")
if stacksave.IsNil() {
fnType := llvm.FunctionType(b.dataPtrType, nil, false)
stacksave = llvm.AddFunction(b.mod, name, fnType)
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
}
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
}
+34 -26
View File
@@ -1,5 +1,5 @@
// Package llvmutil contains utility functions used across multiple compiler
// packages. For example, they may be used by both the compiler package and
// packages. For example, they may be used by both the compiler pacakge and
// transformation packages.
//
// Normally, utility packages are avoided. However, in this case, the utility
@@ -14,6 +14,16 @@ import (
"tinygo.org/x/go-llvm"
)
// Major returns the LLVM major version.
func Major() int {
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil {
// sanity check, should be unreachable
panic("could not parse LLVM version: " + err.Error())
}
return llvmMajor
}
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
// the IR builder is located elsewhere. It assumes that the insert point is
// at the end of the current block.
@@ -31,19 +41,21 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
}
// CreateTemporaryAlloca creates a new alloca in the entry block and adds
// lifetime start information in the IR signalling that the alloca won't be used
// lifetime start infromation in the IR signalling that the alloca won't be used
// before this point.
//
// This is useful for creating temporary allocas for intrinsics. Don't forget to
// end the lifetime using emitLifetimeEnd after you're done with it.
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) {
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
ctx := t.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca = CreateEntryBlockAlloca(builder, t, name)
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
return
}
@@ -52,19 +64,21 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca := CreateEntryBlockAlloca(builder, t, name)
builder.SetInsertPointBefore(inst)
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
fnType, fn := getLifetimeStartFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
if next := llvm.NextInstruction(inst); !next.IsNil() {
builder.SetInsertPointBefore(next)
} else {
builder.SetInsertPointAtEnd(inst.InstructionParent())
}
fnType, fn = getLifetimeEndFunc(mod)
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
return alloca
}
@@ -80,10 +94,13 @@ func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value
// first if it doesn't exist yet.
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
fnName := "llvm.lifetime.start.p0"
if Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.lifetime.start.p0i8"
}
fn := mod.NamedFunction(fnName)
ctx := mod.Context()
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
if fn.IsNil() {
fn = llvm.AddFunction(mod, fnName, fnType)
}
@@ -94,10 +111,13 @@ func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
// first if it doesn't exist yet.
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
fnName := "llvm.lifetime.end.p0"
if Major() < 15 {
fnName = "llvm.lifetime.end.p0i8"
}
fn := mod.NamedFunction(fnName)
ctx := mod.Context()
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
if fn.IsNil() {
fn = llvm.AddFunction(mod, fnName, fnType)
}
@@ -176,7 +196,7 @@ func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llv
return newBlock
}
// AppendToGlobal appends the given values to a global array like llvm.used. The global might
// Append the given values to a global array like llvm.used. The global might
// not exist yet. The values can be any pointer type, they will be cast to i8*.
func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
// Read the existing values in the llvm.used array (if it exists).
@@ -193,26 +213,14 @@ func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
}
// Add the new values.
ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
for _, value := range values {
// Note: the bitcast is necessary to cast AVR function pointers to
// address space 0 pointer types.
usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType))
usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType))
}
// Create a new array (with the old and new values).
usedInitializer := llvm.ConstArray(ptrType, usedValues)
usedInitializer := llvm.ConstArray(i8ptrType, usedValues)
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
used.SetInitializer(usedInitializer)
used.SetLinkage(llvm.AppendingLinkage)
}
// Return the LLVM major version.
func Version() int {
majorStr := strings.Split(llvm.Version, ".")[0]
major, err := strconv.Atoi(majorStr)
if err != nil {
panic("unexpected error while parsing LLVM version: " + err.Error()) // should not happen
}
return major
}
+30 -23
View File
@@ -65,7 +65,7 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// Allocate the memory for the resulting type. Do not zero this memory: it
// will be zeroed by the hashmap get implementation if the key is not
// present in the map.
mapValueAlloca, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
mapValueAlloca, mapValuePtr, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
// We need the map size (with type uintptr) to pass to the hashmap*Get
// functions. This is necessary because those *Get functions are valid on
@@ -82,19 +82,19 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
params := []llvm.Value{m, key, mapValuePtr, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
} else if hashmapIsBinaryKey(keyType) {
// key can be compared with runtime.memequal
// Store the key in an alloca, in the entry block to avoid dynamic stack
// growth.
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, mapKeyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
// Fetch the value from the hashmap.
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
b.emitLifetimeEnd(mapKeyPtr, mapKeySize)
} else {
// Not trivially comparable using memcmp. Make it an interface instead.
itfKey := key
@@ -102,14 +102,14 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// Not already an interface, so convert it to an interface now.
itfKey = b.createMakeInterface(key, origKeyType, pos)
}
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
}
// Load the resulting value from the hashmap. The value is set to the zero
// value if the key doesn't exist in the hashmap.
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
b.emitLifetimeEnd(mapValueAlloca, mapValueAllocaSize)
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
if commaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false))
@@ -124,22 +124,22 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// createMapUpdate updates a map key to a given value, by creating an
// appropriate runtime call.
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
valueAlloca, valuePtr, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
b.CreateStore(value, valueAlloca)
origKeyType := keyType
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, key, valueAlloca}
params := []llvm.Value{m, key, valuePtr}
b.createRuntimeCall("hashmapStringSet", params, "")
} else if hashmapIsBinaryKey(keyType) {
// key can be compared with runtime.memequal
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyAlloca, valueAlloca}
params := []llvm.Value{m, keyPtr, valuePtr}
b.createRuntimeCall("hashmapBinarySet", params, "")
b.emitLifetimeEnd(keyAlloca, keySize)
b.emitLifetimeEnd(keyPtr, keySize)
} else {
// Key is not trivially comparable, so compare it as an interface instead.
itfKey := key
@@ -147,10 +147,10 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
// Not already an interface, so convert it to an interface first.
itfKey = b.createMakeInterface(key, origKeyType, pos)
}
params := []llvm.Value{m, itfKey, valueAlloca}
params := []llvm.Value{m, itfKey, valuePtr}
b.createRuntimeCall("hashmapInterfaceSet", params, "")
}
b.emitLifetimeEnd(valueAlloca, valueSize)
b.emitLifetimeEnd(valuePtr, valueSize)
}
// createMapDelete deletes a key from a map by calling the appropriate runtime
@@ -164,12 +164,12 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
b.createRuntimeCall("hashmapStringDelete", params, "")
return nil
} else if hashmapIsBinaryKey(keyType) {
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyAlloca}
params := []llvm.Value{m, keyPtr}
b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyAlloca, keySize)
b.emitLifetimeEnd(keyPtr, keySize)
return nil
} else {
// Key is not trivially comparable, so compare it as an interface
@@ -225,9 +225,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
}
// Extract the key and value from the map.
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
@@ -238,8 +238,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
}
// End the lifetimes of the allocas, because we're done with them.
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
b.emitLifetimeEnd(mapKeyPtr, mapKeySize)
b.emitLifetimeEnd(mapValuePtr, mapValueSize)
// Construct the *ssa.Next return value: {ok, mapKey, mapValue}
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
@@ -333,7 +333,14 @@ func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
if fieldEndOffset != nextOffset {
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
gepPtr := elemPtr
if gepPtr.Type() != b.i8ptrType {
gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14
}
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "")
if paddingStart.Type() != b.i8ptrType {
paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14
}
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
}
}
+33 -56
View File
@@ -96,7 +96,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// Add an extra parameter as the function context. This context is used in
// closures and bound methods, but should be optimized away when not used.
if !info.exported {
paramInfos = append(paramInfos, paramInfo{llvmType: c.dataPtrType, name: "context", elemSize: 0})
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0})
}
var paramTypes []llvm.Type
@@ -145,18 +145,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
for _, attrName := range []string{"noalias", "nonnull"} {
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
}
// Add attributes to signal to LLVM that this is an allocator function.
// This enables a number of optimizations.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc"))
// Use a special value to indicate the first parameter:
// > allocsize has two integer arguments, but because they're both 32 bits, we can
// > pack them into one 64-bit value, at the cost of making said value
// > nonsensical.
// >
// > In order to do this, we need to reserve one value of the second (optional)
// > allocsize argument to signify "not present."
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff))
if llvmutil.Major() >= 15 { // allockind etc are not available in LLVM 14
// Add attributes to signal to LLVM that this is an allocator
// function. This enables a number of optimizations.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc"))
// Use a special value to indicate the first parameter:
// > allocsize has two integer arguments, but because they're both 32 bits, we can
// > pack them into one 64-bit value, at the cost of making said value
// > nonsensical.
// >
// > In order to do this, we need to reserve one value of the second (optional)
// > allocsize argument to signify "not present."
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff))
}
case "runtime.sliceAppend":
// Appending a slice will only read the to-be-appended slice, it won't
// be modified.
@@ -346,7 +348,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
@@ -360,68 +362,38 @@ func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
} else if f.Signature.Results().Len() == 1 {
result := f.Signature.Results().At(0)
if !isValidWasmType(result.Type(), siteResult) {
if !isValidWasmType(result.Type(), true) {
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
}
}
for _, param := range f.Params {
// Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly.
if !isValidWasmType(param.Type(), siteParam) {
if !isValidWasmType(param.Type(), false) {
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.
//
// 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:
// Check whether the type maps directly to a WebAssembly type, according to:
// https://github.com/golang/go/issues/59149
func isValidWasmType(typ types.Type, site wasmSite) bool {
func isValidWasmType(typ types.Type, isReturn bool) bool {
switch typ := typ.Underlying().(type) {
case *types.Basic:
switch typ.Kind() {
case types.Bool:
return true
case types.Int, types.Uint, types.Int8, types.Uint8, types.Int16, types.Uint16, types.Int32, types.Uint32, types.Int64, types.Uint64:
case types.Int32, types.Uint32, types.Int64, types.Uint64:
return true
case types.Float32, types.Float64:
return true
case types.Uintptr, types.UnsafePointer:
return true
case types.String:
// string flattens to two values, so disallowed as a result
return site == siteParam || site == siteIndirect
}
case *types.Array:
return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect)
case *types.Struct:
if site != siteIndirect {
return false
}
for i := 0; i < typ.NumFields(); i++ {
if !isValidWasmType(typ.Field(i).Type(), siteIndirect) {
return false
case types.UnsafePointer:
if !isReturn {
return true
}
}
return true
case *types.Pointer:
return isValidWasmType(typ.Elem(), siteIndirect)
}
return false
}
type wasmSite int
const (
siteParam wasmSite = iota
siteResult
siteIndirect // pointer or field
)
// getParams returns the function parameters, including the receiver at the
// 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.
@@ -473,10 +445,15 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
if strings.Split(c.Triple, "-")[0] == "x86_64" {
// Required by the ABI.
// The uwtable has two possible values: sync (1) or async (2). We use
// sync because we currently don't use async unwind tables.
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
if llvmutil.Major() < 15 {
// Needed for LLVM 14 support.
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
} else {
// The uwtable has two possible values: sync (1) or async (2). We
// use sync because we currently don't use async unwind tables.
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
}
}
}
+5 -100
View File
@@ -12,8 +12,7 @@ import (
// createRawSyscall creates a system call with the provided system call number
// 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
// return value everywhere).
// result is not further interpreted.
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
num := b.getValue(call.Args[0], getPos(call))
switch {
@@ -34,17 +33,18 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
"{r10}",
"{r8}",
"{r9}",
"{r11}",
"{r12}",
"{r13}",
}[i]
llvmValue := b.getValue(arg, getPos(call))
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
// rcx and r11 are clobbered by the syscall, so make sure they are not used
constraints += ",~{rcx},~{r11}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "386" && b.GOOS == "linux":
// Sources:
// syscall(2) man page
@@ -71,7 +71,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "arm" && b.GOOS == "linux":
// Implement the EABI system call convention for Linux.
// Source: syscall(2) man page.
@@ -104,7 +103,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "arm64" && b.GOOS == "linux":
// Source: syscall(2) man page.
args := []llvm.Value{}
@@ -137,98 +135,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
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 paramter 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:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
@@ -277,7 +183,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
}
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
fn := b.getValue(call.Args[0], getPos(call))
fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "")
// Prepare some functions that will be called later.
setLastError := b.mod.NamedFunction("SetLastError")
@@ -311,7 +217,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
+2 -2
View File
@@ -81,11 +81,11 @@ entry:
%select.send.value = alloca i32, align 4
store i32 1, ptr %select.send.value, align 4
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 8
%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
%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 8
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1
store ptr null, ptr %.repack3, align 4
%select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #4
+3 -3
View File
@@ -25,7 +25,7 @@ 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()
%0 = call ptr @llvm.stacksave()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
@@ -113,7 +113,7 @@ rundefers.end3: ; preds = %rundefers.loophead6
}
; Function Attrs: nocallback nofree nosync nounwind willreturn
declare ptr @llvm.stacksave.p0() #3
declare ptr @llvm.stacksave() #3
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2
@@ -136,7 +136,7 @@ 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()
%0 = call ptr @llvm.stacksave()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
+13 -47
View File
@@ -13,65 +13,31 @@ func implementation() {
type Uint uint32
type S struct {
a [4]uint32
b uintptr
c int
d float32
e float64
}
//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)
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 int
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
// 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 *int32
//
//go:wasmimport modulename invalidparam
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func())
func invalidparam(a int, b string, c []byte, d *int32)
//go:wasmimport modulename validreturn_int32
func validreturn_int32() int32
//go:wasmimport modulename validreturn_int
func validreturn_int() int
//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_unsafe_pointer
func validreturn_unsafe_pointer() unsafe.Pointer
//go:wasmimport modulename validreturn
func validreturn() int32
// ERROR: //go:wasmimport modulename manyreturns: too many return values
//
//go:wasmimport modulename manyreturns
func manyreturns() (int32, int32)
// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
// ERROR: //go:wasmimport modulename invalidreturn: unsupported result type int
//
//go:wasmimport modulename invalidreturn_func
func invalidreturn_func() func()
//go:wasmimport modulename invalidreturn
func invalidreturn() int
// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
// ERROR: //go:wasmimport modulename invalidUnsafePointerReturn: unsupported result type unsafe.Pointer
//
//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
//go:wasmimport modulename invalidUnsafePointerReturn
func invalidUnsafePointerReturn() unsafe.Pointer
+26 -26
View File
@@ -16,13 +16,13 @@ target triple = "wasm32-unknown-wasi"
@main.struct2 = hidden global ptr null, align 4
@main.struct3 = hidden global ptr null, align 4
@main.struct4 = hidden global ptr null, align 4
@main.slice1 = 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.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\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:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128", { i32, [0 x ptr] } zeroinitializer }, 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
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -39,16 +39,16 @@ entry:
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
entry:
%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
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
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
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
store ptr %new3, ptr @main.scalar4, align 4
ret void
@@ -58,13 +58,13 @@ entry:
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
entry:
%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
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
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
store ptr %new2, ptr @main.array3, align 4
ret void
@@ -74,16 +74,16 @@ entry:
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
entry:
%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
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
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-2000000000000001", 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
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-0001", 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
store ptr %new3, ptr @main.struct4, align 4
ret void
@@ -93,7 +93,7 @@ entry:
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
entry:
%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
ret ptr %new
}
@@ -102,21 +102,21 @@ entry:
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
entry:
%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
store ptr %makeslice, ptr @main.slice1, align 4
store ptr %makeslice, ptr @main.slice1, align 8
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), 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
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 8
%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
store ptr %makeslice1, ptr @main.slice2, align 4
store ptr %makeslice1, ptr @main.slice2, align 8
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), 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
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 8
%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
store ptr %makeslice3, ptr @main.slice3, align 4
store ptr %makeslice3, ptr @main.slice3, align 8
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 8
ret void
}
@@ -124,7 +124,7 @@ entry:
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
entry:
%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
store double %v.r, ptr %0, align 8
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
+4 -4
View File
@@ -63,9 +63,9 @@ entry:
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry:
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%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
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
%0 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4
@@ -98,7 +98,7 @@ declare void @runtime.printint32(i32, ptr) #2
; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry:
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
%0 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4
@@ -148,7 +148,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #2
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
%0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4
+9 -9
View File
@@ -21,7 +21,7 @@ entry:
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
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) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
ret void
}
@@ -43,7 +43,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
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) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
ret void
}
@@ -66,17 +66,17 @@ entry:
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry:
%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) #9
%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) #9
store i32 3, ptr %n, align 4
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) #9
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
%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) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
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) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
%2 = load i32, ptr %n, align 4
call void @runtime.printint32(i32 %2, ptr undef) #9
ret void
@@ -106,14 +106,14 @@ declare void @runtime.printint32(i32, ptr) #1
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
%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) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
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) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, ptr undef) #9
ret void
}
@@ -158,7 +158,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #1
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
%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) #9
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
@@ -167,7 +167,7 @@ entry:
store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
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) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
ret void
}
+31 -31
View File
@@ -7,19 +7,15 @@ target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
@"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, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int", { i32, [0 x ptr] } zeroinitializer }, align 4
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:named:error", { i32, [0 x ptr] } zeroinitializer }, 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, { i32, [1 x 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", { 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: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:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 0, 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.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:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, 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, { 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.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:{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: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.typeid:basic:int" = external constant i8
@"interface:{Error:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, align 4
@"interface:{String:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] }, align 4
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -36,8 +32,8 @@ entry:
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
}
@@ -45,8 +41,8 @@ entry:
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
}
@@ -54,8 +50,8 @@ entry:
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
}
@@ -63,15 +59,15 @@ entry:
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
}
; Function Attrs: nounwind
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #5
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -86,7 +82,7 @@ declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
; Function Attrs: nounwind
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{Error:func:{}{basic:string}}$itfmethods", ptr undef) #5
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -96,12 +92,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @runtime.typeImplementsMethodSet(ptr, ptr, ptr) #1
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
; Function Attrs: nounwind
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{String:func:{}{basic:string}}$itfmethods", ptr undef) #5
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -111,30 +107,34 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #5
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
ret i8 %0
}
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #3
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #5
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
%1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
ret %runtime._string %0
}
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #4
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,+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 = { "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 #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #5 = { nounwind }
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() 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,+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,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #7 = { nounwind }
+8 -8
View File
@@ -48,7 +48,7 @@ declare void @runtime.lookupPanic(ptr) #1
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%varargs = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3
store i32 1, ptr %varargs, align 4
%0 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 1
@@ -100,7 +100,7 @@ entry:
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.buf = call ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -122,8 +122,8 @@ entry:
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry
%makeslice.cap = shl nuw i32 %len, 1
%makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.cap = shl i32 %len, 1
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -144,7 +144,7 @@ entry:
slice.next: ; preds = %entry
%makeslice.cap = mul i32 %len, 3
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -164,8 +164,8 @@ entry:
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry
%makeslice.cap = shl nuw i32 %len, 2
%makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.cap = shl i32 %len, 2
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -216,7 +216,7 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #1
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice = call dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
+4 -4
View File
@@ -26,7 +26,7 @@ entry:
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 4
store %main.hasPadding %2, ptr %hashmap.key, align 8
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
@@ -59,7 +59,7 @@ entry:
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 4
store %main.hasPadding %2, ptr %hashmap.key, align 8
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #5
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
@@ -80,7 +80,7 @@ entry:
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
@@ -108,7 +108,7 @@ entry:
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
+2 -3
View File
@@ -51,7 +51,6 @@ func TestCorpus(t *testing.T) {
if *testTarget != "" {
target = *testTarget
}
isWASI := strings.HasPrefix(target, "wasi")
repos, err := loadRepos(*corpus)
if err != nil {
@@ -70,7 +69,7 @@ func TestCorpus(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()
if isWASI && repo.SkipWASI {
if target == "wasi" && repo.SkipWASI {
t.Skip("skip wasi")
}
if repo.Slow && testing.Short() {
@@ -136,7 +135,7 @@ func TestCorpus(t *testing.T) {
t.Run(dir.Pkg, func(t *testing.T) {
t.Parallel()
if isWASI && dir.SkipWASI {
if target == "wasi" && dir.SkipWASI {
t.Skip("skip wasi")
}
if dir.Slow && testing.Short() {
-212
View File
@@ -1,212 +0,0 @@
// Package diagnostics formats compiler errors and prints them in a consistent
// way.
package diagnostics
import (
"bytes"
"fmt"
"go/scanner"
"go/token"
"go/types"
"io"
"path/filepath"
"sort"
"strings"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
)
// A single diagnostic.
type Diagnostic struct {
Pos token.Position
Msg string
}
// One or multiple errors of a particular package.
// It can also represent whole-program errors (like linker errors) that can't
// easily be connected to a single package.
type PackageDiagnostic struct {
ImportPath string // the same ImportPath as in `go list -json`
Diagnostics []Diagnostic
}
// Diagnostics of a whole program. This can include errors belonging to multiple
// packages, or just a single package.
type ProgramDiagnostic []PackageDiagnostic
// CreateDiagnostics reads the underlying errors in the error object and creates
// a set of diagnostics that's sorted and can be readily printed.
func CreateDiagnostics(err error) ProgramDiagnostic {
if err == nil {
return nil
}
// Right now, the compiler will only show errors for the first pacakge that
// fails to build. This is likely to change in the future.
return ProgramDiagnostic{
createPackageDiagnostic(err),
}
}
// Create diagnostics for a single package (though, in practice, it may also be
// used for whole-program diagnostics in some cases).
func createPackageDiagnostic(err error) PackageDiagnostic {
// Extract diagnostics for this package.
var pkgDiag PackageDiagnostic
switch err := err.(type) {
case *builder.MultiError:
if err.ImportPath != "" {
pkgDiag.ImportPath = err.ImportPath
}
for _, err := range err.Errs {
diags := createDiagnostics(err)
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, diags...)
}
case loader.Errors:
if err.Pkg != nil {
pkgDiag.ImportPath = err.Pkg.ImportPath
}
for _, err := range err.Errs {
diags := createDiagnostics(err)
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, diags...)
}
case *interp.Error:
pkgDiag.ImportPath = err.ImportPath
w := &bytes.Buffer{}
fmt.Fprintln(w, err.Error())
if len(err.Inst) != 0 {
fmt.Fprintln(w, err.Inst)
}
if len(err.Traceback) > 0 {
fmt.Fprintln(w, "\ntraceback:")
for _, line := range err.Traceback {
fmt.Fprintln(w, line.Pos.String()+":")
fmt.Fprintln(w, line.Inst)
}
}
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, Diagnostic{
Msg: w.String(),
})
default:
pkgDiag.Diagnostics = createDiagnostics(err)
}
// Sort these diagnostics by file/line/column.
sort.SliceStable(pkgDiag.Diagnostics, func(i, j int) bool {
posI := pkgDiag.Diagnostics[i].Pos
posJ := pkgDiag.Diagnostics[j].Pos
if posI.Filename != posJ.Filename {
return posI.Filename < posJ.Filename
}
if posI.Line != posJ.Line {
return posI.Line < posJ.Line
}
return posI.Column < posJ.Column
})
return pkgDiag
}
// Extract diagnostics from the given error message and return them as a slice
// of errors (which in many cases will just be a single diagnostic).
func createDiagnostics(err error) []Diagnostic {
switch err := err.(type) {
case types.Error:
return []Diagnostic{
{
Pos: err.Fset.Position(err.Pos),
Msg: err.Msg,
},
}
case scanner.Error:
return []Diagnostic{
{
Pos: err.Pos,
Msg: err.Msg,
},
}
case scanner.ErrorList:
var diags []Diagnostic
for _, err := range err {
diags = append(diags, createDiagnostics(*err)...)
}
return diags
case loader.Error:
if err.Err.Pos.Filename != "" {
// Probably a syntax error in a dependency.
return createDiagnostics(err.Err)
} else {
// Probably an "import cycle not allowed" error.
buf := &bytes.Buffer{}
fmt.Fprintln(buf, "package", err.ImportStack[0])
for i := 1; i < len(err.ImportStack); i++ {
pkgPath := err.ImportStack[i]
if i == len(err.ImportStack)-1 {
// last package
fmt.Fprintln(buf, "\timports", pkgPath+": "+err.Err.Error())
} else {
// not the last pacakge
fmt.Fprintln(buf, "\timports", pkgPath)
}
}
return []Diagnostic{
{Msg: buf.String()},
}
}
default:
return []Diagnostic{
{Msg: err.Error()},
}
}
}
// Write program diagnostics to the given writer with 'wd' as the relative
// working directory.
func (progDiag ProgramDiagnostic) WriteTo(w io.Writer, wd string) {
for _, pkgDiag := range progDiag {
pkgDiag.WriteTo(w, wd)
}
}
// Write package diagnostics to the given writer with 'wd' as the relative
// working directory.
func (pkgDiag PackageDiagnostic) WriteTo(w io.Writer, wd string) {
if pkgDiag.ImportPath != "" {
fmt.Fprintln(w, "#", pkgDiag.ImportPath)
}
for _, diag := range pkgDiag.Diagnostics {
diag.WriteTo(w, wd)
}
}
// Write this diagnostic to the given writer with 'wd' as the relative working
// directory.
func (diag Diagnostic) WriteTo(w io.Writer, wd string) {
if diag.Pos == (token.Position{}) {
fmt.Fprintln(w, diag.Msg)
return
}
pos := diag.Pos // make a copy
if !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
// This file is not from the standard library (either the GOROOT or the
// TINYGOROOT). Make the path relative, for easier reading. Ignore any
// errors in the process (falling back to the absolute path).
pos.Filename = tryToMakePathRelative(pos.Filename, wd)
}
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
}
// try to make the path relative to the current working directory. If any error
// occurs, this error is ignored and the absolute path is returned instead.
func tryToMakePathRelative(dir, wd string) string {
if wd == "" {
return dir // working directory not found
}
relpath, err := filepath.Rel(wd, dir)
if err != nil {
return dir
}
return relpath
}
-261
View File
@@ -1,261 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"sort"
"strings"
)
// A pair is a pair of values tracked for both the x and y side of a diff.
// It is typically a pair of line indexes.
type pair struct{ x, y int }
// Diff returns an anchored diff of the two texts old and new
// in the “unified diff” format. If old and new are identical,
// Diff returns a nil slice (no output).
//
// Unix diff implementations typically look for a diff with
// the smallest number of lines inserted and removed,
// which can in the worst case take time quadratic in the
// number of lines in the texts. As a result, many implementations
// either can be made to run for a long time or cut off the search
// after a predetermined amount of work.
//
// In contrast, this implementation looks for a diff with the
// smallest number of “unique” lines inserted and removed,
// where unique means a line that appears just once in both old and new.
// We call this an “anchored diff” because the unique lines anchor
// the chosen matching regions. An anchored diff is usually clearer
// than a standard diff, because the algorithm does not try to
// reuse unrelated blank lines or closing braces.
// The algorithm also guarantees to run in O(n log n) time
// instead of the standard O(n²) time.
//
// Some systems call this approach a “patience diff,” named for
// the “patience sorting” algorithm, itself named for a solitaire card game.
// We avoid that name for two reasons. First, the name has been used
// for a few different variants of the algorithm, so it is imprecise.
// Second, the name is frequently interpreted as meaning that you have
// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm,
// when in fact the algorithm is faster than the standard one.
func Diff(oldName string, old []byte, newName string, new []byte) []byte {
if bytes.Equal(old, new) {
return nil
}
x := lines(old)
y := lines(new)
// Print diff header.
var out bytes.Buffer
fmt.Fprintf(&out, "diff %s %s\n", oldName, newName)
fmt.Fprintf(&out, "--- %s\n", oldName)
fmt.Fprintf(&out, "+++ %s\n", newName)
// Loop over matches to consider,
// expanding each match to include surrounding lines,
// and then printing diff chunks.
// To avoid setup/teardown cases outside the loop,
// tgs returns a leading {0,0} and trailing {len(x), len(y)} pair
// in the sequence of matches.
var (
done pair // printed up to x[:done.x] and y[:done.y]
chunk pair // start lines of current chunk
count pair // number of lines from each side in current chunk
ctext []string // lines for current chunk
)
for _, m := range tgs(x, y) {
if m.x < done.x {
// Already handled scanning forward from earlier match.
continue
}
// Expand matching lines as far as possible,
// establishing that x[start.x:end.x] == y[start.y:end.y].
// Note that on the first (or last) iteration we may (or definitely do)
// have an empty match: start.x==end.x and start.y==end.y.
start := m
for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] {
start.x--
start.y--
}
end := m
for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] {
end.x++
end.y++
}
// Emit the mismatched lines before start into this chunk.
// (No effect on first sentinel iteration, when start = {0,0}.)
for _, s := range x[done.x:start.x] {
ctext = append(ctext, "-"+s)
count.x++
}
for _, s := range y[done.y:start.y] {
ctext = append(ctext, "+"+s)
count.y++
}
// If we're not at EOF and have too few common lines,
// the chunk includes all the common lines and continues.
const C = 3 // number of context lines
if (end.x < len(x) || end.y < len(y)) &&
(end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) {
for _, s := range x[start.x:end.x] {
ctext = append(ctext, " "+s)
count.x++
count.y++
}
done = end
continue
}
// End chunk with common lines for context.
if len(ctext) > 0 {
n := end.x - start.x
if n > C {
n = C
}
for _, s := range x[start.x : start.x+n] {
ctext = append(ctext, " "+s)
count.x++
count.y++
}
done = pair{start.x + n, start.y + n}
// Format and emit chunk.
// Convert line numbers to 1-indexed.
// Special case: empty file shows up as 0,0 not 1,0.
if count.x > 0 {
chunk.x++
}
if count.y > 0 {
chunk.y++
}
fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y)
for _, s := range ctext {
out.WriteString(s)
}
count.x = 0
count.y = 0
ctext = ctext[:0]
}
// If we reached EOF, we're done.
if end.x >= len(x) && end.y >= len(y) {
break
}
// Otherwise start a new chunk.
chunk = pair{end.x - C, end.y - C}
for _, s := range x[chunk.x:end.x] {
ctext = append(ctext, " "+s)
count.x++
count.y++
}
done = end
}
return out.Bytes()
}
// lines returns the lines in the file x, including newlines.
// If the file does not end in a newline, one is supplied
// along with a warning about the missing newline.
func lines(x []byte) []string {
l := strings.SplitAfter(string(x), "\n")
if l[len(l)-1] == "" {
l = l[:len(l)-1]
} else {
// Treat last line as having a message about the missing newline attached,
// using the same text as BSD/GNU diff (including the leading backslash).
l[len(l)-1] += "\n\\ No newline at end of file\n"
}
return l
}
// tgs returns the pairs of indexes of the longest common subsequence
// of unique lines in x and y, where a unique line is one that appears
// once in x and once in y.
//
// The longest common subsequence algorithm is as described in
// Thomas G. Szymanski, “A Special Case of the Maximal Common
// Subsequence Problem,” Princeton TR #170 (January 1975),
// available at https://research.swtch.com/tgs170.pdf.
func tgs(x, y []string) []pair {
// Count the number of times each string appears in a and b.
// We only care about 0, 1, many, counted as 0, -1, -2
// for the x side and 0, -4, -8 for the y side.
// Using negative numbers now lets us distinguish positive line numbers later.
m := make(map[string]int)
for _, s := range x {
if c := m[s]; c > -2 {
m[s] = c - 1
}
}
for _, s := range y {
if c := m[s]; c > -8 {
m[s] = c - 4
}
}
// Now unique strings can be identified by m[s] = -1+-4.
//
// Gather the indexes of those strings in x and y, building:
// xi[i] = increasing indexes of unique strings in x.
// yi[i] = increasing indexes of unique strings in y.
// inv[i] = index j such that x[xi[i]] = y[yi[j]].
var xi, yi, inv []int
for i, s := range y {
if m[s] == -1+-4 {
m[s] = len(yi)
yi = append(yi, i)
}
}
for i, s := range x {
if j, ok := m[s]; ok && j >= 0 {
xi = append(xi, i)
inv = append(inv, j)
}
}
// Apply Algorithm A from Szymanski's paper.
// In those terms, A = J = inv and B = [0, n).
// We add sentinel pairs {0,0}, and {len(x),len(y)}
// to the returned sequence, to help the processing loop.
J := inv
n := len(xi)
T := make([]int, n)
L := make([]int, n)
for i := range T {
T[i] = n + 1
}
for i := 0; i < n; i++ {
k := sort.Search(n, func(k int) bool {
return T[k] >= J[i]
})
T[k] = J[i]
L[i] = k + 1
}
k := 0
for _, v := range L {
if k < v {
k = v
}
}
seq := make([]pair, 2+k)
seq[1+k] = pair{len(x), len(y)} // sentinel at end
lastj := n
for i := n - 1; i >= 0; i-- {
if L[i] == k && J[i] < lastj {
seq[k] = pair{xi[i], yi[J[i]]}
k--
}
}
seq[0] = pair{0, 0} // sentinel at start
return seq
}
+1 -1
View File
@@ -16,7 +16,7 @@ I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help
help:
@echo "Please use \`$(MAKE) <target>' where <target> is one of"
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
-121
View File
@@ -1,121 +0,0 @@
package main
import (
"bytes"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
)
// Test the error messages of the TinyGo compiler.
func TestErrors(t *testing.T) {
for _, name := range []string{
"cgo",
"compiler",
"interp",
"loader-importcycle",
"loader-invaliddep",
"loader-invalidpackage",
"loader-nopackage",
"optimizer",
"syntax",
"types",
} {
t.Run(name, func(t *testing.T) {
testErrorMessages(t, "./testdata/errors/"+name+".go")
})
}
}
func testErrorMessages(t *testing.T, filename string) {
// Parse expected error messages.
expected := readErrorMessages(t, filename)
// Try to build a binary (this should fail with an error).
tmpdir := t.TempDir()
err := Build(filename, tmpdir+"/out", &compileopts.Options{
Target: "wasip1",
Semaphore: sema,
InterpTimeout: 180 * time.Second,
Debug: true,
VerifyIR: true,
Opt: "z",
})
if err == nil {
t.Fatal("expected to get a compiler error")
}
// Get the full ./testdata/errors directory.
wd, absErr := filepath.Abs("testdata/errors")
if absErr != nil {
t.Fatal(absErr)
}
// Write error message out as plain text.
var buf bytes.Buffer
diagnostics.CreateDiagnostics(err).WriteTo(&buf, wd)
actual := strings.TrimRight(buf.String(), "\n")
// Check whether the error is as expected.
if !matchErrors(t, expected, actual) {
t.Errorf("expected error:\n%s\ngot:\n%s", indentText(expected, "> "), indentText(actual, "> "))
}
}
func matchErrors(t *testing.T, pattern, actual string) bool {
patternLines := strings.Split(pattern, "\n")
actualLines := strings.Split(actual, "\n")
if len(patternLines) != len(actualLines) {
return false
}
for i, patternLine := range patternLines {
indices := regexp.MustCompile(`\{\{.*?\}\}`).FindAllStringIndex(patternLine, -1)
patternParts := []string{"^"}
lastStop := 0
for _, startstop := range indices {
start := startstop[0]
stop := startstop[1]
patternParts = append(patternParts,
regexp.QuoteMeta(patternLine[lastStop:start]),
patternLine[start+2:stop-2])
lastStop = stop
}
patternParts = append(patternParts, regexp.QuoteMeta(patternLine[lastStop:]), "$")
pattern := strings.Join(patternParts, "")
re, err := regexp.Compile(pattern)
if err != nil {
t.Fatalf("could not compile regexp for %#v: %v", patternLine, err)
}
if !re.MatchString(actualLines[i]) {
return false
}
}
return true
}
// Indent the given text with a given indentation string.
func indentText(text, indent string) string {
return indent + strings.ReplaceAll(text, "\n", "\n"+indent)
}
// Read "// ERROR:" prefixed messages from the given file.
func readErrorMessages(t *testing.T, file string) string {
data, err := os.ReadFile(file)
if err != nil {
t.Fatal("could not read input file:", err)
}
var errors []string
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "// ERROR: ") {
errors = append(errors, strings.TrimRight(line[len("// ERROR: "):], "\r\n"))
}
}
return strings.Join(errors, "\n")
}
Generated
-60
View File
@@ -1,60 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1703068421,
"narHash": "sha256-WSw5Faqlw75McIflnl5v7qVD/B3S2sLh+968bpOGrWA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d65bceaee0fb1e64363f7871bc43dc1c6ecad99f",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-23.11",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
-85
View File
@@ -1,85 +0,0 @@
# A Nix flake file, mainly intended for developing TinyGo.
# You can download Nix here, for use on your Linux or macOS system:
# https://nixos.org/download.html
# After you have installed Nix, you can enter the development environment as
# follows:
#
# nix develop
#
# This drops you into a bash shell, where you can install TinyGo simply using
# the following command:
#
# go install
#
# That's all! Assuming you've set up your $PATH correctly, you can now use the
# tinygo command as usual:
#
# tinygo version
#
# But you'll need a bit more to make TinyGo actually able to compile code:
#
# make llvm-source # fetch compiler-rt
# git submodule update --init # fetch lots of other libraries and SVD files
# make gen-device -j4 # build src/device/*/*.go files
# make wasi-libc # build support for wasi/wasm
#
# With this, you should have an environment that can compile anything - except
# for the Xtensa architecture (ESP8266/ESP32) because support for that lives in
# a separate LLVM fork.
#
# You can also do many other things from this environment. Building and flashing
# should work as you're used to: it's not a VM or container so there are no
# access restrictions and you're running in the same host environment - just
# with a slightly different set of tools available.
{
inputs = {
# Use a recent stable release, but fix the version to make it reproducible.
# This version should be updated from time to time.
nixpkgs.url = "nixpkgs/nixos-23.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
with pkgs;
{
devShells.default = mkShell {
buildInputs = [
# These dependencies are required for building tinygo (go install).
go
llvmPackages_17.llvm
llvmPackages_17.libclang
# Additional dependencies needed at runtime, for building and/or
# flashing.
llvmPackages_17.lld
avrdude
binaryen
# Additional dependencies needed for on-chip debugging.
# These tools are rather big (especially GDB) and not frequently
# used, so are commented out. On-chip debugging is still possible if
# these tools are available in the host environment.
#gdb
#openocd
];
shellHook= ''
# Configure CLANG, LLVM_AR, and LLVM_NM for `make wasi-libc`.
# Without setting these explicitly, Homebrew versions might be used
# or the default `ar` and `nm` tools might be used (which don't
# support wasi).
export CLANG="clang-17 -resource-dir ${llvmPackages_17.clang.cc.lib}/lib/clang/17"
export LLVM_AR=llvm-ar
export LLVM_NM=llvm-nm
# Make `make smoketest` work (the default is `md5`, while Nix only
# has `md5sum`).
export MD5SUM=md5sum
# Ugly hack to make the Clang resources directory available.
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_17.clang.cc.lib}/lib/clang/17\" -tags=llvm17"
'';
};
}
);
}
+7 -21
View File
@@ -1,47 +1,33 @@
module github.com/tinygo-org/tinygo
go 1.19
go 1.18
require (
github.com/aykevl/go-wasm v0.0.2-0.20240312204833-50275154210c
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
github.com/chromedp/chromedp v0.7.6
github.com/client9/misspell v0.3.4
github.com/gofrs/flock v0.8.1
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-tty v0.0.4
github.com/mgechev/revive v1.3.7
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
go.bug.st/serial v1.6.0
golang.org/x/net v0.26.0
golang.org/x/sys v0.21.0
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504
golang.org/x/sys v0.11.0
golang.org/x/tools v0.12.0
gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc
)
require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/chavacava/garif v0.1.0 // indirect
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/spf13/afero v1.11.0 // indirect
golang.org/x/text v0.16.0 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
)
+18 -59
View File
@@ -1,11 +1,7 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/aykevl/go-wasm v0.0.2-0.20240312204833-50275154210c h1:4T0Vj1UkGgcpkRrmn7SbokebnlfxJcMZPgWtOYACAAA=
github.com/aykevl/go-wasm v0.0.2-0.20240312204833-50275154210c/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc h1:Yp49g+qqgQRPk/gcRSmAsXgnT16XPJ6Y5JM1poc6gYM=
github.com/aykevl/go-wasm v0.0.2-0.20220616010729-4a0a888aebdc/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee h1:+SFdIVfQpG0s0DHYzou0kgfE0n0ZjKPwbiRJsXrZegU=
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
@@ -13,17 +9,9 @@ github.com/chromedp/chromedp v0.7.6 h1:2juGaktzjwULlsn+DnvIZXFUckEp5xs+GOBroaea+
github.com/chromedp/chromedp v0.7.6/go.mod h1:ayT4YU/MGAALNfOg9gNrpGSAdnU51PMx+FCeuT1iXzo=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
@@ -43,68 +31,39 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E=
github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE=
github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5 h1:1SoBaSPudixRecmlHXb/GxmaD3fLMtHIDN13QujwQuc=
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504 h1:MMsD8mMfluf/578+3wrTn22pjI/Xkzm+gPW47SYfspY=
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8 h1:bLsZXRUBavt++CJlMN7sppNziqu3LyamESLhFJcpqFQ=
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc h1:IVX1dqCX3c88P7iEMBtz1xCAM4UIqCMgbqHdSefBaWE=
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+5 -110
View File
@@ -14,8 +14,6 @@ import (
"runtime"
"strings"
"sync"
"tinygo.org/x/go-llvm"
)
// Keys is a slice of all available environment variable keys.
@@ -35,20 +33,11 @@ func init() {
}
}
// Set to true if we're linking statically against LLVM.
var hasBuiltinTools = false
// TINYGOROOT is the path to the final location for checking tinygo files. If
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
// directory.
var TINYGOROOT string
// If a particular Clang resource dir must always be used and TinyGo can't
// figure out the directory using heuristics, this global can be set using a
// linker flag.
// This is needed for Nix.
var clangResourceDir string
// Variables read from a `go env` command invocation.
var goEnvVars struct {
GOPATH string
@@ -142,8 +131,11 @@ func Get(name string) string {
}
return filepath.Join(dir, "tinygo")
case "CGO_ENABLED":
// Always enable CGo. It is required by a number of targets, including
// macOS and the rp2040.
val := os.Getenv("CGO_ENABLED")
if val == "1" || val == "0" {
return val
}
// Default to enabling CGo.
return "1"
case "TINYGOROOT":
return sourceDir()
@@ -159,11 +151,6 @@ func Get(name string) string {
}
return findWasmOpt()
case "WASMTOOLS":
if path := os.Getenv("WASMTOOLS"); path != "" {
return path
}
return "wasm-tools"
default:
return ""
}
@@ -297,95 +284,3 @@ func isSourceDir(root string) bool {
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
return err == nil
}
// ClangResourceDir returns the clang resource dir if available. This is the
// -resource-dir flag. If it isn't available, an empty string is returned and
// -resource-dir should be left unset.
// The libclang flag must be set if the resource dir is read for use by
// libclang.
// In that case, the resource dir is always returned (even when linking
// dynamically against LLVM) because libclang always needs this directory.
func ClangResourceDir(libclang bool) string {
if clangResourceDir != "" {
// The resource dir is forced to a particular value at build time.
// This is needed on Nix for example, where Clang and libclang don't
// know their own resource dir.
// Also see:
// https://discourse.nixos.org/t/why-is-the-clang-resource-dir-split-in-a-separate-package/34114
return clangResourceDir
}
if !hasBuiltinTools && !libclang {
// Using external tools, so the resource dir doesn't need to be
// specified. Clang knows where to find it.
return ""
}
// Check whether we're running from a TinyGo release directory.
// This is the case for release binaries on GitHub.
root := Get("TINYGOROOT")
releaseHeaderDir := filepath.Join(root, "lib", "clang")
if _, err := os.Stat(releaseHeaderDir); !errors.Is(err, fs.ErrNotExist) {
return releaseHeaderDir
}
if hasBuiltinTools {
// We are statically linked to LLVM.
// Check whether we're running from the source directory.
// This typically happens when TinyGo was built using `make` as part of
// development.
llvmMajor := strings.Split(llvm.Version, ".")[0]
buildResourceDir := filepath.Join(root, "llvm-build", "lib", "clang", llvmMajor)
if _, err := os.Stat(buildResourceDir); !errors.Is(err, fs.ErrNotExist) {
return buildResourceDir
}
} else {
// We use external tools, either when installed using `go install` or
// when packaged in a Linux distribution (Linux distros typically prefer
// dynamic linking).
// Try to detect the system clang resources directory.
resourceDir := findSystemClangResources(root)
if resourceDir != "" {
return resourceDir
}
}
// Resource directory not found.
return ""
}
// Find the Clang resource dir on this particular system.
// Return the empty string when they aren't found.
func findSystemClangResources(TINYGOROOT string) string {
llvmMajor := strings.Split(llvm.Version, ".")[0]
switch runtime.GOOS {
case "linux", "android":
// Header files are typically stored in /usr/lib/clang/<version>/include.
// Tested on Fedora 39, Debian 12, and Arch Linux.
path := filepath.Join("/usr/lib/clang", llvmMajor)
_, err := os.Stat(filepath.Join(path, "include", "stdint.h"))
if err == nil {
return path
}
case "darwin":
// This assumes a Homebrew installation, like in builder/commands.go.
var prefix string
switch runtime.GOARCH {
case "amd64":
prefix = "/usr/local/opt/llvm@" + llvmMajor
case "arm64":
prefix = "/opt/homebrew/opt/llvm@" + llvmMajor
default:
return "" // very unlikely for now
}
path := fmt.Sprintf("%s/lib/clang/%s", prefix, llvmMajor)
_, err := os.Stat(path + "/include/stdint.h")
if err == nil {
return path
}
}
// Could not find it.
return ""
}
-7
View File
@@ -1,7 +0,0 @@
//go:build byollvm
package goenv
func init() {
hasBuiltinTools = true
}
+1 -11
View File
@@ -9,7 +9,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const version = "0.33.0-dev"
const Version = "0.30.0"
var (
// This variable is set at build time using -ldflags parameters.
@@ -17,16 +17,6 @@ var (
GitSha1 string
)
// Return TinyGo version, either in the form 0.30.0 or as a development version
// (like 0.30.0-dev-abcd012).
func Version() string {
v := version
if strings.HasSuffix(version, "-dev") && GitSha1 != "" {
v += "-" + GitSha1
}
return v
}
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
func GetGorootVersion() (major, minor int, err error) {
-13
View File
@@ -1,13 +0,0 @@
//go:build tools
// Install tools specified in go.mod.
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
package tools
import (
_ "github.com/client9/misspell"
_ "github.com/mgechev/revive"
)
//go:generate go install github.com/client9/misspell/cmd/misspell
//go:generate go install github.com/mgechev/revive
-5
View File
@@ -1,5 +0,0 @@
# wasm-tools directory
This directory has a separate `go.mod` file because the `wasm-tools-go` module requires Go 1.22, while TinyGo itself supports Go 1.19.
When the minimum Go version for TinyGo is 1.22, this directory can be folded into `internal/tools` and the `go.mod` and `go.sum` files deleted.
-12
View File
@@ -1,12 +0,0 @@
module github.com/tinygo-org/tinygo/internal/tools
go 1.22.4
require github.com/ydnar/wasm-tools-go v0.1.4
require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/urfave/cli/v3 v3.0.0-alpha9 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.19.0 // indirect
)
-25
View File
@@ -1,25 +0,0 @@
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo=
github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/ydnar/wasm-tools-go v0.1.4 h1:+25WqBj0AhLx8OFvZvrs7bQO6L3WtQ7t6JzQEYsXQb8=
github.com/ydnar/wasm-tools-go v0.1.4/go.mod h1:lQfv2Tde3tRgZDSYriro0EmdSHzP1mrHPMmYNahSS/g=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-11
View File
@@ -1,11 +0,0 @@
//go:build tools
// Install tools specified in go.mod.
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
package tools
import (
_ "github.com/ydnar/wasm-tools-go/cmd/wit-bindgen-go"
)
//go:generate go install github.com/ydnar/wasm-tools-go/cmd/wit-bindgen-go
+2 -2
View File
@@ -60,10 +60,10 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
pos := getPosition(inst.llvmInst)
return &Error{
ImportPath: r.pkgName,
Inst: inst.llvmInst.String(),
Inst: inst.String(),
Pos: pos,
Err: err,
Traceback: []ErrorLine{{pos, inst.llvmInst.String()}},
Traceback: []ErrorLine{{pos, inst.String()}},
}
}
+6 -5
View File
@@ -21,7 +21,7 @@ type runner struct {
targetData llvm.TargetData
builder llvm.Builder
pointerSize uint32 // cached pointer size from the TargetData
dataPtrType llvm.Type // often used type so created in advance
i8ptrType llvm.Type // often used type so created in advance
uintptrType llvm.Type // equivalent to uintptr in Go
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
debug bool // log debug messages
@@ -46,9 +46,9 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
timeout: timeout,
}
r.pointerSize = uint32(r.targetData.PointerSize())
r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
r.uintptrType = mod.Context().IntType(r.targetData.PointerSize() * 8)
r.maxAlign = r.targetData.PrefTypeAlignment(r.dataPtrType) // assume pointers are maximally aligned (this is not always the case)
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
return &r
}
@@ -126,7 +126,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
mem.revert()
// Create a call to the package initializer (which was
// previously deleted).
i8undef := llvm.Undef(r.dataPtrType)
i8undef := llvm.Undef(r.i8ptrType)
r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "")
// Make sure that any globals touched by the package
// initializer, won't be accessed by later package initializers.
@@ -174,7 +174,8 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
// TODO: copy debug info, unnamed_addr, ...
obj.llvmGlobal.ReplaceAllUsesWith(newGlobal)
bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type())
obj.llvmGlobal.ReplaceAllUsesWith(bitcast)
name := obj.llvmGlobal.Name()
obj.llvmGlobal.EraseFromParentAsGlobal()
newGlobal.SetName(name)
+6 -3
View File
@@ -77,9 +77,12 @@ func runTest(t *testing.T, pathPrefix string) {
}
// Run some cleanup passes to get easy-to-read outputs.
to := llvm.NewPassBuilderOptions()
defer to.Dispose()
mod.RunPasses("globalopt,dse,adce", llvm.TargetMachine{}, to)
pm := llvm.NewPassManager()
defer pm.Dispose()
pm.AddGlobalOptimizerPass()
pm.AddDeadStoreEliminationPass()
pm.AddAggressiveDCEPass()
pm.Run(mod)
// Read the expected output IR.
out, err := os.ReadFile(pathPrefix + ".out.ll")

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